GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: sbin/dhclient/options.c Lines: 0 344 0.0 %
Date: 2016-12-06 Branches: 0 203 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: options.c,v 1.75 2016/02/06 19:30:52 krw Exp $	*/
2
3
/* DHCP options parsing and reassembly. */
4
5
/*
6
 * Copyright (c) 1995, 1996, 1997, 1998 The Internet Software Consortium.
7
 * All rights reserved.
8
 *
9
 * Redistribution and use in source and binary forms, with or without
10
 * modification, are permitted provided that the following conditions
11
 * are met:
12
 *
13
 * 1. Redistributions of source code must retain the above copyright
14
 *    notice, this list of conditions and the following disclaimer.
15
 * 2. Redistributions in binary form must reproduce the above copyright
16
 *    notice, this list of conditions and the following disclaimer in the
17
 *    documentation and/or other materials provided with the distribution.
18
 * 3. Neither the name of The Internet Software Consortium nor the names
19
 *    of its contributors may be used to endorse or promote products derived
20
 *    from this software without specific prior written permission.
21
 *
22
 * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
23
 * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
24
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
25
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
26
 * DISCLAIMED.  IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
27
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
29
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
30
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
31
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
32
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
33
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34
 * SUCH DAMAGE.
35
 *
36
 * This software has been written for the Internet Software Consortium
37
 * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
38
 * Enterprises.  To learn more about the Internet Software Consortium,
39
 * see ``http://www.vix.com/isc''.  To learn more about Vixie
40
 * Enterprises, see ``http://www.vix.com''.
41
 */
42
43
#include <sys/queue.h>
44
#include <sys/socket.h>
45
46
#include <arpa/inet.h>
47
48
#include <net/if.h>
49
50
#include <netinet/in.h>
51
#include <netinet/if_ether.h>
52
53
#include <ctype.h>
54
#include <signal.h>
55
#include <stdio.h>
56
#include <stdlib.h>
57
#include <string.h>
58
#include <vis.h>
59
60
#include "dhcp.h"
61
#include "dhcpd.h"
62
63
int parse_option_buffer(struct option_data *, unsigned char *, int);
64
int expand_search_domain_name(unsigned char *, size_t, int *, unsigned char *);
65
66
/*
67
 * Parse options out of the specified buffer, storing addresses of
68
 * option values in options. Return 0 if errors, 1 if not.
69
 */
70
int
71
parse_option_buffer(struct option_data *options, unsigned char *buffer,
72
    int length)
73
{
74
	unsigned char *s, *t, *end = buffer + length;
75
	int len, code;
76
77
	for (s = buffer; *s != DHO_END && s < end; ) {
78
		code = s[0];
79
80
		/* Pad options don't have a length - just skip them. */
81
		if (code == DHO_PAD) {
82
			s++;
83
			continue;
84
		}
85
86
		/*
87
		 * All options other than DHO_PAD and DHO_END have a one-byte
88
		 * length field. It could be 0! Make sure that the length byte
89
		 * is present, and all the data is available.
90
		 */
91
		if (s + 1 < end) {
92
			len = s[1];
93
			if (s + 1 + len < end) {
94
				; /* option data is all there. */
95
			} else {
96
				warning("option %s (%d) larger than buffer.",
97
				    dhcp_options[code].name, len);
98
				return (0);
99
			}
100
		} else {
101
			warning("option %s has no length field.",
102
			    dhcp_options[code].name);
103
			return (0);
104
		}
105
106
		/*
107
		 * Strip trailing NULs from ascii ('t') options. They
108
		 * will be treated as DHO_PAD options. i.e. ignored. RFC 2132
109
		 * says "Options containing NVT ASCII data SHOULD NOT include
110
		 * a trailing NULL; however, the receiver of such options
111
		 * MUST be prepared to delete trailing nulls if they exist."
112
		 */
113
		if (dhcp_options[code].format[0] == 't') {
114
			while (len > 0 && s[len + 1] == '\0')
115
				len--;
116
		}
117
118
		/*
119
		 * If we haven't seen this option before, just make
120
		 * space for it and copy it there.
121
		 */
122
		if (!options[code].data) {
123
			if (!(t = calloc(1, len + 1)))
124
				error("Can't allocate storage for option %s.",
125
				    dhcp_options[code].name);
126
			/*
127
			 * Copy and NUL-terminate the option (in case
128
			 * it's an ASCII string).
129
			 */
130
			memcpy(t, &s[2], len);
131
			t[len] = 0;
132
			options[code].len = len;
133
			options[code].data = t;
134
		} else {
135
			/*
136
			 * If it's a repeat, concatenate it to whatever
137
			 * we last saw.
138
			 */
139
			t = calloc(1, len + options[code].len + 1);
140
			if (!t)
141
				error("Can't expand storage for option %s.",
142
				    dhcp_options[code].name);
143
			memcpy(t, options[code].data, options[code].len);
144
			memcpy(t + options[code].len, &s[2], len);
145
			options[code].len += len;
146
			t[options[code].len] = 0;
147
			free(options[code].data);
148
			options[code].data = t;
149
		}
150
		s += len + 2;
151
	}
152
153
	return (1);
154
}
155
156
/*
157
 * Copy as many options as fit in buflen bytes of buf. Return the
158
 * offset of the start of the last option copied. A caller can check
159
 * to see if it's DHO_END to decide if all the options were copied.
160
 */
