147c08596SBrooks Davis /* $OpenBSD: options.c,v 1.15 2004/12/26 03:17:07 deraadt Exp $ */
247c08596SBrooks Davis
347c08596SBrooks Davis /* DHCP options parsing and reassembly. */
447c08596SBrooks Davis
58a16b7a1SPedro F. Giffuni /*-
68a16b7a1SPedro F. Giffuni * SPDX-License-Identifier: BSD-3-Clause
78a16b7a1SPedro F. Giffuni *
847c08596SBrooks Davis * Copyright (c) 1995, 1996, 1997, 1998 The Internet Software Consortium.
947c08596SBrooks Davis * All rights reserved.
1047c08596SBrooks Davis *
1147c08596SBrooks Davis * Redistribution and use in source and binary forms, with or without
1247c08596SBrooks Davis * modification, are permitted provided that the following conditions
1347c08596SBrooks Davis * are met:
1447c08596SBrooks Davis *
1547c08596SBrooks Davis * 1. Redistributions of source code must retain the above copyright
1647c08596SBrooks Davis * notice, this list of conditions and the following disclaimer.
1747c08596SBrooks Davis * 2. Redistributions in binary form must reproduce the above copyright
1847c08596SBrooks Davis * notice, this list of conditions and the following disclaimer in the
1947c08596SBrooks Davis * documentation and/or other materials provided with the distribution.
2047c08596SBrooks Davis * 3. Neither the name of The Internet Software Consortium nor the names
2147c08596SBrooks Davis * of its contributors may be used to endorse or promote products derived
2247c08596SBrooks Davis * from this software without specific prior written permission.
2347c08596SBrooks Davis *
2447c08596SBrooks Davis * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
2547c08596SBrooks Davis * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
2647c08596SBrooks Davis * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
2747c08596SBrooks Davis * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
2847c08596SBrooks Davis * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
2947c08596SBrooks Davis * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
3047c08596SBrooks Davis * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
3147c08596SBrooks Davis * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
3247c08596SBrooks Davis * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
3347c08596SBrooks Davis * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
3447c08596SBrooks Davis * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
3547c08596SBrooks Davis * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3647c08596SBrooks Davis * SUCH DAMAGE.
3747c08596SBrooks Davis *
3847c08596SBrooks Davis * This software has been written for the Internet Software Consortium
3947c08596SBrooks Davis * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
4047c08596SBrooks Davis * Enterprises. To learn more about the Internet Software Consortium,
4147c08596SBrooks Davis * see ``http://www.vix.com/isc''. To learn more about Vixie
4247c08596SBrooks Davis * Enterprises, see ``http://www.vix.com''.
4347c08596SBrooks Davis */
4447c08596SBrooks Davis
458794fdbbSBrooks Davis #include <sys/cdefs.h>
4647c08596SBrooks Davis #include <ctype.h>
4747c08596SBrooks Davis
4847c08596SBrooks Davis #define DHCP_OPTION_DATA
4947c08596SBrooks Davis #include "dhcpd.h"
5047c08596SBrooks Davis
5171c6c44dSEitan Adler static int bad_options = 0;
5271c6c44dSEitan Adler static int bad_options_max = 5;
5347c08596SBrooks Davis
5447c08596SBrooks Davis void parse_options(struct packet *);
5547c08596SBrooks Davis void parse_option_buffer(struct packet *, unsigned char *, int);
56afe6f835SAlan Somers unsigned store_options(unsigned char *, int, struct tree_cache **,
5747c08596SBrooks Davis unsigned char *, int, int, int, int);
58409139f0SJean-Sébastien Pédron void expand_domain_search(struct packet *packet);
59afe6f835SAlan Somers int find_search_domain_name_len(struct option_data *option, size_t *offset);
60afe6f835SAlan Somers void expand_search_domain_name(struct option_data *option, size_t *offset,
61409139f0SJean-Sébastien Pédron unsigned char **domain_search);
6247c08596SBrooks Davis
6347c08596SBrooks Davis
6447c08596SBrooks Davis /*
6547c08596SBrooks Davis * Parse all available options out of the specified packet.
6647c08596SBrooks Davis */
6747c08596SBrooks Davis void
parse_options(struct packet * packet)6847c08596SBrooks Davis parse_options(struct packet *packet)
6947c08596SBrooks Davis {
7047c08596SBrooks Davis /* Initially, zero all option pointers. */
7147c08596SBrooks Davis memset(packet->options, 0, sizeof(packet->options));
7247c08596SBrooks Davis
7347c08596SBrooks Davis /* If we don't see the magic cookie, there's nothing to parse. */
7447c08596SBrooks Davis if (memcmp(packet->raw->options, DHCP_OPTIONS_COOKIE, 4)) {
7547c08596SBrooks Davis packet->options_valid = 0;
7647c08596SBrooks Davis return;
7747c08596SBrooks Davis }
7847c08596SBrooks Davis
7947c08596SBrooks Davis /*
8047c08596SBrooks Davis * Go through the options field, up to the end of the packet or
8147c08596SBrooks Davis * the End field.
8247c08596SBrooks Davis */
8347c08596SBrooks Davis parse_option_buffer(packet, &packet->raw->options[4],
8447c08596SBrooks Davis packet->packet_length - DHCP_FIXED_NON_UDP - 4);
8547c08596SBrooks Davis
8647c08596SBrooks Davis /*
8747c08596SBrooks Davis * If we parsed a DHCP Option Overload option, parse more
8847c08596SBrooks Davis * options out of the buffer(s) containing them.
8947c08596SBrooks Davis */
9047c08596SBrooks Davis if (packet->options_valid &&
9147c08596SBrooks Davis packet->options[DHO_DHCP_OPTION_OVERLOAD].data) {
9247c08596SBrooks Davis if (packet->options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 1)
9347c08596SBrooks Davis parse_option_buffer(packet,
9447c08596SBrooks Davis (unsigned char *)packet->raw->file,
9547c08596SBrooks Davis sizeof(packet->raw->file));
9647c08596SBrooks Davis if (packet->options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 2)
9747c08596SBrooks Davis parse_option_buffer(packet,
9847c08596SBrooks Davis (unsigned char *)packet->raw->sname,
9947c08596SBrooks Davis sizeof(packet->raw->sname));
10047c08596SBrooks Davis }
101409139f0SJean-Sébastien Pédron
102409139f0SJean-Sébastien Pédron /* Expand DHCP Domain Search option. */
103409139f0SJean-Sébastien Pédron if (packet->options_valid) {
104409139f0SJean-Sébastien Pédron expand_domain_search(packet);
105409139f0SJean-Sébastien Pédron }
10647c08596SBrooks Davis }
10747c08596SBrooks Davis
10847c08596SBrooks Davis /*
10947c08596SBrooks Davis * Parse options out of the specified buffer, storing addresses of
11047c08596SBrooks Davis * option values in packet->options and setting packet->options_valid if
11147c08596SBrooks Davis * no errors are encountered.
11247c08596SBrooks Davis */
11347c08596SBrooks Davis void
parse_option_buffer(struct packet * packet,unsigned char * buffer,int length)11447c08596SBrooks Davis parse_option_buffer(struct packet *packet,
11547c08596SBrooks Davis unsigned char *buffer, int length)
11647c08596SBrooks Davis {
11747c08596SBrooks Davis unsigned char *s, *t, *end = buffer + length;
11847c08596SBrooks Davis int len, code;
11947c08596SBrooks Davis
12047c08596SBrooks Davis for (s = buffer; *s != DHO_END && s < end; ) {
12147c08596SBrooks Davis code = s[0];
12247c08596SBrooks Davis
12347c08596SBrooks Davis /* Pad options don't have a length - just skip them. */
12447c08596SBrooks Davis if (code == DHO_PAD) {
12547c08596SBrooks Davis s++;
12647c08596SBrooks Davis continue;
12747c08596SBrooks Davis }
12847c08596SBrooks Davis if (s + 2 > end) {
12947c08596SBrooks Davis len = 65536;
13047c08596SBrooks Davis goto bogus;
13147c08596SBrooks Davis }
13247c08596SBrooks Davis
13347c08596SBrooks Davis /*
13447c08596SBrooks Davis * All other fields (except end, see above) have a
13547c08596SBrooks Davis * one-byte length.
13647c08596SBrooks Davis */
13747c08596SBrooks Davis len = s[1];
13847c08596SBrooks Davis
13947c08596SBrooks Davis /*
14047c08596SBrooks Davis * If the length is outrageous, silently skip the rest,
14147c08596SBrooks Davis * and mark the packet bad. Unfortunately some crappy
14247c08596SBrooks Davis * dhcp servers always seem to give us garbage on the
14347c08596SBrooks Davis * end of a packet. so rather than keep refusing, give
14447c08596SBrooks Davis * up and try to take one after seeing a few without
14547c08596SBrooks Davis * anything good.
14647c08596SBrooks Davis */
14747c08596SBrooks Davis if (s + len + 2 > end) {
14847c08596SBrooks Davis bogus:
14947c08596SBrooks Davis bad_options++;
15047c08596SBrooks Davis warning("option %s (%d) %s.",
15147c08596SBrooks Davis dhcp_options[code].name, len,
15247c08596SBrooks Davis "larger than buffer");
15347c08596SBrooks Davis if (bad_options == bad_options_max) {
15447c08596SBrooks Davis packet->options_valid = 1;
15547c08596SBrooks Davis bad_options = 0;
15647c08596SBrooks Davis warning("Many bogus options seen in offers. "
15747c08596SBrooks Davis "Taking this offer in spite of bogus "
15847c08596SBrooks Davis "options - hope for the best!");
15947c08596SBrooks Davis } else {
16047c08596SBrooks Davis warning("rejecting bogus offer.");
16147c08596SBrooks Davis packet->options_valid = 0;
16247c08596SBrooks Davis }
16347c08596SBrooks Davis return;
16447c08596SBrooks Davis }
16547c08596SBrooks Davis /*
16647c08596SBrooks Davis * If we haven't seen this option before, just make
16747c08596SBrooks Davis * space for it and copy it there.
16847c08596SBrooks Davis */
16947c08596SBrooks Davis if (!packet->options[code].data) {
17047c08596SBrooks Davis if (!(t = calloc(1, len + 1)))
17147c08596SBrooks Davis error("Can't allocate storage for option %s.",
17247c08596SBrooks Davis dhcp_options[code].name);
17347c08596SBrooks Davis /*
17447c08596SBrooks Davis * Copy and NUL-terminate the option (in case
17547c08596SBrooks Davis * it's an ASCII string.
17647c08596SBrooks Davis */
17747c08596SBrooks Davis memcpy(t, &s[2], len);
17847c08596SBrooks Davis t[len] = 0;
17947c08596SBrooks Davis packet->options[code].len = len;
18047c08596SBrooks Davis packet->options[code].data = t;
18147c08596SBrooks Davis } else {
18247c08596SBrooks Davis /*
18347c08596SBrooks Davis * If it's a repeat, concatenate it to whatever
18447c08596SBrooks Davis * we last saw. This is really only required
18547c08596SBrooks Davis * for clients, but what the heck...
18647c08596SBrooks Davis */
18747c08596SBrooks Davis t = calloc(1, len + packet->options[code].len + 1);
18847c08596SBrooks Davis if (!t)
18947c08596SBrooks Davis error("Can't expand storage for option %s.",
19047c08596SBrooks Davis dhcp_options[code].name);
19147c08596SBrooks Davis memcpy(t, packet->options[code].data,
19247c08596SBrooks Davis packet->options[code].len);
19347c08596SBrooks Davis memcpy(t + packet->options[code].len,
19447c08596SBrooks Davis &s[2], len);
19547c08596SBrooks Davis packet->options[code].len += len;
19647c08596SBrooks Davis t[packet->options[code].len] = 0;
19747c08596SBrooks Davis free(packet->options[code].data);
19847c08596SBrooks Davis packet->options[code].data = t;
19947c08596SBrooks Davis }
20047c08596SBrooks Davis s += len + 2;
20147c08596SBrooks Davis }
20247c08596SBrooks Davis packet->options_valid = 1;
20347c08596SBrooks Davis }
20447c08596SBrooks Davis
20547c08596SBrooks Davis /*
206409139f0SJean-Sébastien Pédron * Expand DHCP Domain Search option. The value of this option is
207409139f0SJean-Sébastien Pédron * encoded like DNS' list of labels. See:
208409139f0SJean-Sébastien Pédron * RFC 3397
209409139f0SJean-Sébastien Pédron * RFC 1035
210409139f0SJean-Sébastien Pédron */
211409139f0SJean-Sébastien Pédron void
expand_domain_search(struct packet * packet)212409139f0SJean-Sébastien Pédron expand_domain_search(struct packet *packet)
213409139f0SJean-Sébastien Pédron {
214afe6f835SAlan Somers size_t offset;
215afe6f835SAlan Somers int expanded_len, next_domain_len;
216409139f0SJean-Sébastien Pédron struct option_data *option;
217409139f0SJean-Sébastien Pédron unsigned char *domain_search, *cursor;
218409139f0SJean-Sébastien Pédron
219409139f0SJean-Sébastien Pédron if (packet->options[DHO_DOMAIN_SEARCH].data == NULL)
220409139f0SJean-Sébastien Pédron return;
221409139f0SJean-Sébastien Pédron
222409139f0SJean-Sébastien Pédron option = &packet->options[DHO_DOMAIN_SEARCH];
223409139f0SJean-Sébastien Pédron
224409139f0SJean-Sébastien Pédron /* Compute final expanded length. */
225409139f0SJean-Sébastien Pédron expanded_len = 0;
226409139f0SJean-Sébastien Pédron offset = 0;
227409139f0SJean-Sébastien Pédron while (offset < option->len) {
22833d5b032SJean-Sébastien Pédron next_domain_len = find_search_domain_name_len(option, &offset);
22933d5b032SJean-Sébastien Pédron if (next_domain_len < 0)
23033d5b032SJean-Sébastien Pédron /* The Domain Search option value is invalid. */
23133d5b032SJean-Sébastien Pédron return;
23233d5b032SJean-Sébastien Pédron
233409139f0SJean-Sébastien Pédron /* We add 1 for the space between domain names. */
23433d5b032SJean-Sébastien Pédron expanded_len += next_domain_len + 1;
235409139f0SJean-Sébastien Pédron }
236409139f0SJean-Sébastien Pédron if (expanded_len > 0)
237409139f0SJean-Sébastien Pédron /* Remove 1 for the superfluous trailing space. */
238409139f0SJean-Sébastien Pédron --expanded_len;
239409139f0SJean-Sébastien Pédron
240409139f0SJean-Sébastien Pédron domain_search = malloc(expanded_len + 1);
241409139f0SJean-Sébastien Pédron if (domain_search == NULL)
242409139f0SJean-Sébastien Pédron error("Can't allocate storage for expanded domain-search\n");
243409139f0SJean-Sébastien Pédron
244409139f0SJean-Sébastien Pédron offset = 0;
245409139f0SJean-Sébastien Pédron cursor = domain_search;
246409139f0SJean-Sébastien Pédron while (offset < option->len) {
247409139f0SJean-Sébastien Pédron expand_search_domain_name(option, &offset, &cursor);
248409139f0SJean-Sébastien Pédron cursor[0] = ' ';
249409139f0SJean-Sébastien Pédron cursor++;
250409139f0SJean-Sébastien Pédron }
251409139f0SJean-Sébastien Pédron domain_search[expanded_len] = '\0';
252409139f0SJean-Sébastien Pédron
253409139f0SJean-Sébastien Pédron free(option->data);
254409139f0SJean-Sébastien Pédron option->len = expanded_len;
255409139f0SJean-Sébastien Pédron option->data = domain_search;
256409139f0SJean-Sébastien Pédron }
257409139f0SJean-Sébastien Pédron
258409139f0SJean-Sébastien Pédron int
find_search_domain_name_len(struct option_data * option,size_t * offset)259afe6f835SAlan Somers find_search_domain_name_len(struct option_data *option, size_t *offset)
260409139f0SJean-Sébastien Pédron {
261afe6f835SAlan Somers int domain_name_len, label_len, pointed_len;
262afe6f835SAlan Somers size_t i, pointer;
263409139f0SJean-Sébastien Pédron
264409139f0SJean-Sébastien Pédron domain_name_len = 0;
265409139f0SJean-Sébastien Pédron
266409139f0SJean-Sébastien Pédron i = *offset;
267409139f0SJean-Sébastien Pédron while (i < option->len) {
268409139f0SJean-Sébastien Pédron label_len = option->data[i];
269409139f0SJean-Sébastien Pédron if (label_len == 0) {
270409139f0SJean-Sébastien Pédron /*
271409139f0SJean-Sébastien Pédron * A zero-length label marks the end of this
272409139f0SJean-Sébastien Pédron * domain name.
273409139f0SJean-Sébastien Pédron */
274409139f0SJean-Sébastien Pédron *offset = i + 1;
275409139f0SJean-Sébastien Pédron return (domain_name_len);
276409139f0SJean-Sébastien Pédron } else if (label_len & 0xC0) {
277409139f0SJean-Sébastien Pédron /* This is a pointer to another list of labels. */
278409139f0SJean-Sébastien Pédron if (i + 1 >= option->len) {
279409139f0SJean-Sébastien Pédron /* The pointer is truncated. */
28033d5b032SJean-Sébastien Pédron warning("Truncated pointer in DHCP Domain "
281409139f0SJean-Sébastien Pédron "Search option.");
28233d5b032SJean-Sébastien Pédron return (-1);
283409139f0SJean-Sébastien Pédron }
284409139f0SJean-Sébastien Pédron
285409139f0SJean-Sébastien Pédron pointer = ((label_len & ~(0xC0)) << 8) +
286409139f0SJean-Sébastien Pédron option->data[i + 1];
287409139f0SJean-Sébastien Pédron if (pointer >= *offset) {
288409139f0SJean-Sébastien Pédron /*
2894b85a12fSUlrich Spörlein * The pointer must indicate a prior
2904b85a12fSUlrich Spörlein * occurrence.
291409139f0SJean-Sébastien Pédron */
29233d5b032SJean-Sébastien Pédron warning("Invalid forward pointer in DHCP "
29333d5b032SJean-Sébastien Pédron "Domain Search option compression.");
29433d5b032SJean-Sébastien Pédron return (-1);
295409139f0SJean-Sébastien Pédron }
296409139f0SJean-Sébastien Pédron
297409139f0SJean-Sébastien Pédron pointed_len = find_search_domain_name_len(option,
298409139f0SJean-Sébastien Pédron &pointer);
299*57b278f8SEd Maste if (pointed_len < 0)
300*57b278f8SEd Maste return (-1);
301409139f0SJean-Sébastien Pédron domain_name_len += pointed_len;
302409139f0SJean-Sébastien Pédron
303409139f0SJean-Sébastien Pédron *offset = i + 2;
304409139f0SJean-Sébastien Pédron return (domain_name_len);
305409139f0SJean-Sébastien Pédron }
306409139f0SJean-Sébastien Pédron
307409139f0SJean-Sébastien Pédron if (i + label_len >= option->len) {
30833d5b032SJean-Sébastien Pédron warning("Truncated label in DHCP Domain Search "
30933d5b032SJean-Sébastien Pédron "option.");
31033d5b032SJean-Sébastien Pédron return (-1);
311409139f0SJean-Sébastien Pédron }
312409139f0SJean-Sébastien Pédron
313409139f0SJean-Sébastien Pédron /*
314409139f0SJean-Sébastien Pédron * Update the domain name length with the length of the
315409139f0SJean-Sébastien Pédron * current label, plus a trailing dot ('.').
316409139f0SJean-Sébastien Pédron */
317409139f0SJean-Sébastien Pédron domain_name_len += label_len + 1;
318409139f0SJean-Sébastien Pédron
319409139f0SJean-Sébastien Pédron /* Move cursor. */
320409139f0SJean-Sébastien Pédron i += label_len + 1;
321409139f0SJean-Sébastien Pédron }
322409139f0SJean-Sébastien Pédron
32333d5b032SJean-Sébastien Pédron warning("Truncated DHCP Domain Search option.");
324409139f0SJean-Sébastien Pédron
32533d5b032SJean-Sébastien Pédron return (-1);
326409139f0SJean-Sébastien Pédron }
327409139f0SJean-Sébastien Pédron
328409139f0SJean-Sébastien Pédron void
expand_search_domain_name(struct option_data * option,size_t * offset,unsigned char ** domain_search)329afe6f835SAlan Somers expand_search_domain_name(struct option_data *option, size_t *offset,
330409139f0SJean-Sébastien Pédron unsigned char **domain_search)
331409139f0SJean-Sébastien Pédron {
332afe6f835SAlan Somers int label_len;
333afe6f835SAlan Somers size_t i, pointer;
334409139f0SJean-Sébastien Pédron unsigned char *cursor;
335409139f0SJean-Sébastien Pédron
336409139f0SJean-Sébastien Pédron /*
337409139f0SJean-Sébastien Pédron * This is the same loop than the function above
338409139f0SJean-Sébastien Pédron * (find_search_domain_name_len). Therefore, we remove checks,
339409139f0SJean-Sébastien Pédron * they're already done. Here, we just make the copy.
340409139f0SJean-Sébastien Pédron */
341409139f0SJean-Sébastien Pédron i = *offset;
342409139f0SJean-Sébastien Pédron cursor = *domain_search;
343409139f0SJean-Sébastien Pédron while (i < option->len) {
344409139f0SJean-Sébastien Pédron label_len = option->data[i];
345409139f0SJean-Sébastien Pédron if (label_len == 0) {
346409139f0SJean-Sébastien Pédron /*
347409139f0SJean-Sébastien Pédron * A zero-length label marks the end of this
348409139f0SJean-Sébastien Pédron * domain name.
349409139f0SJean-Sébastien Pédron */
350409139f0SJean-Sébastien Pédron *offset = i + 1;
351409139f0SJean-Sébastien Pédron *domain_search = cursor;
352409139f0SJean-Sébastien Pédron return;
353409139f0SJean-Sébastien Pédron } else if (label_len & 0xC0) {
354409139f0SJean-Sébastien Pédron /* This is a pointer to another list of labels. */
355409139f0SJean-Sébastien Pédron pointer = ((label_len & ~(0xC0)) << 8) +
356409139f0SJean-Sébastien Pédron option->data[i + 1];
357409139f0SJean-Sébastien Pédron
358409139f0SJean-Sébastien Pédron expand_search_domain_name(option, &pointer, &cursor);
359409139f0SJean-Sébastien Pédron
360409139f0SJean-Sébastien Pédron *offset = i + 2;
361409139f0SJean-Sébastien Pédron *domain_search = cursor;
362409139f0SJean-Sébastien Pédron return;
363409139f0SJean-Sébastien Pédron }
364409139f0SJean-Sébastien Pédron
365409139f0SJean-Sébastien Pédron /* Copy the label found. */
366409139f0SJean-Sébastien Pédron memcpy(cursor, option->data + i + 1, label_len);
367409139f0SJean-Sébastien Pédron cursor[label_len] = '.';
368409139f0SJean-Sébastien Pédron
369409139f0SJean-Sébastien Pédron /* Move cursor. */
370409139f0SJean-Sébastien Pédron i += label_len + 1;
371409139f0SJean-Sébastien Pédron cursor += label_len + 1;
372409139f0SJean-Sébastien Pédron }
373409139f0SJean-Sébastien Pédron }
374409139f0SJean-Sébastien Pédron
375409139f0SJean-Sébastien Pédron /*
37647c08596SBrooks Davis * cons options into a big buffer, and then split them out into the
37747c08596SBrooks Davis * three separate buffers if needed. This allows us to cons up a set of
37847c08596SBrooks Davis * vendor options using the same routine.
37947c08596SBrooks Davis */
38047c08596SBrooks Davis int
cons_options(struct packet * inpacket,struct dhcp_packet * outpacket,int mms,struct tree_cache ** options,int overload,int terminate,int bootpp,u_int8_t * prl,int prl_len)38147c08596SBrooks Davis cons_options(struct packet *inpacket, struct dhcp_packet *outpacket,
38247c08596SBrooks Davis int mms, struct tree_cache **options,
38347c08596SBrooks Davis int overload, /* Overload flags that may be set. */
38447c08596SBrooks Davis int terminate, int bootpp, u_int8_t *prl, int prl_len)
38547c08596SBrooks Davis {
38647c08596SBrooks Davis unsigned char priority_list[300], buffer[4096];
387afe6f835SAlan Somers unsigned priority_len;
388afe6f835SAlan Somers size_t main_buffer_size;
389afe6f835SAlan Somers unsigned option_size, bufix, mainbufix;
390afe6f835SAlan Somers int length;
39147c08596SBrooks Davis
39247c08596SBrooks Davis /*
39347c08596SBrooks Davis * If the client has provided a maximum DHCP message size, use
39447c08596SBrooks Davis * that; otherwise, if it's BOOTP, only 64 bytes; otherwise use
39547c08596SBrooks Davis * up to the minimum IP MTU size (576 bytes).
39647c08596SBrooks Davis *
39747c08596SBrooks Davis * XXX if a BOOTP client specifies a max message size, we will
39847c08596SBrooks Davis * honor it.
39947c08596SBrooks Davis */
40047c08596SBrooks Davis if (!mms &&
40147c08596SBrooks Davis inpacket &&
40247c08596SBrooks Davis inpacket->options[DHO_DHCP_MAX_MESSAGE_SIZE].data &&
40347c08596SBrooks Davis (inpacket->options[DHO_DHCP_MAX_MESSAGE_SIZE].len >=
40447c08596SBrooks Davis sizeof(u_int16_t)))
40547c08596SBrooks Davis mms = getUShort(
40647c08596SBrooks Davis inpacket->options[DHO_DHCP_MAX_MESSAGE_SIZE].data);
40747c08596SBrooks Davis
40847c08596SBrooks Davis if (mms)
40947c08596SBrooks Davis main_buffer_size = mms - DHCP_FIXED_LEN;
41047c08596SBrooks Davis else if (bootpp)
41147c08596SBrooks Davis main_buffer_size = 64;
41247c08596SBrooks Davis else
41347c08596SBrooks Davis main_buffer_size = 576 - DHCP_FIXED_LEN;
41447c08596SBrooks Davis
41547c08596SBrooks Davis if (main_buffer_size > sizeof(buffer))
41647c08596SBrooks Davis main_buffer_size = sizeof(buffer);
41747c08596SBrooks Davis
41847c08596SBrooks Davis /* Preload the option priority list with mandatory options. */
41947c08596SBrooks Davis priority_len = 0;
42047c08596SBrooks Davis priority_list[priority_len++] = DHO_DHCP_MESSAGE_TYPE;
42147c08596SBrooks Davis priority_list[priority_len++] = DHO_DHCP_SERVER_IDENTIFIER;
42247c08596SBrooks Davis priority_list[priority_len++] = DHO_DHCP_LEASE_TIME;
42347c08596SBrooks Davis priority_list[priority_len++] = DHO_DHCP_MESSAGE;
42447c08596SBrooks Davis
42547c08596SBrooks Davis /*
42647c08596SBrooks Davis * If the client has provided a list of options that it wishes
42747c08596SBrooks Davis * returned, use it to prioritize. Otherwise, prioritize based
42847c08596SBrooks Davis * on the default priority list.
42947c08596SBrooks Davis */
43047c08596SBrooks Davis if (inpacket &&
43147c08596SBrooks Davis inpacket->options[DHO_DHCP_PARAMETER_REQUEST_LIST].data) {
432afe6f835SAlan Somers unsigned prlen =
43347c08596SBrooks Davis inpacket->options[DHO_DHCP_PARAMETER_REQUEST_LIST].len;
43447c08596SBrooks Davis if (prlen + priority_len > sizeof(priority_list))
43547c08596SBrooks Davis prlen = sizeof(priority_list) - priority_len;
43647c08596SBrooks Davis
43747c08596SBrooks Davis memcpy(&priority_list[priority_len],
43847c08596SBrooks Davis inpacket->options[DHO_DHCP_PARAMETER_REQUEST_LIST].data,
43947c08596SBrooks Davis prlen);
44047c08596SBrooks Davis priority_len += prlen;
44147c08596SBrooks Davis prl = priority_list;
44247c08596SBrooks Davis } else if (prl) {
44347c08596SBrooks Davis if (prl_len + priority_len > sizeof(priority_list))
44447c08596SBrooks Davis prl_len = sizeof(priority_list) - priority_len;
44547c08596SBrooks Davis
44647c08596SBrooks Davis memcpy(&priority_list[priority_len], prl, prl_len);
44747c08596SBrooks Davis priority_len += prl_len;
44847c08596SBrooks Davis prl = priority_list;
44947c08596SBrooks Davis } else {
45047c08596SBrooks Davis memcpy(&priority_list[priority_len],
45147c08596SBrooks Davis dhcp_option_default_priority_list,
45247c08596SBrooks Davis sizeof_dhcp_option_default_priority_list);
45347c08596SBrooks Davis priority_len += sizeof_dhcp_option_default_priority_list;
45447c08596SBrooks Davis }
45547c08596SBrooks Davis
45647c08596SBrooks Davis /* Copy the options into the big buffer... */
45747c08596SBrooks Davis option_size = store_options(
45847c08596SBrooks Davis buffer,
45947c08596SBrooks Davis (main_buffer_size - 7 + ((overload & 1) ? DHCP_FILE_LEN : 0) +
46047c08596SBrooks Davis ((overload & 2) ? DHCP_SNAME_LEN : 0)),
46147c08596SBrooks Davis options, priority_list, priority_len, main_buffer_size,
46247c08596SBrooks Davis (main_buffer_size + ((overload & 1) ? DHCP_FILE_LEN : 0)),
46347c08596SBrooks Davis terminate);
46447c08596SBrooks Davis
46547c08596SBrooks Davis /* Put the cookie up front... */
46647c08596SBrooks Davis memcpy(outpacket->options, DHCP_OPTIONS_COOKIE, 4);
46747c08596SBrooks Davis mainbufix = 4;
46847c08596SBrooks Davis
46947c08596SBrooks Davis /*
47047c08596SBrooks Davis * If we're going to have to overload, store the overload option
47147c08596SBrooks Davis * at the beginning. If we can, though, just store the whole
47247c08596SBrooks Davis * thing in the packet's option buffer and leave it at that.
47347c08596SBrooks Davis */
47447c08596SBrooks Davis if (option_size <= main_buffer_size - mainbufix) {
47547c08596SBrooks Davis memcpy(&outpacket->options[mainbufix],
47647c08596SBrooks Davis buffer, option_size);
47747c08596SBrooks Davis mainbufix += option_size;
47847c08596SBrooks Davis if (mainbufix < main_buffer_size)
47947c08596SBrooks Davis outpacket->options[mainbufix++] = DHO_END;
48047c08596SBrooks Davis length = DHCP_FIXED_NON_UDP + mainbufix;
48147c08596SBrooks Davis } else {
48247c08596SBrooks Davis outpacket->options[mainbufix++] = DHO_DHCP_OPTION_OVERLOAD;
48347c08596SBrooks Davis outpacket->options[mainbufix++] = 1;
48447c08596SBrooks Davis if (option_size >
48547c08596SBrooks Davis main_buffer_size - mainbufix + DHCP_FILE_LEN)
48647c08596SBrooks Davis outpacket->options[mainbufix++] = 3;
48747c08596SBrooks Davis else
48847c08596SBrooks Davis outpacket->options[mainbufix++] = 1;
48947c08596SBrooks Davis
49047c08596SBrooks Davis memcpy(&outpacket->options[mainbufix],
49147c08596SBrooks Davis buffer, main_buffer_size - mainbufix);
49247c08596SBrooks Davis bufix = main_buffer_size - mainbufix;
49347c08596SBrooks Davis length = DHCP_FIXED_NON_UDP + mainbufix;
49447c08596SBrooks Davis if (overload & 1) {
49547c08596SBrooks Davis if (option_size - bufix <= DHCP_FILE_LEN) {
49647c08596SBrooks Davis memcpy(outpacket->file,
49747c08596SBrooks Davis &buffer[bufix], option_size - bufix);
49847c08596SBrooks Davis mainbufix = option_size - bufix;
49947c08596SBrooks Davis if (mainbufix < DHCP_FILE_LEN)
50047c08596SBrooks Davis outpacket->file[mainbufix++] = (char)DHO_END;
50147c08596SBrooks Davis while (mainbufix < DHCP_FILE_LEN)
50247c08596SBrooks Davis outpacket->file[mainbufix++] = (char)DHO_PAD;
50347c08596SBrooks Davis } else {
50447c08596SBrooks Davis memcpy(outpacket->file,
50547c08596SBrooks Davis &buffer[bufix], DHCP_FILE_LEN);
50647c08596SBrooks Davis bufix += DHCP_FILE_LEN;
50747c08596SBrooks Davis }
50847c08596SBrooks Davis }
50947c08596SBrooks Davis if ((overload & 2) && option_size < bufix) {
51047c08596SBrooks Davis memcpy(outpacket->sname,
51147c08596SBrooks Davis &buffer[bufix], option_size - bufix);
51247c08596SBrooks Davis
51347c08596SBrooks Davis mainbufix = option_size - bufix;
51447c08596SBrooks Davis if (mainbufix < DHCP_SNAME_LEN)
51547c08596SBrooks Davis outpacket->file[mainbufix++] = (char)DHO_END;
51647c08596SBrooks Davis while (mainbufix < DHCP_SNAME_LEN)
51747c08596SBrooks Davis outpacket->file[mainbufix++] = (char)DHO_PAD;
51847c08596SBrooks Davis }
51947c08596SBrooks Davis }
52047c08596SBrooks Davis return (length);
52147c08596SBrooks Davis }
52247c08596SBrooks Davis
52347c08596SBrooks Davis /*
52447c08596SBrooks Davis * Store all the requested options into the requested buffer.
52547c08596SBrooks Davis */
526afe6f835SAlan Somers unsigned
store_options(unsigned char * buffer,int buflen,struct tree_cache ** options,unsigned char * priority_list,int priority_len,int first_cutoff,int second_cutoff,int terminate)52747c08596SBrooks Davis store_options(unsigned char *buffer, int buflen, struct tree_cache **options,
52847c08596SBrooks Davis unsigned char *priority_list, int priority_len, int first_cutoff,
52947c08596SBrooks Davis int second_cutoff, int terminate)
53047c08596SBrooks Davis {
53147c08596SBrooks Davis int bufix = 0, option_stored[256], i, ix, tto;
53247c08596SBrooks Davis
53347c08596SBrooks Davis /* Zero out the stored-lengths array. */
53447c08596SBrooks Davis memset(option_stored, 0, sizeof(option_stored));
53547c08596SBrooks Davis
53647c08596SBrooks Davis /*
53747c08596SBrooks Davis * Copy out the options in the order that they appear in the
53847c08596SBrooks Davis * priority list...
53947c08596SBrooks Davis */
54047c08596SBrooks Davis for (i = 0; i < priority_len; i++) {
54147c08596SBrooks Davis /* Code for next option to try to store. */
54247c08596SBrooks Davis int code = priority_list[i];
54347c08596SBrooks Davis int optstart;
54447c08596SBrooks Davis
54547c08596SBrooks Davis /*
54647c08596SBrooks Davis * Number of bytes left to store (some may already have
54747c08596SBrooks Davis * been stored by a previous pass).
54847c08596SBrooks Davis */
54947c08596SBrooks Davis int length;
55047c08596SBrooks Davis
55147c08596SBrooks Davis /* If no data is available for this option, skip it. */
55247c08596SBrooks Davis if (!options[code]) {
55347c08596SBrooks Davis continue;
55447c08596SBrooks Davis }
55547c08596SBrooks Davis
55647c08596SBrooks Davis /*
55747c08596SBrooks Davis * The client could ask for things that are mandatory,
55847c08596SBrooks Davis * in which case we should avoid storing them twice...
55947c08596SBrooks Davis */
56047c08596SBrooks Davis if (option_stored[code])
56147c08596SBrooks Davis continue;
56247c08596SBrooks Davis option_stored[code] = 1;
56347c08596SBrooks Davis
56447c08596SBrooks Davis /* We should now have a constant length for the option. */
56547c08596SBrooks Davis length = options[code]->len;
56647c08596SBrooks Davis
56747c08596SBrooks Davis /* Do we add a NUL? */
56847c08596SBrooks Davis if (terminate && dhcp_options[code].format[0] == 't') {
56947c08596SBrooks Davis length++;
57047c08596SBrooks Davis tto = 1;
57147c08596SBrooks Davis } else
57247c08596SBrooks Davis tto = 0;
57347c08596SBrooks Davis
57447c08596SBrooks Davis /* Try to store the option. */
57547c08596SBrooks Davis
57647c08596SBrooks Davis /*
57747c08596SBrooks Davis * If the option's length is more than 255, we must
57847c08596SBrooks Davis * store it in multiple hunks. Store 255-byte hunks
57947c08596SBrooks Davis * first. However, in any case, if the option data will
58047c08596SBrooks Davis * cross a buffer boundary, split it across that
58147c08596SBrooks Davis * boundary.
58247c08596SBrooks Davis */
58347c08596SBrooks Davis ix = 0;
58447c08596SBrooks Davis
58547c08596SBrooks Davis optstart = bufix;
58647c08596SBrooks Davis while (length) {
58747c08596SBrooks Davis unsigned char incr = length > 255 ? 255 : length;
58847c08596SBrooks Davis
58947c08596SBrooks Davis /*
59047c08596SBrooks Davis * If this hunk of the buffer will cross a
59147c08596SBrooks Davis * boundary, only go up to the boundary in this
59247c08596SBrooks Davis * pass.
59347c08596SBrooks Davis */
59447c08596SBrooks Davis if (bufix < first_cutoff &&
59547c08596SBrooks Davis bufix + incr > first_cutoff)
59647c08596SBrooks Davis incr = first_cutoff - bufix;
59747c08596SBrooks Davis else if (bufix < second_cutoff &&
59847c08596SBrooks Davis bufix + incr > second_cutoff)
59947c08596SBrooks Davis incr = second_cutoff - bufix;
60047c08596SBrooks Davis
60147c08596SBrooks Davis /*
60247c08596SBrooks Davis * If this option is going to overflow the
60347c08596SBrooks Davis * buffer, skip it.
60447c08596SBrooks Davis */
60547c08596SBrooks Davis if (bufix + 2 + incr > buflen) {
60647c08596SBrooks Davis bufix = optstart;
60747c08596SBrooks Davis break;
60847c08596SBrooks Davis }
60947c08596SBrooks Davis
61047c08596SBrooks Davis /* Everything looks good - copy it in! */
61147c08596SBrooks Davis buffer[bufix] = code;
61247c08596SBrooks Davis buffer[bufix + 1] = incr;
61347c08596SBrooks Davis if (tto && incr == length) {
61447c08596SBrooks Davis memcpy(buffer + bufix + 2,
61547c08596SBrooks Davis options[code]->value + ix, incr - 1);
61647c08596SBrooks Davis buffer[bufix + 2 + incr - 1] = 0;
61747c08596SBrooks Davis } else
61847c08596SBrooks Davis memcpy(buffer + bufix + 2,
61947c08596SBrooks Davis options[code]->value + ix, incr);
62047c08596SBrooks Davis length -= incr;
62147c08596SBrooks Davis ix += incr;
62247c08596SBrooks Davis bufix += 2 + incr;
62347c08596SBrooks Davis }
62447c08596SBrooks Davis }
62547c08596SBrooks Davis return (bufix);
62647c08596SBrooks Davis }
62747c08596SBrooks Davis
62847c08596SBrooks Davis /*
62947c08596SBrooks Davis * Format the specified option so that a human can easily read it.
63047c08596SBrooks Davis */
63179a1d195SAlan Somers const char *
pretty_print_option(unsigned int code,unsigned char * data,int len,int emit_commas,int emit_quotes)63247c08596SBrooks Davis pretty_print_option(unsigned int code, unsigned char *data, int len,
63347c08596SBrooks Davis int emit_commas, int emit_quotes)
63447c08596SBrooks Davis {
63547c08596SBrooks Davis static char optbuf[32768]; /* XXX */
63647c08596SBrooks Davis int hunksize = 0, numhunk = -1, numelem = 0;
63747c08596SBrooks Davis char fmtbuf[32], *op = optbuf;
63847c08596SBrooks Davis int i, j, k, opleft = sizeof(optbuf);
63947c08596SBrooks Davis unsigned char *dp = data;
64047c08596SBrooks Davis struct in_addr foo;
64147c08596SBrooks Davis char comma;
64247c08596SBrooks Davis
64347c08596SBrooks Davis /* Code should be between 0 and 255. */
64447c08596SBrooks Davis if (code > 255)
64547c08596SBrooks Davis error("pretty_print_option: bad code %d", code);
64647c08596SBrooks Davis
64747c08596SBrooks Davis if (emit_commas)
64847c08596SBrooks Davis comma = ',';
64947c08596SBrooks Davis else
65047c08596SBrooks Davis comma = ' ';
65147c08596SBrooks Davis
65247c08596SBrooks Davis /* Figure out the size of the data. */
65347c08596SBrooks Davis for (i = 0; dhcp_options[code].format[i]; i++) {
65447c08596SBrooks Davis if (!numhunk) {
65547c08596SBrooks Davis warning("%s: Excess information in format string: %s",
65647c08596SBrooks Davis dhcp_options[code].name,
65747c08596SBrooks Davis &(dhcp_options[code].format[i]));
65847c08596SBrooks Davis break;
65947c08596SBrooks Davis }
66047c08596SBrooks Davis numelem++;
66147c08596SBrooks Davis fmtbuf[i] = dhcp_options[code].format[i];
66247c08596SBrooks Davis switch (dhcp_options[code].format[i]) {
66347c08596SBrooks Davis case 'A':
66447c08596SBrooks Davis --numelem;
66547c08596SBrooks Davis fmtbuf[i] = 0;
66647c08596SBrooks Davis numhunk = 0;
66747c08596SBrooks Davis break;
66847c08596SBrooks Davis case 'X':
66947c08596SBrooks Davis for (k = 0; k < len; k++)
67047c08596SBrooks Davis if (!isascii(data[k]) ||
67147c08596SBrooks Davis !isprint(data[k]))
67247c08596SBrooks Davis break;
67347c08596SBrooks Davis if (k == len) {
67447c08596SBrooks Davis fmtbuf[i] = 't';
67547c08596SBrooks Davis numhunk = -2;
67647c08596SBrooks Davis } else {
67747c08596SBrooks Davis fmtbuf[i] = 'x';
67847c08596SBrooks Davis hunksize++;
67947c08596SBrooks Davis comma = ':';
68047c08596SBrooks Davis numhunk = 0;
68147c08596SBrooks Davis }
68247c08596SBrooks Davis fmtbuf[i + 1] = 0;
68347c08596SBrooks Davis break;
68447c08596SBrooks Davis case 't':
68547c08596SBrooks Davis fmtbuf[i] = 't';
68647c08596SBrooks Davis fmtbuf[i + 1] = 0;
68747c08596SBrooks Davis numhunk = -2;
68847c08596SBrooks Davis break;
68947c08596SBrooks Davis case 'I':
69047c08596SBrooks Davis case 'l':
69147c08596SBrooks Davis case 'L':
69247c08596SBrooks Davis hunksize += 4;
69347c08596SBrooks Davis break;
69447c08596SBrooks Davis case 's':
69547c08596SBrooks Davis case 'S':
69647c08596SBrooks Davis hunksize += 2;
69747c08596SBrooks Davis break;
69847c08596SBrooks Davis case 'b':
69947c08596SBrooks Davis case 'B':
70047c08596SBrooks Davis case 'f':
70147c08596SBrooks Davis hunksize++;
70247c08596SBrooks Davis break;
70347c08596SBrooks Davis case 'e':
70447c08596SBrooks Davis break;
70547c08596SBrooks Davis default:
70647c08596SBrooks Davis warning("%s: garbage in format string: %s",
70747c08596SBrooks Davis dhcp_options[code].name,
70847c08596SBrooks Davis &(dhcp_options[code].format[i]));
70947c08596SBrooks Davis break;
71047c08596SBrooks Davis }
71147c08596SBrooks Davis }
71247c08596SBrooks Davis
71347c08596SBrooks Davis /* Check for too few bytes... */
71447c08596SBrooks Davis if (hunksize > len) {
71547c08596SBrooks Davis warning("%s: expecting at least %d bytes; got %d",
71647c08596SBrooks Davis dhcp_options[code].name, hunksize, len);
71747c08596SBrooks Davis return ("<error>");
71847c08596SBrooks Davis }
71947c08596SBrooks Davis /* Check for too many bytes... */
72047c08596SBrooks Davis if (numhunk == -1 && hunksize < len)
72147c08596SBrooks Davis warning("%s: %d extra bytes",
72247c08596SBrooks Davis dhcp_options[code].name, len - hunksize);
72347c08596SBrooks Davis
72447c08596SBrooks Davis /* If this is an array, compute its size. */
72547c08596SBrooks Davis if (!numhunk)
72647c08596SBrooks Davis numhunk = len / hunksize;
72747c08596SBrooks Davis /* See if we got an exact number of hunks. */
72847c08596SBrooks Davis if (numhunk > 0 && numhunk * hunksize < len)
72947c08596SBrooks Davis warning("%s: %d extra bytes at end of array",
73047c08596SBrooks Davis dhcp_options[code].name, len - numhunk * hunksize);
73147c08596SBrooks Davis
73247c08596SBrooks Davis /* A one-hunk array prints the same as a single hunk. */
73347c08596SBrooks Davis if (numhunk < 0)
73447c08596SBrooks Davis numhunk = 1;
73547c08596SBrooks Davis
73647c08596SBrooks Davis /* Cycle through the array (or hunk) printing the data. */
73747c08596SBrooks Davis for (i = 0; i < numhunk; i++) {
73847c08596SBrooks Davis for (j = 0; j < numelem; j++) {
73947c08596SBrooks Davis int opcount;
74047c08596SBrooks Davis switch (fmtbuf[j]) {
74147c08596SBrooks Davis case 't':
74247c08596SBrooks Davis if (emit_quotes) {
74347c08596SBrooks Davis *op++ = '"';
74447c08596SBrooks Davis opleft--;
74547c08596SBrooks Davis }
74647c08596SBrooks Davis for (; dp < data + len; dp++) {
74747c08596SBrooks Davis if (!isascii(*dp) ||
74847c08596SBrooks Davis !isprint(*dp)) {
74947c08596SBrooks Davis if (dp + 1 != data + len ||
75047c08596SBrooks Davis *dp != 0) {
75147c08596SBrooks Davis snprintf(op, opleft,
75247c08596SBrooks Davis "\\%03o", *dp);
75347c08596SBrooks Davis op += 4;
75447c08596SBrooks Davis opleft -= 4;
75547c08596SBrooks Davis }
75647c08596SBrooks Davis } else if (*dp == '"' ||
75747c08596SBrooks Davis *dp == '\'' ||
75847c08596SBrooks Davis *dp == '$' ||
75947c08596SBrooks Davis *dp == '`' ||
76047c08596SBrooks Davis *dp == '\\') {
76147c08596SBrooks Davis *op++ = '\\';
76247c08596SBrooks Davis *op++ = *dp;
76347c08596SBrooks Davis opleft -= 2;
76447c08596SBrooks Davis } else {
76547c08596SBrooks Davis *op++ = *dp;
76647c08596SBrooks Davis opleft--;
76747c08596SBrooks Davis }
76847c08596SBrooks Davis }
76947c08596SBrooks Davis if (emit_quotes) {
77047c08596SBrooks Davis *op++ = '"';
77147c08596SBrooks Davis opleft--;
77247c08596SBrooks Davis }
77347c08596SBrooks Davis
77447c08596SBrooks Davis *op = 0;
77547c08596SBrooks Davis break;
77647c08596SBrooks Davis case 'I':
77747c08596SBrooks Davis foo.s_addr = htonl(getULong(dp));
77847c08596SBrooks Davis opcount = strlcpy(op, inet_ntoa(foo), opleft);
77947c08596SBrooks Davis if (opcount >= opleft)
78047c08596SBrooks Davis goto toobig;
78147c08596SBrooks Davis opleft -= opcount;
78247c08596SBrooks Davis dp += 4;
78347c08596SBrooks Davis break;
78447c08596SBrooks Davis case 'l':
78547c08596SBrooks Davis opcount = snprintf(op, opleft, "%ld",
78647c08596SBrooks Davis (long)getLong(dp));
78747c08596SBrooks Davis if (opcount >= opleft || opcount == -1)
78847c08596SBrooks Davis goto toobig;
78947c08596SBrooks Davis opleft -= opcount;
79047c08596SBrooks Davis dp += 4;
79147c08596SBrooks Davis break;
79247c08596SBrooks Davis case 'L':
793cfbb427cSNick Hibma opcount = snprintf(op, opleft, "%lu",
79447c08596SBrooks Davis (unsigned long)getULong(dp));
79547c08596SBrooks Davis if (opcount >= opleft || opcount == -1)
79647c08596SBrooks Davis goto toobig;
79747c08596SBrooks Davis opleft -= opcount;
79847c08596SBrooks Davis dp += 4;
79947c08596SBrooks Davis break;
80047c08596SBrooks Davis case 's':
80147c08596SBrooks Davis opcount = snprintf(op, opleft, "%d",
80247c08596SBrooks Davis getShort(dp));
80347c08596SBrooks Davis if (opcount >= opleft || opcount == -1)
80447c08596SBrooks Davis goto toobig;
80547c08596SBrooks Davis opleft -= opcount;
80647c08596SBrooks Davis dp += 2;
80747c08596SBrooks Davis break;
80847c08596SBrooks Davis case 'S':
809cfbb427cSNick Hibma opcount = snprintf(op, opleft, "%u",
81047c08596SBrooks Davis getUShort(dp));
81147c08596SBrooks Davis if (opcount >= opleft || opcount == -1)
81247c08596SBrooks Davis goto toobig;
81347c08596SBrooks Davis opleft -= opcount;
81447c08596SBrooks Davis dp += 2;
81547c08596SBrooks Davis break;
81647c08596SBrooks Davis case 'b':
81747c08596SBrooks Davis opcount = snprintf(op, opleft, "%d",
81847c08596SBrooks Davis *(char *)dp++);
81947c08596SBrooks Davis if (opcount >= opleft || opcount == -1)
82047c08596SBrooks Davis goto toobig;
82147c08596SBrooks Davis opleft -= opcount;
82247c08596SBrooks Davis break;
82347c08596SBrooks Davis case 'B':
82447c08596SBrooks Davis opcount = snprintf(op, opleft, "%d", *dp++);
82547c08596SBrooks Davis if (opcount >= opleft || opcount == -1)
82647c08596SBrooks Davis goto toobig;
82747c08596SBrooks Davis opleft -= opcount;
82847c08596SBrooks Davis break;
82947c08596SBrooks Davis case 'x':
83047c08596SBrooks Davis opcount = snprintf(op, opleft, "%x", *dp++);
83147c08596SBrooks Davis if (opcount >= opleft || opcount == -1)
83247c08596SBrooks Davis goto toobig;
83347c08596SBrooks Davis opleft -= opcount;
83447c08596SBrooks Davis break;
83547c08596SBrooks Davis case 'f':
83647c08596SBrooks Davis opcount = strlcpy(op,
83747c08596SBrooks Davis *dp++ ? "true" : "false", opleft);
83847c08596SBrooks Davis if (opcount >= opleft)
83947c08596SBrooks Davis goto toobig;
84047c08596SBrooks Davis opleft -= opcount;
84147c08596SBrooks Davis break;
84247c08596SBrooks Davis default:
84347c08596SBrooks Davis warning("Unexpected format code %c", fmtbuf[j]);
84447c08596SBrooks Davis }
84547c08596SBrooks Davis op += strlen(op);
84647c08596SBrooks Davis opleft -= strlen(op);
84747c08596SBrooks Davis if (opleft < 1)
84847c08596SBrooks Davis goto toobig;
84947c08596SBrooks Davis if (j + 1 < numelem && comma != ':') {
85047c08596SBrooks Davis *op++ = ' ';
85147c08596SBrooks Davis opleft--;
85247c08596SBrooks Davis }
85347c08596SBrooks Davis }
85447c08596SBrooks Davis if (i + 1 < numhunk) {
85547c08596SBrooks Davis *op++ = comma;
85647c08596SBrooks Davis opleft--;
85747c08596SBrooks Davis }
85847c08596SBrooks Davis if (opleft < 1)
85947c08596SBrooks Davis goto toobig;
86047c08596SBrooks Davis
86147c08596SBrooks Davis }
86247c08596SBrooks Davis return (optbuf);
86347c08596SBrooks Davis toobig:
86447c08596SBrooks Davis warning("dhcp option too large");
86547c08596SBrooks Davis return ("<error>");
86647c08596SBrooks Davis }
86747c08596SBrooks Davis
86847c08596SBrooks Davis void
do_packet(struct interface_info * interface,struct dhcp_packet * packet,int len,unsigned int from_port,struct iaddr from,struct hardware * hfrom)86947c08596SBrooks Davis do_packet(struct interface_info *interface, struct dhcp_packet *packet,
87047c08596SBrooks Davis int len, unsigned int from_port, struct iaddr from, struct hardware *hfrom)
87147c08596SBrooks Davis {
87247c08596SBrooks Davis struct packet tp;
87347c08596SBrooks Davis int i;
87447c08596SBrooks Davis
87547c08596SBrooks Davis if (packet->hlen > sizeof(packet->chaddr)) {
87647c08596SBrooks Davis note("Discarding packet with invalid hlen.");
87747c08596SBrooks Davis return;
87847c08596SBrooks Davis }
87947c08596SBrooks Davis
88047c08596SBrooks Davis memset(&tp, 0, sizeof(tp));
88147c08596SBrooks Davis tp.raw = packet;
88247c08596SBrooks Davis tp.packet_length = len;
88347c08596SBrooks Davis tp.client_port = from_port;
88447c08596SBrooks Davis tp.client_addr = from;
88547c08596SBrooks Davis tp.interface = interface;
88647c08596SBrooks Davis tp.haddr = hfrom;
88747c08596SBrooks Davis
88847c08596SBrooks Davis parse_options(&tp);
88947c08596SBrooks Davis if (tp.options_valid &&
89047c08596SBrooks Davis tp.options[DHO_DHCP_MESSAGE_TYPE].data)
89147c08596SBrooks Davis tp.packet_type = tp.options[DHO_DHCP_MESSAGE_TYPE].data[0];
89247c08596SBrooks Davis if (tp.packet_type)
89347c08596SBrooks Davis dhcp(&tp);
89447c08596SBrooks Davis else
89547c08596SBrooks Davis bootp(&tp);
89647c08596SBrooks Davis
89747c08596SBrooks Davis /* Free the data associated with the options. */
89847c08596SBrooks Davis for (i = 0; i < 256; i++)
89947c08596SBrooks Davis free(tp.options[i].data);
90047c08596SBrooks Davis }
901