add resources for newdev.dll
[reactos.git] / reactos / services / dhcp / options.c
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 * 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 <ctype.h>
44
45 #define DHCP_OPTION_DATA
46 #include "rosdhcp.h"
47 #include "dhcpd.h"
48
49 int bad_options = 0;
50 int bad_options_max = 5;
51
52 void parse_options(struct packet *);
53 void parse_option_buffer(struct packet *, unsigned char *, int);
54 int store_options(unsigned char *, int, struct tree_cache **,
55 unsigned char *, int, int, int, int);
56
57
58 /*
59 * Parse all available options out of the specified packet.
60 */
61 void
62 parse_options(struct packet *packet)
63 {
64 /* Initially, zero all option pointers. */
65 memset(packet->options, 0, sizeof(packet->options));
66
67 /* If we don't see the magic cookie, there's nothing to parse. */
68 if (memcmp(packet->raw->options, DHCP_OPTIONS_COOKIE, 4)) {
69 packet->options_valid = 0;
70 return;
71 }
72
73 /*
74 * Go through the options field, up to the end of the packet or
75 * the End field.
76 */
77 parse_option_buffer(packet, &packet->raw->options[4],
78 packet->packet_length - DHCP_FIXED_NON_UDP - 4);
79
80 /*
81 * If we parsed a DHCP Option Overload option, parse more
82 * options out of the buffer(s) containing them.
83 */
84 if (packet->options_valid &&
85 packet->options[DHO_DHCP_OPTION_OVERLOAD].data) {
86 if (packet->options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 1)
87 parse_option_buffer(packet,
88 (unsigned char *)packet->raw->file,
89 sizeof(packet->raw->file));
90 if (packet->options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 2)
91 parse_option_buffer(packet,
92 (unsigned char *)packet->raw->sname,
93 sizeof(packet->raw->sname));
94 }
95 }
96
97 /*
98 * Parse options out of the specified buffer, storing addresses of
99 * option values in packet->options and setting packet->options_valid if
100 * no errors are encountered.
101 */
102 void
103 parse_option_buffer(struct packet *packet,
104 unsigned char *buffer, int length)
105 {
106 unsigned char *s, *t, *end = buffer + length;
107 int len, code;
108
109 for (s = buffer; *s != DHO_END && s < end; ) {
110 code = s[0];
111
112 /* Pad options don't have a length - just skip them. */
113 if (code == DHO_PAD) {
114 s++;
115 continue;
116 }
117 if (s + 2 > end) {
118 len = 65536;
119 goto bogus;
120 }
121
122 /*
123 * All other fields (except end, see above) have a
124 * one-byte length.
125 */
126 len = s[1];
127
128 /*
129 * If the length is outrageous, silently skip the rest,
130 * and mark the packet bad. Unfortunately some crappy
131 * dhcp servers always seem to give us garbage on the
132 * end of a packet. so rather than keep refusing, give
133 * up and try to take one after seeing a few without
134 * anything good.
135 */
136 if (s + len + 2 > end) {
137 bogus:
138 bad_options++;
139 warning("option %s (%d) %s.",
140 dhcp_options[code].name, len,
141 "larger than buffer");
142 if (bad_options == bad_options_max) {
143 packet->options_valid = 1;
144 bad_options = 0;
145 warning("Many bogus options seen in offers. "
146 "Taking this offer in spite of bogus "
147 "options - hope for the best!");
148 } else {
149 warning("rejecting bogus offer.");
150 packet->options_valid = 0;
151 }
152 return;
153 }
154 /*
155 * If we haven't seen this option before, just make
156 * space for it and copy it there.
157 */
158 if (!packet->options[code].data) {
159 if (!(t = calloc(1, len + 1)))
160 error("Can't allocate storage for option %s.",
161 dhcp_options[code].name);
162 /*
163 * Copy and NUL-terminate the option (in case
164 * it's an ASCII string.
165 */
166 memcpy(t, &s[2], len);
167 t[len] = 0;
168 packet->options[code].len = len;
169 packet->options[code].data = t;
170 } else {
171 /*
172 * If it's a repeat, concatenate it to whatever
173 * we last saw. This is really only required
174 * for clients, but what the heck...
175 */
176 t = calloc(1, len + packet->options[code].len + 1);
177 if (!t)
178 error("Can't expand storage for option %s.",
179 dhcp_options[code].name);
180 memcpy(t, packet->options[code].data,
181 packet->options[code].len);
182 memcpy(t + packet->options[code].len,
183 &s[2], len);
184 packet->options[code].len += len;
185 t[packet->options[code].len] = 0;
186 free(packet->options[code].data);
187 packet->options[code].data = t;
188 }
189 s += len + 2;
190 }
191 packet->options_valid = 1;
192 }
193
194 /*
195 * cons options into a big buffer, and then split them out into the
196 * three separate buffers if needed. This allows us to cons up a set of
197 * vendor options using the same routine.
198 */
199 int
200 cons_options(struct packet *inpacket, struct dhcp_packet *outpacket,
201 int mms, struct tree_cache **options,
202 int overload, /* Overload flags that may be set. */
203 int terminate, int bootpp, u_int8_t *prl, int prl_len)
204 {
205 unsigned char priority_list[300], buffer[4096];
206 int priority_len, main_buffer_size, mainbufix, bufix;
207 int option_size, length;
208
209 /*
210 * If the client has provided a maximum DHCP message size, use
211 * that; otherwise, if it's BOOTP, only 64 bytes; otherwise use
212 * up to the minimum IP MTU size (576 bytes).
213 *
214 * XXX if a BOOTP client specifies a max message size, we will
215 * honor it.
216 */
217 if (!mms &&
218 inpacket &&
219 inpacket->options[DHO_DHCP_MAX_MESSAGE_SIZE].data &&
220 (inpacket->options[DHO_DHCP_MAX_MESSAGE_SIZE].len >=
221 sizeof(u_int16_t)))
222 mms = getUShort(
223 inpacket->options[DHO_DHCP_MAX_MESSAGE_SIZE].data);
224
225 if (mms)
226 main_buffer_size = mms - DHCP_FIXED_LEN;
227 else if (bootpp)
228 main_buffer_size = 64;
229 else
230 main_buffer_size = 576 - DHCP_FIXED_LEN;
231
232 if (main_buffer_size > sizeof(buffer))
233 main_buffer_size = sizeof(buffer);
234
235 /* Preload the option priority list with mandatory options. */
236 priority_len = 0;
237 priority_list[priority_len++] = DHO_DHCP_MESSAGE_TYPE;
238 priority_list[priority_len++] = DHO_DHCP_SERVER_IDENTIFIER;
239 priority_list[priority_len++] = DHO_DHCP_LEASE_TIME;
240 priority_list[priority_len++] = DHO_DHCP_MESSAGE;
241
242 /*
243 * If the client has provided a list of options that it wishes
244 * returned, use it to prioritize. Otherwise, prioritize based
245 * on the default priority list.
246 */
247 if (inpacket &&
248 inpacket->options[DHO_DHCP_PARAMETER_REQUEST_LIST].data) {
249 int prlen =
250 inpacket->options[DHO_DHCP_PARAMETER_REQUEST_LIST].len;
251 if (prlen + priority_len > sizeof(priority_list))
252 prlen = sizeof(priority_list) - priority_len;
253
254 memcpy(&priority_list[priority_len],
255 inpacket->options[DHO_DHCP_PARAMETER_REQUEST_LIST].data,
256 prlen);
257 priority_len += prlen;
258 prl = priority_list;
259 } else if (prl) {
260 if (prl_len + priority_len > sizeof(priority_list))
261 prl_len = sizeof(priority_list) - priority_len;
262
263 memcpy(&priority_list[priority_len], prl, prl_len);
264 priority_len += prl_len;
265 prl = priority_list;
266 } else {
267 memcpy(&priority_list[priority_len],
268 dhcp_option_default_priority_list,
269 sizeof_dhcp_option_default_priority_list);
270 priority_len += sizeof_dhcp_option_default_priority_list;
271 }
272
273 /* Copy the options into the big buffer... */
274 option_size = store_options(
275 buffer,
276 (main_buffer_size - 7 + ((overload & 1) ? DHCP_FILE_LEN : 0) +
277 ((overload & 2) ? DHCP_SNAME_LEN : 0)),
278 options, priority_list, priority_len, main_buffer_size,
279 (main_buffer_size + ((overload & 1) ? DHCP_FILE_LEN : 0)),
280 terminate);
281
282 /* Put the cookie up front... */
283 memcpy(outpacket->options, DHCP_OPTIONS_COOKIE, 4);
284 mainbufix = 4;
285
286 /*
287 * If we're going to have to overload, store the overload option
288 * at the beginning. If we can, though, just store the whole
289 * thing in the packet's option buffer and leave it at that.
290 */
291 if (option_size <= main_buffer_size - mainbufix) {
292 memcpy(&outpacket->options[mainbufix],
293 buffer, option_size);
294 mainbufix += option_size;
295 if (mainbufix < main_buffer_size)
296 outpacket->options[mainbufix++] = DHO_END;
297 length = DHCP_FIXED_NON_UDP + mainbufix;
298 } else {
299 outpacket->options[mainbufix++] = DHO_DHCP_OPTION_OVERLOAD;
300 outpacket->options[mainbufix++] = 1;
301 if (option_size >
302 main_buffer_size - mainbufix + DHCP_FILE_LEN)
303 outpacket->options[mainbufix++] = 3;
304 else
305 outpacket->options[mainbufix++] = 1;
306
307 memcpy(&outpacket->options[mainbufix],
308 buffer, main_buffer_size - mainbufix);
309 bufix = main_buffer_size - mainbufix;
310 length = DHCP_FIXED_NON_UDP + mainbufix;
311 if (overload & 1) {
312 if (option_size - bufix <= DHCP_FILE_LEN) {
313 memcpy(outpacket->file,
314 &buffer[bufix], option_size - bufix);
315 mainbufix = option_size - bufix;
316 if (mainbufix < DHCP_FILE_LEN)
317 outpacket->file[mainbufix++] = (char)DHO_END;
318 while (mainbufix < DHCP_FILE_LEN)
319 outpacket->file[mainbufix++] = (char)DHO_PAD;
320 } else {
321 memcpy(outpacket->file,
322 &buffer[bufix], DHCP_FILE_LEN);
323 bufix += DHCP_FILE_LEN;
324 }
325 }
326 if ((overload & 2) && option_size < bufix) {
327 memcpy(outpacket->sname,
328 &buffer[bufix], option_size - bufix);
329
330 mainbufix = option_size - bufix;
331 if (mainbufix < DHCP_SNAME_LEN)
332 outpacket->file[mainbufix++] = (char)DHO_END;
333 while (mainbufix < DHCP_SNAME_LEN)
334 outpacket->file[mainbufix++] = (char)DHO_PAD;
335 }
336 }
337 return (length);
338 }
339
340 /*
341 * Store all the requested options into the requested buffer.
342 */
343 int
344 store_options(unsigned char *buffer, int buflen, struct tree_cache **options,
345 unsigned char *priority_list, int priority_len, int first_cutoff,
346 int second_cutoff, int terminate)
347 {
348 int bufix = 0, option_stored[256], i, ix, tto;
349
350 /* Zero out the stored-lengths array. */
351 memset(option_stored, 0, sizeof(option_stored));
352
353 /*
354 * Copy out the options in the order that they appear in the
355 * priority list...
356 */
357 for (i = 0; i < priority_len; i++) {
358 /* Code for next option to try to store. */
359 int code = priority_list[i];
360 int optstart;
361
362 /*
363 * Number of bytes left to store (some may already have
364 * been stored by a previous pass).
365 */
366 int length;
367
368 /* If no data is available for this option, skip it. */
369 if (!options[code]) {
370 continue;
371 }
372
373 /*
374 * The client could ask for things that are mandatory,
375 * in which case we should avoid storing them twice...
376 */
377 if (option_stored[code])
378 continue;
379 option_stored[code] = 1;
380
381 /* We should now have a constant length for the option. */
382 length = options[code]->len;
383
384 /* Do we add a NUL? */
385 if (terminate && dhcp_options[code].format[0] == 't') {
386 length++;
387 tto = 1;
388 } else
389 tto = 0;
390
391 /* Try to store the option. */
392
393 /*
394 * If the option's length is more than 255, we must
395 * store it in multiple hunks. Store 255-byte hunks
396 * first. However, in any case, if the option data will
397 * cross a buffer boundary, split it across that
398 * boundary.
399 */
400 ix = 0;
401
402 optstart = bufix;
403 while (length) {
404 unsigned char incr = length > 255 ? 255 : length;
405
406 /*
407 * If this hunk of the buffer will cross a
408 * boundary, only go up to the boundary in this
409 * pass.
410 */
411 if (bufix < first_cutoff &&
412 bufix + incr > first_cutoff)
413 incr = first_cutoff - bufix;
414 else if (bufix < second_cutoff &&
415 bufix + incr > second_cutoff)
416 incr = second_cutoff - bufix;
417
418 /*
419 * If this option is going to overflow the
420 * buffer, skip it.
421 */
422 if (bufix + 2 + incr > buflen) {
423 bufix = optstart;
424 break;
425 }
426
427 /* Everything looks good - copy it in! */
428 buffer[bufix] = code;
429 buffer[bufix + 1] = incr;
430 if (tto && incr == length) {
431 memcpy(buffer + bufix + 2,
432 options[code]->value + ix, incr - 1);
433 buffer[bufix + 2 + incr - 1] = 0;
434 } else
435 memcpy(buffer + bufix + 2,
436 options[code]->value + ix, incr);
437 length -= incr;
438 ix += incr;
439 bufix += 2 + incr;
440 }
441 }
442 return (bufix);
443 }
444
445 /*
446 * Format the specified option so that a human can easily read it.
447 */
448 char *
449 pretty_print_option(unsigned int code, unsigned char *data, int len,
450 int emit_commas, int emit_quotes)
451 {
452 static char optbuf[32768]; /* XXX */
453 int hunksize = 0, numhunk = -1, numelem = 0;
454 char fmtbuf[32], *op = optbuf;
455 int i, j, k, opleft = sizeof(optbuf);
456 unsigned char *dp = data;
457 struct in_addr foo;
458 char comma;
459
460 /* Code should be between 0 and 255. */
461 if (code > 255)
462 error("pretty_print_option: bad code %d", code);
463
464 if (emit_commas)
465 comma = ',';
466 else
467 comma = ' ';
468
469 /* Figure out the size of the data. */
470 for (i = 0; dhcp_options[code].format[i]; i++) {
471 if (!numhunk) {
472 warning("%s: Excess information in format string: %s",
473 dhcp_options[code].name,
474 &(dhcp_options[code].format[i]));
475 break;
476 }
477 numelem++;
478 fmtbuf[i] = dhcp_options[code].format[i];
479 switch (dhcp_options[code].format[i]) {
480 case 'A':
481 --numelem;
482 fmtbuf[i] = 0;
483 numhunk = 0;
484 break;
485 case 'X':
486 for (k = 0; k < len; k++)
487 if (!isascii(data[k]) ||
488 !isprint(data[k]))
489 break;
490 if (k == len) {
491 fmtbuf[i] = 't';
492 numhunk = -2;
493 } else {
494 fmtbuf[i] = 'x';
495 hunksize++;
496 comma = ':';
497 numhunk = 0;
498 }
499 fmtbuf[i + 1] = 0;
500 break;
501 case 't':
502 fmtbuf[i] = 't';
503 fmtbuf[i + 1] = 0;
504 numhunk = -2;
505 break;
506 case 'I':
507 case 'l':
508 case 'L':
509 hunksize += 4;
510 break;
511 case 's':
512 case 'S':
513 hunksize += 2;
514 break;
515 case 'b':
516 case 'B':
517 case 'f':
518 hunksize++;
519 break;
520 case 'e':
521 break;
522 default:
523 warning("%s: garbage in format string: %s",
524 dhcp_options[code].name,
525 &(dhcp_options[code].format[i]));
526 break;
527 }
528 }
529
530 /* Check for too few bytes... */
531 if (hunksize > len) {
532 warning("%s: expecting at least %d bytes; got %d",
533 dhcp_options[code].name, hunksize, len);
534 return ("<error>");
535 }
536 /* Check for too many bytes... */
537 if (numhunk == -1 && hunksize < len)
538 warning("%s: %d extra bytes",
539 dhcp_options[code].name, len - hunksize);
540
541 /* If this is an array, compute its size. */
542 if (!numhunk)
543 numhunk = len / hunksize;
544 /* See if we got an exact number of hunks. */
545 if (numhunk > 0 && numhunk * hunksize < len)
546 warning("%s: %d extra bytes at end of array",
547 dhcp_options[code].name, len - numhunk * hunksize);
548
549 /* A one-hunk array prints the same as a single hunk. */
550 if (numhunk < 0)
551 numhunk = 1;
552
553 /* Cycle through the array (or hunk) printing the data. */
554 for (i = 0; i < numhunk; i++) {
555 for (j = 0; j < numelem; j++) {
556 int opcount;
557 switch (fmtbuf[j]) {
558 case 't':
559 if (emit_quotes) {
560 *op++ = '"';
561 opleft--;
562 }
563 for (; dp < data + len; dp++) {
564 if (!isascii(*dp) ||
565 !isprint(*dp)) {
566 if (dp + 1 != data + len ||
567 *dp != 0) {
568 snprintf(op, opleft,
569 "\\%03o", *dp);
570 op += 4;
571 opleft -= 4;
572 }
573 } else if (*dp == '"' ||
574 *dp == '\'' ||
575 *dp == '$' ||
576 *dp == '`' ||
577 *dp == '\\') {
578 *op++ = '\\';
579 *op++ = *dp;
580 opleft -= 2;
581 } else {
582 *op++ = *dp;
583 opleft--;
584 }
585 }
586 if (emit_quotes) {
587 *op++ = '"';
588 opleft--;
589 }
590
591 *op = 0;
592 break;
593 case 'I':
594 foo.s_addr = htonl(getULong(dp));
595 opcount = strlcpy(op, inet_ntoa(foo), opleft);
596 if (opcount >= opleft)
597 goto toobig;
598 opleft -= opcount;
599 dp += 4;
600 break;
601 case 'l':
602 opcount = snprintf(op, opleft, "%ld",
603 (long)getLong(dp));
604 if (opcount >= opleft || opcount == -1)
605 goto toobig;
606 opleft -= opcount;
607 dp += 4;
608 break;
609 case 'L':
610 opcount = snprintf(op, opleft, "%ld",
611 (unsigned long)getULong(dp));
612 if (opcount >= opleft || opcount == -1)
613 goto toobig;
614 opleft -= opcount;
615 dp += 4;
616 break;
617 case 's':
618 opcount = snprintf(op, opleft, "%d",
619 getShort(dp));
620 if (opcount >= opleft || opcount == -1)
621 goto toobig;
622 opleft -= opcount;
623 dp += 2;
624 break;
625 case 'S':
626 opcount = snprintf(op, opleft, "%d",
627 getUShort(dp));
628 if (opcount >= opleft || opcount == -1)
629 goto toobig;
630 opleft -= opcount;
631 dp += 2;
632 break;
633 case 'b':
634 opcount = snprintf(op, opleft, "%d",
635 *(char *)dp++);
636 if (opcount >= opleft || opcount == -1)
637 goto toobig;
638 opleft -= opcount;
639 break;
640 case 'B':
641 opcount = snprintf(op, opleft, "%d", *dp++);
642 if (opcount >= opleft || opcount == -1)
643 goto toobig;
644 opleft -= opcount;
645 break;
646 case 'x':
647 opcount = snprintf(op, opleft, "%x", *dp++);
648 if (opcount >= opleft || opcount == -1)
649 goto toobig;
650 opleft -= opcount;
651 break;
652 case 'f':
653 opcount = strlcpy(op,
654 *dp++ ? "true" : "false", opleft);
655 if (opcount >= opleft)
656 goto toobig;
657 opleft -= opcount;
658 break;
659 default:
660 warning("Unexpected format code %c", fmtbuf[j]);
661 }
662 op += strlen(op);
663 opleft -= strlen(op);
664 if (opleft < 1)
665 goto toobig;
666 if (j + 1 < numelem && comma != ':') {
667 *op++ = ' ';
668 opleft--;
669 }
670 }
671 if (i + 1 < numhunk) {
672 *op++ = comma;
673 opleft--;
674 }
675 if (opleft < 1)
676 goto toobig;
677
678 }
679 return (optbuf);
680 toobig:
681 warning("dhcp option too large");
682 return ("<error>");
683 }
684
685 void
686 do_packet(struct interface_info *interface, struct dhcp_packet *packet,
687 int len, unsigned int from_port, struct iaddr from, struct hardware *hfrom)
688 {
689 struct packet tp;
690 int i;
691
692 if (packet->hlen > sizeof(packet->chaddr)) {
693 note("Discarding packet with invalid hlen.");
694 return;
695 }
696
697 memset(&tp, 0, sizeof(tp));
698 tp.raw = packet;
699 tp.packet_length = len;
700 tp.client_port = from_port;
701 tp.client_addr = from;
702 tp.interface = interface;
703 tp.haddr = hfrom;
704
705 parse_options(&tp);
706 if (tp.options_valid &&
707 tp.options[DHO_DHCP_MESSAGE_TYPE].data)
708 tp.packet_type = tp.options[DHO_DHCP_MESSAGE_TYPE].data[0];
709 if (tp.packet_type)
710 dhcp(&tp);
711 else
712 bootp(&tp);
713
714 /* Free the data associated with the options. */
715 for (i = 0; i < 256; i++)
716 if (tp.options[i].len && tp.options[i].data)
717 free(tp.options[i].data);
718 }