161
int
162
cons_options(struct option_data *options)
163
{
164
	unsigned char *buf = client->bootrequest_packet.options;
165
	int buflen = 576 - DHCP_FIXED_LEN;
166
	int ix, incr, length, bufix, code, lastopt = -1;
167
168
	memset(buf, 0, buflen);
169
170
	memcpy(buf, DHCP_OPTIONS_COOKIE, 4);
171
	if (options[DHO_DHCP_MESSAGE_TYPE].data) {
172
		memcpy(&buf[4], DHCP_OPTIONS_MESSAGE_TYPE, 3);
173
		buf[6] = options[DHO_DHCP_MESSAGE_TYPE].data[0];
174
		bufix = 7;
175
	} else
176
		bufix = 4;
177
178
	for (code = DHO_SUBNET_MASK; code < DHO_END; code++) {
179
		if (!options[code].data || code == DHO_DHCP_MESSAGE_TYPE)
180
			continue;
181
182
		length = options[code].len;
183
		if (bufix + length + 2*((length+254)/255) >= buflen)
184
			return (lastopt);
185
186
		lastopt = bufix;
187
		ix = 0;
188
189
		while (length) {
190
			incr = length > 255 ? 255 : length;
191
192
			buf[bufix++] = code;
193
			buf[bufix++] = incr;
194
			memcpy(buf + bufix, options[code].data + ix, incr);
195
196
			length -= incr;
197
			ix += incr;
198
			bufix += incr;
199
		}
200
	}
201
202
	if (bufix < buflen) {
203
		buf[bufix] = DHO_END;
204
		lastopt = bufix;
205
	}
206
207
	return (lastopt);
208
}
209
210
/*
211
 * Use vis() to encode characters of src and append encoded characters onto
212
 * dst. Also encode ", ', $, ` and \, to ensure resulting strings can be
213
 * represented as '"' delimited strings and safely passed to scripts. Surround
214
 * result with double quotes if emit_punct is true.
215
 */
216
int
217
pretty_print_string(unsigned char *dst, size_t dstlen, unsigned char *src,
218
    size_t srclen, int emit_punct)
219
{
220
	char visbuf[5];
221
	unsigned char *origsrc = src;
222
	int opcount = 0, total = 0;
223
224
	if (emit_punct) {
225
		opcount = snprintf(dst, dstlen, "\"");
226
		if (opcount == -1)
227
			return (-1);
228
		total += opcount;
229
		if (opcount >= dstlen)
230
			goto done;
231
		dstlen -= opcount;
232
		dst += opcount;
233
	}
234
235
	for (; src < origsrc + srclen; src++) {
236
		if (*src && strchr("\"'$`\\", *src))
237
			opcount = snprintf(dst, dstlen, "\\%c", *src);
238
		else {
239
			vis(visbuf, *src, VIS_OCTAL, *src+1);
240
			opcount = snprintf(dst, dstlen, "%s", visbuf);
241
		}
242
		if (opcount == -1)
243
			return (-1);
244
		total += opcount;
245
		if (opcount >= dstlen)
246
			goto done;
247
		dstlen -= opcount;
248
		dst += opcount;
249
	}
250
251
	if (emit_punct) {
252
		opcount = snprintf(dst, dstlen, "\"");
253
		if (opcount == -1)
254
			return (-1);
255
		total += opcount;
256
		if (opcount >= dstlen)
257
			goto done;
258
		dstlen -= opcount;
259
		dst += opcount;
260
	}
261
done:
262
	return (total);
263
}
264
265
/*
266
 * Must special case *_CLASSLESS_* route options due to the variable size
267
 * of the CIDR element in its CIA format.
268
 */
