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