options.c revision 149399
1147072Sbrooks/*	$OpenBSD: options.c,v 1.15 2004/12/26 03:17:07 deraadt Exp $	*/
2147072Sbrooks
3147072Sbrooks/* DHCP options parsing and reassembly. */
4147072Sbrooks
5147072Sbrooks/*
6147072Sbrooks * Copyright (c) 1995, 1996, 1997, 1998 The Internet Software Consortium.
7147072Sbrooks * All rights reserved.
8147072Sbrooks *
9147072Sbrooks * Redistribution and use in source and binary forms, with or without
10147072Sbrooks * modification, are permitted provided that the following conditions
11147072Sbrooks * are met:
12147072Sbrooks *
13147072Sbrooks * 1. Redistributions of source code must retain the above copyright
14147072Sbrooks *    notice, this list of conditions and the following disclaimer.
15147072Sbrooks * 2. Redistributions in binary form must reproduce the above copyright
16147072Sbrooks *    notice, this list of conditions and the following disclaimer in the
17147072Sbrooks *    documentation and/or other materials provided with the distribution.
18147072Sbrooks * 3. Neither the name of The Internet Software Consortium nor the names
19147072Sbrooks *    of its contributors may be used to endorse or promote products derived
20147072Sbrooks *    from this software without specific prior written permission.
21147072Sbrooks *
22147072Sbrooks * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
23147072Sbrooks * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
24147072Sbrooks * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
25147072Sbrooks * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
26147072Sbrooks * DISCLAIMED.  IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
27147072Sbrooks * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28147072Sbrooks * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
29147072Sbrooks * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
30147072Sbrooks * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
31147072Sbrooks * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
32147072Sbrooks * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
33147072Sbrooks * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34147072Sbrooks * SUCH DAMAGE.
35147072Sbrooks *
36147072Sbrooks * This software has been written for the Internet Software Consortium
37147072Sbrooks * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
38147072Sbrooks * Enterprises.  To learn more about the Internet Software Consortium,
39147072Sbrooks * see ``http://www.vix.com/isc''.  To learn more about Vixie
40147072Sbrooks * Enterprises, see ``http://www.vix.com''.
41147072Sbrooks */
42147072Sbrooks
43149399Sbrooks#include <sys/cdefs.h>
44149399Sbrooks__FBSDID("$FreeBSD: head/sbin/dhclient/options.c 149399 2005-08-23 23:59:55Z brooks $");
45149399Sbrooks
46147072Sbrooks#include <ctype.h>
47147072Sbrooks
48147072Sbrooks#define DHCP_OPTION_DATA
49147072Sbrooks#include "dhcpd.h"
50147072Sbrooks
51147072Sbrooksint bad_options = 0;
52147072Sbrooksint bad_options_max = 5;
53147072Sbrooks
54147072Sbrooksvoid	parse_options(struct packet *);
55147072Sbrooksvoid	parse_option_buffer(struct packet *, unsigned char *, int);
56147072Sbrooksint	store_options(unsigned char *, int, struct tree_cache **,
57147072Sbrooks	    unsigned char *, int, int, int, int);
58147072Sbrooks
59147072Sbrooks
60147072Sbrooks/*
61147072Sbrooks * Parse all available options out of the specified packet.
62147072Sbrooks */
63147072Sbrooksvoid
64147072Sbrooksparse_options(struct packet *packet)
65147072Sbrooks{
66147072Sbrooks	/* Initially, zero all option pointers. */
67147072Sbrooks	memset(packet->options, 0, sizeof(packet->options));
68147072Sbrooks
69147072Sbrooks	/* If we don't see the magic cookie, there's nothing to parse. */
70147072Sbrooks	if (memcmp(packet->raw->options, DHCP_OPTIONS_COOKIE, 4)) {
71147072Sbrooks		packet->options_valid = 0;
72147072Sbrooks		return;
73147072Sbrooks	}
74147072Sbrooks
75147072Sbrooks	/*
76147072Sbrooks	 * Go through the options field, up to the end of the packet or
77147072Sbrooks	 * the End field.
78147072Sbrooks	 */
79147072Sbrooks	parse_option_buffer(packet, &packet->raw->options[4],
80147072Sbrooks	    packet->packet_length - DHCP_FIXED_NON_UDP - 4);
81147072Sbrooks
82147072Sbrooks	/*
83147072Sbrooks	 * If we parsed a DHCP Option Overload option, parse more
84147072Sbrooks	 * options out of the buffer(s) containing them.
85147072Sbrooks	 */
86147072Sbrooks	if (packet->options_valid &&
87147072Sbrooks	    packet->options[DHO_DHCP_OPTION_OVERLOAD].data) {
88147072Sbrooks		if (packet->options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 1)
89147072Sbrooks			parse_option_buffer(packet,
90147072Sbrooks			    (unsigned char *)packet->raw->file,
91147072Sbrooks			    sizeof(packet->raw->file));
92147072Sbrooks		if (packet->options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 2)
93147072Sbrooks			parse_option_buffer(packet,
94147072Sbrooks			    (unsigned char *)packet->raw->sname,
95147072Sbrooks			    sizeof(packet->raw->sname));
96147072Sbrooks	}
97147072Sbrooks}
98147072Sbrooks
99147072Sbrooks/*
100147072Sbrooks * Parse options out of the specified buffer, storing addresses of
101147072Sbrooks * option values in packet->options and setting packet->options_valid if
102147072Sbrooks * no errors are encountered.
103147072Sbrooks */
104147072Sbrooksvoid
105147072Sbrooksparse_option_buffer(struct packet *packet,
106147072Sbrooks    unsigned char *buffer, int length)
107147072Sbrooks{
108147072Sbrooks	unsigned char *s, *t, *end = buffer + length;
109147072Sbrooks	int len, code;
110147072Sbrooks
111147072Sbrooks	for (s = buffer; *s != DHO_END && s < end; ) {
112147072Sbrooks		code = s[0];
113147072Sbrooks
114147072Sbrooks		/* Pad options don't have a length - just skip them. */
115147072Sbrooks		if (code == DHO_PAD) {
116147072Sbrooks			s++;
117147072Sbrooks			continue;
118147072Sbrooks		}
119147072Sbrooks		if (s + 2 > end) {
120147072Sbrooks			len = 65536;
121147072Sbrooks			goto bogus;
122147072Sbrooks		}
123147072Sbrooks
124147072Sbrooks		/*
125147072Sbrooks		 * All other fields (except end, see above) have a
126147072Sbrooks		 * one-byte length.
127147072Sbrooks		 */
128147072Sbrooks		len = s[1];
129147072Sbrooks
130147072Sbrooks		/*
131147072Sbrooks		 * If the length is outrageous, silently skip the rest,
132147072Sbrooks		 * and mark the packet bad. Unfortunately some crappy
133147072Sbrooks		 * dhcp servers always seem to give us garbage on the
134147072Sbrooks		 * end of a packet. so rather than keep refusing, give
135147072Sbrooks		 * up and try to take one after seeing a few without
136147072Sbrooks		 * anything good.
137147072Sbrooks		 */
138147072Sbrooks		if (s + len + 2 > end) {
139147072Sbrooks		    bogus:
140147072Sbrooks			bad_options++;
141147072Sbrooks			warning("option %s (%d) %s.",
142147072Sbrooks			    dhcp_options[code].name, len,
143147072Sbrooks			    "larger than buffer");
144147072Sbrooks			if (bad_options == bad_options_max) {
145147072Sbrooks				packet->options_valid = 1;
146147072Sbrooks				bad_options = 0;
147147072Sbrooks				warning("Many bogus options seen in offers. "
148147072Sbrooks				    "Taking this offer in spite of bogus "
149147072Sbrooks				    "options - hope for the best!");
150147072Sbrooks			} else {
151147072Sbrooks				warning("rejecting bogus offer.");
152147072Sbrooks				packet->options_valid = 0;
153147072Sbrooks			}
154147072Sbrooks			return;
155147072Sbrooks		}
156147072Sbrooks		/*
157147072Sbrooks		 * If we haven't seen this option before, just make
158147072Sbrooks		 * space for it and copy it there.
159147072Sbrooks		 */
160147072Sbrooks		if (!packet->options[code].data) {
161147072Sbrooks			if (!(t = calloc(1, len + 1)))
162147072Sbrooks				error("Can't allocate storage for option %s.",
163147072Sbrooks				    dhcp_options[code].name);
164147072Sbrooks			/*
165147072Sbrooks			 * Copy and NUL-terminate the option (in case
166147072Sbrooks			 * it's an ASCII string.
167147072Sbrooks			 */
168147072Sbrooks			memcpy(t, &s[2], len);
169147072Sbrooks			t[len] = 0;
170147072Sbrooks			packet->options[code].len = len;
171147072Sbrooks			packet->options[code].data = t;
172147072Sbrooks		} else {
173147072Sbrooks			/*
174147072Sbrooks			 * If it's a repeat, concatenate it to whatever
175147072Sbrooks			 * we last saw.   This is really only required
176147072Sbrooks			 * for clients, but what the heck...
177147072Sbrooks			 */
178147072Sbrooks			t = calloc(1, len + packet->options[code].len + 1);
179147072Sbrooks			if (!t)
180147072Sbrooks				error("Can't expand storage for option %s.",
181147072Sbrooks				    dhcp_options[code].name);
182147072Sbrooks			memcpy(t, packet->options[code].data,
183147072Sbrooks				packet->options[code].len);
184147072Sbrooks			memcpy(t + packet->options[code].len,
185147072Sbrooks				&s[2], len);
186147072Sbrooks			packet->options[code].len += len;
187147072Sbrooks			t[packet->options[code].len] = 0;
188147072Sbrooks			free(packet->options[code].data);
189147072Sbrooks			packet->options[code].data = t;
190147072Sbrooks		}
191147072Sbrooks		s += len + 2;
192147072Sbrooks	}
193147072Sbrooks	packet->options_valid = 1;
194147072Sbrooks}
195147072Sbrooks
196147072Sbrooks/*
197147072Sbrooks * cons options into a big buffer, and then split them out into the
198147072Sbrooks * three separate buffers if needed.  This allows us to cons up a set of
199147072Sbrooks * vendor options using the same routine.
200147072Sbrooks */
201147072Sbrooksint
202147072Sbrookscons_options(struct packet *inpacket, struct dhcp_packet *outpacket,
203147072Sbrooks    int mms, struct tree_cache **options,
204147072Sbrooks    int overload, /* Overload flags that may be set. */
205147072Sbrooks    int terminate, int bootpp, u_int8_t *prl, int prl_len)
206147072Sbrooks{
207147072Sbrooks	unsigned char priority_list[300], buffer[4096];
208147072Sbrooks	int priority_len, main_buffer_size, mainbufix, bufix;
209147072Sbrooks	int option_size, length;
210147072Sbrooks
211147072Sbrooks	/*
212147072Sbrooks	 * If the client has provided a maximum DHCP message size, use
213147072Sbrooks	 * that; otherwise, if it's BOOTP, only 64 bytes; otherwise use
214147072Sbrooks	 * up to the minimum IP MTU size (576 bytes).
215147072Sbrooks	 *
216147072Sbrooks	 * XXX if a BOOTP client specifies a max message size, we will
217147072Sbrooks	 * honor it.
218147072Sbrooks	 */
219147072Sbrooks	if (!mms &&
220147072Sbrooks	    inpacket &&
221147072Sbrooks	    inpacket->options[DHO_DHCP_MAX_MESSAGE_SIZE].data &&
222147072Sbrooks	    (inpacket->options[DHO_DHCP_MAX_MESSAGE_SIZE].len >=
223147072Sbrooks	    sizeof(u_int16_t)))
224147072Sbrooks		mms = getUShort(
225147072Sbrooks		    inpacket->options[DHO_DHCP_MAX_MESSAGE_SIZE].data);
226147072Sbrooks
227147072Sbrooks	if (mms)
228147072Sbrooks		main_buffer_size = mms - DHCP_FIXED_LEN;
229147072Sbrooks	else if (bootpp)
230147072Sbrooks		main_buffer_size = 64;
231147072Sbrooks	else
232147072Sbrooks		main_buffer_size = 576 - DHCP_FIXED_LEN;
233147072Sbrooks
234147072Sbrooks	if (main_buffer_size > sizeof(buffer))
235147072Sbrooks		main_buffer_size = sizeof(buffer);
236147072Sbrooks
237147072Sbrooks	/* Preload the option priority list with mandatory options. */
238147072Sbrooks	priority_len = 0;
239147072Sbrooks	priority_list[priority_len++] = DHO_DHCP_MESSAGE_TYPE;
240147072Sbrooks	priority_list[priority_len++] = DHO_DHCP_SERVER_IDENTIFIER;
241147072Sbrooks	priority_list[priority_len++] = DHO_DHCP_LEASE_TIME;
242147072Sbrooks	priority_list[priority_len++] = DHO_DHCP_MESSAGE;
243147072Sbrooks
244147072Sbrooks	/*
245147072Sbrooks	 * If the client has provided a list of options that it wishes
246147072Sbrooks	 * returned, use it to prioritize.  Otherwise, prioritize based
247147072Sbrooks	 * on the default priority list.
248147072Sbrooks	 */
249147072Sbrooks	if (inpacket &&
250147072Sbrooks	    inpacket->options[DHO_DHCP_PARAMETER_REQUEST_LIST].data) {
251147072Sbrooks		int prlen =
252147072Sbrooks		    inpacket->options[DHO_DHCP_PARAMETER_REQUEST_LIST].len;
253147072Sbrooks		if (prlen + priority_len > sizeof(priority_list))
254147072Sbrooks			prlen = sizeof(priority_list) - priority_len;
255147072Sbrooks
256147072Sbrooks		memcpy(&priority_list[priority_len],
257147072Sbrooks		    inpacket->options[DHO_DHCP_PARAMETER_REQUEST_LIST].data,
258147072Sbrooks		    prlen);
259147072Sbrooks		priority_len += prlen;
260147072Sbrooks		prl = priority_list;
261147072Sbrooks	} else if (prl) {
262147072Sbrooks		if (prl_len + priority_len > sizeof(priority_list))
263147072Sbrooks			prl_len = sizeof(priority_list) - priority_len;
264147072Sbrooks
265147072Sbrooks		memcpy(&priority_list[priority_len], prl, prl_len);
266147072Sbrooks		priority_len += prl_len;
267147072Sbrooks		prl = priority_list;
268147072Sbrooks	} else {
269147072Sbrooks		memcpy(&priority_list[priority_len],
270147072Sbrooks		    dhcp_option_default_priority_list,
271147072Sbrooks		    sizeof_dhcp_option_default_priority_list);
272147072Sbrooks		priority_len += sizeof_dhcp_option_default_priority_list;
273147072Sbrooks	}
274147072Sbrooks
275147072Sbrooks	/* Copy the options into the big buffer... */
276147072Sbrooks	option_size = store_options(
277147072Sbrooks	    buffer,
278147072Sbrooks	    (main_buffer_size - 7 + ((overload & 1) ? DHCP_FILE_LEN : 0) +
279147072Sbrooks		((overload & 2) ? DHCP_SNAME_LEN : 0)),
280147072Sbrooks	    options, priority_list, priority_len, main_buffer_size,
281147072Sbrooks	    (main_buffer_size + ((overload & 1) ? DHCP_FILE_LEN : 0)),
282147072Sbrooks	    terminate);
283147072Sbrooks
284147072Sbrooks	/* Put the cookie up front... */
285147072Sbrooks	memcpy(outpacket->options, DHCP_OPTIONS_COOKIE, 4);
286147072Sbrooks	mainbufix = 4;
287147072Sbrooks
288147072Sbrooks	/*
289147072Sbrooks	 * If we're going to have to overload, store the overload option
290147072Sbrooks	 * at the beginning.  If we can, though, just store the whole
291147072Sbrooks	 * thing in the packet's option buffer and leave it at that.
292147072Sbrooks	 */
293147072Sbrooks	if (option_size <= main_buffer_size - mainbufix) {
294147072Sbrooks		memcpy(&outpacket->options[mainbufix],
295147072Sbrooks		    buffer, option_size);
296147072Sbrooks		mainbufix += option_size;
297147072Sbrooks		if (mainbufix < main_buffer_size)
298147072Sbrooks			outpacket->options[mainbufix++] = DHO_END;
299147072Sbrooks		length = DHCP_FIXED_NON_UDP + mainbufix;
300147072Sbrooks	} else {
301147072Sbrooks		outpacket->options[mainbufix++] = DHO_DHCP_OPTION_OVERLOAD;
302147072Sbrooks		outpacket->options[mainbufix++] = 1;
303147072Sbrooks		if (option_size >
304147072Sbrooks		    main_buffer_size - mainbufix + DHCP_FILE_LEN)
305147072Sbrooks			outpacket->options[mainbufix++] = 3;
306147072Sbrooks		else
307147072Sbrooks			outpacket->options[mainbufix++] = 1;
308147072Sbrooks
309147072Sbrooks		memcpy(&outpacket->options[mainbufix],
310147072Sbrooks		    buffer, main_buffer_size - mainbufix);
311147072Sbrooks		bufix = main_buffer_size - mainbufix;
312147072Sbrooks		length = DHCP_FIXED_NON_UDP + mainbufix;
313147072Sbrooks		if (overload & 1) {
314147072Sbrooks			if (option_size - bufix <= DHCP_FILE_LEN) {
315147072Sbrooks				memcpy(outpacket->file,
316147072Sbrooks				    &buffer[bufix], option_size - bufix);
317147072Sbrooks				mainbufix = option_size - bufix;
318147072Sbrooks				if (mainbufix < DHCP_FILE_LEN)
319147072Sbrooks					outpacket->file[mainbufix++] = (char)DHO_END;
320147072Sbrooks				while (mainbufix < DHCP_FILE_LEN)
321147072Sbrooks					outpacket->file[mainbufix++] = (char)DHO_PAD;
322147072Sbrooks			} else {
323147072Sbrooks				memcpy(outpacket->file,
324147072Sbrooks				    &buffer[bufix], DHCP_FILE_LEN);
325147072Sbrooks				bufix += DHCP_FILE_LEN;
326147072Sbrooks			}
327147072Sbrooks		}
328147072Sbrooks		if ((overload & 2) && option_size < bufix) {
329147072Sbrooks			memcpy(outpacket->sname,
330147072Sbrooks			    &buffer[bufix], option_size - bufix);
331147072Sbrooks
332147072Sbrooks			mainbufix = option_size - bufix;
333147072Sbrooks			if (mainbufix < DHCP_SNAME_LEN)
334147072Sbrooks				outpacket->file[mainbufix++] = (char)DHO_END;
335147072Sbrooks			while (mainbufix < DHCP_SNAME_LEN)
336147072Sbrooks				outpacket->file[mainbufix++] = (char)DHO_PAD;
337147072Sbrooks		}
338147072Sbrooks	}
339147072Sbrooks	return (length);
340147072Sbrooks}
341147072Sbrooks
342147072Sbrooks/*
343147072Sbrooks * Store all the requested options into the requested buffer.
344147072Sbrooks */
345147072Sbrooksint
346147072Sbrooksstore_options(unsigned char *buffer, int buflen, struct tree_cache **options,
347147072Sbrooks    unsigned char *priority_list, int priority_len, int first_cutoff,
348147072Sbrooks    int second_cutoff, int terminate)
349147072Sbrooks{
350147072Sbrooks	int bufix = 0, option_stored[256], i, ix, tto;
351147072Sbrooks
352147072Sbrooks	/* Zero out the stored-lengths array. */
353147072Sbrooks	memset(option_stored, 0, sizeof(option_stored));
354147072Sbrooks
355147072Sbrooks	/*
356147072Sbrooks	 * Copy out the options in the order that they appear in the
357147072Sbrooks	 * priority list...
358147072Sbrooks	 */
359147072Sbrooks	for (i = 0; i < priority_len; i++) {
360147072Sbrooks		/* Code for next option to try to store. */
361147072Sbrooks		int code = priority_list[i];
362147072Sbrooks		int optstart;
363147072Sbrooks
364147072Sbrooks		/*
365147072Sbrooks		 * Number of bytes left to store (some may already have
366147072Sbrooks		 * been stored by a previous pass).
367147072Sbrooks		 */
368147072Sbrooks		int length;
369147072Sbrooks
370147072Sbrooks		/* If no data is available for this option, skip it. */
371147072Sbrooks		if (!options[code]) {
372147072Sbrooks			continue;
373147072Sbrooks		}
374147072Sbrooks
375147072Sbrooks		/*
376147072Sbrooks		 * The client could ask for things that are mandatory,
377147072Sbrooks		 * in which case we should avoid storing them twice...
378147072Sbrooks		 */
379147072Sbrooks		if (option_stored[code])
380147072Sbrooks			continue;
381147072Sbrooks		option_stored[code] = 1;
382147072Sbrooks
383147072Sbrooks		/* We should now have a constant length for the option. */
384147072Sbrooks		length = options[code]->len;
385147072Sbrooks
386147072Sbrooks		/* Do we add a NUL? */
387147072Sbrooks		if (terminate && dhcp_options[code].format[0] == 't') {
388147072Sbrooks			length++;
389147072Sbrooks			tto = 1;
390147072Sbrooks		} else
391147072Sbrooks			tto = 0;
392147072Sbrooks
393147072Sbrooks		/* Try to store the option. */
394147072Sbrooks
395147072Sbrooks		/*
396147072Sbrooks		 * If the option's length is more than 255, we must
397147072Sbrooks		 * store it in multiple hunks.   Store 255-byte hunks
398147072Sbrooks		 * first.  However, in any case, if the option data will
399147072Sbrooks		 * cross a buffer boundary, split it across that
400147072Sbrooks		 * boundary.
401147072Sbrooks		 */
402147072Sbrooks		ix = 0;
403147072Sbrooks
404147072Sbrooks		optstart = bufix;
405147072Sbrooks		while (length) {
406147072Sbrooks			unsigned char incr = length > 255 ? 255 : length;
407147072Sbrooks
408147072Sbrooks			/*
409147072Sbrooks			 * If this hunk of the buffer will cross a
410147072Sbrooks			 * boundary, only go up to the boundary in this
411147072Sbrooks			 * pass.
412147072Sbrooks			 */
413147072Sbrooks			if (bufix < first_cutoff &&
414147072Sbrooks			    bufix + incr > first_cutoff)
415147072Sbrooks				incr = first_cutoff - bufix;
416147072Sbrooks			else if (bufix < second_cutoff &&
417147072Sbrooks			    bufix + incr > second_cutoff)
418147072Sbrooks				incr = second_cutoff - bufix;
419147072Sbrooks
420147072Sbrooks			/*
421147072Sbrooks			 * If this option is going to overflow the
422147072Sbrooks			 * buffer, skip it.
423147072Sbrooks			 */
424147072Sbrooks			if (bufix + 2 + incr > buflen) {
425147072Sbrooks				bufix = optstart;
426147072Sbrooks				break;
427147072Sbrooks			}
428147072Sbrooks
429147072Sbrooks			/* Everything looks good - copy it in! */
430147072Sbrooks			buffer[bufix] = code;
431147072Sbrooks			buffer[bufix + 1] = incr;
432147072Sbrooks			if (tto && incr == length) {
433147072Sbrooks				memcpy(buffer + bufix + 2,
434147072Sbrooks				    options[code]->value + ix, incr - 1);
435147072Sbrooks				buffer[bufix + 2 + incr - 1] = 0;
436147072Sbrooks			} else
437147072Sbrooks				memcpy(buffer + bufix + 2,
438147072Sbrooks				    options[code]->value + ix, incr);
439147072Sbrooks			length -= incr;
440147072Sbrooks			ix += incr;
441147072Sbrooks			bufix += 2 + incr;
442147072Sbrooks		}
443147072Sbrooks	}
444147072Sbrooks	return (bufix);
445147072Sbrooks}
446147072Sbrooks
447147072Sbrooks/*
448147072Sbrooks * Format the specified option so that a human can easily read it.
449147072Sbrooks */
450147072Sbrookschar *
451147072Sbrookspretty_print_option(unsigned int code, unsigned char *data, int len,
452147072Sbrooks    int emit_commas, int emit_quotes)
453147072Sbrooks{
454147072Sbrooks	static char optbuf[32768]; /* XXX */
455147072Sbrooks	int hunksize = 0, numhunk = -1, numelem = 0;
456147072Sbrooks	char fmtbuf[32], *op = optbuf;
457147072Sbrooks	int i, j, k, opleft = sizeof(optbuf);
458147072Sbrooks	unsigned char *dp = data;
459147072Sbrooks	struct in_addr foo;
460147072Sbrooks	char comma;
461147072Sbrooks
462147072Sbrooks	/* Code should be between 0 and 255. */
463147072Sbrooks	if (code > 255)
464147072Sbrooks		error("pretty_print_option: bad code %d", code);
465147072Sbrooks
466147072Sbrooks	if (emit_commas)
467147072Sbrooks		comma = ',';
468147072Sbrooks	else
469147072Sbrooks		comma = ' ';
470147072Sbrooks
471147072Sbrooks	/* Figure out the size of the data. */
472147072Sbrooks	for (i = 0; dhcp_options[code].format[i]; i++) {
473147072Sbrooks		if (!numhunk) {
474147072Sbrooks			warning("%s: Excess information in format string: %s",
475147072Sbrooks			    dhcp_options[code].name,
476147072Sbrooks			    &(dhcp_options[code].format[i]));
477147072Sbrooks			break;
478147072Sbrooks		}
479147072Sbrooks		numelem++;
480147072Sbrooks		fmtbuf[i] = dhcp_options[code].format[i];
481147072Sbrooks		switch (dhcp_options[code].format[i]) {
482147072Sbrooks		case 'A':
483147072Sbrooks			--numelem;
484147072Sbrooks			fmtbuf[i] = 0;
485147072Sbrooks			numhunk = 0;
486147072Sbrooks			break;
487147072Sbrooks		case 'X':
488147072Sbrooks			for (k = 0; k < len; k++)
489147072Sbrooks				if (!isascii(data[k]) ||
490147072Sbrooks				    !isprint(data[k]))
491147072Sbrooks					break;
492147072Sbrooks			if (k == len) {
493147072Sbrooks				fmtbuf[i] = 't';
494147072Sbrooks				numhunk = -2;
495147072Sbrooks			} else {
496147072Sbrooks				fmtbuf[i] = 'x';
497147072Sbrooks				hunksize++;
498147072Sbrooks				comma = ':';
499147072Sbrooks				numhunk = 0;
500147072Sbrooks			}
501147072Sbrooks			fmtbuf[i + 1] = 0;
502147072Sbrooks			break;
503147072Sbrooks		case 't':
504147072Sbrooks			fmtbuf[i] = 't';
505147072Sbrooks			fmtbuf[i + 1] = 0;
506147072Sbrooks			numhunk = -2;
507147072Sbrooks			break;
508147072Sbrooks		case 'I':
509147072Sbrooks		case 'l':
510147072Sbrooks		case 'L':
511147072Sbrooks			hunksize += 4;
512147072Sbrooks			break;
513147072Sbrooks		case 's':
514147072Sbrooks		case 'S':
515147072Sbrooks			hunksize += 2;
516147072Sbrooks			break;
517147072Sbrooks		case 'b':
518147072Sbrooks		case 'B':
519147072Sbrooks		case 'f':
520147072Sbrooks			hunksize++;
521147072Sbrooks			break;
522147072Sbrooks		case 'e':
523147072Sbrooks			break;
524147072Sbrooks		default:
525147072Sbrooks			warning("%s: garbage in format string: %s",
526147072Sbrooks			    dhcp_options[code].name,
527147072Sbrooks			    &(dhcp_options[code].format[i]));
528147072Sbrooks			break;
529147072Sbrooks		}
530147072Sbrooks	}
531147072Sbrooks
532147072Sbrooks	/* Check for too few bytes... */
533147072Sbrooks	if (hunksize > len) {
534147072Sbrooks		warning("%s: expecting at least %d bytes; got %d",
535147072Sbrooks		    dhcp_options[code].name, hunksize, len);
536147072Sbrooks		return ("<error>");
537147072Sbrooks	}
538147072Sbrooks	/* Check for too many bytes... */
539147072Sbrooks	if (numhunk == -1 && hunksize < len)
540147072Sbrooks		warning("%s: %d extra bytes",
541147072Sbrooks		    dhcp_options[code].name, len - hunksize);
542147072Sbrooks
543147072Sbrooks	/* If this is an array, compute its size. */
544147072Sbrooks	if (!numhunk)
545147072Sbrooks		numhunk = len / hunksize;
546147072Sbrooks	/* See if we got an exact number of hunks. */
547147072Sbrooks	if (numhunk > 0 && numhunk * hunksize < len)
548147072Sbrooks		warning("%s: %d extra bytes at end of array",
549147072Sbrooks		    dhcp_options[code].name, len - numhunk * hunksize);
550147072Sbrooks
551147072Sbrooks	/* A one-hunk array prints the same as a single hunk. */
552147072Sbrooks	if (numhunk < 0)
553147072Sbrooks		numhunk = 1;
554147072Sbrooks
555147072Sbrooks	/* Cycle through the array (or hunk) printing the data. */
556147072Sbrooks	for (i = 0; i < numhunk; i++) {
557147072Sbrooks		for (j = 0; j < numelem; j++) {
558147072Sbrooks			int opcount;
559147072Sbrooks			switch (fmtbuf[j]) {
560147072Sbrooks			case 't':
561147072Sbrooks				if (emit_quotes) {
562147072Sbrooks					*op++ = '"';
563147072Sbrooks					opleft--;
564147072Sbrooks				}
565147072Sbrooks				for (; dp < data + len; dp++) {
566147072Sbrooks					if (!isascii(*dp) ||
567147072Sbrooks					    !isprint(*dp)) {
568147072Sbrooks						if (dp + 1 != data + len ||
569147072Sbrooks						    *dp != 0) {
570147072Sbrooks							snprintf(op, opleft,
571147072Sbrooks							    "\\%03o", *dp);
572147072Sbrooks							op += 4;
573147072Sbrooks							opleft -= 4;
574147072Sbrooks						}
575147072Sbrooks					} else if (*dp == '"' ||
576147072Sbrooks					    *dp == '\'' ||
577147072Sbrooks					    *dp == '$' ||
578147072Sbrooks					    *dp == '`' ||
579147072Sbrooks					    *dp == '\\') {
580147072Sbrooks						*op++ = '\\';
581147072Sbrooks						*op++ = *dp;
582147072Sbrooks						opleft -= 2;
583147072Sbrooks					} else {
584147072Sbrooks						*op++ = *dp;
585147072Sbrooks						opleft--;
586147072Sbrooks					}
587147072Sbrooks				}
588147072Sbrooks				if (emit_quotes) {
589147072Sbrooks					*op++ = '"';
590147072Sbrooks					opleft--;
591147072Sbrooks				}
592147072Sbrooks
593147072Sbrooks				*op = 0;
594147072Sbrooks				break;
595147072Sbrooks			case 'I':
596147072Sbrooks				foo.s_addr = htonl(getULong(dp));
597147072Sbrooks				opcount = strlcpy(op, inet_ntoa(foo), opleft);
598147072Sbrooks				if (opcount >= opleft)
599147072Sbrooks					goto toobig;
600147072Sbrooks				opleft -= opcount;
601147072Sbrooks				dp += 4;
602147072Sbrooks				break;
603147072Sbrooks			case 'l':
604147072Sbrooks				opcount = snprintf(op, opleft, "%ld",
605147072Sbrooks				    (long)getLong(dp));
606147072Sbrooks				if (opcount >= opleft || opcount == -1)
607147072Sbrooks					goto toobig;
608147072Sbrooks				opleft -= opcount;
609147072Sbrooks				dp += 4;
610147072Sbrooks				break;
611147072Sbrooks			case 'L':
612147072Sbrooks				opcount = snprintf(op, opleft, "%ld",
613147072Sbrooks				    (unsigned long)getULong(dp));
614147072Sbrooks				if (opcount >= opleft || opcount == -1)
615147072Sbrooks					goto toobig;
616147072Sbrooks				opleft -= opcount;
617147072Sbrooks				dp += 4;
618147072Sbrooks				break;
619147072Sbrooks			case 's':
620147072Sbrooks				opcount = snprintf(op, opleft, "%d",
621147072Sbrooks				    getShort(dp));
622147072Sbrooks				if (opcount >= opleft || opcount == -1)
623147072Sbrooks					goto toobig;
624147072Sbrooks				opleft -= opcount;
625147072Sbrooks				dp += 2;
626147072Sbrooks				break;
627147072Sbrooks			case 'S':
628147072Sbrooks				opcount = snprintf(op, opleft, "%d",
629147072Sbrooks				    getUShort(dp));
630147072Sbrooks				if (opcount >= opleft || opcount == -1)
631147072Sbrooks					goto toobig;
632147072Sbrooks				opleft -= opcount;
633147072Sbrooks				dp += 2;
634147072Sbrooks				break;
635147072Sbrooks			case 'b':
636147072Sbrooks				opcount = snprintf(op, opleft, "%d",
637147072Sbrooks				    *(char *)dp++);
638147072Sbrooks				if (opcount >= opleft || opcount == -1)
639147072Sbrooks					goto toobig;
640147072Sbrooks				opleft -= opcount;
641147072Sbrooks				break;
642147072Sbrooks			case 'B':
643147072Sbrooks				opcount = snprintf(op, opleft, "%d", *dp++);
644147072Sbrooks				if (opcount >= opleft || opcount == -1)
645147072Sbrooks					goto toobig;
646147072Sbrooks				opleft -= opcount;
647147072Sbrooks				break;
648147072Sbrooks			case 'x':
649147072Sbrooks				opcount = snprintf(op, opleft, "%x", *dp++);
650147072Sbrooks				if (opcount >= opleft || opcount == -1)
651147072Sbrooks					goto toobig;
652147072Sbrooks				opleft -= opcount;
653147072Sbrooks				break;
654147072Sbrooks			case 'f':
655147072Sbrooks				opcount = strlcpy(op,
656147072Sbrooks				    *dp++ ? "true" : "false", opleft);
657147072Sbrooks				if (opcount >= opleft)
658147072Sbrooks					goto toobig;
659147072Sbrooks				opleft -= opcount;
660147072Sbrooks				break;
661147072Sbrooks			default:
662147072Sbrooks				warning("Unexpected format code %c", fmtbuf[j]);
663147072Sbrooks			}
664147072Sbrooks			op += strlen(op);
665147072Sbrooks			opleft -= strlen(op);
666147072Sbrooks			if (opleft < 1)
667147072Sbrooks				goto toobig;
668147072Sbrooks			if (j + 1 < numelem && comma != ':') {
669147072Sbrooks				*op++ = ' ';
670147072Sbrooks				opleft--;
671147072Sbrooks			}
672147072Sbrooks		}
673147072Sbrooks		if (i + 1 < numhunk) {
674147072Sbrooks			*op++ = comma;
675147072Sbrooks			opleft--;
676147072Sbrooks		}
677147072Sbrooks		if (opleft < 1)
678147072Sbrooks			goto toobig;
679147072Sbrooks
680147072Sbrooks	}
681147072Sbrooks	return (optbuf);
682147072Sbrooks toobig:
683147072Sbrooks	warning("dhcp option too large");
684147072Sbrooks	return ("<error>");
685147072Sbrooks}
686147072Sbrooks
687147072Sbrooksvoid
688147072Sbrooksdo_packet(struct interface_info *interface, struct dhcp_packet *packet,
689147072Sbrooks    int len, unsigned int from_port, struct iaddr from, struct hardware *hfrom)
690147072Sbrooks{
691147072Sbrooks	struct packet tp;
692147072Sbrooks	int i;
693147072Sbrooks
694147072Sbrooks	if (packet->hlen > sizeof(packet->chaddr)) {
695147072Sbrooks		note("Discarding packet with invalid hlen.");
696147072Sbrooks		return;
697147072Sbrooks	}
698147072Sbrooks
699147072Sbrooks	memset(&tp, 0, sizeof(tp));
700147072Sbrooks	tp.raw = packet;
701147072Sbrooks	tp.packet_length = len;
702147072Sbrooks	tp.client_port = from_port;
703147072Sbrooks	tp.client_addr = from;
704147072Sbrooks	tp.interface = interface;
705147072Sbrooks	tp.haddr = hfrom;
706147072Sbrooks
707147072Sbrooks	parse_options(&tp);
708147072Sbrooks	if (tp.options_valid &&
709147072Sbrooks	    tp.options[DHO_DHCP_MESSAGE_TYPE].data)
710147072Sbrooks		tp.packet_type = tp.options[DHO_DHCP_MESSAGE_TYPE].data[0];
711147072Sbrooks	if (tp.packet_type)
712147072Sbrooks		dhcp(&tp);
713147072Sbrooks	else
714147072Sbrooks		bootp(&tp);
715147072Sbrooks
716147072Sbrooks	/* Free the data associated with the options. */
717147072Sbrooks	for (i = 0; i < 256; i++)
718147072Sbrooks		if (tp.options[i].len && tp.options[i].data)
719147072Sbrooks			free(tp.options[i].data);
720147072Sbrooks}
721