269
int
270
pretty_print_classless_routes(unsigned char *dst, size_t dstlen,
271
    unsigned char *src, size_t srclen)
272
{
273
	struct in_addr mask, gateway;
274
	int opcount = 0, total = 0, bits, bytes;
275
	char ntoabuf[INET_ADDRSTRLEN];
276
277
	while (srclen && dstlen) {
278
		bits = *src;
279
		src++;
280
		srclen--;
281
		bytes = (bits + 7) / 8;
282
		if (srclen < bytes || bytes > sizeof(mask.s_addr))
283
			break;
284
		memset(&mask, 0, sizeof(mask));
285
		memcpy(&mask.s_addr, src, bytes);
286
		src += bytes;
287
		srclen -= bytes;
288
		strlcpy(ntoabuf, inet_ntoa(mask), sizeof(ntoabuf));
289
		if (srclen < sizeof(gateway.s_addr))
290
			break;
291
		memcpy(&gateway.s_addr, src, sizeof(gateway.s_addr));
292
		src += sizeof(gateway.s_addr);
293
		srclen -= sizeof(gateway.s_addr);
294
		opcount = snprintf(dst, dstlen, "%s%s/%u %s",
295
		    total ? ", " : "", ntoabuf, bits,
296
		    inet_ntoa(gateway));
297
		if (opcount == -1)
298
			return (-1);
299
		total += opcount;
300
		if (opcount >= dstlen)
301
			break;
302
		dst += opcount;
303
		dstlen -= opcount;
304
	}
305
306
	return (total);
307
}
308
309
int
310
expand_search_domain_name(unsigned char *src, size_t srclen, int *offset,
311
    unsigned char *domain_search)
312
{
313
	int domain_name_len, i, label_len, pointer, pointed_len;
314
	char *cursor;
315
316
	cursor = domain_search + strlen(domain_search);
317
	domain_name_len = 0;
318
319
	i = *offset;
320
	while (i <= srclen) {
321
		label_len = src[i];
322
		if (label_len == 0) {
323
			/*
324
			 * A zero-length label marks the end of this
325
			 * domain name.
326
			 */
327
			*offset = i + 1;
328
			return (domain_name_len);
329
		} else if (label_len & 0xC0) {
330
			/* This is a pointer to another list of labels. */
331
			if (i + 1 >= srclen) {
332
				/* The pointer is truncated. */
333
				warning("Truncated pointer in DHCP Domain "
334
				    "Search option.");
335
				return (-1);
336
			}
337
338
			pointer = ((label_len & ~(0xC0)) << 8) + src[i + 1];
339
			if (pointer >= *offset) {
340
				/*
341
				 * The pointer must indicates a prior
342
				 * occurance.
343
				 */
344
				warning("Invalid forward pointer in DHCP "
345
				    "Domain Search option compression.");
346
				return (-1);
347
			}
348
349
			pointed_len = expand_search_domain_name(src, srclen,
350
			    &pointer, domain_search);
351
			domain_name_len += pointed_len;
352
353
			*offset = i + 2;
354
			return (domain_name_len);
355
		}
356
		if (i + label_len + 1 > srclen) {
357
			warning("Truncated label in DHCP Domain Search "
358
			    "option.");
359
			return (-1);
360
		}
361
		/*
362
		 * Update the domain name length with the length of the
363
		 * current label, plus a trailing dot ('.').
364
		 */
365
		domain_name_len += label_len + 1;
366
367
		if (strlen(domain_search) + domain_name_len >=
368
		    DHCP_DOMAIN_SEARCH_LEN) {
369
			warning("Domain search list too long.");
370
			return (-1);
371
		}
372
373
		/* Copy the label found. */
374
		memcpy(cursor, src + i + 1, label_len);
375
		cursor[label_len] = '.';
376
377
		/* Move cursor. */
378
		i += label_len + 1;
379
		cursor += label_len + 1;
380
	}
381
382
	warning("Truncated DHCP Domain Search option.");
383
384
	return (-1);
385
}
386
387
/*
388
 * Must special case DHO_DOMAIN_SEARCH because it is encoded as described
389
 * in RFC 1035 section 4.1.4.
390
 */
