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