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