391
int
392
pretty_print_domain_search(unsigned char *dst, size_t dstlen,
393
    unsigned char *src, size_t srclen)
394
{
395
	int offset, len, expanded_len, domains;
396
	unsigned char *domain_search, *cursor;
397
398
	domain_search = calloc(1, DHCP_DOMAIN_SEARCH_LEN);
399
	if (domain_search == NULL)
400
		error("Can't allocate storage for expanded domain-search\n");
401
402
	/* Compute expanded length. */
403
	expanded_len = len = 0;
404
	domains = 0;
405
	offset = 0;
406
	while (offset < srclen) {
407
		cursor = domain_search + strlen(domain_search);
408
		if (domain_search[0]) {
409
			*cursor = ' ';
410
			expanded_len++;
411
		}
412
		len = expand_search_domain_name(src, srclen, &offset,
413
		    domain_search);
414
		if (len == -1) {
415
			free(domain_search);
416
			return (-1);
417
		}
418
		domains++;
419
		expanded_len += len;
420
		if (domains > DHCP_DOMAIN_SEARCH_CNT) {
421
			free(domain_search);
422
			return (-1);
423
		}
424
	}
425
426
	strlcat(dst, domain_search, dstlen);
427
	free(domain_search);
428
429
	return (0);
430
}
431
432
/*
433
 * Format the specified option so that a human can easily read it.
434
 */
435
char *
436
pretty_print_option(unsigned int code, struct option_data *option,
437
    int emit_punct)
