1/* $OpenBSD: options.c,v 1.123 2020/07/07 19:48:31 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 <resolv.h> 55#include <signal.h> 56#include <stdio.h> 57#include <stdlib.h> 58#include <string.h> 59#include <vis.h> 60 61#include "dhcp.h" 62#include "dhcpd.h" 63#include "log.h" 64 65int parse_option_buffer(struct option_data *, unsigned char *, int); 66void pretty_print_classless_routes(unsigned char *, size_t, unsigned char *, 67 size_t); 68void pretty_print_domain_list(unsigned char *, size_t, unsigned char *, 69 size_t); 70 71/* 72 * DHCP Option names, formats and codes, from RFC1533. 73 * 74 * Format codes: 75 * 76 * e - end of data 77 * I - IP address 78 * l - 32-bit signed integer 79 * L - 32-bit unsigned integer 80 * S - 16-bit unsigned integer 81 * B - 8-bit unsigned integer 82 * t - ASCII text 83 * f - flag (true or false) 84 * A - array of whatever precedes (e.g., IA means array of IP addresses) 85 * C - CIDR description 86 * X - hex octets 87 * D - domain name list, comma separated list of domain names. 88 */ 89 90static const struct { 91 char *name; 92 char *format; 93} dhcp_options[DHO_COUNT] = { 94 /* 0 */ { "pad", "" }, 95 /* 1 */ { "subnet-mask", "I" }, 96 /* 2 */ { "time-offset", "l" }, 97 /* 3 */ { "routers", "IA" }, 98 /* 4 */ { "time-servers", "IA" }, 99 /* 5 */ { "ien116-name-servers", "IA" }, 100 /* 6 */ { "domain-name-servers", "IA" }, 101 /* 7 */ { "log-servers", "IA" }, 102 /* 8 */ { "cookie-servers", "IA" }, 103 /* 9 */ { "lpr-servers", "IA" }, 104 /* 10 */ { "impress-servers", "IA" }, 105 /* 11 */ { "resource-location-servers", "IA" }, 106 /* 12 */ { "host-name", "t" }, 107 /* 13 */ { "boot-size", "S" }, 108 /* 14 */ { "merit-dump", "t" }, 109 /* 15 */ { "domain-name", "t" }, 110 /* 16 */ { "swap-server", "I" }, 111 /* 17 */ { "root-path", "t" }, 112 /* 18 */ { "extensions-path", "t" }, 113 /* 19 */ { "ip-forwarding", "f" }, 114 /* 20 */ { "non-local-source-routing", "f" }, 115 /* 21 */ { "policy-filter", "IIA" }, 116 /* 22 */ { "max-dgram-reassembly", "S" }, 117 /* 23 */ { "default-ip-ttl", "B" }, 118 /* 24 */ { "path-mtu-aging-timeout", "L" }, 119 /* 25 */ { "path-mtu-plateau-table", "SA" }, 120 /* 26 */ { "interface-mtu", "S" }, 121 /* 27 */ { "all-subnets-local", "f" }, 122 /* 28 */ { "broadcast-address", "I" }, 123 /* 29 */ { "perform-mask-discovery", "f" }, 124 /* 30 */ { "mask-supplier", "f" }, 125 /* 31 */ { "router-discovery", "f" }, 126 /* 32 */ { "router-solicitation-address", "I" }, 127 /* 33 */ { "static-routes", "IIA" }, 128 /* 34 */ { "trailer-encapsulation", "f" }, 129 /* 35 */ { "arp-cache-timeout", "L" }, 130 /* 36 */ { "ieee802-3-encapsulation", "f" }, 131 /* 37 */ { "default-tcp-ttl", "B" }, 132 /* 38 */ { "tcp-keepalive-interval", "L" }, 133 /* 39 */ { "tcp-keepalive-garbage", "f" }, 134 /* 40 */ { "nis-domain", "t" }, 135 /* 41 */ { "nis-servers", "IA" }, 136 /* 42 */ { "ntp-servers", "IA" }, 137 /* 43 */ { "vendor-encapsulated-options", "X" }, 138 /* 44 */ { "netbios-name-servers", "IA" }, 139 /* 45 */ { "netbios-dd-server", "IA" }, 140 /* 46 */ { "netbios-node-type", "B" }, 141 /* 47 */ { "netbios-scope", "t" }, 142 /* 48 */ { "font-servers", "IA" }, 143 /* 49 */ { "x-display-manager", "IA" }, 144 /* 50 */ { "dhcp-requested-address", "I" }, 145 /* 51 */ { "dhcp-lease-time", "L" }, 146 /* 52 */ { "dhcp-option-overload", "B" }, 147 /* 53 */ { "dhcp-message-type", "B" }, 148 /* 54 */ { "dhcp-server-identifier", "I" }, 149 /* 55 */ { "dhcp-parameter-request-list", "BA" }, 150 /* 56 */ { "dhcp-message", "t" }, 151 /* 57 */ { "dhcp-max-message-size", "S" }, 152 /* 58 */ { "dhcp-renewal-time", "L" }, 153 /* 59 */ { "dhcp-rebinding-time", "L" }, 154 /* 60 */ { "dhcp-class-identifier", "t" }, 155 /* 61 */ { "dhcp-client-identifier", "X" }, 156 /* 62 */ { NULL, NULL }, 157 /* 63 */ { NULL, NULL }, 158 /* 64 */ { "nisplus-domain", "t" }, 159 /* 65 */ { "nisplus-servers", "IA" }, 160 /* 66 */ { "tftp-server-name", "t" }, 161 /* 67 */ { "bootfile-name", "t" }, 162 /* 68 */ { "mobile-ip-home-agent", "IA" }, 163 /* 69 */ { "smtp-server", "IA" }, 164 /* 70 */ { "pop-server", "IA" }, 165 /* 71 */ { "nntp-server", "IA" }, 166 /* 72 */ { "www-server", "IA" }, 167 /* 73 */ { "finger-server", "IA" }, 168 /* 74 */ { "irc-server", "IA" }, 169 /* 75 */ { "streettalk-server", "IA" }, 170 /* 76 */ { "streettalk-directory-assistance-server", "IA" }, 171 /* 77 */ { "user-class", "t" }, 172 /* 78 */ { NULL, NULL }, 173 /* 79 */ { NULL, NULL }, 174 /* 80 */ { NULL, NULL }, 175 /* 81 */ { NULL, NULL }, 176 /* 82 */ { "relay-agent-information", "X" }, 177 /* 83 */ { NULL, NULL }, 178 /* 84 */ { NULL, NULL }, 179 /* 85 */ { "nds-servers", "IA" }, 180 /* 86 */ { "nds-tree-name", "X" }, 181 /* 87 */ { "nds-context", "X" }, 182 /* 88 */ { NULL, NULL }, 183 /* 89 */ { NULL, NULL }, 184 /* 90 */ { NULL, NULL }, 185 /* 91 */ { NULL, NULL }, 186 /* 92 */ { NULL, NULL }, 187 /* 93 */ { NULL, NULL }, 188 /* 94 */ { NULL, NULL }, 189 /* 95 */ { NULL, NULL }, 190 /* 96 */ { NULL, NULL }, 191 /* 97 */ { NULL, NULL }, 192 /* 98 */ { NULL, NULL }, 193 /* 99 */ { NULL, NULL }, 194 /* 100 */ { NULL, NULL }, 195 /* 101 */ { NULL, NULL }, 196 /* 102 */ { NULL, NULL }, 197 /* 103 */ { NULL, NULL }, 198 /* 104 */ { NULL, NULL }, 199 /* 105 */ { NULL, NULL }, 200 /* 106 */ { NULL, NULL }, 201 /* 107 */ { NULL, NULL }, 202 /* 108 */ { NULL, NULL }, 203 /* 109 */ { NULL, NULL }, 204 /* 110 */ { NULL, NULL }, 205 /* 111 */ { NULL, NULL }, 206 /* 112 */ { NULL, NULL }, 207 /* 113 */ { NULL, NULL }, 208 /* 114 */ { NULL, NULL }, 209 /* 115 */ { NULL, NULL }, 210 /* 116 */ { NULL, NULL }, 211 /* 117 */ { NULL, NULL }, 212 /* 118 */ { NULL, NULL }, 213 /* 119 */ { "domain-search", "D" }, 214 /* 120 */ { NULL, NULL }, 215 /* 121 */ { "classless-static-routes", "CIA" }, 216 /* 122 */ { NULL, NULL }, 217 /* 123 */ { NULL, NULL }, 218 /* 124 */ { NULL, NULL }, 219 /* 125 */ { NULL, NULL }, 220 /* 126 */ { NULL, NULL }, 221 /* 127 */ { NULL, NULL }, 222 /* 128 */ { NULL, NULL }, 223 /* 129 */ { NULL, NULL }, 224 /* 130 */ { NULL, NULL }, 225 /* 131 */ { NULL, NULL }, 226 /* 132 */ { NULL, NULL }, 227 /* 133 */ { NULL, NULL }, 228 /* 134 */ { NULL, NULL }, 229 /* 135 */ { NULL, NULL }, 230 /* 136 */ { NULL, NULL }, 231 /* 137 */ { NULL, NULL }, 232 /* 138 */ { NULL, NULL }, 233 /* 139 */ { NULL, NULL }, 234 /* 140 */ { NULL, NULL }, 235 /* 141 */ { NULL, NULL }, 236 /* 142 */ { NULL, NULL }, 237 /* 143 */ { NULL, NULL }, 238 /* 144 */ { "tftp-config-file", "t" }, 239 /* 145 */ { NULL, NULL }, 240 /* 146 */ { NULL, NULL }, 241 /* 147 */ { NULL, NULL }, 242 /* 148 */ { NULL, NULL }, 243 /* 149 */ { NULL, NULL }, 244 /* 150 */ { "voip-configuration-server", "IA" }, 245 /* 151 */ { NULL, NULL }, 246 /* 152 */ { NULL, NULL }, 247 /* 153 */ { NULL, NULL }, 248 /* 154 */ { NULL, NULL }, 249 /* 155 */ { NULL, NULL }, 250 /* 156 */ { NULL, NULL }, 251 /* 157 */ { NULL, NULL }, 252 /* 158 */ { NULL, NULL }, 253 /* 159 */ { NULL, NULL }, 254 /* 160 */ { NULL, NULL }, 255 /* 161 */ { NULL, NULL }, 256 /* 162 */ { NULL, NULL }, 257 /* 163 */ { NULL, NULL }, 258 /* 164 */ { NULL, NULL }, 259 /* 165 */ { NULL, NULL }, 260 /* 166 */ { NULL, NULL }, 261 /* 167 */ { NULL, NULL }, 262 /* 168 */ { NULL, NULL }, 263 /* 169 */ { NULL, NULL }, 264 /* 170 */ { NULL, NULL }, 265 /* 171 */ { NULL, NULL }, 266 /* 172 */ { NULL, NULL }, 267 /* 173 */ { NULL, NULL }, 268 /* 174 */ { NULL, NULL }, 269 /* 175 */ { NULL, NULL }, 270 /* 176 */ { NULL, NULL }, 271 /* 177 */ { NULL, NULL }, 272 /* 178 */ { NULL, NULL }, 273 /* 179 */ { NULL, NULL }, 274 /* 180 */ { NULL, NULL }, 275 /* 181 */ { NULL, NULL }, 276 /* 182 */ { NULL, NULL }, 277 /* 183 */ { NULL, NULL }, 278 /* 184 */ { NULL, NULL }, 279 /* 185 */ { NULL, NULL }, 280 /* 186 */ { NULL, NULL }, 281 /* 187 */ { NULL, NULL }, 282 /* 188 */ { NULL, NULL }, 283 /* 189 */ { NULL, NULL }, 284 /* 190 */ { NULL, NULL }, 285 /* 191 */ { NULL, NULL }, 286 /* 192 */ { NULL, NULL }, 287 /* 193 */ { NULL, NULL }, 288 /* 194 */ { NULL, NULL }, 289 /* 195 */ { NULL, NULL }, 290 /* 196 */ { NULL, NULL }, 291 /* 197 */ { NULL, NULL }, 292 /* 198 */ { NULL, NULL }, 293 /* 199 */ { NULL, NULL }, 294 /* 200 */ { NULL, NULL }, 295 /* 201 */ { NULL, NULL }, 296 /* 202 */ { NULL, NULL }, 297 /* 203 */ { NULL, NULL }, 298 /* 204 */ { NULL, NULL }, 299 /* 205 */ { NULL, NULL }, 300 /* 206 */ { NULL, NULL }, 301 /* 207 */ { NULL, NULL }, 302 /* 208 */ { NULL, NULL }, 303 /* 209 */ { NULL, NULL }, 304 /* 210 */ { NULL, NULL }, 305 /* 211 */ { NULL, NULL }, 306 /* 212 */ { NULL, NULL }, 307 /* 213 */ { NULL, NULL }, 308 /* 214 */ { NULL, NULL }, 309 /* 215 */ { NULL, NULL }, 310 /* 216 */ { NULL, NULL }, 311 /* 217 */ { NULL, NULL }, 312 /* 218 */ { NULL, NULL }, 313 /* 219 */ { NULL, NULL }, 314 /* 220 */ { NULL, NULL }, 315 /* 221 */ { NULL, NULL }, 316 /* 222 */ { NULL, NULL }, 317 /* 223 */ { NULL, NULL }, 318 /* 224 */ { NULL, NULL }, 319 /* 225 */ { NULL, NULL }, 320 /* 226 */ { NULL, NULL }, 321 /* 227 */ { NULL, NULL }, 322 /* 228 */ { NULL, NULL }, 323 /* 229 */ { NULL, NULL }, 324 /* 230 */ { NULL, NULL }, 325 /* 231 */ { NULL, NULL }, 326 /* 232 */ { NULL, NULL }, 327 /* 233 */ { NULL, NULL }, 328 /* 234 */ { NULL, NULL }, 329 /* 235 */ { NULL, NULL }, 330 /* 236 */ { NULL, NULL }, 331 /* 237 */ { NULL, NULL }, 332 /* 238 */ { NULL, NULL }, 333 /* 239 */ { NULL, NULL }, 334 /* 240 */ { NULL, NULL }, 335 /* 241 */ { NULL, NULL }, 336 /* 242 */ { NULL, NULL }, 337 /* 243 */ { NULL, NULL }, 338 /* 244 */ { NULL, NULL }, 339 /* 245 */ { NULL, NULL }, 340 /* 246 */ { NULL, NULL }, 341 /* 247 */ { NULL, NULL }, 342 /* 248 */ { NULL, NULL }, 343 /* 249 */ { "classless-ms-static-routes", "CIA" }, 344 /* 250 */ { NULL, NULL }, 345 /* 251 */ { NULL, NULL }, 346 /* 252 */ { "autoproxy-script", "t" }, 347 /* 253 */ { NULL, NULL }, 348 /* 254 */ { NULL, NULL }, 349 /* 255 */ { "option-end", "e" }, 350}; 351 352char * 353code_to_name(int code) 354{ 355 static char unknown[11]; /* "option-NNN" */ 356 int ret; 357 358 if (code < 0 || code >= DHO_COUNT) 359 return ""; 360 361 if (dhcp_options[code].name != NULL) 362 return dhcp_options[code].name; 363 364 ret = snprintf(unknown, sizeof(unknown), "option-%d", code); 365 if (ret < 0 || ret >= (int)sizeof(unknown)) 366 return ""; 367 368 return unknown; 369} 370 371int 372name_to_code(char *name) 373{ 374 char unknown[11]; /* "option-NNN" */ 375 int code, ret; 376 377 for (code = 1; code < DHO_END; code++) { 378 if (dhcp_options[code].name == NULL) { 379 ret = snprintf(unknown, sizeof(unknown), "option-%d", 380 code); 381 if (ret < 0 || ret >= (int)sizeof(unknown)) 382 return DHO_END; 383 if (strcasecmp(unknown, name) == 0) 384 return code; 385 } else if (strcasecmp(dhcp_options[code].name, name) == 0) { 386 return code; 387 } 388 } 389 390 return DHO_END; 391} 392 393char * 394code_to_format(int code) 395{ 396 if (code < 0 || code >= DHO_COUNT) 397 return ""; 398 399 if (dhcp_options[code].format == NULL) 400 return "X"; 401 402 return dhcp_options[code].format; 403} 404 405/* 406 * Some option data types cannot be appended or prepended to. For 407 * such options change ACTION_PREPEND to ACTION_SUPERSEDE and 408 * ACTION_APPEND to ACTION_DEFAULT. 409 */ 410int 411code_to_action(int code, int action) 412{ 413 char *fmt; 414 415 fmt = code_to_format(code); 416 if (fmt == NULL || strpbrk(fmt, "ADtX") != NULL) 417 return action; 418 419 /* 420 * For our protection all formats which have been excluded shall be 421 * deemed included. 422 */ 423 switch (action) { 424 case ACTION_APPEND: 425 action = ACTION_DEFAULT; 426 break; 427 case ACTION_PREPEND: 428 action = ACTION_SUPERSEDE; 429 break; 430 default: 431 break; 432 } 433 434 return action; 435} 436 437/* 438 * Parse options out of the specified buffer, storing addresses of 439 * option values in options. Return 0 if errors, 1 if not. 440 */ 441int 442parse_option_buffer(struct option_data *options, unsigned char *buffer, 443 int length) 444{ 445 unsigned char *s, *t, *end; 446 char *name, *fmt; 447 int code, len, newlen; 448 449 s = buffer; 450 end = s + length; 451 while (s < end) { 452 code = s[0]; 453 454 /* End options terminate processing. */ 455 if (code == DHO_END) 456 break; 457 458 /* Pad options don't have a length - just skip them. */ 459 if (code == DHO_PAD) { 460 s++; 461 continue; 462 } 463 464 name = code_to_name(code); 465 fmt = code_to_format(code); 466 467 /* 468 * All options other than DHO_PAD and DHO_END have a one-byte 469 * length field. It could be 0! Make sure that the length byte 470 * is present, and all the data is available. 471 */ 472 if (s + 1 < end) { 473 len = s[1]; 474 if (s + 1 + len < end) { 475 ; /* option data is all there. */ 476 } else { 477 log_warnx("%s: option %s (%d) larger than " 478 "buffer", log_procname, name, len); 479 return 0; 480 } 481 } else { 482 log_warnx("%s: option %s has no length field", 483 log_procname, name); 484 return 0; 485 } 486 487 /* 488 * Strip trailing NULs from ascii ('t') options. RFC 2132 489 * says "Options containing NVT ASCII data SHOULD NOT include 490 * a trailing NULL; however, the receiver of such options 491 * MUST be prepared to delete trailing nulls if they exist." 492 */ 493 if (fmt[0] == 't') { 494 while (len > 0 && s[len + 1] == '\0') 495 len--; 496 } 497 498 /* 499 * Concatenate new data + NUL to existing option data. 500 * 501 * Note that the NUL is *not* counted in the len field! 502 */ 503 newlen = options[code].len + len; 504 if ((t = realloc(options[code].data, newlen + 1)) == NULL) 505 fatal("option %s", name); 506 507 memcpy(t + options[code].len, &s[2], len); 508 t[newlen] = 0; 509 510 options[code].len = newlen; 511 options[code].data = t; 512 513 s += s[1] + 2; 514 } 515 516 return 1; 517} 518 519/* 520 * Pack as many options as fit in buflen bytes of buf. Return the 521 * offset of the start of the last option copied. A caller can check 522 * to see if it's DHO_END to decide if all the options were copied. 523 */ 524int 525pack_options(unsigned char *buf, int buflen, struct option_data *options) 526{ 527 int ix, incr, length, bufix, code, lastopt = -1; 528 529 memset(buf, 0, buflen); 530 531 memcpy(buf, DHCP_OPTIONS_COOKIE, 4); 532 if (options[DHO_DHCP_MESSAGE_TYPE].data != NULL) { 533 memcpy(&buf[4], DHCP_OPTIONS_MESSAGE_TYPE, 3); 534 buf[6] = options[DHO_DHCP_MESSAGE_TYPE].data[0]; 535 bufix = 7; 536 } else 537 bufix = 4; 538 539 for (code = DHO_SUBNET_MASK; code < DHO_END; code++) { 540 if (options[code].data == NULL || 541 code == DHO_DHCP_MESSAGE_TYPE) 542 continue; 543 544 length = options[code].len; 545 if (bufix + length + 2*((length+254)/255) >= buflen) 546 return lastopt; 547 548 lastopt = bufix; 549 ix = 0; 550 551 while (length) { 552 incr = length > 255 ? 255 : length; 553 554 buf[bufix++] = code; 555 buf[bufix++] = incr; 556 memcpy(buf + bufix, options[code].data + ix, incr); 557 558 length -= incr; 559 ix += incr; 560 bufix += incr; 561 } 562 } 563 564 if (bufix < buflen) { 565 buf[bufix] = DHO_END; 566 lastopt = bufix; 567 } 568 569 return lastopt; 570} 571 572/* 573 * Use vis() to encode characters of src and append encoded characters onto 574 * dst. Also encode ", ', $, ` and \, to ensure resulting strings can be 575 * represented as '"' delimited strings and safely passed to scripts. Surround 576 * result with double quotes if emit_punct is true. 577 */ 578char * 579pretty_print_string(unsigned char *src, size_t srclen, int emit_punct) 580{ 581 static char string[8196]; 582 char visbuf[5]; 583 unsigned char *origsrc = src; 584 size_t rslt = 0; 585 586 memset(string, 0, sizeof(string)); 587 588 if (emit_punct != 0) 589 rslt = strlcat(string, "\"", sizeof(string)); 590 591 for (; src < origsrc + srclen; src++) { 592 if (*src && strchr("\"'$`\\", *src)) 593 vis(visbuf, *src, VIS_ALL | VIS_OCTAL, *src+1); 594 else 595 vis(visbuf, *src, VIS_OCTAL, *src+1); 596 rslt = strlcat(string, visbuf, sizeof(string)); 597 } 598 599 if (emit_punct != 0) 600 rslt = strlcat(string, "\"", sizeof(string)); 601 602 if (rslt >= sizeof(string)) 603 return NULL; 604 605 return string; 606} 607 608/* 609 * Must special case *_CLASSLESS_* route options due to the variable size 610 * of the CIDR element in its CIA format. 611 */ 612void 613pretty_print_classless_routes(unsigned char *src, size_t srclen, 614 unsigned char *buf, size_t buflen) 615{ 616 char bitsbuf[5]; /* to hold "/nn " */ 617 struct in_addr dest, netmask, gateway; 618 unsigned int bits, i, len; 619 uint32_t m; 620 int rslt; 621 622 i = 0; 623 while (i < srclen) { 624 len = extract_route(&src[i], srclen - i, &dest.s_addr, 625 &netmask.s_addr, &gateway.s_addr); 626 if (len == 0) 627 goto bad; 628 i += len; 629 630 m = ntohl(netmask.s_addr); 631 bits = 32; 632 while ((bits > 0) && ((m & 1) == 0)) { 633 m >>= 1; 634 bits--; 635 } 636 637 rslt = snprintf(bitsbuf, sizeof(bitsbuf), "/%d ", bits); 638 if (rslt < 0 || (unsigned int)rslt >= sizeof(bitsbuf)) 639 goto bad; 640 641 if (strlen(buf) > 0) 642 strlcat(buf, ", ", buflen); 643 strlcat(buf, inet_ntoa(dest), buflen); 644 strlcat(buf, bitsbuf, buflen); 645 if (strlcat(buf, inet_ntoa(gateway), buflen) >= buflen) 646 goto bad; 647 } 648 649 return; 650 651bad: 652 memset(buf, 0, buflen); 653} 654 655/* 656 * Print string containing blank separated list of domain names 657 * as a comma separated list of double-quote delimited strings. 658 * 659 * e.g. "eng.apple.com. marketing.apple.com." 660 * 661 * will be translated to 662 * 663 * "eng.apple.com.", "marketing.apple.com." 664 */ 665void 666pretty_print_domain_list(unsigned char *src, size_t srclen, 667 unsigned char *buf, size_t buflen) 668{ 669 char *dupnames, *hn, *inputstring; 670 int count; 671 672 memset(buf, 0, buflen); 673 674 /* 675 * N.B.: option data is *NOT* guaranteed to be NUL 676 * terminated. Avoid strlen(), strdup(), etc.! 677 */ 678 if (srclen >= DHCP_DOMAIN_SEARCH_LEN || src[0] == '\0') 679 return; 680 681 inputstring = malloc(srclen + 1); 682 if (inputstring == NULL) 683 fatal("domain name list"); 684 memcpy(inputstring, src, srclen); 685 inputstring[srclen] = '\0'; 686 dupnames = inputstring; 687 688 count = 0; 689 while ((hn = strsep(&inputstring, " \t")) != NULL) { 690 if (strlen(hn) == 0) 691 continue; 692 if (res_hnok(hn) == 0) 693 goto bad; 694 if (count > 0) 695 strlcat(buf, ", ", buflen); 696 strlcat(buf, "\"", buflen); 697 strlcat(buf, hn, buflen); 698 if (strlcat(buf, "\"", buflen) >= buflen) 699 goto bad; 700 count++; 701 if (count > DHCP_DOMAIN_SEARCH_CNT) 702 goto bad; 703 } 704 705 free(dupnames); 706 return; 707 708bad: 709 free(dupnames); 710 memset(buf, 0, buflen); 711} 712 713/* 714 * Format the specified option so that a human can easily read it. 715 */ 716char * 717pretty_print_option(unsigned int code, struct option_data *option, 718 int emit_punct) 719{ 720 static char optbuf[8192]; /* XXX */ 721 char fmtbuf[32]; 722 struct in_addr foo; 723 unsigned char *data = option->data; 724 unsigned char *dp = data; 725 char *op = optbuf, *buf, *name, *fmt; 726 int hunksize = 0, numhunk = -1, numelem = 0; 727 int i, j, k, opleft = sizeof(optbuf); 728 int len = option->len; 729 int opcount = 0; 730 int32_t int32val; 731 uint32_t uint32val; 732 uint16_t uint16val; 733 char comma; 734 735 memset(optbuf, 0, sizeof(optbuf)); 736 737 /* Code should be between 0 and 255. */ 738 if (code > 255) { 739 log_warnx("%s: pretty_print_option: bad code %d", log_procname, 740 code); 741 goto done; 742 } 743 744 if (emit_punct != 0) 745 comma = ','; 746 else 747 comma = ' '; 748 749 /* Handle the princess class options with weirdo formats. */ 750 switch (code) { 751 case DHO_CLASSLESS_STATIC_ROUTES: 752 case DHO_CLASSLESS_MS_STATIC_ROUTES: 753 pretty_print_classless_routes(dp, len, optbuf, sizeof(optbuf)); 754 goto done; 755 case DHO_DOMAIN_SEARCH: 756 pretty_print_domain_list(dp, len, optbuf, sizeof(optbuf)); 757 goto done; 758 default: 759 break; 760 } 761 762 name = code_to_name(code); 763 fmt = code_to_format(code); 764 765 /* Figure out the size of the data. */ 766 for (i = 0; fmt[i]; i++) { 767 if (numhunk == 0) { 768 log_warnx("%s: %s: excess information in format " 769 "string: %s", log_procname, name, &fmt[i]); 770 goto done; 771 } 772 numelem++; 773 fmtbuf[i] = fmt[i]; 774 switch (fmt[i]) { 775 case 'A': 776 --numelem; 777 fmtbuf[i] = 0; 778 numhunk = 0; 779 if (hunksize == 0) { 780 log_warnx("%s: %s: no size indicator before A" 781 " in format string: %s", log_procname, 782 name, fmt); 783 goto done; 784 } 785 break; 786 case 'X': 787 for (k = 0; k < len; k++) 788 if (isascii(data[k]) == 0 || 789 isprint(data[k]) == 0) 790 break; 791 if (k == len) { 792 fmtbuf[i] = 't'; 793 numhunk = -2; 794 } else { 795 hunksize++; 796 comma = ':'; 797 numhunk = 0; 798 } 799 fmtbuf[i + 1] = 0; 800 break; 801 case 't': 802 fmtbuf[i + 1] = 0; 803 numhunk = -2; 804 break; 805 case 'I': 806 case 'l': 807 case 'L': 808 hunksize += 4; 809 break; 810 case 'S': 811 hunksize += 2; 812 break; 813 case 'B': 814 case 'f': 815 hunksize++; 816 break; 817 case 'e': 818 break; 819 default: 820 log_warnx("%s: %s: garbage in format string: %s", 821 log_procname, name, &fmt[i]); 822 goto done; 823 } 824 } 825 826 /* Check for too few bytes. */ 827 if (hunksize > len) { 828 log_warnx("%s: %s: expecting at least %d bytes; got %d", 829 log_procname, name, hunksize, len); 830 goto done; 831 } 832 /* Check for too many bytes. */ 833 if (numhunk == -1 && hunksize < len) { 834 log_warnx("%s: %s: expecting only %d bytes: got %d", 835 log_procname, name, hunksize, len); 836 goto done; 837 } 838 839 /* If this is an array, compute its size. */ 840 if (numhunk == 0) 841 numhunk = len / hunksize; 842 /* See if we got an exact number of hunks. */ 843 if (numhunk > 0 && numhunk * hunksize != len) { 844 log_warnx("%s: %s: expecting %d bytes: got %d", log_procname, 845 name, numhunk * hunksize, len); 846 goto done; 847 } 848 849 /* A one-hunk array prints the same as a single hunk. */ 850 if (numhunk < 0) 851 numhunk = 1; 852 853 /* Cycle through the array (or hunk) printing the data. */ 854 for (i = 0; i < numhunk; i++) { 855 for (j = 0; j < numelem; j++) { 856 switch (fmtbuf[j]) { 857 case 't': 858 buf = pretty_print_string(dp, len, emit_punct); 859 if (buf == NULL) 860 opcount = -1; 861 else 862 opcount = strlcat(op, buf, opleft); 863 break; 864 case 'I': 865 memcpy(&foo.s_addr, dp, sizeof(foo.s_addr)); 866 opcount = snprintf(op, opleft, "%s", 867 inet_ntoa(foo)); 868 dp += sizeof(foo.s_addr); 869 break; 870 case 'l': 871 memcpy(&int32val, dp, sizeof(int32val)); 872 opcount = snprintf(op, opleft, "%d", 873 ntohl(int32val)); 874 dp += sizeof(int32val); 875 break; 876 case 'L': 877 memcpy(&uint32val, dp, sizeof(uint32val)); 878 opcount = snprintf(op, opleft, "%u", 879 ntohl(uint32val)); 880 dp += sizeof(uint32val); 881 break; 882 case 'S': 883 memcpy(&uint16val, dp, sizeof(uint16val)); 884 opcount = snprintf(op, opleft, "%hu", 885 ntohs(uint16val)); 886 dp += sizeof(uint16val); 887 break; 888 case 'B': 889 opcount = snprintf(op, opleft, "%u", *dp); 890 dp++; 891 break; 892 case 'X': 893 opcount = snprintf(op, opleft, "%x", *dp); 894 dp++; 895 break; 896 case 'f': 897 opcount = snprintf(op, opleft, "%s", 898 *dp ? "true" : "false"); 899 dp++; 900 break; 901 default: 902 log_warnx("%s: unexpected format code %c", 903 log_procname, fmtbuf[j]); 904 goto toobig; 905 } 906 if (opcount < 0 || opcount >= opleft) 907 goto toobig; 908 opleft -= opcount; 909 op += opcount; 910 if (j + 1 < numelem && comma != ':') { 911 opcount = snprintf(op, opleft, " "); 912 if (opcount < 0 || opcount >= opleft) 913 goto toobig; 914 opleft -= opcount; 915 op += opcount; 916 } 917 } 918 if (i + 1 < numhunk) { 919 opcount = snprintf(op, opleft, "%c", comma); 920 if (opcount < 0 || opcount >= opleft) 921 goto toobig; 922 opleft -= opcount; 923 op += opcount; 924 } 925 } 926 927done: 928 return optbuf; 929 930toobig: 931 memset(optbuf, 0, sizeof(optbuf)); 932 return optbuf; 933} 934 935struct option_data * 936unpack_options(struct dhcp_packet *packet) 937{ 938 static struct option_data options[DHO_COUNT]; 939 int i; 940 941 for (i = 0; i < DHO_COUNT; i++) { 942 free(options[i].data); 943 options[i].data = NULL; 944 options[i].len = 0; 945 } 946 947 if (memcmp(&packet->options, DHCP_OPTIONS_COOKIE, 4) == 0) { 948 /* Parse the BOOTP/DHCP options field. */ 949 parse_option_buffer(options, &packet->options[4], 950 sizeof(packet->options) - 4); 951 952 /* DHCP packets can also use overload areas for options. */ 953 if (options[DHO_DHCP_MESSAGE_TYPE].data != NULL && 954 options[DHO_DHCP_OPTION_OVERLOAD].data != NULL) { 955 if ((options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 1) != 956 0) 957 parse_option_buffer(options, 958 (unsigned char *)packet->file, 959 sizeof(packet->file)); 960 if ((options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 2) != 961 0) 962 parse_option_buffer(options, 963 (unsigned char *)packet->sname, 964 sizeof(packet->sname)); 965 } 966 } 967 968 return options; 969} 970 971void 972merge_option_data(char *fmt, struct option_data *first, 973 struct option_data *second, struct option_data *dest) 974{ 975 int space = 0; 976 977 free(dest->data); 978 dest->data = NULL; 979 dest->len = first->len + second->len; 980 if (dest->len == 0) 981 return; 982 983 /* 984 * N.B.: option data is *NOT* guaranteed to be NUL 985 * terminated. Avoid strlen(), strdup(), etc.! 986 */ 987 if (fmt[0] == 'D') { 988 if (first->len > 0 && second->len > 0) 989 space = 1; 990 } 991 992 dest->len += space; 993 dest->data = malloc(dest->len); 994 if (dest->data == NULL) 995 fatal("merged option data"); 996 997 memcpy(dest->data, first->data, first->len); 998 if (space == 1) 999 dest->data[first->len] = ' '; 1000 memcpy(dest->data + first->len + space, second->data, second->len); 1001} 1002