1 /* $OpenBSD: clparse.c,v 1.18 2004/09/15 18:15:18 henning Exp $ */ 2 3 /* Parser for dhclient config and lease files... */ 4 5 /*- 6 * SPDX-License-Identifier: BSD-3-Clause 7 * 8 * Copyright (c) 1997 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 "dhcpd.h" 47 #include "dhctoken.h" 48 49 struct client_config top_level_config; 50 static struct interface_info *dummy_interfaces; 51 52 static char client_script_name[] = "/sbin/dhclient-script"; 53 54 /* 55 * Options the user ignores must not be asked for either. 56 * This matters for the IPv6-Only Preferred option. 57 */ 58 static void 59 unrequest_ignored_options(struct client_config *config) 60 { 61 bool ignored[256] = {}; 62 int i, n; 63 64 for (i = 0; config->ignored_options[i] != 0; i++) 65 ignored[config->ignored_options[i]] = true; 66 67 for (i = n = 0; i < config->requested_option_count; i++) { 68 if (ignored[config->requested_options[i]]) 69 continue; 70 config->requested_options[n++] = config->requested_options[i]; 71 } 72 config->requested_option_count = n; 73 } 74 75 /* 76 * client-conf-file :== client-declarations EOF 77 * client-declarations :== <nil> 78 * | client-declaration 79 * | client-declarations client-declaration 80 */ 81 int 82 read_client_conf(void) 83 { 84 FILE *cfile; 85 char *val; 86 int token; 87 struct client_config *config; 88 89 new_parse(path_dhclient_conf); 90 91 /* Set up the initial dhcp option universe. */ 92 initialize_universes(); 93 94 /* Initialize the top level client configuration. */ 95 memset(&top_level_config, 0, sizeof(top_level_config)); 96 97 /* Set some defaults... */ 98 top_level_config.vlan_pcp = 0; 99 top_level_config.timeout = 60; 100 top_level_config.select_interval = 0; 101 top_level_config.reboot_timeout = 10; 102 top_level_config.retry_interval = 300; 103 top_level_config.backoff_cutoff = 15; 104 top_level_config.initial_interval = 3; 105 top_level_config.bootp_policy = ACCEPT; 106 top_level_config.script_name = client_script_name; 107 top_level_config.requested_options 108 [top_level_config.requested_option_count++] = DHO_SUBNET_MASK; 109 top_level_config.requested_options 110 [top_level_config.requested_option_count++] = DHO_BROADCAST_ADDRESS; 111 top_level_config.requested_options 112 [top_level_config.requested_option_count++] = DHO_TIME_OFFSET; 113 top_level_config.requested_options 114 [top_level_config.requested_option_count++] = DHO_CLASSLESS_ROUTES; 115 top_level_config.requested_options 116 [top_level_config.requested_option_count++] = DHO_ROUTERS; 117 top_level_config.requested_options 118 [top_level_config.requested_option_count++] = DHO_DOMAIN_NAME; 119 top_level_config.requested_options 120 [top_level_config.requested_option_count++] = 121 DHO_DOMAIN_NAME_SERVERS; 122 top_level_config.requested_options 123 [top_level_config.requested_option_count++] = DHO_HOST_NAME; 124 top_level_config.requested_options 125 [top_level_config.requested_option_count++] = DHO_DOMAIN_SEARCH; 126 top_level_config.requested_options 127 [top_level_config.requested_option_count++] = DHO_INTERFACE_MTU; 128 top_level_config.requested_options 129 [top_level_config.requested_option_count++] = DHO_CAPTIVE_PORTAL; 130 top_level_config.requested_options 131 [top_level_config.requested_option_count++] = 132 DHO_CAPTIVE_PORTAL_LEGACY; 133 #ifdef INET6 134 /* 135 * RFC 8925 sec 3.2: The DHCPv4 client on an IPv4-requiring host MUST 136 * NOT include the IPv6-Only Preferred option code in the Parameter 137 * Request List. 138 */ 139 top_level_config.requested_options 140 [top_level_config.requested_option_count++] = DHO_IPV6_ONLY; 141 #endif 142 143 if ((cfile = fopen(path_dhclient_conf, "r")) != NULL) { 144 do { 145 token = peek_token(&val, cfile); 146 if (token == EOF) 147 break; 148 parse_client_statement(cfile, NULL, &top_level_config); 149 } while (1); 150 token = next_token(&val, cfile); /* Clear the peek buffer */ 151 fclose(cfile); 152 } 153 154 /* 155 * Set up state and config structures for clients that don't 156 * have per-interface configuration declarations. 157 */ 158 config = NULL; 159 if (!ifi->client) { 160 ifi->client = malloc(sizeof(struct client_state)); 161 if (!ifi->client) 162 error("no memory for client state."); 163 memset(ifi->client, 0, sizeof(*(ifi->client))); 164 } 165 if (!ifi->client->config) { 166 if (!config) { 167 config = malloc(sizeof(struct client_config)); 168 if (!config) 169 error("no memory for client config."); 170 memcpy(config, &top_level_config, 171 sizeof(top_level_config)); 172 } 173 ifi->client->config = config; 174 } 175 unrequest_ignored_options(ifi->client->config); 176 177 return (!warnings_occurred); 178 } 179 180 /* 181 * lease-file :== client-lease-statements EOF 182 * client-lease-statements :== <nil> 183 * | client-lease-statements LEASE client-lease-statement 184 */ 185 void 186 read_client_leases(void) 187 { 188 FILE *cfile; 189 char *val; 190 int token; 191 192 new_parse(path_dhclient_db); 193 194 /* Open the lease file. If we can't open it, just return - 195 we can safely trust the server to remember our state. */ 196 if ((cfile = fopen(path_dhclient_db, "r")) == NULL) 197 return; 198 do { 199 token = next_token(&val, cfile); 200 if (token == EOF) 201 break; 202 if (token != LEASE) { 203 warning("Corrupt lease file - possible data loss!"); 204 skip_to_semi(cfile); 205 break; 206 } else 207 parse_client_lease_statement(cfile, 0); 208 209 } while (1); 210 fclose(cfile); 211 } 212 213 /* 214 * client-declaration :== 215 * SEND option-decl | 216 * DEFAULT option-decl | 217 * SUPERSEDE option-decl | 218 * PREPEND option-decl | 219 * APPEND option-decl | 220 * hardware-declaration | 221 * REQUEST option-list | 222 * REQUIRE option-list | 223 * IGNORE option-list | 224 * TIMEOUT number | 225 * RETRY number | 226 * REBOOT number | 227 * SELECT_TIMEOUT number | 228 * SCRIPT string | 229 * interface-declaration | 230 * LEASE client-lease-statement | 231 * ALIAS client-lease-statement 232 */ 233 void 234 parse_client_statement(FILE *cfile, struct interface_info *ip, 235 struct client_config *config) 236 { 237 char *val; 238 struct option *option; 239 time_t tmp; 240 241 switch (next_token(&val, cfile)) { 242 case SEND: 243 parse_option_decl(cfile, &config->send_options[0]); 244 return; 245 case DEFAULT: 246 option = parse_option_decl(cfile, &config->defaults[0]); 247 if (option) 248 config->default_actions[option->code] = ACTION_DEFAULT; 249 return; 250 case SUPERSEDE: 251 option = parse_option_decl(cfile, &config->defaults[0]); 252 if (option) 253 config->default_actions[option->code] = 254 ACTION_SUPERSEDE; 255 return; 256 case APPEND: 257 option = parse_option_decl(cfile, &config->defaults[0]); 258 if (option) 259 config->default_actions[option->code] = ACTION_APPEND; 260 return; 261 case PREPEND: 262 option = parse_option_decl(cfile, &config->defaults[0]); 263 if (option) 264 config->default_actions[option->code] = ACTION_PREPEND; 265 return; 266 case MEDIA: 267 parse_string_list(cfile, &config->media, 1); 268 return; 269 case HARDWARE: 270 if (ip) 271 parse_hardware_param(cfile, &ip->hw_address); 272 else { 273 parse_warn("hardware address parameter %s", 274 "not allowed here."); 275 skip_to_semi(cfile); 276 } 277 return; 278 case REQUEST: 279 config->requested_option_count = 280 parse_option_list(cfile, config->requested_options); 281 return; 282 case REQUIRE: 283 memset(config->required_options, 0, 284 sizeof(config->required_options)); 285 parse_option_list(cfile, config->required_options); 286 return; 287 case IGNORE: 288 parse_option_list(cfile, config->ignored_options); 289 return; 290 case TIMEOUT: 291 parse_lease_time(cfile, &config->timeout); 292 return; 293 case RETRY: 294 parse_lease_time(cfile, &config->retry_interval); 295 return; 296 case SELECT_TIMEOUT: 297 parse_lease_time(cfile, &config->select_interval); 298 return; 299 case REBOOT: 300 parse_lease_time(cfile, &config->reboot_timeout); 301 return; 302 case VLAN_PCP: 303 parse_lease_time(cfile, &tmp); 304 config->vlan_pcp = (u_int)tmp; 305 return; 306 case BACKOFF_CUTOFF: 307 parse_lease_time(cfile, &config->backoff_cutoff); 308 return; 309 case INITIAL_INTERVAL: 310 parse_lease_time(cfile, &config->initial_interval); 311 return; 312 case SCRIPT: 313 config->script_name = parse_string(cfile); 314 return; 315 case INTERFACE: 316 if (ip) 317 parse_warn("nested interface declaration."); 318 parse_interface_declaration(cfile, config); 319 return; 320 case LEASE: 321 parse_client_lease_statement(cfile, 1); 322 return; 323 case ALIAS: 324 parse_client_lease_statement(cfile, 2); 325 return; 326 case REJECT: 327 parse_reject_statement(cfile, config); 328 return; 329 default: 330 break; 331 } 332 333 parse_warn("expecting a statement."); 334 skip_to_semi(cfile); 335 } 336 337 unsigned 338 parse_X(FILE *cfile, u_int8_t *buf, unsigned max) 339 { 340 int token; 341 char *val; 342 unsigned len; 343 344 token = peek_token(&val, cfile); 345 if (token == NUMBER_OR_NAME || token == NUMBER) { 346 len = 0; 347 do { 348 token = next_token(&val, cfile); 349 if (token != NUMBER && token != NUMBER_OR_NAME) { 350 parse_warn("expecting hexadecimal constant."); 351 skip_to_semi(cfile); 352 return (0); 353 } 354 convert_num(&buf[len], val, 16, 8); 355 if (len++ > max) { 356 parse_warn("hexadecimal constant too long."); 357 skip_to_semi(cfile); 358 return (0); 359 } 360 token = peek_token(&val, cfile); 361 if (token == COLON) 362 token = next_token(&val, cfile); 363 } while (token == COLON); 364 val = (char *)buf; 365 } else if (token == STRING) { 366 token = next_token(&val, cfile); 367 len = strlen(val); 368 if (len + 1 > max) { 369 parse_warn("string constant too long."); 370 skip_to_semi(cfile); 371 return (0); 372 } 373 memcpy(buf, val, len + 1); 374 } else { 375 parse_warn("expecting string or hexadecimal data"); 376 skip_to_semi(cfile); 377 return (0); 378 } 379 return (len); 380 } 381 382 /* 383 * option-list :== option_name | 384 * option_list COMMA option_name 385 */ 386 int 387 parse_option_list(FILE *cfile, u_int8_t *list) 388 { 389 int ix, i; 390 int token; 391 char *val; 392 393 ix = 0; 394 do { 395 token = next_token(&val, cfile); 396 if (!is_identifier(token)) { 397 parse_warn("expected option name."); 398 skip_to_semi(cfile); 399 return (0); 400 } 401 for (i = 0; i < 256; i++) 402 if (!strcasecmp(dhcp_options[i].name, val)) 403 break; 404 405 if (i == 256) { 406 parse_warn("%s: unexpected option name.", val); 407 skip_to_semi(cfile); 408 return (0); 409 } 410 list[ix++] = i; 411 if (ix == 256) { 412 parse_warn("%s: too many options.", val); 413 skip_to_semi(cfile); 414 return (0); 415 } 416 token = next_token(&val, cfile); 417 } while (token == COMMA); 418 if (token != SEMI) { 419 parse_warn("expecting semicolon."); 420 skip_to_semi(cfile); 421 return (0); 422 } 423 return (ix); 424 } 425 426 /* 427 * interface-declaration :== 428 * INTERFACE string LBRACE client-declarations RBRACE 429 */ 430 void 431 parse_interface_declaration(FILE *cfile, struct client_config *outer_config) 432 { 433 int token; 434 char *val; 435 struct interface_info *ip; 436 437 token = next_token(&val, cfile); 438 if (token != STRING) { 439 parse_warn("expecting interface name (in quotes)."); 440 skip_to_semi(cfile); 441 return; 442 } 443 444 ip = interface_or_dummy(val); 445 446 if (!ip->client) 447 make_client_state(ip); 448 449 if (!ip->client->config) 450 make_client_config(ip, outer_config); 451 452 token = next_token(&val, cfile); 453 if (token != LBRACE) { 454 parse_warn("expecting left brace."); 455 skip_to_semi(cfile); 456 return; 457 } 458 459 do { 460 token = peek_token(&val, cfile); 461 if (token == EOF) { 462 parse_warn("unterminated interface declaration."); 463 return; 464 } 465 if (token == RBRACE) 466 break; 467 parse_client_statement(cfile, ip, ip->client->config); 468 } while (1); 469 token = next_token(&val, cfile); 470 } 471 472 struct interface_info * 473 interface_or_dummy(char *name) 474 { 475 struct interface_info *ip; 476 477 /* Find the interface (if any) that matches the name. */ 478 if (!strcmp(ifi->name, name)) 479 return (ifi); 480 481 /* If it's not a real interface, see if it's on the dummy list. */ 482 for (ip = dummy_interfaces; ip; ip = ip->next) 483 if (!strcmp(ip->name, name)) 484 return (ip); 485 486 /* 487 * If we didn't find an interface, make a dummy interface as a 488 * placeholder. 489 */ 490 ip = malloc(sizeof(*ip)); 491 if (!ip) 492 error("Insufficient memory to record interface %s", name); 493 memset(ip, 0, sizeof(*ip)); 494 strlcpy(ip->name, name, IFNAMSIZ); 495 ip->next = dummy_interfaces; 496 dummy_interfaces = ip; 497 return (ip); 498 } 499 500 void 501 make_client_state(struct interface_info *ip) 502 { 503 ip->client = malloc(sizeof(*(ip->client))); 504 if (!ip->client) 505 error("no memory for state on %s", ip->name); 506 memset(ip->client, 0, sizeof(*(ip->client))); 507 } 508 509 void 510 make_client_config(struct interface_info *ip, struct client_config *config) 511 { 512 ip->client->config = malloc(sizeof(struct client_config)); 513 if (!ip->client->config) 514 error("no memory for config for %s", ip->name); 515 memset(ip->client->config, 0, sizeof(*(ip->client->config))); 516 memcpy(ip->client->config, config, sizeof(*config)); 517 } 518 519 /* 520 * client-lease-statement :== 521 * RBRACE client-lease-declarations LBRACE 522 * 523 * client-lease-declarations :== 524 * <nil> | 525 * client-lease-declaration | 526 * client-lease-declarations client-lease-declaration 527 */ 528 void 529 parse_client_lease_statement(FILE *cfile, int is_static) 530 { 531 struct client_lease *lease, *lp, *pl; 532 struct interface_info *ip; 533 int token; 534 char *val; 535 536 token = next_token(&val, cfile); 537 if (token != LBRACE) { 538 parse_warn("expecting left brace."); 539 skip_to_semi(cfile); 540 return; 541 } 542 543 lease = malloc(sizeof(struct client_lease)); 544 if (!lease) 545 error("no memory for lease."); 546 memset(lease, 0, sizeof(*lease)); 547 lease->is_static = is_static; 548 549 ip = NULL; 550 551 do { 552 token = peek_token(&val, cfile); 553 if (token == EOF) { 554 parse_warn("unterminated lease declaration."); 555 free_client_lease(lease); 556 return; 557 } 558 if (token == RBRACE) 559 break; 560 parse_client_lease_declaration(cfile, lease, &ip); 561 } while (1); 562 token = next_token(&val, cfile); 563 564 /* If the lease declaration didn't include an interface 565 * declaration that we recognized, it's of no use to us. 566 */ 567 if (!ip) { 568 free_client_lease(lease); 569 return; 570 } 571 572 /* Make sure there's a client state structure... */ 573 if (!ip->client) 574 make_client_state(ip); 575 576 /* If this is an alias lease, it doesn't need to be sorted in. */ 577 if (is_static == 2) { 578 ip->client->alias = lease; 579 return; 580 } 581 582 /* 583 * The new lease may supersede a lease that's not the active 584 * lease but is still on the lease list, so scan the lease list 585 * looking for a lease with the same address, and if we find it, 586 * toss it. 587 */ 588 pl = NULL; 589 for (lp = ip->client->leases; lp; lp = lp->next) { 590 if (lp->address.len == lease->address.len && 591 !memcmp(lp->address.iabuf, lease->address.iabuf, 592 lease->address.len)) { 593 if (pl) 594 pl->next = lp->next; 595 else 596 ip->client->leases = lp->next; 597 free_client_lease(lp); 598 break; 599 } 600 } 601 602 /* 603 * If this is a preloaded lease, just put it on the list of 604 * recorded leases - don't make it the active lease. 605 */ 606 if (is_static) { 607 lease->next = ip->client->leases; 608 ip->client->leases = lease; 609 return; 610 } 611 612 /* 613 * The last lease in the lease file on a particular interface is 614 * the active lease for that interface. Of course, we don't 615 * know what the last lease in the file is until we've parsed 616 * the whole file, so at this point, we assume that the lease we 617 * just parsed is the active lease for its interface. If 618 * there's already an active lease for the interface, and this 619 * lease is for the same ip address, then we just toss the old 620 * active lease and replace it with this one. If this lease is 621 * for a different address, then if the old active lease has 622 * expired, we dump it; if not, we put it on the list of leases 623 * for this interface which are still valid but no longer 624 * active. 625 */ 626 if (ip->client->active) { 627 if (ip->client->active->expiry < cur_time) 628 free_client_lease(ip->client->active); 629 else if (ip->client->active->address.len == 630 lease->address.len && 631 !memcmp(ip->client->active->address.iabuf, 632 lease->address.iabuf, lease->address.len)) 633 free_client_lease(ip->client->active); 634 else { 635 ip->client->active->next = ip->client->leases; 636 ip->client->leases = ip->client->active; 637 } 638 } 639 ip->client->active = lease; 640 641 /* Phew. */ 642 } 643 644 /* 645 * client-lease-declaration :== 646 * BOOTP | 647 * INTERFACE string | 648 * FIXED_ADDR ip_address | 649 * FILENAME string | 650 * SERVER_NAME string | 651 * OPTION option-decl | 652 * RENEW time-decl | 653 * REBIND time-decl | 654 * EXPIRE time-decl 655 */ 656 void 657 parse_client_lease_declaration(FILE *cfile, struct client_lease *lease, 658 struct interface_info **ipp) 659 { 660 int token; 661 char *val; 662 struct interface_info *ip; 663 664 switch (next_token(&val, cfile)) { 665 case BOOTP: 666 lease->is_bootp = 1; 667 break; 668 case INTERFACE: 669 token = next_token(&val, cfile); 670 if (token != STRING) { 671 parse_warn("expecting interface name (in quotes)."); 672 skip_to_semi(cfile); 673 return; 674 } 675 ip = interface_or_dummy(val); 676 *ipp = ip; 677 break; 678 case FIXED_ADDR: 679 if (!parse_ip_addr(cfile, &lease->address)) 680 return; 681 break; 682 case MEDIUM: 683 parse_string_list(cfile, &lease->medium, 0); 684 return; 685 case FILENAME: 686 lease->filename = parse_string(cfile); 687 return; 688 case NEXT_SERVER: 689 if (!parse_ip_addr(cfile, &lease->nextserver)) 690 return; 691 break; 692 case SERVER_NAME: 693 lease->server_name = parse_string(cfile); 694 return; 695 case RENEW: 696 lease->renewal = parse_date(cfile); 697 return; 698 case REBIND: 699 lease->rebind = parse_date(cfile); 700 return; 701 case EXPIRE: 702 lease->expiry = parse_date(cfile); 703 return; 704 case OPTION: 705 parse_option_decl(cfile, lease->options); 706 return; 707 default: 708 parse_warn("expecting lease declaration."); 709 skip_to_semi(cfile); 710 return; 711 } 712 token = next_token(&val, cfile); 713 if (token != SEMI) { 714 parse_warn("expecting semicolon."); 715 skip_to_semi(cfile); 716 } 717 } 718 719 struct option * 720 parse_option_decl(FILE *cfile, struct option_data *options) 721 { 722 char *val; 723 int token; 724 u_int8_t buf[4]; 725 u_int8_t hunkbuf[1024]; 726 unsigned hunkix = 0; 727 char *vendor; 728 const char *fmt; 729 struct universe *universe; 730 struct option *option; 731 struct iaddr ip_addr; 732 u_int8_t *dp; 733 unsigned len; 734 int nul_term = 0; 735 736 token = next_token(&val, cfile); 737 if (!is_identifier(token)) { 738 parse_warn("expecting identifier after option keyword."); 739 if (token != SEMI) 740 skip_to_semi(cfile); 741 return (NULL); 742 } 743 if ((vendor = strdup(val)) == NULL) 744 error("no memory for vendor information."); 745 746 token = peek_token(&val, cfile); 747 if (token == DOT) { 748 /* Go ahead and take the DOT token... */ 749 token = next_token(&val, cfile); 750 751 /* The next token should be an identifier... */ 752 token = next_token(&val, cfile); 753 if (!is_identifier(token)) { 754 parse_warn("expecting identifier after '.'"); 755 if (token != SEMI) 756 skip_to_semi(cfile); 757 free(vendor); 758 return (NULL); 759 } 760 761 /* Look up the option name hash table for the specified 762 vendor. */ 763 universe = ((struct universe *)hash_lookup(&universe_hash, 764 (unsigned char *)vendor, 0)); 765 /* If it's not there, we can't parse the rest of the 766 declaration. */ 767 if (!universe) { 768 parse_warn("no vendor named %s.", vendor); 769 skip_to_semi(cfile); 770 free(vendor); 771 return (NULL); 772 } 773 } else { 774 /* Use the default hash table, which contains all the 775 standard dhcp option names. */ 776 val = vendor; 777 universe = &dhcp_universe; 778 } 779 780 /* Look up the actual option info... */ 781 option = (struct option *)hash_lookup(universe->hash, 782 (unsigned char *)val, 0); 783 784 /* If we didn't get an option structure, it's an undefined option. */ 785 if (!option) { 786 if (val == vendor) 787 parse_warn("no option named %s", val); 788 else 789 parse_warn("no option named %s for vendor %s", 790 val, vendor); 791 skip_to_semi(cfile); 792 free(vendor); 793 return (NULL); 794 } 795 796 /* Free the initial identifier token. */ 797 free(vendor); 798 799 /* Parse the option data... */ 800 do { 801 for (fmt = option->format; *fmt; fmt++) { 802 if (*fmt == 'A') 803 break; 804 switch (*fmt) { 805 case 'X': 806 len = parse_X(cfile, &hunkbuf[hunkix], 807 sizeof(hunkbuf) - hunkix); 808 hunkix += len; 809 break; 810 case 't': /* Text string... */ 811 token = next_token(&val, cfile); 812 if (token != STRING) { 813 parse_warn("expecting string."); 814 skip_to_semi(cfile); 815 return (NULL); 816 } 817 len = strlen(val); 818 if (hunkix + len + 1 > sizeof(hunkbuf)) { 819 parse_warn("option data buffer %s", 820 "overflow"); 821 skip_to_semi(cfile); 822 return (NULL); 823 } 824 memcpy(&hunkbuf[hunkix], val, len + 1); 825 nul_term = 1; 826 hunkix += len; 827 break; 828 case 'I': /* IP address. */ 829 if (!parse_ip_addr(cfile, &ip_addr)) 830 return (NULL); 831 len = ip_addr.len; 832 dp = ip_addr.iabuf; 833 alloc: 834 if (hunkix + len > sizeof(hunkbuf)) { 835 parse_warn("option data buffer " 836 "overflow"); 837 skip_to_semi(cfile); 838 return (NULL); 839 } 840 memcpy(&hunkbuf[hunkix], dp, len); 841 hunkix += len; 842 break; 843 case 'L': /* Unsigned 32-bit integer... */ 844 case 'l': /* Signed 32-bit integer... */ 845 token = next_token(&val, cfile); 846 if (token != NUMBER) { 847 need_number: 848 parse_warn("expecting number."); 849 if (token != SEMI) 850 skip_to_semi(cfile); 851 return (NULL); 852 } 853 convert_num(buf, val, 0, 32); 854 len = 4; 855 dp = buf; 856 goto alloc; 857 case 's': /* Signed 16-bit integer. */ 858 case 'S': /* Unsigned 16-bit integer. */ 859 token = next_token(&val, cfile); 860 if (token != NUMBER) 861 goto need_number; 862 convert_num(buf, val, 0, 16); 863 len = 2; 864 dp = buf; 865 goto alloc; 866 case 'b': /* Signed 8-bit integer. */ 867 case 'B': /* Unsigned 8-bit integer. */ 868 token = next_token(&val, cfile); 869 if (token != NUMBER) 870 goto need_number; 871 convert_num(buf, val, 0, 8); 872 len = 1; 873 dp = buf; 874 goto alloc; 875 case 'f': /* Boolean flag. */ 876 token = next_token(&val, cfile); 877 if (!is_identifier(token)) { 878 parse_warn("expecting identifier."); 879 bad_flag: 880 if (token != SEMI) 881 skip_to_semi(cfile); 882 return (NULL); 883 } 884 if (!strcasecmp(val, "true") || 885 !strcasecmp(val, "on")) 886 buf[0] = 1; 887 else if (!strcasecmp(val, "false") || 888 !strcasecmp(val, "off")) 889 buf[0] = 0; 890 else { 891 parse_warn("expecting boolean."); 892 goto bad_flag; 893 } 894 len = 1; 895 dp = buf; 896 goto alloc; 897 default: 898 warning("Bad format %c in parse_option_param.", 899 *fmt); 900 skip_to_semi(cfile); 901 return (NULL); 902 } 903 } 904 token = next_token(&val, cfile); 905 } while (*fmt == 'A' && token == COMMA); 906 907 if (token != SEMI) { 908 parse_warn("semicolon expected."); 909 skip_to_semi(cfile); 910 return (NULL); 911 } 912 913 options[option->code].data = malloc(hunkix + nul_term); 914 if (!options[option->code].data) 915 error("out of memory allocating option data."); 916 memcpy(options[option->code].data, hunkbuf, hunkix + nul_term); 917 options[option->code].len = hunkix; 918 return (option); 919 } 920 921 void 922 parse_string_list(FILE *cfile, struct string_list **lp, int multiple) 923 { 924 int token; 925 char *val; 926 size_t valsize; 927 struct string_list *cur, *tmp; 928 929 /* Find the last medium in the media list. */ 930 if (*lp) 931 for (cur = *lp; cur->next; cur = cur->next) 932 ; /* nothing */ 933 else 934 cur = NULL; 935 936 do { 937 token = next_token(&val, cfile); 938 if (token != STRING) { 939 parse_warn("Expecting media options."); 940 skip_to_semi(cfile); 941 return; 942 } 943 944 valsize = strlen(val) + 1; 945 tmp = new_string_list(valsize); 946 if (tmp == NULL) 947 error("no memory for string list entry."); 948 memcpy(tmp->string, val, valsize); 949 tmp->next = NULL; 950 951 /* Store this medium at the end of the media list. */ 952 if (cur) 953 cur->next = tmp; 954 else 955 *lp = tmp; 956 cur = tmp; 957 958 token = next_token(&val, cfile); 959 } while (multiple && token == COMMA); 960 961 if (token != SEMI) { 962 parse_warn("expecting semicolon."); 963 skip_to_semi(cfile); 964 } 965 } 966 967 void 968 parse_reject_statement(FILE *cfile, struct client_config *config) 969 { 970 int token; 971 char *val; 972 struct iaddr addr; 973 struct iaddrlist *list; 974 975 do { 976 if (!parse_ip_addr(cfile, &addr)) { 977 parse_warn("expecting IP address."); 978 skip_to_semi(cfile); 979 return; 980 } 981 982 list = malloc(sizeof(struct iaddrlist)); 983 if (!list) 984 error("no memory for reject list!"); 985 986 list->addr = addr; 987 list->next = config->reject_list; 988 config->reject_list = list; 989 990 token = next_token(&val, cfile); 991 } while (token == COMMA); 992 993 if (token != SEMI) { 994 parse_warn("expecting semicolon."); 995 skip_to_semi(cfile); 996 } 997 } 998