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