xref: /freebsd/libexec/bootpd/dovend.c (revision 2008043f386721d58158e37e0d7e50df8095942d)
1 /*
2  * dovend.c : Inserts all but the first few vendor options.
3  */
4 
5 #include <sys/types.h>
6 
7 #include <netinet/in.h>
8 #include <arpa/inet.h>			/* inet_ntoa */
9 
10 #include <stdlib.h>
11 #include <stdio.h>
12 #include <string.h>
13 #include <errno.h>
14 #include <syslog.h>
15 
16 #include "bootp.h"
17 #include "bootpd.h"
18 #include "report.h"
19 #include "dovend.h"
20 
21 PRIVATE int insert_generic(struct shared_bindata *, byte **, int *);
22 
23 /*
24  * Insert the 2nd part of the options into an option buffer.
25  * Return amount of space used.
26  *
27  * This inserts everything EXCEPT:
28  *   magic cookie, subnet mask, gateway, bootsize, extension file
29  * Those are handled separately (in bootpd.c) to allow this function
30  * to be shared between bootpd and bootpef.
31  *
32  * When an "extension file" is in use, the options inserted by
33  * this function go into the exten_file, not the bootp response.
34  */
35 
36 int
37 dovend_rfc1497(struct host *hp, byte *buf, int len)
38 {
39 	int bytesleft = len;
40 	byte *vp = buf;
41 
42 	static const char noroom[] = "%s: No room for \"%s\" option";
43 #define	NEED(LEN, MSG) do                       \
44 		if (bytesleft < (LEN)) {         	    \
45 			report(LOG_NOTICE, noroom,          \
46 				   hp->hostname->string, MSG);  \
47 			return (vp - buf);                  \
48 		} while (0)
49 
50 	/*
51 	 * Note that the following have already been inserted:
52 	 *   magic_cookie, subnet_mask, gateway, bootsize
53 	 *
54 	 * The remaining options are inserted in order of importance.
55 	 * (Of course the importance of each is a matter of opinion.)
56 	 * The option insertion order should probably be configurable.
57 	 *
58 	 * This is the order used in the NetBSD version.  Can anyone
59 	 * explain why the time_offset and swap_server are first?
60 	 * Also, why is the hostname so far down the list?  -gwr
61 	 */
62 
63 	if (hp->flags.time_offset) {
64 		NEED(6, "to");
65 		*vp++ = TAG_TIME_OFFSET;/* -1 byte  */
66 		*vp++ = 4;				/* -1 byte  */
67 		insert_u_long(htonl(hp->time_offset), &vp);	/* -4 bytes */
68 		bytesleft -= 6;
69 	}
70 	/*
71 	 * swap server, root path, dump path
72 	 */
73 	if (hp->flags.swap_server) {
74 		NEED(6, "sw");
75 		/* There is just one SWAP_SERVER, so it is not an iplist. */
76 		*vp++ = TAG_SWAP_SERVER;/* -1 byte  */
77 		*vp++ = 4;				/* -1 byte  */
78 		insert_u_long(hp->swap_server.s_addr, &vp);	/* -4 bytes */
79 		bytesleft -= 6;			/* Fix real count */
80 	}
81 	if (hp->flags.root_path) {
82 		/*
83 		 * Check for room for root_path.  Add 2 to account for
84 		 * TAG_ROOT_PATH and length.
85 		 */
86 		len = strlen(hp->root_path->string);
87 		NEED((len + 2), "rp");
88 		*vp++ = TAG_ROOT_PATH;
89 		*vp++ = (byte) (len & 0xFF);
90 		bcopy(hp->root_path->string, vp, len);
91 		vp += len;
92 		bytesleft -= len + 2;
93 	}
94 	if (hp->flags.dump_file) {
95 		/*
96 		 * Check for room for dump_file.  Add 2 to account for
97 		 * TAG_DUMP_FILE and length.
98 		 */
99 		len = strlen(hp->dump_file->string);
100 		NEED((len + 2), "df");
101 		*vp++ = TAG_DUMP_FILE;
102 		*vp++ = (byte) (len & 0xFF);
103 		bcopy(hp->dump_file->string, vp, len);
104 		vp += len;
105 		bytesleft -= len + 2;
106 	}
107 	/*
108 	 * DNS server and domain
109 	 */
110 	if (hp->flags.domain_server) {
111 		if (insert_ip(TAG_DOMAIN_SERVER,
112 					  hp->domain_server,
113 					  &vp, &bytesleft))
114 			NEED(8, "ds");
115 	}
116 	if (hp->flags.domain_name) {
117 		/*
118 		 * Check for room for domain_name.  Add 2 to account for
119 		 * TAG_DOMAIN_NAME and length.
120 		 */
121 		len = strlen(hp->domain_name->string);
122 		NEED((len + 2), "dn");
123 		*vp++ = TAG_DOMAIN_NAME;
124 		*vp++ = (byte) (len & 0xFF);
125 		bcopy(hp->domain_name->string, vp, len);
126 		vp += len;
127 		bytesleft -= len + 2;
128 	}
129 	/*
130 	 * NIS (YP) server and domain
131 	 */
132 	if (hp->flags.nis_server) {
133 		if (insert_ip(TAG_NIS_SERVER,
134 					  hp->nis_server,
135 					  &vp, &bytesleft))
136 			NEED(8, "ys");
137 	}
138 	if (hp->flags.nis_domain) {
139 		/*
140 		 * Check for room for nis_domain.  Add 2 to account for
141 		 * TAG_NIS_DOMAIN and length.
142 		 */
143 		len = strlen(hp->nis_domain->string);
144 		NEED((len + 2), "yn");
145 		*vp++ = TAG_NIS_DOMAIN;
146 		*vp++ = (byte) (len & 0xFF);
147 		bcopy(hp->nis_domain->string, vp, len);
148 		vp += len;
149 		bytesleft -= len + 2;
150 	}
151 	/* IEN 116 name server */
152 	if (hp->flags.name_server) {
153 		if (insert_ip(TAG_NAME_SERVER,
154 					  hp->name_server,
155 					  &vp, &bytesleft))
156 			NEED(8, "ns");
157 	}
158 	if (hp->flags.rlp_server) {
159 		if (insert_ip(TAG_RLP_SERVER,
160 					  hp->rlp_server,
161 					  &vp, &bytesleft))
162 			NEED(8, "rl");
163 	}
164 	/* Time server (RFC 868) */
165 	if (hp->flags.time_server) {
166 		if (insert_ip(TAG_TIME_SERVER,
167 					  hp->time_server,
168 					  &vp, &bytesleft))
169 			NEED(8, "ts");
170 	}
171 	/* NTP (time) Server (RFC 1129) */
172 	if (hp->flags.ntp_server) {
173 		if (insert_ip(TAG_NTP_SERVER,
174 					  hp->ntp_server,
175 					  &vp, &bytesleft))
176 			NEED(8, "nt");
177 	}
178 	/*
179 	 * I wonder:  If the hostname were "promoted" into the BOOTP
180 	 * response part, might these "extension" files possibly be
181 	 * shared between several clients?
182 	 *
183 	 * Also, why not just use longer BOOTP packets with all the
184 	 * additional length used as option data.  This bootpd version
185 	 * already supports that feature by replying with the same
186 	 * packet length as the client request packet. -gwr
187 	 */
188 	if (hp->flags.name_switch && hp->flags.send_name) {
189 		/*
190 		 * Check for room for hostname.  Add 2 to account for
191 		 * TAG_HOST_NAME and length.
192 		 */
193 		len = strlen(hp->hostname->string);
194 #if 0
195 		/*
196 		 * XXX - Too much magic.  The user can always set the hostname
197 		 * to the short version in the bootptab file. -gwr
198 		 */
199 		if ((len + 2) > bytesleft) {
200 			/*
201 			 * Not enough room for full (domain-qualified) hostname, try
202 			 * stripping it down to just the first field (host).
203 			 */
204 			char *tmpstr = hp->hostname->string;
205 			len = 0;
206 			while (*tmpstr && (*tmpstr != '.')) {
207 				tmpstr++;
208 				len++;
209 			}
210 		}
211 #endif
212 		NEED((len + 2), "hn");
213 		*vp++ = TAG_HOST_NAME;
214 		*vp++ = (byte) (len & 0xFF);
215 		bcopy(hp->hostname->string, vp, len);
216 		vp += len;
217 		bytesleft -= len + 2;
218 	}
219 	/*
220 	 * The rest of these are less important, so they go last.
221 	 */
222 	if (hp->flags.lpr_server) {
223 		if (insert_ip(TAG_LPR_SERVER,
224 					  hp->lpr_server,
225 					  &vp, &bytesleft))
226 			NEED(8, "lp");
227 	}
228 	if (hp->flags.cookie_server) {
229 		if (insert_ip(TAG_COOKIE_SERVER,
230 					  hp->cookie_server,
231 					  &vp, &bytesleft))
232 			NEED(8, "cs");
233 	}
234 	if (hp->flags.log_server) {
235 		if (insert_ip(TAG_LOG_SERVER,
236 					  hp->log_server,
237 					  &vp, &bytesleft))
238 			NEED(8, "lg");
239 	}
240 	/*
241 	 * XXX - Add new tags here (to insert options)
242 	 */
243 	if (hp->flags.generic) {
244 		if (insert_generic(hp->generic, &vp, &bytesleft))
245 			NEED(64, "(generic)");
246 	}
247 	/*
248 	 * The end marker is inserted by the caller.
249 	 */
250 	return (vp - buf);
251 #undef	NEED
252 }								/* dovend_rfc1497 */
253 
254 
255 
256 /*
257  * Insert a tag value, a length value, and a list of IP addresses into the
258  * memory buffer indirectly pointed to by "dest".  "tag" is the RFC1048 tag
259  * number to use, "iplist" is a pointer to a list of IP addresses
260  * (struct in_addr_list), and "bytesleft" points to an integer which
261  * indicates the size of the "dest" buffer.
262  *
263  * Return zero if everything fits.
264  *
265  * This is used to fill the vendor-specific area of a bootp packet in
266  * conformance to RFC1048.
267  */
268 
269 int
270 insert_ip(byte tag, struct in_addr_list *iplist, byte **dest, int *bytesleft)
271 {
272 	struct in_addr *addrptr;
273 	unsigned addrcount = 1;
274 	byte *d;
275 
276 	if (iplist == NULL)
277 		return (0);
278 
279 	if (*bytesleft >= 6) {
280 		d = *dest;				/* Save pointer for later */
281 		**dest = tag;
282 		(*dest) += 2;
283 		(*bytesleft) -= 2;		/* Account for tag and length */
284 		addrptr = iplist->addr;
285 		addrcount = iplist->addrcount;
286 		while ((*bytesleft >= 4) && (addrcount > 0)) {
287 			insert_u_long(addrptr->s_addr, dest);
288 			addrptr++;
289 			addrcount--;
290 			(*bytesleft) -= 4;	/* Four bytes per address */
291 		}
292 		d[1] = (byte) ((*dest - d - 2) & 0xFF);
293 	}
294 	return (addrcount);
295 }
296 
297 
298 
299 /*
300  * Insert generic data into a bootp packet.  The data is assumed to already
301  * be in RFC1048 format.  It is inserted using a first-fit algorithm which
302  * attempts to insert as many tags as possible.  Tags and data which are
303  * too large to fit are skipped; any remaining tags are tried until they
304  * have all been exhausted.
305  * Return zero if everything fits.
306  */
307 
308 static int
309 insert_generic(struct shared_bindata *gendata, byte **buff, int *bytesleft)
310 {
311 	byte *srcptr;
312 	int length, numbytes;
313 	int skipped = 0;
314 
315 	if (gendata == NULL)
316 		return (0);
317 
318 	srcptr = gendata->data;
319 	length = gendata->length;
320 	while ((length > 0) && (*bytesleft > 0)) {
321 		switch (*srcptr) {
322 		case TAG_END:
323 			length = 0;			/* Force an exit on next iteration */
324 			break;
325 		case TAG_PAD:
326 			*(*buff)++ = *srcptr++;
327 			(*bytesleft)--;
328 			length--;
329 			break;
330 		default:
331 			numbytes = srcptr[1] + 2;
332 			if (*bytesleft < numbytes)
333 				skipped += numbytes;
334 			else {
335 				bcopy(srcptr, *buff, numbytes);
336 				(*buff) += numbytes;
337 				(*bytesleft) -= numbytes;
338 			}
339 			srcptr += numbytes;
340 			length -= numbytes;
341 			break;
342 		}
343 	} /* while */
344 	return (skipped);
345 }
346 
347 /*
348  * Insert the unsigned long "value" into memory starting at the byte
349  * pointed to by the byte pointer (*dest).  (*dest) is updated to
350  * point to the next available byte.
351  *
352  * Since it is desirable to internally store network addresses in network
353  * byte order (in struct in_addr's), this routine expects longs to be
354  * passed in network byte order.
355  *
356  * However, due to the nature of the main algorithm, the long must be in
357  * host byte order, thus necessitating the use of ntohl() first.
358  */
359 
360 void
361 insert_u_long(u_int32 value, byte **dest)
362 {
363 	byte *temp;
364 	int n;
365 
366 	value = ntohl(value);		/* Must use host byte order here */
367 	temp = (*dest += 4);
368 	for (n = 4; n > 0; n--) {
369 		*--temp = (byte) (value & 0xFF);
370 		value >>= 8;
371 	}
372 	/* Final result is network byte order */
373 }
374 
375 /*
376  * Local Variables:
377  * tab-width: 4
378  * c-indent-level: 4
379  * c-argdecl-indent: 4
380  * c-continued-statement-offset: 4
381  * c-continued-brace-offset: -4
382  * c-label-offset: -4
383  * c-brace-offset: 0
384  * End:
385  */
386