Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sbin/dhclient/options.c
39476 views
1
/* $OpenBSD: options.c,v 1.15 2004/12/26 03:17:07 deraadt Exp $ */
2
3
/* DHCP options parsing and reassembly. */
4
5
/*-
6
* SPDX-License-Identifier: BSD-3-Clause
7
*
8
* Copyright (c) 1995, 1996, 1997, 1998 The Internet Software Consortium.
9
* All rights reserved.
10
*
11
* Redistribution and use in source and binary forms, with or without
12
* modification, are permitted provided that the following conditions
13
* are met:
14
*
15
* 1. Redistributions of source code must retain the above copyright
16
* notice, this list of conditions and the following disclaimer.
17
* 2. Redistributions in binary form must reproduce the above copyright
18
* notice, this list of conditions and the following disclaimer in the
19
* documentation and/or other materials provided with the distribution.
20
* 3. Neither the name of The Internet Software Consortium nor the names
21
* of its contributors may be used to endorse or promote products derived
22
* from this software without specific prior written permission.
23
*
24
* THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
25
* CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
26
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
27
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
28
* DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
29
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
31
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
32
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
33
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
34
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
35
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36
* SUCH DAMAGE.
37
*
38
* This software has been written for the Internet Software Consortium
39
* by Ted Lemon <[email protected]> in cooperation with Vixie
40
* Enterprises. To learn more about the Internet Software Consortium,
41
* see ``http://www.vix.com/isc''. To learn more about Vixie
42
* Enterprises, see ``http://www.vix.com''.
43
*/
44
45
#include <sys/cdefs.h>
46
#include <ctype.h>
47
48
#define DHCP_OPTION_DATA
49
#include "dhcpd.h"
50
51
static int bad_options = 0;
52
static int bad_options_max = 5;
53
54
void parse_options(struct packet *);
55
void parse_option_buffer(struct packet *, unsigned char *, int);
56
unsigned store_options(unsigned char *, int, struct tree_cache **,
57
unsigned char *, int, int, int, int);
58
void expand_domain_search(struct packet *packet);
59
int find_search_domain_name_len(struct option_data *option, size_t *offset);
60
void expand_search_domain_name(struct option_data *option, size_t *offset,
61
unsigned char **domain_search);
62
63
64
/*
65
* Parse all available options out of the specified packet.
66
*/
67
void
68
parse_options(struct packet *packet)
69
{
70
/* Initially, zero all option pointers. */
71
memset(packet->options, 0, sizeof(packet->options));
72
73
/* If we don't see the magic cookie, there's nothing to parse. */
74
if (memcmp(packet->raw->options, DHCP_OPTIONS_COOKIE, 4)) {
75
packet->options_valid = 0;
76
return;
77
}
78
79
/*
80
* Go through the options field, up to the end of the packet or
81
* the End field.
82
*/
83
parse_option_buffer(packet, &packet->raw->options[4],
84
packet->packet_length - DHCP_FIXED_NON_UDP - 4);
85
86
/*
87
* If we parsed a DHCP Option Overload option, parse more
88
* options out of the buffer(s) containing them.
89
*/
90
if (packet->options_valid &&
91
packet->options[DHO_DHCP_OPTION_OVERLOAD].data) {
92
if (packet->options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 1)
93
parse_option_buffer(packet,
94
(unsigned char *)packet->raw->file,
95
sizeof(packet->raw->file));
96
if (packet->options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 2)
97
parse_option_buffer(packet,
98
(unsigned char *)packet->raw->sname,
99
sizeof(packet->raw->sname));
100
}
101
102
/* Expand DHCP Domain Search option. */
103
if (packet->options_valid) {
104
expand_domain_search(packet);
105
}
106
}
107
108
/*
109
* Parse options out of the specified buffer, storing addresses of
110
* option values in packet->options and setting packet->options_valid if
111
* no errors are encountered.
112
*/
113
void
114
parse_option_buffer(struct packet *packet,
115
unsigned char *buffer, int length)
116
{
117
unsigned char *s, *t, *end = buffer + length;
118
int len, code;
119
120
for (s = buffer; *s != DHO_END && s < end; ) {
121
code = s[0];
122
123
/* Pad options don't have a length - just skip them. */
124
if (code == DHO_PAD) {
125
s++;
126
continue;
127
}
128
if (s + 2 > end) {
129
len = 65536;
130
goto bogus;
131
}
132
133
/*
134
* All other fields (except end, see above) have a
135
* one-byte length.
136
*/
137
len = s[1];
138
139
/*
140
* If the length is outrageous, silently skip the rest,
141
* and mark the packet bad. Unfortunately some crappy
142
* dhcp servers always seem to give us garbage on the
143
* end of a packet. so rather than keep refusing, give
144
* up and try to take one after seeing a few without
145
* anything good.
146
*/
147
if (s + len + 2 > end) {
148
bogus:
149
bad_options++;
150
warning("option %s (%d) %s.",
151
dhcp_options[code].name, len,
152
"larger than buffer");
153
if (bad_options == bad_options_max) {
154
packet->options_valid = 1;
155
bad_options = 0;
156
warning("Many bogus options seen in offers. "
157
"Taking this offer in spite of bogus "
158
"options - hope for the best!");
159
} else {
160
warning("rejecting bogus offer.");
161
packet->options_valid = 0;
162
}
163
return;
164
}
165
/*
166
* If we haven't seen this option before, just make
167
* space for it and copy it there.
168
*/
169
if (!packet->options[code].data) {
170
if (!(t = calloc(1, len + 1)))
171
error("Can't allocate storage for option %s.",
172
dhcp_options[code].name);
173
/*
174
* Copy and NUL-terminate the option (in case
175
* it's an ASCII string.
176
*/
177
memcpy(t, &s[2], len);
178
t[len] = 0;
179
packet->options[code].len = len;
180
packet->options[code].data = t;
181
} else {
182
/*
183
* If it's a repeat, concatenate it to whatever
184
* we last saw. This is really only required
185
* for clients, but what the heck...
186
*/
187
t = calloc(1, len + packet->options[code].len + 1);
188
if (!t)
189
error("Can't expand storage for option %s.",
190
dhcp_options[code].name);
191
memcpy(t, packet->options[code].data,
192
packet->options[code].len);
193
memcpy(t + packet->options[code].len,
194
&s[2], len);
195
packet->options[code].len += len;
196
t[packet->options[code].len] = 0;
197
free(packet->options[code].data);
198
packet->options[code].data = t;
199
}
200
s += len + 2;
201
}
202
packet->options_valid = 1;
203
}
204
205
/*
206
* Expand DHCP Domain Search option. The value of this option is
207
* encoded like DNS' list of labels. See:
208
* RFC 3397
209
* RFC 1035
210
*/
211
void
212
expand_domain_search(struct packet *packet)
213
{
214
size_t offset;
215
int expanded_len, next_domain_len;
216
struct option_data *option;
217
unsigned char *domain_search, *cursor;
218
219
if (packet->options[DHO_DOMAIN_SEARCH].data == NULL)
220
return;
221
222
option = &packet->options[DHO_DOMAIN_SEARCH];
223
224
/* Compute final expanded length. */
225
expanded_len = 0;
226
offset = 0;
227
while (offset < option->len) {
228
next_domain_len = find_search_domain_name_len(option, &offset);
229
if (next_domain_len < 0)
230
/* The Domain Search option value is invalid. */
231
return;
232
233
/* We add 1 for the space between domain names. */
234
expanded_len += next_domain_len + 1;
235
}
236
if (expanded_len > 0)
237
/* Remove 1 for the superfluous trailing space. */
238
--expanded_len;
239
240
domain_search = malloc(expanded_len + 1);
241
if (domain_search == NULL)
242
error("Can't allocate storage for expanded domain-search\n");
243
244
offset = 0;
245
cursor = domain_search;
246
while (offset < option->len) {
247
expand_search_domain_name(option, &offset, &cursor);
248
cursor[0] = ' ';
249
cursor++;
250
}
251
domain_search[expanded_len] = '\0';
252
253
free(option->data);
254
option->len = expanded_len;
255
option->data = domain_search;
256
}
257
258
int
259
find_search_domain_name_len(struct option_data *option, size_t *offset)
260
{
261
int domain_name_len, label_len, pointed_len;
262
size_t i, pointer;
263
264
domain_name_len = 0;
265
266
i = *offset;
267
while (i < option->len) {
268
label_len = option->data[i];
269
if (label_len == 0) {
270
/*
271
* A zero-length label marks the end of this
272
* domain name.
273
*/
274
*offset = i + 1;
275
return (domain_name_len);
276
} else if (label_len & 0xC0) {
277
/* This is a pointer to another list of labels. */
278
if (i + 1 >= option->len) {
279
/* The pointer is truncated. */
280
warning("Truncated pointer in DHCP Domain "
281
"Search option.");
282
return (-1);
283
}
284
285
pointer = ((label_len & ~(0xC0)) << 8) +
286
option->data[i + 1];
287
if (pointer >= *offset) {
288
/*
289
* The pointer must indicate a prior
290
* occurrence.
291
*/
292
warning("Invalid forward pointer in DHCP "
293
"Domain Search option compression.");
294
return (-1);
295
}
296
297
pointed_len = find_search_domain_name_len(option,
298
&pointer);
299
if (pointed_len < 0)
300
return (-1);
301
domain_name_len += pointed_len;
302
303
*offset = i + 2;
304
return (domain_name_len);
305
}
306
307
if (i + label_len >= option->len) {
308
warning("Truncated label in DHCP Domain Search "
309
"option.");
310
return (-1);
311
}
312
313
/*
314
* Update the domain name length with the length of the
315
* current label, plus a trailing dot ('.').
316
*/
317
domain_name_len += label_len + 1;
318
319
/* Move cursor. */
320
i += label_len + 1;
321
}
322
323
warning("Truncated DHCP Domain Search option.");
324
325
return (-1);
326
}
327
328
void
329
expand_search_domain_name(struct option_data *option, size_t *offset,
330
unsigned char **domain_search)
331
{
332
int label_len;
333
size_t i, pointer;
334
unsigned char *cursor;
335
336
/*
337
* This is the same loop than the function above
338
* (find_search_domain_name_len). Therefore, we remove checks,
339
* they're already done. Here, we just make the copy.
340
*/
341
i = *offset;
342
cursor = *domain_search;
343
while (i < option->len) {
344
label_len = option->data[i];
345
if (label_len == 0) {
346
/*
347
* A zero-length label marks the end of this
348
* domain name.
349
*/
350
*offset = i + 1;
351
*domain_search = cursor;
352
return;
353
} else if (label_len & 0xC0) {
354
/* This is a pointer to another list of labels. */
355
pointer = ((label_len & ~(0xC0)) << 8) +
356
option->data[i + 1];
357
358
expand_search_domain_name(option, &pointer, &cursor);
359
360
*offset = i + 2;
361
*domain_search = cursor;
362
return;
363
}
364
365
/* Copy the label found. */
366
memcpy(cursor, option->data + i + 1, label_len);
367
cursor[label_len] = '.';
368
369
/* Move cursor. */
370
i += label_len + 1;
371
cursor += label_len + 1;
372
}
373
}
374
375
/*
376
* cons options into a big buffer, and then split them out into the
377
* three separate buffers if needed. This allows us to cons up a set of
378
* vendor options using the same routine.
379
*/
380
int
381
cons_options(struct packet *inpacket, struct dhcp_packet *outpacket,
382
int mms, struct tree_cache **options,
383
int overload, /* Overload flags that may be set. */
384
int terminate, int bootpp, u_int8_t *prl, int prl_len)
385
{
386
unsigned char priority_list[300], buffer[4096];
387
unsigned priority_len;
388
size_t main_buffer_size;
389
unsigned option_size, bufix, mainbufix;
390
int length;
391
392
/*
393
* If the client has provided a maximum DHCP message size, use
394
* that; otherwise, if it's BOOTP, only 64 bytes; otherwise use
395
* up to the minimum IP MTU size (576 bytes).
396
*
397
* XXX if a BOOTP client specifies a max message size, we will
398
* honor it.
399
*/
400
if (!mms &&
401
inpacket &&
402
inpacket->options[DHO_DHCP_MAX_MESSAGE_SIZE].data &&
403
(inpacket->options[DHO_DHCP_MAX_MESSAGE_SIZE].len >=
404
sizeof(u_int16_t)))
405
mms = getUShort(
406
inpacket->options[DHO_DHCP_MAX_MESSAGE_SIZE].data);
407
408
if (mms)
409
main_buffer_size = mms - DHCP_FIXED_LEN;
410
else if (bootpp)
411
main_buffer_size = 64;
412
else
413
main_buffer_size = 576 - DHCP_FIXED_LEN;
414
415
if (main_buffer_size > sizeof(buffer))
416
main_buffer_size = sizeof(buffer);
417
418
/* Preload the option priority list with mandatory options. */
419
priority_len = 0;
420
priority_list[priority_len++] = DHO_DHCP_MESSAGE_TYPE;
421
priority_list[priority_len++] = DHO_DHCP_SERVER_IDENTIFIER;
422
priority_list[priority_len++] = DHO_DHCP_LEASE_TIME;
423
priority_list[priority_len++] = DHO_DHCP_MESSAGE;
424
425
/*
426
* If the client has provided a list of options that it wishes
427
* returned, use it to prioritize. Otherwise, prioritize based
428
* on the default priority list.
429
*/
430
if (inpacket &&
431
inpacket->options[DHO_DHCP_PARAMETER_REQUEST_LIST].data) {
432
unsigned prlen =
433
inpacket->options[DHO_DHCP_PARAMETER_REQUEST_LIST].len;
434
if (prlen + priority_len > sizeof(priority_list))
435
prlen = sizeof(priority_list) - priority_len;
436
437
memcpy(&priority_list[priority_len],
438
inpacket->options[DHO_DHCP_PARAMETER_REQUEST_LIST].data,
439
prlen);
440
priority_len += prlen;
441
prl = priority_list;
442
} else if (prl) {
443
if (prl_len + priority_len > sizeof(priority_list))
444
prl_len = sizeof(priority_list) - priority_len;
445
446
memcpy(&priority_list[priority_len], prl, prl_len);
447
priority_len += prl_len;
448
prl = priority_list;
449
} else {
450
memcpy(&priority_list[priority_len],
451
dhcp_option_default_priority_list,
452
sizeof_dhcp_option_default_priority_list);
453
priority_len += sizeof_dhcp_option_default_priority_list;
454
}
455
456
/* Copy the options into the big buffer... */
457
option_size = store_options(
458
buffer,
459
(main_buffer_size - 7 + ((overload & 1) ? DHCP_FILE_LEN : 0) +
460
((overload & 2) ? DHCP_SNAME_LEN : 0)),
461
options, priority_list, priority_len, main_buffer_size,
462
(main_buffer_size + ((overload & 1) ? DHCP_FILE_LEN : 0)),
463
terminate);
464
465
/* Put the cookie up front... */
466
memcpy(outpacket->options, DHCP_OPTIONS_COOKIE, 4);
467
mainbufix = 4;
468
469
/*
470
* If we're going to have to overload, store the overload option
471
* at the beginning. If we can, though, just store the whole
472
* thing in the packet's option buffer and leave it at that.
473
*/
474
if (option_size <= main_buffer_size - mainbufix) {
475
memcpy(&outpacket->options[mainbufix],
476
buffer, option_size);
477
mainbufix += option_size;
478
if (mainbufix < main_buffer_size)
479
outpacket->options[mainbufix++] = DHO_END;
480
length = DHCP_FIXED_NON_UDP + mainbufix;
481
} else {
482
outpacket->options[mainbufix++] = DHO_DHCP_OPTION_OVERLOAD;
483
outpacket->options[mainbufix++] = 1;
484
if (option_size >
485
main_buffer_size - mainbufix + DHCP_FILE_LEN)
486
outpacket->options[mainbufix++] = 3;
487
else
488
outpacket->options[mainbufix++] = 1;
489
490
memcpy(&outpacket->options[mainbufix],
491
buffer, main_buffer_size - mainbufix);
492
bufix = main_buffer_size - mainbufix;
493
length = DHCP_FIXED_NON_UDP + mainbufix;
494
if (overload & 1) {
495
if (option_size - bufix <= DHCP_FILE_LEN) {
496
memcpy(outpacket->file,
497
&buffer[bufix], option_size - bufix);
498
mainbufix = option_size - bufix;
499
if (mainbufix < DHCP_FILE_LEN)
500
outpacket->file[mainbufix++] = (char)DHO_END;
501
while (mainbufix < DHCP_FILE_LEN)
502
outpacket->file[mainbufix++] = (char)DHO_PAD;
503
} else {
504
memcpy(outpacket->file,
505
&buffer[bufix], DHCP_FILE_LEN);
506
bufix += DHCP_FILE_LEN;
507
}
508
}
509
if ((overload & 2) && option_size < bufix) {
510
memcpy(outpacket->sname,
511
&buffer[bufix], option_size - bufix);
512
513
mainbufix = option_size - bufix;
514
if (mainbufix < DHCP_SNAME_LEN)
515
outpacket->file[mainbufix++] = (char)DHO_END;
516
while (mainbufix < DHCP_SNAME_LEN)
517
outpacket->file[mainbufix++] = (char)DHO_PAD;
518
}
519
}
520
return (length);
521
}
522
523
/*
524
* Store all the requested options into the requested buffer.
525
*/
526
unsigned
527
store_options(unsigned char *buffer, int buflen, struct tree_cache **options,
528
unsigned char *priority_list, int priority_len, int first_cutoff,
529
int second_cutoff, int terminate)
530
{
531
int bufix = 0, option_stored[256], i, ix, tto;
532
533
/* Zero out the stored-lengths array. */
534
memset(option_stored, 0, sizeof(option_stored));
535
536
/*
537
* Copy out the options in the order that they appear in the
538
* priority list...
539
*/
540
for (i = 0; i < priority_len; i++) {
541
/* Code for next option to try to store. */
542
int code = priority_list[i];
543
int optstart;
544
545
/*
546
* Number of bytes left to store (some may already have
547
* been stored by a previous pass).
548
*/
549
int length;
550
551
/* If no data is available for this option, skip it. */
552
if (!options[code]) {
553
continue;
554
}
555
556
/*
557
* The client could ask for things that are mandatory,
558
* in which case we should avoid storing them twice...
559
*/
560
if (option_stored[code])
561
continue;
562
option_stored[code] = 1;
563
564
/* We should now have a constant length for the option. */
565
length = options[code]->len;
566
567
/* Do we add a NUL? */
568
if (terminate && dhcp_options[code].format[0] == 't') {
569
length++;
570
tto = 1;
571
} else
572
tto = 0;
573
574
/* Try to store the option. */
575
576
/*
577
* If the option's length is more than 255, we must
578
* store it in multiple hunks. Store 255-byte hunks
579
* first. However, in any case, if the option data will
580
* cross a buffer boundary, split it across that
581
* boundary.
582
*/
583
ix = 0;
584
585
optstart = bufix;
586
while (length) {
587
unsigned char incr = length > 255 ? 255 : length;
588
589
/*
590
* If this hunk of the buffer will cross a
591
* boundary, only go up to the boundary in this
592
* pass.
593
*/
594
if (bufix < first_cutoff &&
595
bufix + incr > first_cutoff)
596
incr = first_cutoff - bufix;
597
else if (bufix < second_cutoff &&
598
bufix + incr > second_cutoff)
599
incr = second_cutoff - bufix;
600
601
/*
602
* If this option is going to overflow the
603
* buffer, skip it.
604
*/
605
if (bufix + 2 + incr > buflen) {
606
bufix = optstart;
607
break;
608
}
609
610
/* Everything looks good - copy it in! */
611
buffer[bufix] = code;
612
buffer[bufix + 1] = incr;
613
if (tto && incr == length) {
614
memcpy(buffer + bufix + 2,
615
options[code]->value + ix, incr - 1);
616
buffer[bufix + 2 + incr - 1] = 0;
617
} else
618
memcpy(buffer + bufix + 2,
619
options[code]->value + ix, incr);
620
length -= incr;
621
ix += incr;
622
bufix += 2 + incr;
623
}
624
}
625
return (bufix);
626
}
627
628
/*
629
* Format the specified option so that a human can easily read it.
630
*/
631
const char *
632
pretty_print_option(unsigned int code, unsigned char *data, int len,
633
int emit_commas, int emit_quotes)
634
{
635
static char optbuf[32768]; /* XXX */
636
int hunksize = 0, numhunk = -1, numelem = 0;
637
char fmtbuf[32], *op = optbuf;
638
int i, j, k, opleft = sizeof(optbuf);
639
unsigned char *dp = data;
640
struct in_addr foo;
641
char comma;
642
643
/* Code should be between 0 and 255. */
644
if (code > 255)
645
error("pretty_print_option: bad code %d", code);
646
647
if (emit_commas)
648
comma = ',';
649
else
650
comma = ' ';
651
652
/* Figure out the size of the data. */
653
for (i = 0; dhcp_options[code].format[i]; i++) {
654
if (!numhunk) {
655
warning("%s: Excess information in format string: %s",
656
dhcp_options[code].name,
657
&(dhcp_options[code].format[i]));
658
break;
659
}
660
numelem++;
661
fmtbuf[i] = dhcp_options[code].format[i];
662
switch (dhcp_options[code].format[i]) {
663
case 'A':
664
--numelem;
665
fmtbuf[i] = 0;
666
numhunk = 0;
667
break;
668
case 'X':
669
for (k = 0; k < len; k++)
670
if (!isascii(data[k]) ||
671
!isprint(data[k]))
672
break;
673
if (k == len) {
674
fmtbuf[i] = 't';
675
numhunk = -2;
676
} else {
677
fmtbuf[i] = 'x';
678
hunksize++;
679
comma = ':';
680
numhunk = 0;
681
}
682
fmtbuf[i + 1] = 0;
683
break;
684
case 't':
685
fmtbuf[i] = 't';
686
fmtbuf[i + 1] = 0;
687
numhunk = -2;
688
break;
689
case 'I':
690
case 'l':
691
case 'L':
692
hunksize += 4;
693
break;
694
case 's':
695
case 'S':
696
hunksize += 2;
697
break;
698
case 'b':
699
case 'B':
700
case 'f':
701
hunksize++;
702
break;
703
case 'e':
704
break;
705
default:
706
warning("%s: garbage in format string: %s",
707
dhcp_options[code].name,
708
&(dhcp_options[code].format[i]));
709
break;
710
}
711
}
712
713
/* Check for too few bytes... */
714
if (hunksize > len) {
715
warning("%s: expecting at least %d bytes; got %d",
716
dhcp_options[code].name, hunksize, len);
717
return ("<error>");
718
}
719
/* Check for too many bytes... */
720
if (numhunk == -1 && hunksize < len)
721
warning("%s: %d extra bytes",
722
dhcp_options[code].name, len - hunksize);
723
724
/* If this is an array, compute its size. */
725
if (!numhunk)
726
numhunk = len / hunksize;
727
/* See if we got an exact number of hunks. */
728
if (numhunk > 0 && numhunk * hunksize < len)
729
warning("%s: %d extra bytes at end of array",
730
dhcp_options[code].name, len - numhunk * hunksize);
731
732
/* A one-hunk array prints the same as a single hunk. */
733
if (numhunk < 0)
734
numhunk = 1;
735
736
/* Cycle through the array (or hunk) printing the data. */
737
for (i = 0; i < numhunk; i++) {
738
for (j = 0; j < numelem; j++) {
739
int opcount;
740
switch (fmtbuf[j]) {
741
case 't':
742
if (emit_quotes) {
743
*op++ = '"';
744
opleft--;
745
}
746
for (; dp < data + len; dp++) {
747
if (!isascii(*dp) ||
748
!isprint(*dp)) {
749
if (dp + 1 != data + len ||
750
*dp != 0) {
751
snprintf(op, opleft,
752
"\\%03o", *dp);
753
op += 4;
754
opleft -= 4;
755
}
756
} else if (*dp == '"' ||
757
*dp == '\'' ||
758
*dp == '$' ||
759
*dp == '`' ||
760
*dp == '\\') {
761
*op++ = '\\';
762
*op++ = *dp;
763
opleft -= 2;
764
} else {
765
*op++ = *dp;
766
opleft--;
767
}
768
}
769
if (emit_quotes) {
770
*op++ = '"';
771
opleft--;
772
}
773
774
*op = 0;
775
break;
776
case 'I':
777
foo.s_addr = htonl(getULong(dp));
778
opcount = strlcpy(op, inet_ntoa(foo), opleft);
779
if (opcount >= opleft)
780
goto toobig;
781
opleft -= opcount;
782
dp += 4;
783
break;
784
case 'l':
785
opcount = snprintf(op, opleft, "%ld",
786
(long)getLong(dp));
787
if (opcount >= opleft || opcount == -1)
788
goto toobig;
789
opleft -= opcount;
790
dp += 4;
791
break;
792
case 'L':
793
opcount = snprintf(op, opleft, "%lu",
794
(unsigned long)getULong(dp));
795
if (opcount >= opleft || opcount == -1)
796
goto toobig;
797
opleft -= opcount;
798
dp += 4;
799
break;
800
case 's':
801
opcount = snprintf(op, opleft, "%d",
802
getShort(dp));
803
if (opcount >= opleft || opcount == -1)
804
goto toobig;
805
opleft -= opcount;
806
dp += 2;
807
break;
808
case 'S':
809
opcount = snprintf(op, opleft, "%u",
810
getUShort(dp));
811
if (opcount >= opleft || opcount == -1)
812
goto toobig;
813
opleft -= opcount;
814
dp += 2;
815
break;
816
case 'b':
817
opcount = snprintf(op, opleft, "%d",
818
*(char *)dp++);
819
if (opcount >= opleft || opcount == -1)
820
goto toobig;
821
opleft -= opcount;
822
break;
823
case 'B':
824
opcount = snprintf(op, opleft, "%d", *dp++);
825
if (opcount >= opleft || opcount == -1)
826
goto toobig;
827
opleft -= opcount;
828
break;
829
case 'x':
830
opcount = snprintf(op, opleft, "%x", *dp++);
831
if (opcount >= opleft || opcount == -1)
832
goto toobig;
833
opleft -= opcount;
834
break;
835
case 'f':
836
opcount = strlcpy(op,
837
*dp++ ? "true" : "false", opleft);
838
if (opcount >= opleft)
839
goto toobig;
840
opleft -= opcount;
841
break;
842
default:
843
warning("Unexpected format code %c", fmtbuf[j]);
844
}
845
op += strlen(op);
846
opleft -= strlen(op);
847
if (opleft < 1)
848
goto toobig;
849
if (j + 1 < numelem && comma != ':') {
850
*op++ = ' ';
851
opleft--;
852
}
853
}
854
if (i + 1 < numhunk) {
855
*op++ = comma;
856
opleft--;
857
}
858
if (opleft < 1)
859
goto toobig;
860
861
}
862
return (optbuf);
863
toobig:
864
warning("dhcp option too large");
865
return ("<error>");
866
}
867
868
void
869
do_packet(struct interface_info *interface, struct dhcp_packet *packet,
870
int len, unsigned int from_port, struct iaddr from, struct hardware *hfrom)
871
{
872
struct packet tp;
873
int i;
874
875
if (packet->hlen > sizeof(packet->chaddr)) {
876
note("Discarding packet with invalid hlen.");
877
return;
878
}
879
880
memset(&tp, 0, sizeof(tp));
881
tp.raw = packet;
882
tp.packet_length = len;
883
tp.client_port = from_port;
884
tp.client_addr = from;
885
tp.interface = interface;
886
tp.haddr = hfrom;
887
888
parse_options(&tp);
889
if (tp.options_valid &&
890
tp.options[DHO_DHCP_MESSAGE_TYPE].data)
891
tp.packet_type = tp.options[DHO_DHCP_MESSAGE_TYPE].data[0];
892
if (tp.packet_type)
893
dhcp(&tp);
894
else
895
bootp(&tp);
896
897
/* Free the data associated with the options. */
898
for (i = 0; i < 256; i++)
899
free(tp.options[i].data);
900
}
901
902