xref: /openbsd/sbin/dhclient/options.c (revision 6fc9f4f6)
1*6fc9f4f6Skrw /*	$OpenBSD: options.c,v 1.25 2005/08/22 20:30:52 krw Exp $	*/
29a2590e5Sderaadt 
3e7eb2effShenning /* DHCP options parsing and reassembly. */
49a2590e5Sderaadt 
59a2590e5Sderaadt /*
69a2590e5Sderaadt  * Copyright (c) 1995, 1996, 1997, 1998 The Internet Software Consortium.
79a2590e5Sderaadt  * All rights reserved.
89a2590e5Sderaadt  *
99a2590e5Sderaadt  * Redistribution and use in source and binary forms, with or without
109a2590e5Sderaadt  * modification, are permitted provided that the following conditions
119a2590e5Sderaadt  * are met:
129a2590e5Sderaadt  *
139a2590e5Sderaadt  * 1. Redistributions of source code must retain the above copyright
149a2590e5Sderaadt  *    notice, this list of conditions and the following disclaimer.
159a2590e5Sderaadt  * 2. Redistributions in binary form must reproduce the above copyright
169a2590e5Sderaadt  *    notice, this list of conditions and the following disclaimer in the
179a2590e5Sderaadt  *    documentation and/or other materials provided with the distribution.
189a2590e5Sderaadt  * 3. Neither the name of The Internet Software Consortium nor the names
199a2590e5Sderaadt  *    of its contributors may be used to endorse or promote products derived
209a2590e5Sderaadt  *    from this software without specific prior written permission.
219a2590e5Sderaadt  *
229a2590e5Sderaadt  * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
239a2590e5Sderaadt  * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
249a2590e5Sderaadt  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
259a2590e5Sderaadt  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
269a2590e5Sderaadt  * DISCLAIMED.  IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
279a2590e5Sderaadt  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
289a2590e5Sderaadt  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
299a2590e5Sderaadt  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
309a2590e5Sderaadt  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
319a2590e5Sderaadt  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
329a2590e5Sderaadt  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
339a2590e5Sderaadt  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
349a2590e5Sderaadt  * SUCH DAMAGE.
359a2590e5Sderaadt  *
369a2590e5Sderaadt  * This software has been written for the Internet Software Consortium
379a2590e5Sderaadt  * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
389a2590e5Sderaadt  * Enterprises.  To learn more about the Internet Software Consortium,
399a2590e5Sderaadt  * see ``http://www.vix.com/isc''.  To learn more about Vixie
409a2590e5Sderaadt  * Enterprises, see ``http://www.vix.com''.
419a2590e5Sderaadt  */
429a2590e5Sderaadt 
43c714dadcShenning #include <ctype.h>
44c714dadcShenning 
459a2590e5Sderaadt #include "dhcpd.h"
469a2590e5Sderaadt 
479a2590e5Sderaadt int bad_options = 0;
489a2590e5Sderaadt int bad_options_max = 5;
499a2590e5Sderaadt 
50fdcd93f4Shenning void	parse_options(struct packet *);
51fdcd93f4Shenning void	parse_option_buffer(struct packet *, unsigned char *, int);
52fdcd93f4Shenning 
53c714dadcShenning /*
54c714dadcShenning  * Parse all available options out of the specified packet.
55c714dadcShenning  */
56c714dadcShenning void
57c714dadcShenning parse_options(struct packet *packet)
589a2590e5Sderaadt {
599a2590e5Sderaadt 	/* Initially, zero all option pointers. */
609a2590e5Sderaadt 	memset(packet->options, 0, sizeof(packet->options));
619a2590e5Sderaadt 
629a2590e5Sderaadt 	/* If we don't see the magic cookie, there's nothing to parse. */
639a2590e5Sderaadt 	if (memcmp(packet->raw->options, DHCP_OPTIONS_COOKIE, 4)) {
649a2590e5Sderaadt 		packet->options_valid = 0;
659a2590e5Sderaadt 		return;
669a2590e5Sderaadt 	}
679a2590e5Sderaadt 
68c714dadcShenning 	/*
69c714dadcShenning 	 * Go through the options field, up to the end of the packet or
70c714dadcShenning 	 * the End field.
71c714dadcShenning 	 */
729a2590e5Sderaadt 	parse_option_buffer(packet, &packet->raw->options[4],
739a2590e5Sderaadt 	    packet->packet_length - DHCP_FIXED_NON_UDP - 4);
74d4ce89fdSderaadt 
75c714dadcShenning 	/*
76c714dadcShenning 	 * If we parsed a DHCP Option Overload option, parse more
77c714dadcShenning 	 * options out of the buffer(s) containing them.
78c714dadcShenning 	 */
79c714dadcShenning 	if (packet->options_valid &&
80c714dadcShenning 	    packet->options[DHO_DHCP_OPTION_OVERLOAD].data) {
819a2590e5Sderaadt 		if (packet->options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 1)
829a2590e5Sderaadt 			parse_option_buffer(packet,
83c714dadcShenning 			    (unsigned char *)packet->raw->file,
84c714dadcShenning 			    sizeof(packet->raw->file));
859a2590e5Sderaadt 		if (packet->options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 2)
869a2590e5Sderaadt 			parse_option_buffer(packet,
87c714dadcShenning 			    (unsigned char *)packet->raw->sname,
88c714dadcShenning 			    sizeof(packet->raw->sname));
899a2590e5Sderaadt 	}
909a2590e5Sderaadt }
919a2590e5Sderaadt 
92c714dadcShenning /*
93c714dadcShenning  * Parse options out of the specified buffer, storing addresses of
94c714dadcShenning  * option values in packet->options and setting packet->options_valid if
95c714dadcShenning  * no errors are encountered.
96c714dadcShenning  */
97c714dadcShenning void
98c714dadcShenning parse_option_buffer(struct packet *packet,
99c714dadcShenning     unsigned char *buffer, int length)
1009a2590e5Sderaadt {
101285f06efSderaadt 	unsigned char *s, *t, *end = buffer + length;
102285f06efSderaadt 	int len, code;
1039a2590e5Sderaadt 
1049a2590e5Sderaadt 	for (s = buffer; *s != DHO_END && s < end; ) {
1059a2590e5Sderaadt 		code = s[0];
1069a2590e5Sderaadt 
1079a2590e5Sderaadt 		/* Pad options don't have a length - just skip them. */
1089a2590e5Sderaadt 		if (code == DHO_PAD) {
109f1e89499Shenning 			s++;
1109a2590e5Sderaadt 			continue;
1119a2590e5Sderaadt 		}
1129a2590e5Sderaadt 		if (s + 2 > end) {
1139a2590e5Sderaadt 			len = 65536;
1149a2590e5Sderaadt 			goto bogus;
1159a2590e5Sderaadt 		}
1169a2590e5Sderaadt 
117c714dadcShenning 		/*
118c714dadcShenning 		 * All other fields (except end, see above) have a
119c714dadcShenning 		 * one-byte length.
120c714dadcShenning 		 */
1219a2590e5Sderaadt 		len = s[1];
1229a2590e5Sderaadt 
123c714dadcShenning 		/*
124c714dadcShenning 		 * If the length is outrageous, silently skip the rest,
125fd9f780dSdavid 		 * and mark the packet bad. Unfortunately some crappy
126c714dadcShenning 		 * dhcp servers always seem to give us garbage on the
127c714dadcShenning 		 * end of a packet. so rather than keep refusing, give
128c714dadcShenning 		 * up and try to take one after seeing a few without
129c714dadcShenning 		 * anything good.
1309a2590e5Sderaadt 		 */
1319a2590e5Sderaadt 		if (s + len + 2 > end) {
1329a2590e5Sderaadt 		    bogus:
1339a2590e5Sderaadt 			bad_options++;
134c955dd46Smickey 			warning("option %s (%d) %s.",
1359a2590e5Sderaadt 			    dhcp_options[code].name, len,
1369a2590e5Sderaadt 			    "larger than buffer");
1379a2590e5Sderaadt 			if (bad_options == bad_options_max) {
1389a2590e5Sderaadt 				packet->options_valid = 1;
1399a2590e5Sderaadt 				bad_options = 0;
140c955dd46Smickey 				warning("Many bogus options seen in offers. "
141c955dd46Smickey 				    "Taking this offer in spite of bogus "
142c955dd46Smickey 				    "options - hope for the best!");
1439a2590e5Sderaadt 			} else {
144c955dd46Smickey 				warning("rejecting bogus offer.");
1459a2590e5Sderaadt 				packet->options_valid = 0;
1469a2590e5Sderaadt 			}
1479a2590e5Sderaadt 			return;
1489a2590e5Sderaadt 		}
149c714dadcShenning 		/*
150c714dadcShenning 		 * If we haven't seen this option before, just make
151c714dadcShenning 		 * space for it and copy it there.
152c714dadcShenning 		 */
1539a2590e5Sderaadt 		if (!packet->options[code].data) {
1548e916ab9Shenning 			if (!(t = calloc(1, len + 1)))
1559a2590e5Sderaadt 				error("Can't allocate storage for option %s.",
1569a2590e5Sderaadt 				    dhcp_options[code].name);
157c714dadcShenning 			/*
158c714dadcShenning 			 * Copy and NUL-terminate the option (in case
159c714dadcShenning 			 * it's an ASCII string.
160c714dadcShenning 			 */
1619a2590e5Sderaadt 			memcpy(t, &s[2], len);
1629a2590e5Sderaadt 			t[len] = 0;
1639a2590e5Sderaadt 			packet->options[code].len = len;
1649a2590e5Sderaadt 			packet->options[code].data = t;
1659a2590e5Sderaadt 		} else {
166c714dadcShenning 			/*
167c714dadcShenning 			 * If it's a repeat, concatenate it to whatever
168c714dadcShenning 			 * we last saw.   This is really only required
169c714dadcShenning 			 * for clients, but what the heck...
170c714dadcShenning 			 */
1718e916ab9Shenning 			t = calloc(1, len + packet->options[code].len + 1);
1729a2590e5Sderaadt 			if (!t)
1739a2590e5Sderaadt 				error("Can't expand storage for option %s.",
1749a2590e5Sderaadt 				    dhcp_options[code].name);
1759a2590e5Sderaadt 			memcpy(t, packet->options[code].data,
1769a2590e5Sderaadt 				packet->options[code].len);
1779a2590e5Sderaadt 			memcpy(t + packet->options[code].len,
1789a2590e5Sderaadt 				&s[2], len);
1799a2590e5Sderaadt 			packet->options[code].len += len;
1809a2590e5Sderaadt 			t[packet->options[code].len] = 0;
1818e916ab9Shenning 			free(packet->options[code].data);
1829a2590e5Sderaadt 			packet->options[code].data = t;
1839a2590e5Sderaadt 		}
1849a2590e5Sderaadt 		s += len + 2;
1859a2590e5Sderaadt 	}
1869a2590e5Sderaadt 	packet->options_valid = 1;
1879a2590e5Sderaadt }
1889a2590e5Sderaadt 
189c714dadcShenning /*
19096978980Skrw  * Copy as many options as fit in buflen bytes of buf. Return the
19196978980Skrw  * offset of the start of the last option copied. A caller can check
19296978980Skrw  * to see if it's DHO_END to decide if all the options were copied.
193c714dadcShenning  */
194c714dadcShenning int
19596978980Skrw cons_options(unsigned char *buf, const int buflen, struct option_data *options)
1969a2590e5Sderaadt {
19796978980Skrw 	int ix, incr, length, bufix, code, lastopt = -1;
1989a2590e5Sderaadt 
19996978980Skrw 	bzero(buf, buflen);
2009a2590e5Sderaadt 
20196978980Skrw 	if (buflen > 3)
20296978980Skrw 		memcpy(buf, DHCP_OPTIONS_COOKIE, 4);
20396978980Skrw 	bufix = 4;
2049a2590e5Sderaadt 
20596978980Skrw 	for (code = DHO_SUBNET_MASK; code < DHO_END; code++) {
206f5396ce2Skrw 		if (!options[code].data)
2079a2590e5Sderaadt 			continue;
2089a2590e5Sderaadt 
209d7d9bbf5Skrw 		length = options[code].len;
21096978980Skrw 		if (bufix + length + 2*((length+254)/255) >= buflen)
21196978980Skrw 			return (lastopt);
2129a2590e5Sderaadt 
21396978980Skrw 		lastopt = bufix;
2149a2590e5Sderaadt 		ix = 0;
2159a2590e5Sderaadt 
2169a2590e5Sderaadt 		while (length) {
21796978980Skrw 			incr = length > 255 ? 255 : length;
2189a2590e5Sderaadt 
21996978980Skrw 			buf[bufix++] = code;
22096978980Skrw 			buf[bufix++] = incr;
22196978980Skrw 			memcpy(buf + bufix, options[code].data + ix, incr);
2229a2590e5Sderaadt 
2239a2590e5Sderaadt 			length -= incr;
2249a2590e5Sderaadt 			ix += incr;
225*6fc9f4f6Skrw 			bufix += incr;
2269a2590e5Sderaadt 		}
2279a2590e5Sderaadt 	}
22896978980Skrw 
22996978980Skrw 	if (bufix < buflen) {
23096978980Skrw 		buf[bufix] = DHO_END;
23196978980Skrw 		lastopt = bufix;
23296978980Skrw 	}
23396978980Skrw 
23496978980Skrw 	return (lastopt);
2359a2590e5Sderaadt }
2369a2590e5Sderaadt 
237c714dadcShenning /*
238c714dadcShenning  * Format the specified option so that a human can easily read it.
239c714dadcShenning  */
240c714dadcShenning char *
241c714dadcShenning pretty_print_option(unsigned int code, unsigned char *data, int len,
242c714dadcShenning     int emit_commas, int emit_quotes)
2439a2590e5Sderaadt {
2449a2590e5Sderaadt 	static char optbuf[32768]; /* XXX */
245285f06efSderaadt 	int hunksize = 0, numhunk = -1, numelem = 0;
246285f06efSderaadt 	char fmtbuf[32], *op = optbuf;
247285f06efSderaadt 	int i, j, k, opleft = sizeof(optbuf);
2489a2590e5Sderaadt 	unsigned char *dp = data;
2499a2590e5Sderaadt 	struct in_addr foo;
2509a2590e5Sderaadt 	char comma;
2519a2590e5Sderaadt 
2529a2590e5Sderaadt 	/* Code should be between 0 and 255. */
2539a2590e5Sderaadt 	if (code > 255)
2545eb2300aShenning 		error("pretty_print_option: bad code %d", code);
2559a2590e5Sderaadt 
2569a2590e5Sderaadt 	if (emit_commas)
2579a2590e5Sderaadt 		comma = ',';
2589a2590e5Sderaadt 	else
2599a2590e5Sderaadt 		comma = ' ';
2609a2590e5Sderaadt 
2619a2590e5Sderaadt 	/* Figure out the size of the data. */
2629a2590e5Sderaadt 	for (i = 0; dhcp_options[code].format[i]; i++) {
2639a2590e5Sderaadt 		if (!numhunk) {
264c955dd46Smickey 			warning("%s: Excess information in format string: %s",
2659a2590e5Sderaadt 			    dhcp_options[code].name,
2669a2590e5Sderaadt 			    &(dhcp_options[code].format[i]));
2679a2590e5Sderaadt 			break;
2689a2590e5Sderaadt 		}
2699a2590e5Sderaadt 		numelem++;
2709a2590e5Sderaadt 		fmtbuf[i] = dhcp_options[code].format[i];
2719a2590e5Sderaadt 		switch (dhcp_options[code].format[i]) {
2729a2590e5Sderaadt 		case 'A':
2739a2590e5Sderaadt 			--numelem;
2749a2590e5Sderaadt 			fmtbuf[i] = 0;
2759a2590e5Sderaadt 			numhunk = 0;
2769a2590e5Sderaadt 			break;
2779a2590e5Sderaadt 		case 'X':
278c714dadcShenning 			for (k = 0; k < len; k++)
2799a2590e5Sderaadt 				if (!isascii(data[k]) ||
2809a2590e5Sderaadt 				    !isprint(data[k]))
2819a2590e5Sderaadt 					break;
282b54c879eShenning 			if (k == len) {
2839a2590e5Sderaadt 				fmtbuf[i] = 't';
2849a2590e5Sderaadt 				numhunk = -2;
2859a2590e5Sderaadt 			} else {
2869a2590e5Sderaadt 				fmtbuf[i] = 'x';
2879a2590e5Sderaadt 				hunksize++;
2889a2590e5Sderaadt 				comma = ':';
2899a2590e5Sderaadt 				numhunk = 0;
2909a2590e5Sderaadt 			}
2919a2590e5Sderaadt 			fmtbuf[i + 1] = 0;
2929a2590e5Sderaadt 			break;
2939a2590e5Sderaadt 		case 't':
2949a2590e5Sderaadt 			fmtbuf[i] = 't';
2959a2590e5Sderaadt 			fmtbuf[i + 1] = 0;
2969a2590e5Sderaadt 			numhunk = -2;
2979a2590e5Sderaadt 			break;
2989a2590e5Sderaadt 		case 'I':
2999a2590e5Sderaadt 		case 'l':
3009a2590e5Sderaadt 		case 'L':
3019a2590e5Sderaadt 			hunksize += 4;
3029a2590e5Sderaadt 			break;
3039a2590e5Sderaadt 		case 's':
3049a2590e5Sderaadt 		case 'S':
3059a2590e5Sderaadt 			hunksize += 2;
3069a2590e5Sderaadt 			break;
3079a2590e5Sderaadt 		case 'b':
3089a2590e5Sderaadt 		case 'B':
3099a2590e5Sderaadt 		case 'f':
3109a2590e5Sderaadt 			hunksize++;
3119a2590e5Sderaadt 			break;
3129a2590e5Sderaadt 		case 'e':
3139a2590e5Sderaadt 			break;
3149a2590e5Sderaadt 		default:
315c955dd46Smickey 			warning("%s: garbage in format string: %s",
3169a2590e5Sderaadt 			    dhcp_options[code].name,
3179a2590e5Sderaadt 			    &(dhcp_options[code].format[i]));
3189a2590e5Sderaadt 			break;
3199a2590e5Sderaadt 		}
3209a2590e5Sderaadt 	}
3219a2590e5Sderaadt 
3229a2590e5Sderaadt 	/* Check for too few bytes... */
3239a2590e5Sderaadt 	if (hunksize > len) {
324c955dd46Smickey 		warning("%s: expecting at least %d bytes; got %d",
325c714dadcShenning 		    dhcp_options[code].name, hunksize, len);
326c714dadcShenning 		return ("<error>");
3279a2590e5Sderaadt 	}
3289a2590e5Sderaadt 	/* Check for too many bytes... */
3299a2590e5Sderaadt 	if (numhunk == -1 && hunksize < len)
330c955dd46Smickey 		warning("%s: %d extra bytes",
331c714dadcShenning 		    dhcp_options[code].name, len - hunksize);
3329a2590e5Sderaadt 
3339a2590e5Sderaadt 	/* If this is an array, compute its size. */
3349a2590e5Sderaadt 	if (!numhunk)
3359a2590e5Sderaadt 		numhunk = len / hunksize;
3369a2590e5Sderaadt 	/* See if we got an exact number of hunks. */
3379a2590e5Sderaadt 	if (numhunk > 0 && numhunk * hunksize < len)
338c955dd46Smickey 		warning("%s: %d extra bytes at end of array",
339c714dadcShenning 		    dhcp_options[code].name, len - numhunk * hunksize);
3409a2590e5Sderaadt 
3419a2590e5Sderaadt 	/* A one-hunk array prints the same as a single hunk. */
3429a2590e5Sderaadt 	if (numhunk < 0)
3439a2590e5Sderaadt 		numhunk = 1;
3449a2590e5Sderaadt 
3459a2590e5Sderaadt 	/* Cycle through the array (or hunk) printing the data. */
3469a2590e5Sderaadt 	for (i = 0; i < numhunk; i++) {
3479a2590e5Sderaadt 		for (j = 0; j < numelem; j++) {
3489a2590e5Sderaadt 			int opcount;
3499a2590e5Sderaadt 			switch (fmtbuf[j]) {
3509a2590e5Sderaadt 			case 't':
3519a2590e5Sderaadt 				if (emit_quotes) {
3529a2590e5Sderaadt 					*op++ = '"';
3539a2590e5Sderaadt 					opleft--;
3549a2590e5Sderaadt 				}
3559a2590e5Sderaadt 				for (; dp < data + len; dp++) {
3569a2590e5Sderaadt 					if (!isascii(*dp) ||
3579a2590e5Sderaadt 					    !isprint(*dp)) {
3589a2590e5Sderaadt 						if (dp + 1 != data + len ||
3599a2590e5Sderaadt 						    *dp != 0) {
3609a2590e5Sderaadt 							snprintf(op, opleft,
3619a2590e5Sderaadt 							    "\\%03o", *dp);
3629a2590e5Sderaadt 							op += 4;
3639a2590e5Sderaadt 							opleft -= 4;
3649a2590e5Sderaadt 						}
3659a2590e5Sderaadt 					} else if (*dp == '"' ||
3669a2590e5Sderaadt 					    *dp == '\'' ||
3679a2590e5Sderaadt 					    *dp == '$' ||
3689a2590e5Sderaadt 					    *dp == '`' ||
3699a2590e5Sderaadt 					    *dp == '\\') {
3709a2590e5Sderaadt 						*op++ = '\\';
3719a2590e5Sderaadt 						*op++ = *dp;
3729a2590e5Sderaadt 						opleft -= 2;
3739a2590e5Sderaadt 					} else {
3749a2590e5Sderaadt 						*op++ = *dp;
3759a2590e5Sderaadt 						opleft--;
3769a2590e5Sderaadt 					}
3779a2590e5Sderaadt 				}
3789a2590e5Sderaadt 				if (emit_quotes) {
3799a2590e5Sderaadt 					*op++ = '"';
3809a2590e5Sderaadt 					opleft--;
3819a2590e5Sderaadt 				}
3829a2590e5Sderaadt 
3839a2590e5Sderaadt 				*op = 0;
3849a2590e5Sderaadt 				break;
3859a2590e5Sderaadt 			case 'I':
3869a2590e5Sderaadt 				foo.s_addr = htonl(getULong(dp));
387c714dadcShenning 				opcount = strlcpy(op, inet_ntoa(foo), opleft);
3889a2590e5Sderaadt 				if (opcount >= opleft)
3899a2590e5Sderaadt 					goto toobig;
3909a2590e5Sderaadt 				opleft -= opcount;
3919a2590e5Sderaadt 				dp += 4;
3929a2590e5Sderaadt 				break;
3939a2590e5Sderaadt 			case 'l':
3949a2590e5Sderaadt 				opcount = snprintf(op, opleft, "%ld",
3959a2590e5Sderaadt 				    (long)getLong(dp));
3966ed3db51Sderaadt 				if (opcount >= opleft || opcount == -1)
3979a2590e5Sderaadt 					goto toobig;
3989a2590e5Sderaadt 				opleft -= opcount;
3999a2590e5Sderaadt 				dp += 4;
4009a2590e5Sderaadt 				break;
4019a2590e5Sderaadt 			case 'L':
4029a2590e5Sderaadt 				opcount = snprintf(op, opleft, "%ld",
4039a2590e5Sderaadt 				    (unsigned long)getULong(dp));
4046ed3db51Sderaadt 				if (opcount >= opleft || opcount == -1)
4059a2590e5Sderaadt 					goto toobig;
4069a2590e5Sderaadt 				opleft -= opcount;
4079a2590e5Sderaadt 				dp += 4;
4089a2590e5Sderaadt 				break;
4099a2590e5Sderaadt 			case 's':
4109a2590e5Sderaadt 				opcount = snprintf(op, opleft, "%d",
4119a2590e5Sderaadt 				    getShort(dp));
4126ed3db51Sderaadt 				if (opcount >= opleft || opcount == -1)
4139a2590e5Sderaadt 					goto toobig;
4149a2590e5Sderaadt 				opleft -= opcount;
4159a2590e5Sderaadt 				dp += 2;
4169a2590e5Sderaadt 				break;
4179a2590e5Sderaadt 			case 'S':
4189a2590e5Sderaadt 				opcount = snprintf(op, opleft, "%d",
4199a2590e5Sderaadt 				    getUShort(dp));
4206ed3db51Sderaadt 				if (opcount >= opleft || opcount == -1)
4219a2590e5Sderaadt 					goto toobig;
4229a2590e5Sderaadt 				opleft -= opcount;
4239a2590e5Sderaadt 				dp += 2;
4249a2590e5Sderaadt 				break;
4259a2590e5Sderaadt 			case 'b':
4269a2590e5Sderaadt 				opcount = snprintf(op, opleft, "%d",
4279a2590e5Sderaadt 				    *(char *)dp++);
4286ed3db51Sderaadt 				if (opcount >= opleft || opcount == -1)
4299a2590e5Sderaadt 					goto toobig;
4309a2590e5Sderaadt 				opleft -= opcount;
4319a2590e5Sderaadt 				break;
4329a2590e5Sderaadt 			case 'B':
4339a2590e5Sderaadt 				opcount = snprintf(op, opleft, "%d", *dp++);
4346ed3db51Sderaadt 				if (opcount >= opleft || opcount == -1)
4359a2590e5Sderaadt 					goto toobig;
4369a2590e5Sderaadt 				opleft -= opcount;
4379a2590e5Sderaadt 				break;
4389a2590e5Sderaadt 			case 'x':
4399a2590e5Sderaadt 				opcount = snprintf(op, opleft, "%x", *dp++);
4406ed3db51Sderaadt 				if (opcount >= opleft || opcount == -1)
4419a2590e5Sderaadt 					goto toobig;
4429a2590e5Sderaadt 				opleft -= opcount;
4439a2590e5Sderaadt 				break;
4449a2590e5Sderaadt 			case 'f':
4459a2590e5Sderaadt 				opcount = strlcpy(op,
4469a2590e5Sderaadt 				    *dp++ ? "true" : "false", opleft);
4479a2590e5Sderaadt 				if (opcount >= opleft)
4489a2590e5Sderaadt 					goto toobig;
4499a2590e5Sderaadt 				opleft -= opcount;
4509a2590e5Sderaadt 				break;
4519a2590e5Sderaadt 			default:
452c955dd46Smickey 				warning("Unexpected format code %c", fmtbuf[j]);
4539a2590e5Sderaadt 			}
4549a2590e5Sderaadt 			op += strlen(op);
4559a2590e5Sderaadt 			opleft -= strlen(op);
4569a2590e5Sderaadt 			if (opleft < 1)
4579a2590e5Sderaadt 				goto toobig;
4589a2590e5Sderaadt 			if (j + 1 < numelem && comma != ':') {
4599a2590e5Sderaadt 				*op++ = ' ';
4609a2590e5Sderaadt 				opleft--;
4619a2590e5Sderaadt 			}
4629a2590e5Sderaadt 		}
4639a2590e5Sderaadt 		if (i + 1 < numhunk) {
4649a2590e5Sderaadt 			*op++ = comma;
4659a2590e5Sderaadt 			opleft--;
4669a2590e5Sderaadt 		}
4679a2590e5Sderaadt 		if (opleft < 1)
4689a2590e5Sderaadt 			goto toobig;
4699a2590e5Sderaadt 
4709a2590e5Sderaadt 	}
471c714dadcShenning 	return (optbuf);
4729a2590e5Sderaadt  toobig:
473c955dd46Smickey 	warning("dhcp option too large");
474c714dadcShenning 	return ("<error>");
4759a2590e5Sderaadt }
4769a2590e5Sderaadt 
477c714dadcShenning void
478c714dadcShenning do_packet(struct interface_info *interface, struct dhcp_packet *packet,
479c714dadcShenning     int len, unsigned int from_port, struct iaddr from, struct hardware *hfrom)
4809a2590e5Sderaadt {
4819a2590e5Sderaadt 	struct packet tp;
4829a2590e5Sderaadt 	int i;
4839a2590e5Sderaadt 
484c714dadcShenning 	if (packet->hlen > sizeof(packet->chaddr)) {
4859a2590e5Sderaadt 		note("Discarding packet with invalid hlen.");
4869a2590e5Sderaadt 		return;
4879a2590e5Sderaadt 	}
4889a2590e5Sderaadt 
489c714dadcShenning 	memset(&tp, 0, sizeof(tp));
4909a2590e5Sderaadt 	tp.raw = packet;
4919a2590e5Sderaadt 	tp.packet_length = len;
4929a2590e5Sderaadt 	tp.client_port = from_port;
4939a2590e5Sderaadt 	tp.client_addr = from;
4949a2590e5Sderaadt 	tp.interface = interface;
4959a2590e5Sderaadt 	tp.haddr = hfrom;
4969a2590e5Sderaadt 
4979a2590e5Sderaadt 	parse_options(&tp);
4989a2590e5Sderaadt 	if (tp.options_valid &&
4999a2590e5Sderaadt 	    tp.options[DHO_DHCP_MESSAGE_TYPE].data)
500c714dadcShenning 		tp.packet_type = tp.options[DHO_DHCP_MESSAGE_TYPE].data[0];
5019a2590e5Sderaadt 	if (tp.packet_type)
5029a2590e5Sderaadt 		dhcp(&tp);
5039a2590e5Sderaadt 	else
5049a2590e5Sderaadt 		bootp(&tp);
5059a2590e5Sderaadt 
5069a2590e5Sderaadt 	/* Free the data associated with the options. */
507c714dadcShenning 	for (i = 0; i < 256; i++)
5089a2590e5Sderaadt 		if (tp.options[i].len && tp.options[i].data)
5098e916ab9Shenning 			free(tp.options[i].data);
5109a2590e5Sderaadt }
511