xref: /openbsd/sbin/dhclient/options.c (revision 02e02bd5)
1*02e02bd5Skrw /*	$OpenBSD: options.c,v 1.33 2007/01/25 01:21:04 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 
47*02e02bd5Skrw int parse_option_buffer(struct option_data *, unsigned char *, int);
489a2590e5Sderaadt 
49c714dadcShenning /*
50c714dadcShenning  * Parse options out of the specified buffer, storing addresses of
514f062ee3Skrw  * option values in options and setting client->options_valid if
52c714dadcShenning  * no errors are encountered.
53c714dadcShenning  */
54*02e02bd5Skrw int
554f062ee3Skrw parse_option_buffer(struct option_data *options, unsigned char *buffer,
564f062ee3Skrw     int length)
579a2590e5Sderaadt {
58285f06efSderaadt 	unsigned char *s, *t, *end = buffer + length;
59285f06efSderaadt 	int len, code;
609a2590e5Sderaadt 
619a2590e5Sderaadt 	for (s = buffer; *s != DHO_END && s < end; ) {
629a2590e5Sderaadt 		code = s[0];
639a2590e5Sderaadt 
649a2590e5Sderaadt 		/* Pad options don't have a length - just skip them. */
659a2590e5Sderaadt 		if (code == DHO_PAD) {
66f1e89499Shenning 			s++;
679a2590e5Sderaadt 			continue;
689a2590e5Sderaadt 		}
699a2590e5Sderaadt 
70c714dadcShenning 		/*
71b6fc88b9Skrw 		 * All options other than DHO_PAD and DHO_END have a
72b6fc88b9Skrw 		 * one-byte length field.
73c714dadcShenning 		 */
74b6fc88b9Skrw 		if (s + 2 > end)
75b6fc88b9Skrw 			len = 0;
76b6fc88b9Skrw 		else
779a2590e5Sderaadt 			len = s[1];
789a2590e5Sderaadt 
79c714dadcShenning 		/*
80b6fc88b9Skrw 		 * If the option claims to extend beyond the end of the buffer
81b6fc88b9Skrw 		 * then mark the options buffer bad.
829a2590e5Sderaadt 		 */
839a2590e5Sderaadt 		if (s + len + 2 > end) {
84b6fc88b9Skrw 			warning("option %s (%d) larger than buffer.",
85b6fc88b9Skrw 			    dhcp_options[code].name, len);
86c955dd46Smickey 			warning("rejecting bogus offer.");
87*02e02bd5Skrw 			return (0);
889a2590e5Sderaadt 		}
89c714dadcShenning 		/*
90c714dadcShenning 		 * If we haven't seen this option before, just make
91c714dadcShenning 		 * space for it and copy it there.
92c714dadcShenning 		 */
934f062ee3Skrw 		if (!options[code].data) {
948e916ab9Shenning 			if (!(t = calloc(1, len + 1)))
959a2590e5Sderaadt 				error("Can't allocate storage for option %s.",
969a2590e5Sderaadt 				    dhcp_options[code].name);
97c714dadcShenning 			/*
98c714dadcShenning 			 * Copy and NUL-terminate the option (in case
99cff08477Sstevesk 			 * it's an ASCII string).
100c714dadcShenning 			 */
1019a2590e5Sderaadt 			memcpy(t, &s[2], len);
1029a2590e5Sderaadt 			t[len] = 0;
1034f062ee3Skrw 			options[code].len = len;
1044f062ee3Skrw 			options[code].data = t;
1059a2590e5Sderaadt 		} else {
106c714dadcShenning 			/*
107c714dadcShenning 			 * If it's a repeat, concatenate it to whatever
108c714dadcShenning 			 * we last saw.   This is really only required
109c714dadcShenning 			 * for clients, but what the heck...
110c714dadcShenning 			 */
1114f062ee3Skrw 			t = calloc(1, len + options[code].len + 1);
1129a2590e5Sderaadt 			if (!t)
1139a2590e5Sderaadt 				error("Can't expand storage for option %s.",
1149a2590e5Sderaadt 				    dhcp_options[code].name);
1154f062ee3Skrw 			memcpy(t, options[code].data, options[code].len);
1164f062ee3Skrw 			memcpy(t + options[code].len, &s[2], len);
1174f062ee3Skrw 			options[code].len += len;
1184f062ee3Skrw 			t[options[code].len] = 0;
1194f062ee3Skrw 			free(options[code].data);
1204f062ee3Skrw 			options[code].data = t;
1219a2590e5Sderaadt 		}
1229a2590e5Sderaadt 		s += len + 2;
1239a2590e5Sderaadt 	}
124*02e02bd5Skrw 
125*02e02bd5Skrw 	return (1);
1269a2590e5Sderaadt }
1279a2590e5Sderaadt 
128c714dadcShenning /*
12996978980Skrw  * Copy as many options as fit in buflen bytes of buf. Return the
13096978980Skrw  * offset of the start of the last option copied. A caller can check
13196978980Skrw  * to see if it's DHO_END to decide if all the options were copied.
132c714dadcShenning  */
133c714dadcShenning int
13496978980Skrw cons_options(unsigned char *buf, const int buflen, struct option_data *options)
1359a2590e5Sderaadt {
13696978980Skrw 	int ix, incr, length, bufix, code, lastopt = -1;
1379a2590e5Sderaadt 
13896978980Skrw 	bzero(buf, buflen);
1399a2590e5Sderaadt 
14096978980Skrw 	if (buflen > 3)
14196978980Skrw 		memcpy(buf, DHCP_OPTIONS_COOKIE, 4);
14296978980Skrw 	bufix = 4;
1439a2590e5Sderaadt 
14496978980Skrw 	for (code = DHO_SUBNET_MASK; code < DHO_END; code++) {
145f5396ce2Skrw 		if (!options[code].data)
1469a2590e5Sderaadt 			continue;
1479a2590e5Sderaadt 
148d7d9bbf5Skrw 		length = options[code].len;
14996978980Skrw 		if (bufix + length + 2*((length+254)/255) >= buflen)
15096978980Skrw 			return (lastopt);
1519a2590e5Sderaadt 
15296978980Skrw 		lastopt = bufix;
1539a2590e5Sderaadt 		ix = 0;
1549a2590e5Sderaadt 
1559a2590e5Sderaadt 		while (length) {
15696978980Skrw 			incr = length > 255 ? 255 : length;
1579a2590e5Sderaadt 
15896978980Skrw 			buf[bufix++] = code;
15996978980Skrw 			buf[bufix++] = incr;
16096978980Skrw 			memcpy(buf + bufix, options[code].data + ix, incr);
1619a2590e5Sderaadt 
1629a2590e5Sderaadt 			length -= incr;
1639a2590e5Sderaadt 			ix += incr;
1646fc9f4f6Skrw 			bufix += incr;
1659a2590e5Sderaadt 		}
1669a2590e5Sderaadt 	}
16796978980Skrw 
16896978980Skrw 	if (bufix < buflen) {
16996978980Skrw 		buf[bufix] = DHO_END;
17096978980Skrw 		lastopt = bufix;
17196978980Skrw 	}
17296978980Skrw 
17396978980Skrw 	return (lastopt);
1749a2590e5Sderaadt }
1759a2590e5Sderaadt 
176c714dadcShenning /*
177c714dadcShenning  * Format the specified option so that a human can easily read it.
178c714dadcShenning  */
179c714dadcShenning char *
180c714dadcShenning pretty_print_option(unsigned int code, unsigned char *data, int len,
181c714dadcShenning     int emit_commas, int emit_quotes)
1829a2590e5Sderaadt {
1839a2590e5Sderaadt 	static char optbuf[32768]; /* XXX */
184285f06efSderaadt 	int hunksize = 0, numhunk = -1, numelem = 0;
185285f06efSderaadt 	char fmtbuf[32], *op = optbuf;
186285f06efSderaadt 	int i, j, k, opleft = sizeof(optbuf);
1879a2590e5Sderaadt 	unsigned char *dp = data;
1889a2590e5Sderaadt 	struct in_addr foo;
1899a2590e5Sderaadt 	char comma;
1909a2590e5Sderaadt 
1919a2590e5Sderaadt 	/* Code should be between 0 and 255. */
1929a2590e5Sderaadt 	if (code > 255)
1935eb2300aShenning 		error("pretty_print_option: bad code %d", code);
1949a2590e5Sderaadt 
1959a2590e5Sderaadt 	if (emit_commas)
1969a2590e5Sderaadt 		comma = ',';
1979a2590e5Sderaadt 	else
1989a2590e5Sderaadt 		comma = ' ';
1999a2590e5Sderaadt 
2009a2590e5Sderaadt 	/* Figure out the size of the data. */
2019a2590e5Sderaadt 	for (i = 0; dhcp_options[code].format[i]; i++) {
2029a2590e5Sderaadt 		if (!numhunk) {
203c955dd46Smickey 			warning("%s: Excess information in format string: %s",
2049a2590e5Sderaadt 			    dhcp_options[code].name,
2059a2590e5Sderaadt 			    &(dhcp_options[code].format[i]));
2069a2590e5Sderaadt 			break;
2079a2590e5Sderaadt 		}
2089a2590e5Sderaadt 		numelem++;
2099a2590e5Sderaadt 		fmtbuf[i] = dhcp_options[code].format[i];
2109a2590e5Sderaadt 		switch (dhcp_options[code].format[i]) {
2119a2590e5Sderaadt 		case 'A':
2129a2590e5Sderaadt 			--numelem;
2139a2590e5Sderaadt 			fmtbuf[i] = 0;
2149a2590e5Sderaadt 			numhunk = 0;
2159a2590e5Sderaadt 			break;
2169a2590e5Sderaadt 		case 'X':
217c714dadcShenning 			for (k = 0; k < len; k++)
2189a2590e5Sderaadt 				if (!isascii(data[k]) ||
2199a2590e5Sderaadt 				    !isprint(data[k]))
2209a2590e5Sderaadt 					break;
221b54c879eShenning 			if (k == len) {
2229a2590e5Sderaadt 				fmtbuf[i] = 't';
2239a2590e5Sderaadt 				numhunk = -2;
2249a2590e5Sderaadt 			} else {
2259a2590e5Sderaadt 				fmtbuf[i] = 'x';
2269a2590e5Sderaadt 				hunksize++;
2279a2590e5Sderaadt 				comma = ':';
2289a2590e5Sderaadt 				numhunk = 0;
2299a2590e5Sderaadt 			}
2309a2590e5Sderaadt 			fmtbuf[i + 1] = 0;
2319a2590e5Sderaadt 			break;
2329a2590e5Sderaadt 		case 't':
2339a2590e5Sderaadt 			fmtbuf[i] = 't';
2349a2590e5Sderaadt 			fmtbuf[i + 1] = 0;
2359a2590e5Sderaadt 			numhunk = -2;
2369a2590e5Sderaadt 			break;
2379a2590e5Sderaadt 		case 'I':
2389a2590e5Sderaadt 		case 'l':
2399a2590e5Sderaadt 		case 'L':
2409a2590e5Sderaadt 			hunksize += 4;
2419a2590e5Sderaadt 			break;
2429a2590e5Sderaadt 		case 's':
2439a2590e5Sderaadt 		case 'S':
2449a2590e5Sderaadt 			hunksize += 2;
2459a2590e5Sderaadt 			break;
2469a2590e5Sderaadt 		case 'b':
2479a2590e5Sderaadt 		case 'B':
2489a2590e5Sderaadt 		case 'f':
2499a2590e5Sderaadt 			hunksize++;
2509a2590e5Sderaadt 			break;
2519a2590e5Sderaadt 		case 'e':
2529a2590e5Sderaadt 			break;
2539a2590e5Sderaadt 		default:
254c955dd46Smickey 			warning("%s: garbage in format string: %s",
2559a2590e5Sderaadt 			    dhcp_options[code].name,
2569a2590e5Sderaadt 			    &(dhcp_options[code].format[i]));
2579a2590e5Sderaadt 			break;
2589a2590e5Sderaadt 		}
2599a2590e5Sderaadt 	}
2609a2590e5Sderaadt 
2619a2590e5Sderaadt 	/* Check for too few bytes... */
2629a2590e5Sderaadt 	if (hunksize > len) {
263c955dd46Smickey 		warning("%s: expecting at least %d bytes; got %d",
264c714dadcShenning 		    dhcp_options[code].name, hunksize, len);
265c714dadcShenning 		return ("<error>");
2669a2590e5Sderaadt 	}
2679a2590e5Sderaadt 	/* Check for too many bytes... */
2689a2590e5Sderaadt 	if (numhunk == -1 && hunksize < len)
269c955dd46Smickey 		warning("%s: %d extra bytes",
270c714dadcShenning 		    dhcp_options[code].name, len - hunksize);
2719a2590e5Sderaadt 
2729a2590e5Sderaadt 	/* If this is an array, compute its size. */
2739a2590e5Sderaadt 	if (!numhunk)
2749a2590e5Sderaadt 		numhunk = len / hunksize;
2759a2590e5Sderaadt 	/* See if we got an exact number of hunks. */
2769a2590e5Sderaadt 	if (numhunk > 0 && numhunk * hunksize < len)
277c955dd46Smickey 		warning("%s: %d extra bytes at end of array",
278c714dadcShenning 		    dhcp_options[code].name, len - numhunk * hunksize);
2799a2590e5Sderaadt 
2809a2590e5Sderaadt 	/* A one-hunk array prints the same as a single hunk. */
2819a2590e5Sderaadt 	if (numhunk < 0)
2829a2590e5Sderaadt 		numhunk = 1;
2839a2590e5Sderaadt 
2849a2590e5Sderaadt 	/* Cycle through the array (or hunk) printing the data. */
2859a2590e5Sderaadt 	for (i = 0; i < numhunk; i++) {
2869a2590e5Sderaadt 		for (j = 0; j < numelem; j++) {
2879a2590e5Sderaadt 			int opcount;
2889a2590e5Sderaadt 			switch (fmtbuf[j]) {
2899a2590e5Sderaadt 			case 't':
2909a2590e5Sderaadt 				if (emit_quotes) {
2919a2590e5Sderaadt 					*op++ = '"';
2929a2590e5Sderaadt 					opleft--;
2939a2590e5Sderaadt 				}
2949a2590e5Sderaadt 				for (; dp < data + len; dp++) {
2959a2590e5Sderaadt 					if (!isascii(*dp) ||
2969a2590e5Sderaadt 					    !isprint(*dp)) {
2979a2590e5Sderaadt 						if (dp + 1 != data + len ||
2989a2590e5Sderaadt 						    *dp != 0) {
2999a2590e5Sderaadt 							snprintf(op, opleft,
3009a2590e5Sderaadt 							    "\\%03o", *dp);
3019a2590e5Sderaadt 							op += 4;
3029a2590e5Sderaadt 							opleft -= 4;
3039a2590e5Sderaadt 						}
3049a2590e5Sderaadt 					} else if (*dp == '"' ||
3059a2590e5Sderaadt 					    *dp == '\'' ||
3069a2590e5Sderaadt 					    *dp == '$' ||
3079a2590e5Sderaadt 					    *dp == '`' ||
3089a2590e5Sderaadt 					    *dp == '\\') {
3099a2590e5Sderaadt 						*op++ = '\\';
3109a2590e5Sderaadt 						*op++ = *dp;
3119a2590e5Sderaadt 						opleft -= 2;
3129a2590e5Sderaadt 					} else {
3139a2590e5Sderaadt 						*op++ = *dp;
3149a2590e5Sderaadt 						opleft--;
3159a2590e5Sderaadt 					}
3169a2590e5Sderaadt 				}
3179a2590e5Sderaadt 				if (emit_quotes) {
3189a2590e5Sderaadt 					*op++ = '"';
3199a2590e5Sderaadt 					opleft--;
3209a2590e5Sderaadt 				}
3219a2590e5Sderaadt 
3229a2590e5Sderaadt 				*op = 0;
3239a2590e5Sderaadt 				break;
3249a2590e5Sderaadt 			case 'I':
3259a2590e5Sderaadt 				foo.s_addr = htonl(getULong(dp));
326c714dadcShenning 				opcount = strlcpy(op, inet_ntoa(foo), opleft);
3279a2590e5Sderaadt 				if (opcount >= opleft)
3289a2590e5Sderaadt 					goto toobig;
3299a2590e5Sderaadt 				opleft -= opcount;
3309a2590e5Sderaadt 				dp += 4;
3319a2590e5Sderaadt 				break;
3329a2590e5Sderaadt 			case 'l':
3339a2590e5Sderaadt 				opcount = snprintf(op, opleft, "%ld",
3349a2590e5Sderaadt 				    (long)getLong(dp));
3356ed3db51Sderaadt 				if (opcount >= opleft || opcount == -1)
3369a2590e5Sderaadt 					goto toobig;
3379a2590e5Sderaadt 				opleft -= opcount;
3389a2590e5Sderaadt 				dp += 4;
3399a2590e5Sderaadt 				break;
3409a2590e5Sderaadt 			case 'L':
3419a2590e5Sderaadt 				opcount = snprintf(op, opleft, "%ld",
3429a2590e5Sderaadt 				    (unsigned long)getULong(dp));
3436ed3db51Sderaadt 				if (opcount >= opleft || opcount == -1)
3449a2590e5Sderaadt 					goto toobig;
3459a2590e5Sderaadt 				opleft -= opcount;
3469a2590e5Sderaadt 				dp += 4;
3479a2590e5Sderaadt 				break;
3489a2590e5Sderaadt 			case 's':
3499a2590e5Sderaadt 				opcount = snprintf(op, opleft, "%d",
3509a2590e5Sderaadt 				    getShort(dp));
3516ed3db51Sderaadt 				if (opcount >= opleft || opcount == -1)
3529a2590e5Sderaadt 					goto toobig;
3539a2590e5Sderaadt 				opleft -= opcount;
3549a2590e5Sderaadt 				dp += 2;
3559a2590e5Sderaadt 				break;
3569a2590e5Sderaadt 			case 'S':
3579a2590e5Sderaadt 				opcount = snprintf(op, opleft, "%d",
3589a2590e5Sderaadt 				    getUShort(dp));
3596ed3db51Sderaadt 				if (opcount >= opleft || opcount == -1)
3609a2590e5Sderaadt 					goto toobig;
3619a2590e5Sderaadt 				opleft -= opcount;
3629a2590e5Sderaadt 				dp += 2;
3639a2590e5Sderaadt 				break;
3649a2590e5Sderaadt 			case 'b':
3659a2590e5Sderaadt 				opcount = snprintf(op, opleft, "%d",
3669a2590e5Sderaadt 				    *(char *)dp++);
3676ed3db51Sderaadt 				if (opcount >= opleft || opcount == -1)
3689a2590e5Sderaadt 					goto toobig;
3699a2590e5Sderaadt 				opleft -= opcount;
3709a2590e5Sderaadt 				break;
3719a2590e5Sderaadt 			case 'B':
3729a2590e5Sderaadt 				opcount = snprintf(op, opleft, "%d", *dp++);
3736ed3db51Sderaadt 				if (opcount >= opleft || opcount == -1)
3749a2590e5Sderaadt 					goto toobig;
3759a2590e5Sderaadt 				opleft -= opcount;
3769a2590e5Sderaadt 				break;
3779a2590e5Sderaadt 			case 'x':
3789a2590e5Sderaadt 				opcount = snprintf(op, opleft, "%x", *dp++);
3796ed3db51Sderaadt 				if (opcount >= opleft || opcount == -1)
3809a2590e5Sderaadt 					goto toobig;
3819a2590e5Sderaadt 				opleft -= opcount;
3829a2590e5Sderaadt 				break;
3839a2590e5Sderaadt 			case 'f':
3849a2590e5Sderaadt 				opcount = strlcpy(op,
3859a2590e5Sderaadt 				    *dp++ ? "true" : "false", opleft);
3869a2590e5Sderaadt 				if (opcount >= opleft)
3879a2590e5Sderaadt 					goto toobig;
3889a2590e5Sderaadt 				opleft -= opcount;
3899a2590e5Sderaadt 				break;
3909a2590e5Sderaadt 			default:
391c955dd46Smickey 				warning("Unexpected format code %c", fmtbuf[j]);
3929a2590e5Sderaadt 			}
3939a2590e5Sderaadt 			op += strlen(op);
3949a2590e5Sderaadt 			opleft -= strlen(op);
3959a2590e5Sderaadt 			if (opleft < 1)
3969a2590e5Sderaadt 				goto toobig;
3979a2590e5Sderaadt 			if (j + 1 < numelem && comma != ':') {
3989a2590e5Sderaadt 				*op++ = ' ';
3999a2590e5Sderaadt 				opleft--;
4009a2590e5Sderaadt 			}
4019a2590e5Sderaadt 		}
4029a2590e5Sderaadt 		if (i + 1 < numhunk) {
4039a2590e5Sderaadt 			*op++ = comma;
4049a2590e5Sderaadt 			opleft--;
4059a2590e5Sderaadt 		}
4069a2590e5Sderaadt 		if (opleft < 1)
4079a2590e5Sderaadt 			goto toobig;
4089a2590e5Sderaadt 
4099a2590e5Sderaadt 	}
410c714dadcShenning 	return (optbuf);
4119a2590e5Sderaadt  toobig:
412c955dd46Smickey 	warning("dhcp option too large");
413c714dadcShenning 	return ("<error>");
4149a2590e5Sderaadt }
4159a2590e5Sderaadt 
416c714dadcShenning void
417d5dec8a0Skrw do_packet(int len, unsigned int from_port, struct iaddr from,
418d5dec8a0Skrw     struct hardware *hfrom)
4199a2590e5Sderaadt {
420*02e02bd5Skrw 	struct dhcp_packet *packet = &client->packet;
4214f062ee3Skrw 	struct option_data options[256];
422*02e02bd5Skrw 	struct iaddrlist *ap;
423*02e02bd5Skrw 	void (*handler)(struct iaddr, struct option_data *);
424*02e02bd5Skrw 	char *type;
425*02e02bd5Skrw 	int i, options_valid = 1;
4269a2590e5Sderaadt 
427*02e02bd5Skrw 	if (packet->hlen > sizeof(packet->chaddr)) {
4289a2590e5Sderaadt 		note("Discarding packet with invalid hlen.");
4299a2590e5Sderaadt 		return;
4309a2590e5Sderaadt 	}
4319a2590e5Sderaadt 
432*02e02bd5Skrw 	/*
433*02e02bd5Skrw 	 * Silently drop the packet if the client hardware address in the
434*02e02bd5Skrw 	 * packet is not the hardware address of the interface being managed.
435*02e02bd5Skrw 	 */
436*02e02bd5Skrw 	if ((ifi->hw_address.hlen != packet->hlen) ||
437*02e02bd5Skrw 	    (memcmp(ifi->hw_address.haddr, packet->chaddr, packet->hlen)))
438*02e02bd5Skrw 		return;
4399a2590e5Sderaadt 
440*02e02bd5Skrw 	memset(options, 0, sizeof(options));
441*02e02bd5Skrw 
442*02e02bd5Skrw 	if (memcmp(&packet->options, DHCP_OPTIONS_COOKIE, 4) == 0) {
443*02e02bd5Skrw 		/* Parse the BOOTP/DHCP options field. */
444*02e02bd5Skrw 		options_valid = parse_option_buffer(options,
445*02e02bd5Skrw 		    &packet->options[4], sizeof(packet->options) - 4);
446*02e02bd5Skrw 
447*02e02bd5Skrw 		/* Only DHCP packets have overload areas for options. */
448*02e02bd5Skrw 		if (options_valid &&
449*02e02bd5Skrw 		    options[DHO_DHCP_MESSAGE_TYPE].data &&
450*02e02bd5Skrw 		    options[DHO_DHCP_OPTION_OVERLOAD].data) {
451*02e02bd5Skrw 			if (options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 1)
452*02e02bd5Skrw 				options_valid = parse_option_buffer(options,
453*02e02bd5Skrw 				    (unsigned char *)packet->file,
454*02e02bd5Skrw 				    sizeof(packet->file));
455*02e02bd5Skrw 			if (options_valid &&
456*02e02bd5Skrw 			    options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 2)
457*02e02bd5Skrw 				options_valid = parse_option_buffer(options,
458*02e02bd5Skrw 				    (unsigned char *)packet->sname,
459*02e02bd5Skrw 				    sizeof(packet->sname));
460*02e02bd5Skrw 		}
461*02e02bd5Skrw 	}
462*02e02bd5Skrw 
463*02e02bd5Skrw 	type = "";
464*02e02bd5Skrw 	handler = NULL;
465*02e02bd5Skrw 
4664f062ee3Skrw 	if (options[DHO_DHCP_MESSAGE_TYPE].data) {
467*02e02bd5Skrw 		/* Always try a DHCP packet, even if a bad option was seen. */
468*02e02bd5Skrw 		switch (options[DHO_DHCP_MESSAGE_TYPE].data[0]) {
469*02e02bd5Skrw 		case DHCPOFFER:
470*02e02bd5Skrw 			handler = dhcpoffer;
471*02e02bd5Skrw 			type = "DHCPOFFER";
472*02e02bd5Skrw 			break;
473*02e02bd5Skrw 		case DHCPNAK:
474*02e02bd5Skrw 			handler = dhcpnak;
475*02e02bd5Skrw 			type = "DHCPNACK";
476*02e02bd5Skrw 			break;
477*02e02bd5Skrw 		case DHCPACK:
478*02e02bd5Skrw 			handler = dhcpack;
479*02e02bd5Skrw 			type = "DHCPACK";
480*02e02bd5Skrw 			break;
481*02e02bd5Skrw 		default:
482*02e02bd5Skrw 			break;
483*02e02bd5Skrw 		}
484*02e02bd5Skrw 	} else if (options_valid && packet->op == BOOTREPLY) {
485*02e02bd5Skrw 		handler = dhcpoffer;
486*02e02bd5Skrw 		type = "BOOTREPLY";
487*02e02bd5Skrw 	}
4889a2590e5Sderaadt 
489*02e02bd5Skrw 	for (ap = config->reject_list; ap && handler; ap = ap->next)
490*02e02bd5Skrw 		if (addr_eq(from, ap->addr)) {
491*02e02bd5Skrw 			note("%s from %s rejected.", type, piaddr(from));
492*02e02bd5Skrw 			handler = NULL;
493*02e02bd5Skrw 		}
494*02e02bd5Skrw 
495*02e02bd5Skrw 	if (handler)
496*02e02bd5Skrw 		(*handler)(from, options);
497*02e02bd5Skrw 
498c714dadcShenning 	for (i = 0; i < 256; i++)
4994f062ee3Skrw 		if (options[i].len && options[i].data)
5004f062ee3Skrw 			free(options[i].data);
5019a2590e5Sderaadt }
502