438
{
439
	static char optbuf[32768]; /* XXX */
440
	int hunksize = 0, numhunk = -1, numelem = 0;
441
	char fmtbuf[32], *op = optbuf;
442
	int i, j, k, opleft = sizeof(optbuf);
443
	unsigned char *data = option->data;
444
	unsigned char *dp = data;
445
	int len = option->len;
446
	int opcount = 0;
447
	struct in_addr foo;
448
	char comma;
449
	int32_t int32val;
450
	u_int32_t uint32val;
451
	u_int16_t uint16val;
452
453
	memset(optbuf, 0, sizeof(optbuf));
454
455
	/* Code should be between 0 and 255. */
456
	if (code > 255) {
457
		warning("pretty_print_option: bad code %d", code);
458
		goto done;
459
	}
460
461
	if (emit_punct)
462
		comma = ',';
463
	else
464
		comma = ' ';
465
466
	/* Handle the princess class options with weirdo formats. */
467
	switch (code) {
468
	case DHO_CLASSLESS_STATIC_ROUTES:
469
	case DHO_CLASSLESS_MS_STATIC_ROUTES:
470
		opcount = pretty_print_classless_routes(op, opleft, dp, len);
471
		if (opcount >= opleft || opcount == -1)
472
			goto toobig;
473
		goto done;
474
	default:
475
		break;
476
	}
477
478
	/* Figure out the size of the data. */
479
	for (i = 0; dhcp_options[code].format[i]; i++) {
480
		if (!numhunk) {
481
			warning("%s: Excess information in format string: %s",
482
			    dhcp_options[code].name,
483
			    &(dhcp_options[code].format[i]));
484
			goto done;
485
		}
486
		numelem++;
487
		fmtbuf[i] = dhcp_options[code].format[i];
488
		switch (dhcp_options[code].format[i]) {
489
		case 'A':
490
			--numelem;
491
			fmtbuf[i] = 0;
492
			numhunk = 0;
493
			if (hunksize == 0) {
494
				warning("%s: no size indicator before A"
495
				    " in format string: %s",
496
				    dhcp_options[code].name,
497
				    dhcp_options[code].format);
498
				goto done;
499
			}
500
			break;
501
		case 'X':
502
			for (k = 0; k < len; k++)
503
				if (!isascii(data[k]) ||
504
				    !isprint(data[k]))
505
					break;
506
			if (k == len) {
507
				fmtbuf[i] = 't';
508
				numhunk = -2;
509
			} else {
510
				hunksize++;
511
				comma = ':';
512
				numhunk = 0;
513
			}
514
			fmtbuf[i + 1] = 0;
515
			break;
516
		case 't':
517
			fmtbuf[i + 1] = 0;
518
			numhunk = -2;
519
			break;
520
		case 'I':
521
		case 'l':
522
		case 'L':
523
			hunksize += 4;
524
			break;
525
		case 'S':
526
			hunksize += 2;
527
			break;
528
		case 'B':
529
		case 'f':
530
			hunksize++;
531
			break;
532
		case 'e':
533
			break;
534
		default:
535
			warning("%s: garbage in format string: %s",
536
			    dhcp_options[code].name,
537
			    &(dhcp_options[code].format[i]));
538
			goto done;
539
		}
540
	}
541
542
	/* Check for too few bytes. */
543
	if (hunksize > len) {
544
		warning("%s: expecting at least %d bytes; got %d",
545
		    dhcp_options[code].name, hunksize, len);
546
		goto done;
547
	}
548
	/* Check for too many bytes. */
549
	if (numhunk == -1 && hunksize < len) {
550
		warning("%s: expecting only %d bytes: got %d",
551
		    dhcp_options[code].name, hunksize, len);
552
		goto done;
553
	}
554
555
	/* If this is an array, compute its size. */
556
	if (!numhunk)
557
		numhunk = len / hunksize;
558
	/* See if we got an exact number of hunks. */
559
	if (numhunk > 0 && numhunk * hunksize != len) {
560
		warning("%s: expecting %d bytes: got %d",
561
		    dhcp_options[code].name, numhunk * hunksize, len);
562
		goto done;
563
	}
564
565
	/* A one-hunk array prints the same as a single hunk. */
566
	if (numhunk < 0)
567
		numhunk = 1;
568
569
	/* Cycle through the array (or hunk) printing the data. */
570
	for (i = 0; i < numhunk; i++) {
571
		for (j = 0; j < numelem; j++) {
572
			switch (fmtbuf[j]) {
573
			case 't':
574
				opcount = pretty_print_string(op, opleft,
575
				    dp, len, emit_punct);
576
				break;
577
			case 'I':
578
				memcpy(&foo.s_addr, dp, sizeof(foo.s_addr));
579
				opcount = snprintf(op, opleft, "%s",
580
				    inet_ntoa(foo));
581
				dp += sizeof(foo.s_addr);
582
				break;
583
			case 'l':
584
				memcpy(&int32val, dp, sizeof(int32val));
585
				opcount = snprintf(op, opleft, "%d",
586
				    ntohl(int32val));
587
				dp += sizeof(int32val);
588
				break;
589
			case 'L':
590
				memcpy(&uint32val, dp, sizeof(uint32val));
591
				opcount = snprintf(op, opleft, "%u",
592
				    ntohl(uint32val));
593
				dp += sizeof(uint32val);
594
				break;
595
			case 'S':
596
				memcpy(&uint16val, dp, sizeof(uint16val));
597
				opcount = snprintf(op, opleft, "%hu",
598
				    ntohs(uint16val));
599
				dp += sizeof(uint16val);
600
				break;
601
			case 'B':
602
				opcount = snprintf(op, opleft, "%u", *dp);
603
				dp++;
604
				break;
605
			case 'X':
606
				opcount = snprintf(op, opleft, "%x", *dp);
607
				dp++;
608
				break;
609
			case 'f':
610
				opcount = snprintf(op, opleft, "%s",
611
				    *dp ? "true" : "false");
612
				dp++;
613
				break;
614
			default:
615
				warning("Unexpected format code %c", fmtbuf[j]);
616
				goto toobig;
617
			}
618
			if (opcount >= opleft || opcount == -1)
619
				goto toobig;
620
			opleft -= opcount;
621
			op += opcount;
622
			if (j + 1 < numelem && comma != ':') {
623
				opcount = snprintf(op, opleft, " ");
624
				if (opcount >= opleft || opcount == -1)
625
					goto toobig;
626
				opleft -= opcount;
627
				op += opcount;
628
			}
629
		}
630
		if (i + 1 < numhunk) {
631
			opcount = snprintf(op, opleft, "%c", comma);
632
			if (opcount >= opleft || opcount == -1)
633
				goto toobig;
634
			opleft -= opcount;
635
			op += opcount;
636
		}
637
	}
638
639
done:
640
	return (optbuf);
641
642
toobig:
643
	memset(optbuf, 0, sizeof(optbuf));
644
	return (optbuf);
645
}
646
647
void
648
do_packet(unsigned int from_port, struct in_addr from,
649
    struct ether_addr *hfrom)
