xref: /openbsd/sbin/dhclient/options.c (revision de3ca9db)
1*de3ca9dbSkrw /*	$OpenBSD: options.c,v 1.58 2013/12/12 01:40:35 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 
439a2590e5Sderaadt #include "dhcpd.h"
449a2590e5Sderaadt 
4502e02bd5Skrw int parse_option_buffer(struct option_data *, unsigned char *, int);
469a2590e5Sderaadt 
47c714dadcShenning /*
48c714dadcShenning  * Parse options out of the specified buffer, storing addresses of
4992018899Skrw  * option values in options. Return 0 if errors, 1 if not.
50c714dadcShenning  */
5102e02bd5Skrw int
524f062ee3Skrw parse_option_buffer(struct option_data *options, unsigned char *buffer,
534f062ee3Skrw     int length)
549a2590e5Sderaadt {
55285f06efSderaadt 	unsigned char *s, *t, *end = buffer + length;
56285f06efSderaadt 	int len, code;
579a2590e5Sderaadt 
589a2590e5Sderaadt 	for (s = buffer; *s != DHO_END && s < end; ) {
599a2590e5Sderaadt 		code = s[0];
609a2590e5Sderaadt 
619a2590e5Sderaadt 		/* Pad options don't have a length - just skip them. */
629a2590e5Sderaadt 		if (code == DHO_PAD) {
63f1e89499Shenning 			s++;
649a2590e5Sderaadt 			continue;
659a2590e5Sderaadt 		}
669a2590e5Sderaadt 
67c714dadcShenning 		/*
6899c003b1Skrw 		 * All options other than DHO_PAD and DHO_END have a one-byte
6999c003b1Skrw 		 * length field. It could be 0! Make sure that the length byte
7099c003b1Skrw 		 * is present, and all the data is available.
71c714dadcShenning 		 */
7299c003b1Skrw 		if (s + 1 < end) {
739a2590e5Sderaadt 			len = s[1];
7499c003b1Skrw 			if (s + 1 + len < end) {
7599c003b1Skrw 				; /* option data is all there. */
7699c003b1Skrw 			} else {
77b6fc88b9Skrw 				warning("option %s (%d) larger than buffer.",
78b6fc88b9Skrw 				    dhcp_options[code].name, len);
7902e02bd5Skrw 				return (0);
809a2590e5Sderaadt 			}
8199c003b1Skrw 		} else {
8299c003b1Skrw 			warning("option %s has no length field.",
8399c003b1Skrw 			    dhcp_options[code].name);
8499c003b1Skrw 			return (0);
8599c003b1Skrw 		}
86df453039Skrw 
87df453039Skrw 		/*
88df453039Skrw 		 * Strip trailing NULs from ascii ('t') options. They
89df453039Skrw 		 * will be treated as DHO_PAD options. i.e. ignored. RFC 2132
90df453039Skrw 		 * says "Options containing NVT ASCII data SHOULD NOT include
91df453039Skrw 		 * a trailing NULL; however, the receiver of such options
92df453039Skrw 		 * MUST be prepared to delete trailing nulls if they exist."
93df453039Skrw 		 */
94df453039Skrw 		if (dhcp_options[code].format[0] == 't') {
9599c003b1Skrw 			while (len > 0 && s[len + 1] == '\0')
9699c003b1Skrw 				len--;
97df453039Skrw 		}
98df453039Skrw 
99c714dadcShenning 		/*
100c714dadcShenning 		 * If we haven't seen this option before, just make
101c714dadcShenning 		 * space for it and copy it there.
102c714dadcShenning 		 */
1034f062ee3Skrw 		if (!options[code].data) {
1048e916ab9Shenning 			if (!(t = calloc(1, len + 1)))
1059a2590e5Sderaadt 				error("Can't allocate storage for option %s.",
1069a2590e5Sderaadt 				    dhcp_options[code].name);
107c714dadcShenning 			/*
108c714dadcShenning 			 * Copy and NUL-terminate the option (in case
109cff08477Sstevesk 			 * it's an ASCII string).
110c714dadcShenning 			 */
1119a2590e5Sderaadt 			memcpy(t, &s[2], len);
1129a2590e5Sderaadt 			t[len] = 0;
1134f062ee3Skrw 			options[code].len = len;
1144f062ee3Skrw 			options[code].data = t;
1159a2590e5Sderaadt 		} else {
116c714dadcShenning 			/*
117c714dadcShenning 			 * If it's a repeat, concatenate it to whatever
11892018899Skrw 			 * we last saw.
119c714dadcShenning 			 */
1204f062ee3Skrw 			t = calloc(1, len + options[code].len + 1);
1219a2590e5Sderaadt 			if (!t)
1229a2590e5Sderaadt 				error("Can't expand storage for option %s.",
1239a2590e5Sderaadt 				    dhcp_options[code].name);
1244f062ee3Skrw 			memcpy(t, options[code].data, options[code].len);
1254f062ee3Skrw 			memcpy(t + options[code].len, &s[2], len);
1264f062ee3Skrw 			options[code].len += len;
1274f062ee3Skrw 			t[options[code].len] = 0;
1284f062ee3Skrw 			free(options[code].data);
1294f062ee3Skrw 			options[code].data = t;
1309a2590e5Sderaadt 		}
1319a2590e5Sderaadt 		s += len + 2;
1329a2590e5Sderaadt 	}
13302e02bd5Skrw 
13402e02bd5Skrw 	return (1);
1359a2590e5Sderaadt }
1369a2590e5Sderaadt 
137c714dadcShenning /*
13896978980Skrw  * Copy as many options as fit in buflen bytes of buf. Return the
13996978980Skrw  * offset of the start of the last option copied. A caller can check
14096978980Skrw  * to see if it's DHO_END to decide if all the options were copied.
141c714dadcShenning  */
142c714dadcShenning int
143d6a67f0fSkrw cons_options(struct option_data *options)
1449a2590e5Sderaadt {
145e7cf2d10Skrw 	unsigned char *buf = client->bootrequest_packet.options;
146d6a67f0fSkrw 	int buflen = 576 - DHCP_FIXED_LEN;
14796978980Skrw 	int ix, incr, length, bufix, code, lastopt = -1;
1489a2590e5Sderaadt 
149736b0ed2Skrw 	memset(buf, 0, buflen);
1509a2590e5Sderaadt 
15196978980Skrw 	memcpy(buf, DHCP_OPTIONS_COOKIE, 4);
152d6a67f0fSkrw 	if (options[DHO_DHCP_MESSAGE_TYPE].data) {
153d6a67f0fSkrw 		memcpy(&buf[4], DHCP_OPTIONS_MESSAGE_TYPE, 3);
154d6a67f0fSkrw 		buf[6] = options[DHO_DHCP_MESSAGE_TYPE].data[0];
155d6a67f0fSkrw 		bufix = 7;
156d6a67f0fSkrw 	} else
15796978980Skrw 		bufix = 4;
1589a2590e5Sderaadt 
15996978980Skrw 	for (code = DHO_SUBNET_MASK; code < DHO_END; code++) {
160d6a67f0fSkrw 		if (!options[code].data || code == DHO_DHCP_MESSAGE_TYPE)
1619a2590e5Sderaadt 			continue;
1629a2590e5Sderaadt 
163d7d9bbf5Skrw 		length = options[code].len;
16496978980Skrw 		if (bufix + length + 2*((length+254)/255) >= buflen)
16596978980Skrw 			return (lastopt);
1669a2590e5Sderaadt 
16796978980Skrw 		lastopt = bufix;
1689a2590e5Sderaadt 		ix = 0;
1699a2590e5Sderaadt 
1709a2590e5Sderaadt 		while (length) {
17196978980Skrw 			incr = length > 255 ? 255 : length;
1729a2590e5Sderaadt 
17396978980Skrw 			buf[bufix++] = code;
17496978980Skrw 			buf[bufix++] = incr;
17596978980Skrw 			memcpy(buf + bufix, options[code].data + ix, incr);
1769a2590e5Sderaadt 
1779a2590e5Sderaadt 			length -= incr;
1789a2590e5Sderaadt 			ix += incr;
1796fc9f4f6Skrw 			bufix += incr;
1809a2590e5Sderaadt 		}
1819a2590e5Sderaadt 	}
18296978980Skrw 
18396978980Skrw 	if (bufix < buflen) {
18496978980Skrw 		buf[bufix] = DHO_END;
18596978980Skrw 		lastopt = bufix;
18696978980Skrw 	}
18796978980Skrw 
18896978980Skrw 	return (lastopt);
1899a2590e5Sderaadt }
1909a2590e5Sderaadt 
191c714dadcShenning /*
192c714dadcShenning  * Format the specified option so that a human can easily read it.
193c714dadcShenning  */
194c714dadcShenning char *
195acf4c28bSkrw pretty_print_option(unsigned int code, struct option_data *option,
196acf4c28bSkrw     int emit_punct)
1979a2590e5Sderaadt {
1989a2590e5Sderaadt 	static char optbuf[32768]; /* XXX */
199285f06efSderaadt 	int hunksize = 0, numhunk = -1, numelem = 0;
200285f06efSderaadt 	char fmtbuf[32], *op = optbuf;
201285f06efSderaadt 	int i, j, k, opleft = sizeof(optbuf);
202acf4c28bSkrw 	unsigned char *data = option->data;
2039a2590e5Sderaadt 	unsigned char *dp = data;
204acf4c28bSkrw 	int len = option->len;
2059a2590e5Sderaadt 	struct in_addr foo;
2069a2590e5Sderaadt 	char comma;
2079a2590e5Sderaadt 
2082f18daabSkrw 	memset(optbuf, 0, sizeof(optbuf));
2092f18daabSkrw 
2109a2590e5Sderaadt 	/* Code should be between 0 and 255. */
2112f18daabSkrw 	if (code > 255) {
2122f18daabSkrw 		warning("pretty_print_option: bad code %d", code);
2132f18daabSkrw 		goto done;
2142f18daabSkrw 	}
2159a2590e5Sderaadt 
216acf4c28bSkrw 	if (emit_punct)
2179a2590e5Sderaadt 		comma = ',';
2189a2590e5Sderaadt 	else
2199a2590e5Sderaadt 		comma = ' ';
2209a2590e5Sderaadt 
2219a2590e5Sderaadt 	/* Figure out the size of the data. */
2229a2590e5Sderaadt 	for (i = 0; dhcp_options[code].format[i]; i++) {
2239a2590e5Sderaadt 		if (!numhunk) {
224c955dd46Smickey 			warning("%s: Excess information in format string: %s",
2259a2590e5Sderaadt 			    dhcp_options[code].name,
2269a2590e5Sderaadt 			    &(dhcp_options[code].format[i]));
2272f18daabSkrw 			goto done;
2289a2590e5Sderaadt 		}
2299a2590e5Sderaadt 		numelem++;
2309a2590e5Sderaadt 		fmtbuf[i] = dhcp_options[code].format[i];
2319a2590e5Sderaadt 		switch (dhcp_options[code].format[i]) {
2329a2590e5Sderaadt 		case 'A':
2339a2590e5Sderaadt 			--numelem;
2349a2590e5Sderaadt 			fmtbuf[i] = 0;
2359a2590e5Sderaadt 			numhunk = 0;
23629432cd9Sphessler 			if (hunksize == 0) {
23729432cd9Sphessler 				warning("%s: no size indicator before A"
23829432cd9Sphessler 				    " in format string: %s",
23929432cd9Sphessler 				    dhcp_options[code].name,
24029432cd9Sphessler 				    dhcp_options[code].format);
2412f18daabSkrw 				goto done;
24229432cd9Sphessler 			}
2439a2590e5Sderaadt 			break;
2449a2590e5Sderaadt 		case 'X':
245c714dadcShenning 			for (k = 0; k < len; k++)
2469a2590e5Sderaadt 				if (!isascii(data[k]) ||
2479a2590e5Sderaadt 				    !isprint(data[k]))
2489a2590e5Sderaadt 					break;
249b54c879eShenning 			if (k == len) {
2509a2590e5Sderaadt 				fmtbuf[i] = 't';
2519a2590e5Sderaadt 				numhunk = -2;
2529a2590e5Sderaadt 			} else {
2539a2590e5Sderaadt 				fmtbuf[i] = 'x';
2549a2590e5Sderaadt 				hunksize++;
2559a2590e5Sderaadt 				comma = ':';
2569a2590e5Sderaadt 				numhunk = 0;
2579a2590e5Sderaadt 			}
2589a2590e5Sderaadt 			fmtbuf[i + 1] = 0;
2599a2590e5Sderaadt 			break;
2609a2590e5Sderaadt 		case 't':
2619a2590e5Sderaadt 			fmtbuf[i] = 't';
2629a2590e5Sderaadt 			fmtbuf[i + 1] = 0;
2639a2590e5Sderaadt 			numhunk = -2;
2649a2590e5Sderaadt 			break;
2659a2590e5Sderaadt 		case 'I':
2669a2590e5Sderaadt 		case 'l':
2679a2590e5Sderaadt 		case 'L':
2689a2590e5Sderaadt 			hunksize += 4;
2699a2590e5Sderaadt 			break;
2709a2590e5Sderaadt 		case 's':
2719a2590e5Sderaadt 		case 'S':
2729a2590e5Sderaadt 			hunksize += 2;
2739a2590e5Sderaadt 			break;
2749a2590e5Sderaadt 		case 'b':
2759a2590e5Sderaadt 		case 'B':
2769a2590e5Sderaadt 		case 'f':
2779a2590e5Sderaadt 			hunksize++;
2789a2590e5Sderaadt 			break;
2799a2590e5Sderaadt 		case 'e':
2809a2590e5Sderaadt 			break;
2819a2590e5Sderaadt 		default:
282c955dd46Smickey 			warning("%s: garbage in format string: %s",
2839a2590e5Sderaadt 			    dhcp_options[code].name,
2849a2590e5Sderaadt 			    &(dhcp_options[code].format[i]));
2852f18daabSkrw 			goto done;
2869a2590e5Sderaadt 		}
2879a2590e5Sderaadt 	}
2889a2590e5Sderaadt 
289d22f105fSkrw 	/* Check for too few bytes. */
2909a2590e5Sderaadt 	if (hunksize > len) {
291c955dd46Smickey 		warning("%s: expecting at least %d bytes; got %d",
292c714dadcShenning 		    dhcp_options[code].name, hunksize, len);
2932f18daabSkrw 		goto done;
2949a2590e5Sderaadt 	}
295d22f105fSkrw 	/* Check for too many bytes. */
2962f18daabSkrw 	if (numhunk == -1 && hunksize < len) {
29728f2359aSkrw 		warning("%s: expecting only %d bytes: got %d",
29828f2359aSkrw 		    dhcp_options[code].name, hunksize, len);
2992f18daabSkrw 		goto done;
3002f18daabSkrw 	}
3019a2590e5Sderaadt 
3029a2590e5Sderaadt 	/* If this is an array, compute its size. */
3039a2590e5Sderaadt 	if (!numhunk)
3049a2590e5Sderaadt 		numhunk = len / hunksize;
3059a2590e5Sderaadt 	/* See if we got an exact number of hunks. */
3062f18daabSkrw 	if (numhunk > 0 && numhunk * hunksize != len) {
3072f18daabSkrw 		warning("%s: expecting %d bytes: got %d",
3082f18daabSkrw 		    dhcp_options[code].name, numhunk * hunksize, len);
3092f18daabSkrw 		goto done;
3102f18daabSkrw 	}
3119a2590e5Sderaadt 
3129a2590e5Sderaadt 	/* A one-hunk array prints the same as a single hunk. */
3139a2590e5Sderaadt 	if (numhunk < 0)
3149a2590e5Sderaadt 		numhunk = 1;
3159a2590e5Sderaadt 
3169a2590e5Sderaadt 	/* Cycle through the array (or hunk) printing the data. */
3179a2590e5Sderaadt 	for (i = 0; i < numhunk; i++) {
3189a2590e5Sderaadt 		for (j = 0; j < numelem; j++) {
3199a2590e5Sderaadt 			int opcount;
320975511d9Spvalchev 			size_t oplen;
3219a2590e5Sderaadt 			switch (fmtbuf[j]) {
3229a2590e5Sderaadt 			case 't':
323acf4c28bSkrw 				if (emit_punct) {
3249a2590e5Sderaadt 					*op++ = '"';
3259a2590e5Sderaadt 					opleft--;
3269a2590e5Sderaadt 				}
3279a2590e5Sderaadt 				for (; dp < data + len; dp++) {
3289a2590e5Sderaadt 					if (!isascii(*dp) ||
3299a2590e5Sderaadt 					    !isprint(*dp)) {
3309a2590e5Sderaadt 						if (dp + 1 != data + len ||
3319a2590e5Sderaadt 						    *dp != 0) {
332d0538928Spvalchev 							size_t oplen;
3339a2590e5Sderaadt 							snprintf(op, opleft,
3349a2590e5Sderaadt 							    "\\%03o", *dp);
335d0538928Spvalchev 							oplen = strlen(op);
336d0538928Spvalchev 							op += oplen;
337d0538928Spvalchev 							opleft -= oplen;
3389a2590e5Sderaadt 						}
3399a2590e5Sderaadt 					} else if (*dp == '"' ||
3409a2590e5Sderaadt 					    *dp == '\'' ||
3419a2590e5Sderaadt 					    *dp == '$' ||
3429a2590e5Sderaadt 					    *dp == '`' ||
3439a2590e5Sderaadt 					    *dp == '\\') {
3449a2590e5Sderaadt 						*op++ = '\\';
3459a2590e5Sderaadt 						*op++ = *dp;
3469a2590e5Sderaadt 						opleft -= 2;
3479a2590e5Sderaadt 					} else {
3489a2590e5Sderaadt 						*op++ = *dp;
3499a2590e5Sderaadt 						opleft--;
3509a2590e5Sderaadt 					}
3519a2590e5Sderaadt 				}
352acf4c28bSkrw 				if (emit_punct) {
3539a2590e5Sderaadt 					*op++ = '"';
3549a2590e5Sderaadt 					opleft--;
3559a2590e5Sderaadt 				}
3569a2590e5Sderaadt 
3579a2590e5Sderaadt 				*op = 0;
3589a2590e5Sderaadt 				break;
3599a2590e5Sderaadt 			case 'I':
3609a2590e5Sderaadt 				foo.s_addr = htonl(getULong(dp));
361c714dadcShenning 				opcount = strlcpy(op, inet_ntoa(foo), opleft);
3629a2590e5Sderaadt 				if (opcount >= opleft)
3639a2590e5Sderaadt 					goto toobig;
3649a2590e5Sderaadt 				dp += 4;
3659a2590e5Sderaadt 				break;
3669a2590e5Sderaadt 			case 'l':
3679a2590e5Sderaadt 				opcount = snprintf(op, opleft, "%ld",
3689a2590e5Sderaadt 				    (long)getLong(dp));
3696ed3db51Sderaadt 				if (opcount >= opleft || opcount == -1)
3709a2590e5Sderaadt 					goto toobig;
3719a2590e5Sderaadt 				dp += 4;
3729a2590e5Sderaadt 				break;
3739a2590e5Sderaadt 			case 'L':
3749a2590e5Sderaadt 				opcount = snprintf(op, opleft, "%ld",
3759a2590e5Sderaadt 				    (unsigned long)getULong(dp));
3766ed3db51Sderaadt 				if (opcount >= opleft || opcount == -1)
3779a2590e5Sderaadt 					goto toobig;
3789a2590e5Sderaadt 				dp += 4;
3799a2590e5Sderaadt 				break;
3809a2590e5Sderaadt 			case 's':
3819a2590e5Sderaadt 				opcount = snprintf(op, opleft, "%d",
3829a2590e5Sderaadt 				    getShort(dp));
3836ed3db51Sderaadt 				if (opcount >= opleft || opcount == -1)
3849a2590e5Sderaadt 					goto toobig;
3859a2590e5Sderaadt 				dp += 2;
3869a2590e5Sderaadt 				break;
3879a2590e5Sderaadt 			case 'S':
3889a2590e5Sderaadt 				opcount = snprintf(op, opleft, "%d",
3899a2590e5Sderaadt 				    getUShort(dp));
3906ed3db51Sderaadt 				if (opcount >= opleft || opcount == -1)
3919a2590e5Sderaadt 					goto toobig;
3929a2590e5Sderaadt 				dp += 2;
3939a2590e5Sderaadt 				break;
3949a2590e5Sderaadt 			case 'b':
3959a2590e5Sderaadt 				opcount = snprintf(op, opleft, "%d",
396*de3ca9dbSkrw 				    *(char *)dp);
3976ed3db51Sderaadt 				if (opcount >= opleft || opcount == -1)
3989a2590e5Sderaadt 					goto toobig;
399*de3ca9dbSkrw 				dp++;
4009a2590e5Sderaadt 				break;
4019a2590e5Sderaadt 			case 'B':
402*de3ca9dbSkrw 				opcount = snprintf(op, opleft, "%d", *dp);
4036ed3db51Sderaadt 				if (opcount >= opleft || opcount == -1)
4049a2590e5Sderaadt 					goto toobig;
405*de3ca9dbSkrw 				dp++;
4069a2590e5Sderaadt 				break;
4079a2590e5Sderaadt 			case 'x':
408*de3ca9dbSkrw 				opcount = snprintf(op, opleft, "%x", *dp);
4096ed3db51Sderaadt 				if (opcount >= opleft || opcount == -1)
4109a2590e5Sderaadt 					goto toobig;
411*de3ca9dbSkrw 				dp++;
4129a2590e5Sderaadt 				break;
4139a2590e5Sderaadt 			case 'f':
4149a2590e5Sderaadt 				opcount = strlcpy(op,
415*de3ca9dbSkrw 				    *dp ? "true" : "false", opleft);
4169a2590e5Sderaadt 				if (opcount >= opleft)
4179a2590e5Sderaadt 					goto toobig;
418*de3ca9dbSkrw 				dp++;
4199a2590e5Sderaadt 				break;
4209a2590e5Sderaadt 			default:
421c955dd46Smickey 				warning("Unexpected format code %c", fmtbuf[j]);
4229a2590e5Sderaadt 			}
423975511d9Spvalchev 			oplen = strlen(op);
424975511d9Spvalchev 			op += oplen;
425975511d9Spvalchev 			opleft -= oplen;
4269a2590e5Sderaadt 			if (opleft < 1)
4279a2590e5Sderaadt 				goto toobig;
4289a2590e5Sderaadt 			if (j + 1 < numelem && comma != ':') {
4299a2590e5Sderaadt 				*op++ = ' ';
4309a2590e5Sderaadt 				opleft--;
4319a2590e5Sderaadt 			}
4329a2590e5Sderaadt 		}
4339a2590e5Sderaadt 		if (i + 1 < numhunk) {
4349a2590e5Sderaadt 			*op++ = comma;
4359a2590e5Sderaadt 			opleft--;
4369a2590e5Sderaadt 		}
4379a2590e5Sderaadt 		if (opleft < 1)
4389a2590e5Sderaadt 			goto toobig;
4399a2590e5Sderaadt 
4409a2590e5Sderaadt 	}
4412f18daabSkrw 
4422f18daabSkrw done:
443c714dadcShenning 	return (optbuf);
4442f18daabSkrw 
4459a2590e5Sderaadt toobig:
4462f18daabSkrw 	memset(optbuf, 0, sizeof(optbuf));
4472f18daabSkrw 	return (optbuf);
4489a2590e5Sderaadt }
4499a2590e5Sderaadt 
450c714dadcShenning void
451917d8addSkrw do_packet(unsigned int from_port, struct in_addr from,
452393831bbSkrw     struct ether_addr *hfrom)
4539a2590e5Sderaadt {
45402e02bd5Skrw 	struct dhcp_packet *packet = &client->packet;
4554f062ee3Skrw 	struct option_data options[256];
456b21b72f8Skrw 	struct reject_elem *ap;
4576896c986Skrw 	void (*handler)(struct in_addr, struct option_data *, char *);
4586896c986Skrw 	char *type, *info;
4596896c986Skrw 	int i, rslt, options_valid = 1;
4609a2590e5Sderaadt 
461393831bbSkrw 	if (packet->hlen != ETHER_ADDR_LEN) {
462aff84b99Skrw #ifdef DEBUG
463aff84b99Skrw 		debug("Discarding packet with hlen != %s (%u)",
464aff84b99Skrw 		    ifi->name, packet->hlen);
465aff84b99Skrw #endif
466aff84b99Skrw 		return;
467393831bbSkrw 	} else if (memcmp(&ifi->hw_address, packet->chaddr,
468393831bbSkrw 	    sizeof(ifi->hw_address))) {
469aff84b99Skrw #ifdef DEBUG
470aff84b99Skrw 		debug("Discarding packet with chaddr != %s (%s)", ifi->name,
471aff84b99Skrw 		    ether_ntoa((struct ether_addr *)packet->chaddr));
472aff84b99Skrw #endif
4739a2590e5Sderaadt 		return;
4749a2590e5Sderaadt 	}
4759a2590e5Sderaadt 
476aff84b99Skrw 	if (client->xid != client->packet.xid) {
477aff84b99Skrw #ifdef DEBUG
478aff84b99Skrw 		debug("Discarding packet with XID != %u (%u)", client->xid,
479aff84b99Skrw 		    client->packet.xid);
480aff84b99Skrw #endif
48102e02bd5Skrw 		return;
482aff84b99Skrw 	}
483aff84b99Skrw 
484aff84b99Skrw 	for (ap = config->reject_list; ap; ap = ap->next)
485aff84b99Skrw 		if (from.s_addr == ap->addr.s_addr) {
486aff84b99Skrw #ifdef DEBUG
487aff84b99Skrw 			debug("Discarding packet from address on reject list "
488aff84b99Skrw 			    "(%s)", inet_ntoa(from));
489aff84b99Skrw #endif
490aff84b99Skrw 			return;
491aff84b99Skrw 		}
4929a2590e5Sderaadt 
49302e02bd5Skrw 	memset(options, 0, sizeof(options));
49402e02bd5Skrw 
49502e02bd5Skrw 	if (memcmp(&packet->options, DHCP_OPTIONS_COOKIE, 4) == 0) {
49602e02bd5Skrw 		/* Parse the BOOTP/DHCP options field. */
49702e02bd5Skrw 		options_valid = parse_option_buffer(options,
49802e02bd5Skrw 		    &packet->options[4], sizeof(packet->options) - 4);
49902e02bd5Skrw 
50002e02bd5Skrw 		/* Only DHCP packets have overload areas for options. */
50102e02bd5Skrw 		if (options_valid &&
50202e02bd5Skrw 		    options[DHO_DHCP_MESSAGE_TYPE].data &&
50302e02bd5Skrw 		    options[DHO_DHCP_OPTION_OVERLOAD].data) {
50402e02bd5Skrw 			if (options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 1)
50502e02bd5Skrw 				options_valid = parse_option_buffer(options,
50602e02bd5Skrw 				    (unsigned char *)packet->file,
50702e02bd5Skrw 				    sizeof(packet->file));
50802e02bd5Skrw 			if (options_valid &&
50902e02bd5Skrw 			    options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 2)
51002e02bd5Skrw 				options_valid = parse_option_buffer(options,
51102e02bd5Skrw 				    (unsigned char *)packet->sname,
51202e02bd5Skrw 				    sizeof(packet->sname));
51302e02bd5Skrw 		}
51402e02bd5Skrw 	}
51502e02bd5Skrw 
5166896c986Skrw 	type = "<unknown>";
51702e02bd5Skrw 	handler = NULL;
51802e02bd5Skrw 
5194f062ee3Skrw 	if (options[DHO_DHCP_MESSAGE_TYPE].data) {
52002e02bd5Skrw 		/* Always try a DHCP packet, even if a bad option was seen. */
52102e02bd5Skrw 		switch (options[DHO_DHCP_MESSAGE_TYPE].data[0]) {
52202e02bd5Skrw 		case DHCPOFFER:
52302e02bd5Skrw 			handler = dhcpoffer;
52402e02bd5Skrw 			type = "DHCPOFFER";
52502e02bd5Skrw 			break;
52602e02bd5Skrw 		case DHCPNAK:
52702e02bd5Skrw 			handler = dhcpnak;
52802e02bd5Skrw 			type = "DHCPNACK";
52902e02bd5Skrw 			break;
53002e02bd5Skrw 		case DHCPACK:
53102e02bd5Skrw 			handler = dhcpack;
53202e02bd5Skrw 			type = "DHCPACK";
53302e02bd5Skrw 			break;
53402e02bd5Skrw 		default:
535aff84b99Skrw #ifdef DEBUG
536aff84b99Skrw 			debug("Discarding DHCP packet of unknown type (%d)",
537aff84b99Skrw 				options[DHO_DHCP_MESSAGE_TYPE].data[0]);
538aff84b99Skrw #endif
53902e02bd5Skrw 			break;
54002e02bd5Skrw 		}
54102e02bd5Skrw 	} else if (options_valid && packet->op == BOOTREPLY) {
54202e02bd5Skrw 		handler = dhcpoffer;
54302e02bd5Skrw 		type = "BOOTREPLY";
544aff84b99Skrw 	} else {
545aff84b99Skrw #ifdef DEBUG
546aff84b99Skrw 		debug("Discarding packet which is neither DHCP nor BOOTP");
547aff84b99Skrw #endif
54802e02bd5Skrw 	}
5499a2590e5Sderaadt 
5506896c986Skrw 	rslt = asprintf(&info, "%s from %s (%s)", type, inet_ntoa(from),
551393831bbSkrw 	    ether_ntoa(hfrom));
5526896c986Skrw 	if (rslt == -1)
5536896c986Skrw 		error("no memory for info string");
5546896c986Skrw 
55502e02bd5Skrw 	if (handler)
5566896c986Skrw 		(*handler)(from, options, info);
5576896c986Skrw 
5586896c986Skrw 	free(info);
55902e02bd5Skrw 
560c714dadcShenning 	for (i = 0; i < 256; i++)
5614f062ee3Skrw 		if (options[i].len && options[i].data)
5624f062ee3Skrw 			free(options[i].data);
5639a2590e5Sderaadt }
564