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