650
{
651
	struct dhcp_packet *packet = &client->packet;
652
	struct option_data options[256];
653
	struct reject_elem *ap;
654
	void (*handler)(struct in_addr, struct option_data *, char *);
655
	char *type, *info;
656
	int i, rslt, options_valid = 1;
657
658
	if (packet->hlen != ETHER_ADDR_LEN) {
659
#ifdef DEBUG
660
		debug("Discarding packet with hlen != %s (%u)",
661
		    ifi->name, packet->hlen);
662
#endif
663
		return;
664
	} else if (memcmp(&ifi->hw_address, packet->chaddr,
665
	    sizeof(ifi->hw_address))) {
666
#ifdef DEBUG
667
		debug("Discarding packet with chaddr != %s (%s)", ifi->name,
668
		    ether_ntoa((struct ether_addr *)packet->chaddr));
669
#endif
670
		return;
671
	}
672
673
	if (client->xid != client->packet.xid) {
674
#ifdef DEBUG
675
		debug("Discarding packet with XID != %u (%u)", client->xid,
676
		    client->packet.xid);
677
#endif
678
		return;
679
	}
680
681
	TAILQ_FOREACH(ap, &config->reject_list, next)
682
		if (from.s_addr == ap->addr.s_addr) {
683
#ifdef DEBUG
684
			debug("Discarding packet from address on reject list "
685
			    "(%s)", inet_ntoa(from));
686
#endif
687
			return;
688
		}
689
690
	memset(options, 0, sizeof(options));
691
692
	if (memcmp(&packet->options, DHCP_OPTIONS_COOKIE, 4) == 0) {
693
		/* Parse the BOOTP/DHCP options field. */
694
		options_valid = parse_option_buffer(options,
695
		    &packet->options[4], sizeof(packet->options) - 4);
696
697
		/* Only DHCP packets have overload areas for options. */
698
		if (options_valid &&
699
		    options[DHO_DHCP_MESSAGE_TYPE].data &&
700
		    options[DHO_DHCP_OPTION_OVERLOAD].data) {
701
			if (options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 1)
702
				options_valid = parse_option_buffer(options,
703
				    (unsigned char *)packet->file,
704
				    sizeof(packet->file));
705
			if (options_valid &&
706
			    options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 2)
707
				options_valid = parse_option_buffer(options,
708
				    (unsigned char *)packet->sname,
709
				    sizeof(packet->sname));
710
		}
711
	}
712
713
	type = "<unknown>";
714
	handler = NULL;
715
716
	if (options[DHO_DHCP_MESSAGE_TYPE].data) {
717
		/* Always try a DHCP packet, even if a bad option was seen. */
718
		switch (options[DHO_DHCP_MESSAGE_TYPE].data[0]) {
719
		case DHCPOFFER:
720
			handler = dhcpoffer;
721
			type = "DHCPOFFER";
722
			break;
723
		case DHCPNAK:
724
			handler = dhcpnak;
725
			type = "DHCPNACK";
726
			break;
727
		case DHCPACK:
728
			handler = dhcpack;
729
			type = "DHCPACK";
730
			break;
731
		default:
732
#ifdef DEBUG
733
			debug("Discarding DHCP packet of unknown type (%d)",
734
				options[DHO_DHCP_MESSAGE_TYPE].data[0]);
735
#endif
736
			break;
737
		}
738
	} else if (options_valid && packet->op == BOOTREPLY) {
739
		handler = dhcpoffer;
740
		type = "BOOTREPLY";
741
	} else {
742
#ifdef DEBUG
743
		debug("Discarding packet which is neither DHCP nor BOOTP");
744
#endif
745
	}
746
747
	rslt = asprintf(&info, "%s from %s (%s)", type, inet_ntoa(from),
748
	    ether_ntoa(hfrom));
749
	if (rslt == -1)
750
		error("no memory for info string");
751
752
	if (handler)
753
		(*handler)(from, options, info);
754
755
	free(info);
756
757
	for (i = 0; i < 256; i++)
758
		free(options[i].data);
759
}