xref: /freebsd/sbin/dhclient/options.c (revision f9bc005b8ef3b507a5ecbc2d7fe4411ef70eff38)
1 /*	$OpenBSD: options.c,v 1.15 2004/12/26 03:17:07 deraadt Exp $	*/
2 
3 /* DHCP options parsing and reassembly. */
4 
5 /*-
6  * SPDX-License-Identifier: BSD-3-Clause
7  *
8  * Copyright (c) 1995, 1996, 1997, 1998 The Internet Software Consortium.
9  * All rights reserved.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  *
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  * 3. Neither the name of The Internet Software Consortium nor the names
21  *    of its contributors may be used to endorse or promote products derived
22  *    from this software without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
25  * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
26  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
27  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
28  * DISCLAIMED.  IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
29  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
31  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
32  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
33  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
34  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
35  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36  * SUCH DAMAGE.
37  *
38  * This software has been written for the Internet Software Consortium
39  * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
40  * Enterprises.  To learn more about the Internet Software Consortium,
41  * see ``http://www.vix.com/isc''.  To learn more about Vixie
42  * Enterprises, see ``http://www.vix.com''.
43  */
44 
45 #include <sys/cdefs.h>
46 #include <ctype.h>
47 
48 #define DHCP_OPTION_DATA
49 #include "dhcpd.h"
50 
51 static int bad_options = 0;
52 static int bad_options_max = 5;
53 
54 void	parse_options(struct packet *);
55 void	parse_option_buffer(struct packet *, unsigned char *, int);
56 unsigned store_options(unsigned char *, int, struct tree_cache **,
57 	    unsigned char *, int, int, int, int);
58 void	expand_domain_search(struct packet *packet);
59 int	find_search_domain_name_len(struct option_data *option, size_t *offset);
60 void	expand_search_domain_name(struct option_data *option, size_t *offset,
61 	    unsigned char **domain_search);
62 void	ipv6_only_option(struct packet *packet);
63 
64 
65 /*
66  * Parse all available options out of the specified packet.
67  */
68 void
69 parse_options(struct packet *packet)
70 {
71 	/* Initially, zero all option pointers. */
72 	memset(packet->options, 0, sizeof(packet->options));
73 
74 	/* If we don't see the magic cookie, there's nothing to parse. */
75 	if (memcmp(packet->raw->options, DHCP_OPTIONS_COOKIE, 4)) {
76 		packet->options_valid = 0;
77 		return;
78 	}
79 
80 	/*
81 	 * Go through the options field, up to the end of the packet or
82 	 * the End field.
83 	 */
84 	parse_option_buffer(packet, &packet->raw->options[4],
85 	    packet->packet_length - DHCP_FIXED_NON_UDP - 4);
86 
87 	/*
88 	 * If we parsed a DHCP Option Overload option, parse more
89 	 * options out of the buffer(s) containing them.
90 	 */
91 	if (packet->options_valid &&
92 	    packet->options[DHO_DHCP_OPTION_OVERLOAD].data) {
93 		if (packet->options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 1)
94 			parse_option_buffer(packet,
95 			    (unsigned char *)packet->raw->file,
96 			    sizeof(packet->raw->file));
97 		if (packet->options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 2)
98 			parse_option_buffer(packet,
99 			    (unsigned char *)packet->raw->sname,
100 			    sizeof(packet->raw->sname));
101 	}
102 
103 	/* Process options */
104 	if (packet->options_valid) {
105 		expand_domain_search(packet);
106 		ipv6_only_option(packet);
107 	}
108 
109 }
110 
111 /*
112  * Parse options out of the specified buffer, storing addresses of
113  * option values in packet->options and setting packet->options_valid if
114  * no errors are encountered.
115  */
116 void
117 parse_option_buffer(struct packet *packet,
118     unsigned char *buffer, int length)
119 {
120 	unsigned char *s, *t, *end = buffer + length;
121 	int len, code;
122 
123 	for (s = buffer; *s != DHO_END && s < end; ) {
124 		code = s[0];
125 
126 		/* Pad options don't have a length - just skip them. */
127 		if (code == DHO_PAD) {
128 			s++;
129 			continue;
130 		}
131 		if (s + 2 > end) {
132 			len = 65536;
133 			goto bogus;
134 		}
135 
136 		/*
137 		 * All other fields (except end, see above) have a
138 		 * one-byte length.
139 		 */
140 		len = s[1];
141 
142 		/*
143 		 * If the length is outrageous, silently skip the rest,
144 		 * and mark the packet bad. Unfortunately some crappy
145 		 * dhcp servers always seem to give us garbage on the
146 		 * end of a packet. so rather than keep refusing, give
147 		 * up and try to take one after seeing a few without
148 		 * anything good.
149 		 */
150 		if (s + len + 2 > end) {
151 		    bogus:
152 			bad_options++;
153 			warning("option %s (%d) %s.",
154 			    dhcp_options[code].name, len,
155 			    "larger than buffer");
156 			if (bad_options == bad_options_max) {
157 				packet->options_valid = 1;
158 				bad_options = 0;
159 				warning("Many bogus options seen in offers. "
160 				    "Taking this offer in spite of bogus "
161 				    "options - hope for the best!");
162 			} else {
163 				warning("rejecting bogus offer.");
164 				packet->options_valid = 0;
165 			}
166 			return;
167 		}
168 		/*
169 		 * If we haven't seen this option before, just make
170 		 * space for it and copy it there.
171 		 */
172 		if (!packet->options[code].data) {
173 			if (!(t = calloc(1, len + 1)))
174 				error("Can't allocate storage for option %s.",
175 				    dhcp_options[code].name);
176 			/*
177 			 * Copy and NUL-terminate the option (in case
178 			 * it's an ASCII string.
179 			 */
180 			memcpy(t, &s[2], len);
181 			t[len] = 0;
182 			packet->options[code].len = len;
183 			packet->options[code].data = t;
184 		} else {
185 			/*
186 			 * If it's a repeat, concatenate it to whatever
187 			 * we last saw.   This is really only required
188 			 * for clients, but what the heck...
189 			 */
190 			t = calloc(1, len + packet->options[code].len + 1);
191 			if (!t)
192 				error("Can't expand storage for option %s.",
193 				    dhcp_options[code].name);
194 			memcpy(t, packet->options[code].data,
195 				packet->options[code].len);
196 			memcpy(t + packet->options[code].len,
197 				&s[2], len);
198 			packet->options[code].len += len;
199 			t[packet->options[code].len] = 0;
200 			free(packet->options[code].data);
201 			packet->options[code].data = t;
202 		}
203 		s += len + 2;
204 	}
205 	packet->options_valid = 1;
206 }
207 
208 /*
209  * Expand DHCP Domain Search option. The value of this option is
210  * encoded like DNS' list of labels. See:
211  *   RFC 3397
212  *   RFC 1035
213  */
214 void
215 expand_domain_search(struct packet *packet)
216 {
217 	size_t offset;
218 	int expanded_len, next_domain_len;
219 	struct option_data *option;
220 	unsigned char *domain_search, *cursor;
221 
222 	if (packet->options[DHO_DOMAIN_SEARCH].data == NULL)
223 		return;
224 
225 	option = &packet->options[DHO_DOMAIN_SEARCH];
226 
227 	/* Compute final expanded length. */
228 	expanded_len = 0;
229 	offset = 0;
230 	while (offset < option->len) {
231 		next_domain_len = find_search_domain_name_len(option, &offset);
232 		if (next_domain_len < 0)
233 			/* The Domain Search option value is invalid. */
234 			return;
235 
236 		/* We add 1 for the space between domain names. */
237 		expanded_len += next_domain_len + 1;
238 	}
239 	if (expanded_len > 0)
240 		/* Remove 1 for the superfluous trailing space. */
241 		--expanded_len;
242 
243 	domain_search = malloc(expanded_len + 1);
244 	if (domain_search == NULL)
245 		error("Can't allocate storage for expanded domain-search\n");
246 
247 	offset = 0;
248 	cursor = domain_search;
249 	while (offset < option->len) {
250 		expand_search_domain_name(option, &offset, &cursor);
251 		cursor[0] = ' ';
252 		cursor++;
253 	}
254 	domain_search[expanded_len] = '\0';
255 
256 	free(option->data);
257 	option->len = expanded_len;
258 	option->data = domain_search;
259 }
260 
261 int
262 find_search_domain_name_len(struct option_data *option, size_t *offset)
263 {
264 	int domain_name_len, label_len, pointed_len;
265 	size_t i, pointer;
266 
267 	domain_name_len = 0;
268 
269 	i = *offset;
270 	while (i < option->len) {
271 		label_len = option->data[i];
272 		if (label_len == 0) {
273 			/*
274 			 * A zero-length label marks the end of this
275 			 * domain name.
276 			 */
277 			*offset = i + 1;
278 			return (domain_name_len);
279 		} else if (label_len & 0xC0) {
280 			/* This is a pointer to another list of labels. */
281 			if (i + 1 >= option->len) {
282 				/* The pointer is truncated. */
283 				warning("Truncated pointer in DHCP Domain "
284 				    "Search option.");
285 				return (-1);
286 			}
287 
288 			pointer = ((label_len & ~(0xC0)) << 8) +
289 			    option->data[i + 1];
290 			if (pointer >= *offset) {
291 				/*
292 				 * The pointer must indicate a prior
293 				 * occurrence.
294 				 */
295 				warning("Invalid forward pointer in DHCP "
296 				    "Domain Search option compression.");
297 				return (-1);
298 			}
299 
300 			pointed_len = find_search_domain_name_len(option,
301 			    &pointer);
302 			if (pointed_len < 0)
303 				return (-1);
304 			domain_name_len += pointed_len;
305 
306 			*offset = i + 2;
307 			return (domain_name_len);
308 		}
309 
310 		if (i + label_len >= option->len) {
311 			warning("Truncated label in DHCP Domain Search "
312 			    "option.");
313 			return (-1);
314 		}
315 
316 		/*
317 		 * Update the domain name length with the length of the
318 		 * current label, plus a trailing dot ('.').
319 		 */
320 		domain_name_len += label_len + 1;
321 
322 		/* Move cursor. */
323 		i += label_len + 1;
324 	}
325 
326 	warning("Truncated DHCP Domain Search option.");
327 
328 	return (-1);
329 }
330 
331 void
332 expand_search_domain_name(struct option_data *option, size_t *offset,
333     unsigned char **domain_search)
334 {
335 	int label_len;
336 	size_t i, pointer;
337 	unsigned char *cursor;
338 
339 	/*
340 	 * This is the same loop than the function above
341 	 * (find_search_domain_name_len). Therefore, we remove checks,
342 	 * they're already done. Here, we just make the copy.
343 	 */
344 	i = *offset;
345 	cursor = *domain_search;
346 	while (i < option->len) {
347 		label_len = option->data[i];
348 		if (label_len == 0) {
349 			/*
350 			 * A zero-length label marks the end of this
351 			 * domain name.
352 			 */
353 			*offset = i + 1;
354 			*domain_search = cursor;
355 			return;
356 		} else if (label_len & 0xC0) {
357 			/* This is a pointer to another list of labels. */
358 			pointer = ((label_len & ~(0xC0)) << 8) +
359 			    option->data[i + 1];
360 
361 			expand_search_domain_name(option, &pointer, &cursor);
362 
363 			*offset = i + 2;
364 			*domain_search = cursor;
365 			return;
366 		}
367 
368 		/* Copy the label found. */
369 		memcpy(cursor, option->data + i + 1, label_len);
370 		cursor[label_len] = '.';
371 
372 		/* Move cursor. */
373 		i += label_len + 1;
374 		cursor += label_len + 1;
375 	}
376 }
377 
378 /*
379  * process ipv6_only option.
380  * See: RFC 8925
381  */
382 void
383 ipv6_only_option(struct packet *packet)
384 {
385 	struct option_data *option;
386 	struct client_config *config;
387 	uint32_t val;
388 	bool requested;
389 
390 	if (packet->options[DHO_IPV6_ONLY].data == NULL)
391 		return;
392 
393 	option = &packet->options[DHO_IPV6_ONLY];
394 	config = packet->interface->client->config;
395 
396 	/*
397 	 * RFC 8925, sec 3.1: The client MUST ignore the IPv6-Only Preferred
398 	 * option if the length field value is not 4.
399 	 */
400 	if (option->len != 4) {
401 		warning("IPv6-Only preferred option length is invalid");
402 		goto bad;
403 	}
404 	val = getULong(option->data);
405 
406 	/*
407 	 * RFC 8925, sec 3.2: If the client did not include the IPv6-Only Preferred
408 	 * option code in the Parameter Request List in the DHCPDISCOVER or
409 	 * DHCPREQUEST message, it MUST ignore the IPv6-Only Preferred option
410 	 * in any messages received from the server.
411 	 */
412 	requested = false;
413 	for (int i = 0; i < config->requested_option_count; i++) {
414 		if (config->requested_options[i] == DHO_IPV6_ONLY) {
415 			requested = true;
416 			break;
417 		}
418 	}
419 	if (!requested) {
420 		note("Unwanted IPv6-Only Preferred option received, ignore it");
421 		goto bad;
422 	}
423 
424 	/*
425 	 * If the IPv6-Only Preferred option returned by the server contains
426 	 * a value greater than or equal to MIN_V6ONLY_WAIT, the client SHOULD
427 	 * set the V6ONLY_WAIT timer to that value.
428 	 * Otherwise, the client SHOULD set the V6ONLY_WAIT timer
429 	 * to MIN_V6ONLY_WAIT.
430 	 */
431 	if (val < MIN_V6ONLY_WAIT)
432 		putULong(option->data, MIN_V6ONLY_WAIT);
433 
434 	return;
435 bad:
436 	free(option->data);
437 	option->len = 0;
438 	option->data = NULL;
439 }
440 
441 /*
442  * cons options into a big buffer, and then split them out into the
443  * three separate buffers if needed.  This allows us to cons up a set of
444  * vendor options using the same routine.
445  */
446 int
447 cons_options(struct packet *inpacket, struct dhcp_packet *outpacket,
448     int mms, struct tree_cache **options,
449     int overload, /* Overload flags that may be set. */
450     int terminate, int bootpp, u_int8_t *prl, int prl_len)
451 {
452 	unsigned char priority_list[300], buffer[4096];
453 	unsigned priority_len;
454 	size_t main_buffer_size;
455 	unsigned option_size, bufix, mainbufix;
456 	int length;
457 
458 	/*
459 	 * If the client has provided a maximum DHCP message size, use
460 	 * that; otherwise, if it's BOOTP, only 64 bytes; otherwise use
461 	 * up to the minimum IP MTU size (576 bytes).
462 	 *
463 	 * XXX if a BOOTP client specifies a max message size, we will
464 	 * honor it.
465 	 */
466 	if (!mms &&
467 	    inpacket &&
468 	    inpacket->options[DHO_DHCP_MAX_MESSAGE_SIZE].data &&
469 	    (inpacket->options[DHO_DHCP_MAX_MESSAGE_SIZE].len >=
470 	    sizeof(u_int16_t)))
471 		mms = getUShort(
472 		    inpacket->options[DHO_DHCP_MAX_MESSAGE_SIZE].data);
473 
474 	if (mms)
475 		main_buffer_size = mms - DHCP_FIXED_LEN;
476 	else if (bootpp)
477 		main_buffer_size = 64;
478 	else
479 		main_buffer_size = 576 - DHCP_FIXED_LEN;
480 
481 	if (main_buffer_size > sizeof(buffer))
482 		main_buffer_size = sizeof(buffer);
483 
484 	/* Preload the option priority list with mandatory options. */
485 	priority_len = 0;
486 	priority_list[priority_len++] = DHO_DHCP_MESSAGE_TYPE;
487 	priority_list[priority_len++] = DHO_DHCP_SERVER_IDENTIFIER;
488 	priority_list[priority_len++] = DHO_DHCP_LEASE_TIME;
489 	priority_list[priority_len++] = DHO_DHCP_MESSAGE;
490 
491 	/*
492 	 * If the client has provided a list of options that it wishes
493 	 * returned, use it to prioritize.  Otherwise, prioritize based
494 	 * on the default priority list.
495 	 */
496 	if (inpacket &&
497 	    inpacket->options[DHO_DHCP_PARAMETER_REQUEST_LIST].data) {
498 		unsigned prlen =
499 		    inpacket->options[DHO_DHCP_PARAMETER_REQUEST_LIST].len;
500 		if (prlen + priority_len > sizeof(priority_list))
501 			prlen = sizeof(priority_list) - priority_len;
502 
503 		memcpy(&priority_list[priority_len],
504 		    inpacket->options[DHO_DHCP_PARAMETER_REQUEST_LIST].data,
505 		    prlen);
506 		priority_len += prlen;
507 		prl = priority_list;
508 	} else if (prl) {
509 		if (prl_len + priority_len > sizeof(priority_list))
510 			prl_len = sizeof(priority_list) - priority_len;
511 
512 		memcpy(&priority_list[priority_len], prl, prl_len);
513 		priority_len += prl_len;
514 		prl = priority_list;
515 	} else {
516 		memcpy(&priority_list[priority_len],
517 		    dhcp_option_default_priority_list,
518 		    sizeof_dhcp_option_default_priority_list);
519 		priority_len += sizeof_dhcp_option_default_priority_list;
520 	}
521 
522 	/* Copy the options into the big buffer... */
523 	option_size = store_options(
524 	    buffer,
525 	    (main_buffer_size - 7 + ((overload & 1) ? DHCP_FILE_LEN : 0) +
526 		((overload & 2) ? DHCP_SNAME_LEN : 0)),
527 	    options, priority_list, priority_len, main_buffer_size,
528 	    (main_buffer_size + ((overload & 1) ? DHCP_FILE_LEN : 0)),
529 	    terminate);
530 
531 	/* Put the cookie up front... */
532 	memcpy(outpacket->options, DHCP_OPTIONS_COOKIE, 4);
533 	mainbufix = 4;
534 
535 	/*
536 	 * If we're going to have to overload, store the overload option
537 	 * at the beginning.  If we can, though, just store the whole
538 	 * thing in the packet's option buffer and leave it at that.
539 	 */
540 	if (option_size <= main_buffer_size - mainbufix) {
541 		memcpy(&outpacket->options[mainbufix],
542 		    buffer, option_size);
543 		mainbufix += option_size;
544 		if (mainbufix < main_buffer_size)
545 			outpacket->options[mainbufix++] = DHO_END;
546 		length = DHCP_FIXED_NON_UDP + mainbufix;
547 	} else {
548 		outpacket->options[mainbufix++] = DHO_DHCP_OPTION_OVERLOAD;
549 		outpacket->options[mainbufix++] = 1;
550 		if (option_size >
551 		    main_buffer_size - mainbufix + DHCP_FILE_LEN)
552 			outpacket->options[mainbufix++] = 3;
553 		else
554 			outpacket->options[mainbufix++] = 1;
555 
556 		memcpy(&outpacket->options[mainbufix],
557 		    buffer, main_buffer_size - mainbufix);
558 		bufix = main_buffer_size - mainbufix;
559 		length = DHCP_FIXED_NON_UDP + mainbufix;
560 		if (overload & 1) {
561 			if (option_size - bufix <= DHCP_FILE_LEN) {
562 				memcpy(outpacket->file,
563 				    &buffer[bufix], option_size - bufix);
564 				mainbufix = option_size - bufix;
565 				if (mainbufix < DHCP_FILE_LEN)
566 					outpacket->file[mainbufix++] = (char)DHO_END;
567 				while (mainbufix < DHCP_FILE_LEN)
568 					outpacket->file[mainbufix++] = (char)DHO_PAD;
569 			} else {
570 				memcpy(outpacket->file,
571 				    &buffer[bufix], DHCP_FILE_LEN);
572 				bufix += DHCP_FILE_LEN;
573 			}
574 		}
575 		if ((overload & 2) && option_size < bufix) {
576 			memcpy(outpacket->sname,
577 			    &buffer[bufix], option_size - bufix);
578 
579 			mainbufix = option_size - bufix;
580 			if (mainbufix < DHCP_SNAME_LEN)
581 				outpacket->file[mainbufix++] = (char)DHO_END;
582 			while (mainbufix < DHCP_SNAME_LEN)
583 				outpacket->file[mainbufix++] = (char)DHO_PAD;
584 		}
585 	}
586 	return (length);
587 }
588 
589 /*
590  * Store all the requested options into the requested buffer.
591  */
592 unsigned
593 store_options(unsigned char *buffer, int buflen, struct tree_cache **options,
594     unsigned char *priority_list, int priority_len, int first_cutoff,
595     int second_cutoff, int terminate)
596 {
597 	int bufix = 0, option_stored[256], i, ix, tto;
598 
599 	/* Zero out the stored-lengths array. */
600 	memset(option_stored, 0, sizeof(option_stored));
601 
602 	/*
603 	 * Copy out the options in the order that they appear in the
604 	 * priority list...
605 	 */
606 	for (i = 0; i < priority_len; i++) {
607 		/* Code for next option to try to store. */
608 		int code = priority_list[i];
609 		int optstart;
610 
611 		/*
612 		 * Number of bytes left to store (some may already have
613 		 * been stored by a previous pass).
614 		 */
615 		int length;
616 
617 		/* If no data is available for this option, skip it. */
618 		if (!options[code]) {
619 			continue;
620 		}
621 
622 		/*
623 		 * The client could ask for things that are mandatory,
624 		 * in which case we should avoid storing them twice...
625 		 */
626 		if (option_stored[code])
627 			continue;
628 		option_stored[code] = 1;
629 
630 		/* We should now have a constant length for the option. */
631 		length = options[code]->len;
632 
633 		/* Do we add a NUL? */
634 		if (terminate && dhcp_options[code].format[0] == 't') {
635 			length++;
636 			tto = 1;
637 		} else
638 			tto = 0;
639 
640 		/* Try to store the option. */
641 
642 		/*
643 		 * If the option's length is more than 255, we must
644 		 * store it in multiple hunks.   Store 255-byte hunks
645 		 * first.  However, in any case, if the option data will
646 		 * cross a buffer boundary, split it across that
647 		 * boundary.
648 		 */
649 		ix = 0;
650 
651 		optstart = bufix;
652 		while (length) {
653 			unsigned char incr = length > 255 ? 255 : length;
654 
655 			/*
656 			 * If this hunk of the buffer will cross a
657 			 * boundary, only go up to the boundary in this
658 			 * pass.
659 			 */
660 			if (bufix < first_cutoff &&
661 			    bufix + incr > first_cutoff)
662 				incr = first_cutoff - bufix;
663 			else if (bufix < second_cutoff &&
664 			    bufix + incr > second_cutoff)
665 				incr = second_cutoff - bufix;
666 
667 			/*
668 			 * If this option is going to overflow the
669 			 * buffer, skip it.
670 			 */
671 			if (bufix + 2 + incr > buflen) {
672 				bufix = optstart;
673 				break;
674 			}
675 
676 			/* Everything looks good - copy it in! */
677 			buffer[bufix] = code;
678 			buffer[bufix + 1] = incr;
679 			if (tto && incr == length) {
680 				memcpy(buffer + bufix + 2,
681 				    options[code]->value + ix, incr - 1);
682 				buffer[bufix + 2 + incr - 1] = 0;
683 			} else
684 				memcpy(buffer + bufix + 2,
685 				    options[code]->value + ix, incr);
686 			length -= incr;
687 			ix += incr;
688 			bufix += 2 + incr;
689 		}
690 	}
691 	return (bufix);
692 }
693 
694 /*
695  * Format the specified option so that a human can easily read it.
696  */
697 const char *
698 pretty_print_option(unsigned int code, unsigned char *data, int len,
699     int emit_commas, int emit_quotes)
700 {
701 	static char optbuf[32768]; /* XXX */
702 	int hunksize = 0, numhunk = -1, numelem = 0;
703 	char fmtbuf[32], *op = optbuf;
704 	int i, j, k, opleft = sizeof(optbuf);
705 	unsigned char *dp = data;
706 	struct in_addr foo;
707 	char comma;
708 
709 	/* Code should be between 0 and 255. */
710 	if (code > 255)
711 		error("pretty_print_option: bad code %d", code);
712 
713 	if (emit_commas)
714 		comma = ',';
715 	else
716 		comma = ' ';
717 
718 	/* Figure out the size of the data. */
719 	for (i = 0; dhcp_options[code].format[i]; i++) {
720 		if (!numhunk) {
721 			warning("%s: Excess information in format string: %s",
722 			    dhcp_options[code].name,
723 			    &(dhcp_options[code].format[i]));
724 			break;
725 		}
726 		numelem++;
727 		fmtbuf[i] = dhcp_options[code].format[i];
728 		switch (dhcp_options[code].format[i]) {
729 		case 'A':
730 			--numelem;
731 			fmtbuf[i] = 0;
732 			numhunk = 0;
733 			break;
734 		case 'X':
735 			for (k = 0; k < len; k++)
736 				if (!isascii(data[k]) ||
737 				    !isprint(data[k]))
738 					break;
739 			if (k == len) {
740 				fmtbuf[i] = 't';
741 				numhunk = -2;
742 			} else {
743 				fmtbuf[i] = 'x';
744 				hunksize++;
745 				comma = ':';
746 				numhunk = 0;
747 			}
748 			fmtbuf[i + 1] = 0;
749 			break;
750 		case 't':
751 			fmtbuf[i] = 't';
752 			fmtbuf[i + 1] = 0;
753 			numhunk = -2;
754 			break;
755 		case 'I':
756 		case 'l':
757 		case 'L':
758 			hunksize += 4;
759 			break;
760 		case 's':
761 		case 'S':
762 			hunksize += 2;
763 			break;
764 		case 'b':
765 		case 'B':
766 		case 'f':
767 			hunksize++;
768 			break;
769 		case 'e':
770 			break;
771 		default:
772 			warning("%s: garbage in format string: %s",
773 			    dhcp_options[code].name,
774 			    &(dhcp_options[code].format[i]));
775 			break;
776 		}
777 	}
778 
779 	/* Check for too few bytes... */
780 	if (hunksize > len) {
781 		warning("%s: expecting at least %d bytes; got %d",
782 		    dhcp_options[code].name, hunksize, len);
783 		return ("<error>");
784 	}
785 	/* Check for too many bytes... */
786 	if (numhunk == -1 && hunksize < len)
787 		warning("%s: %d extra bytes",
788 		    dhcp_options[code].name, len - hunksize);
789 
790 	/* If this is an array, compute its size. */
791 	if (!numhunk)
792 		numhunk = len / hunksize;
793 	/* See if we got an exact number of hunks. */
794 	if (numhunk > 0 && numhunk * hunksize < len)
795 		warning("%s: %d extra bytes at end of array",
796 		    dhcp_options[code].name, len - numhunk * hunksize);
797 
798 	/* A one-hunk array prints the same as a single hunk. */
799 	if (numhunk < 0)
800 		numhunk = 1;
801 
802 	/* Cycle through the array (or hunk) printing the data. */
803 	for (i = 0; i < numhunk; i++) {
804 		for (j = 0; j < numelem; j++) {
805 			int opcount;
806 			switch (fmtbuf[j]) {
807 			case 't':
808 				if (emit_quotes) {
809 					*op++ = '"';
810 					opleft--;
811 				}
812 				for (; dp < data + len; dp++) {
813 					if (!isascii(*dp) ||
814 					    !isprint(*dp)) {
815 						if (dp + 1 != data + len ||
816 						    *dp != 0) {
817 							snprintf(op, opleft,
818 							    "\\%03o", *dp);
819 							op += 4;
820 							opleft -= 4;
821 						}
822 					} else if (*dp == '"' ||
823 					    *dp == '\'' ||
824 					    *dp == '$' ||
825 					    *dp == '`' ||
826 					    *dp == '\\') {
827 						*op++ = '\\';
828 						*op++ = *dp;
829 						opleft -= 2;
830 					} else {
831 						*op++ = *dp;
832 						opleft--;
833 					}
834 				}
835 				if (emit_quotes) {
836 					*op++ = '"';
837 					opleft--;
838 				}
839 
840 				*op = 0;
841 				break;
842 			case 'I':
843 				foo.s_addr = htonl(getULong(dp));
844 				opcount = strlcpy(op, inet_ntoa(foo), opleft);
845 				if (opcount >= opleft)
846 					goto toobig;
847 				opleft -= opcount;
848 				dp += 4;
849 				break;
850 			case 'l':
851 				opcount = snprintf(op, opleft, "%ld",
852 				    (long)getLong(dp));
853 				if (opcount >= opleft || opcount == -1)
854 					goto toobig;
855 				opleft -= opcount;
856 				dp += 4;
857 				break;
858 			case 'L':
859 				opcount = snprintf(op, opleft, "%lu",
860 				    (unsigned long)getULong(dp));
861 				if (opcount >= opleft || opcount == -1)
862 					goto toobig;
863 				opleft -= opcount;
864 				dp += 4;
865 				break;
866 			case 's':
867 				opcount = snprintf(op, opleft, "%d",
868 				    getShort(dp));
869 				if (opcount >= opleft || opcount == -1)
870 					goto toobig;
871 				opleft -= opcount;
872 				dp += 2;
873 				break;
874 			case 'S':
875 				opcount = snprintf(op, opleft, "%u",
876 				    getUShort(dp));
877 				if (opcount >= opleft || opcount == -1)
878 					goto toobig;
879 				opleft -= opcount;
880 				dp += 2;
881 				break;
882 			case 'b':
883 				opcount = snprintf(op, opleft, "%d",
884 				    *(char *)dp++);
885 				if (opcount >= opleft || opcount == -1)
886 					goto toobig;
887 				opleft -= opcount;
888 				break;
889 			case 'B':
890 				opcount = snprintf(op, opleft, "%d", *dp++);
891 				if (opcount >= opleft || opcount == -1)
892 					goto toobig;
893 				opleft -= opcount;
894 				break;
895 			case 'x':
896 				opcount = snprintf(op, opleft, "%x", *dp++);
897 				if (opcount >= opleft || opcount == -1)
898 					goto toobig;
899 				opleft -= opcount;
900 				break;
901 			case 'f':
902 				opcount = strlcpy(op,
903 				    *dp++ ? "true" : "false", opleft);
904 				if (opcount >= opleft)
905 					goto toobig;
906 				opleft -= opcount;
907 				break;
908 			default:
909 				warning("Unexpected format code %c", fmtbuf[j]);
910 			}
911 			op += strlen(op);
912 			opleft -= strlen(op);
913 			if (opleft < 1)
914 				goto toobig;
915 			if (j + 1 < numelem && comma != ':') {
916 				*op++ = ' ';
917 				opleft--;
918 			}
919 		}
920 		if (i + 1 < numhunk) {
921 			*op++ = comma;
922 			opleft--;
923 		}
924 		if (opleft < 1)
925 			goto toobig;
926 
927 	}
928 	return (optbuf);
929  toobig:
930 	warning("dhcp option too large");
931 	return ("<error>");
932 }
933 
934 void
935 do_packet(struct interface_info *interface, struct dhcp_packet *packet,
936     int len, unsigned int from_port, struct iaddr from, struct hardware *hfrom)
937 {
938 	struct packet tp;
939 	int i;
940 
941 	if (packet->hlen > sizeof(packet->chaddr)) {
942 		note("Discarding packet with invalid hlen.");
943 		return;
944 	}
945 
946 	memset(&tp, 0, sizeof(tp));
947 	tp.raw = packet;
948 	tp.packet_length = len;
949 	tp.client_port = from_port;
950 	tp.client_addr = from;
951 	tp.interface = interface;
952 	tp.haddr = hfrom;
953 
954 	parse_options(&tp);
955 	if (tp.options_valid &&
956 	    tp.options[DHO_DHCP_MESSAGE_TYPE].data)
957 		tp.packet_type = tp.options[DHO_DHCP_MESSAGE_TYPE].data[0];
958 	if (tp.packet_type)
959 		dhcp(&tp);
960 	else
961 		bootp(&tp);
962 
963 	/* Free the data associated with the options. */
964 	for (i = 0; i < 256; i++)
965 		free(tp.options[i].data);
966 }
967