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