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 free_client_lease(lease); 516 return; 517 } 518 if (token == RBRACE) 519 break; 520 parse_client_lease_declaration(cfile, lease, &ip); 521 } while (1); 522 token = next_token(&val, cfile); 523 524 /* If the lease declaration didn't include an interface 525 * declaration that we recognized, it's of no use to us. 526 */ 527 if (!ip) { 528 free_client_lease(lease); 529 return; 530 } 531 532 /* Make sure there's a client state structure... */ 533 if (!ip->client) 534 make_client_state(ip); 535 536 /* If this is an alias lease, it doesn't need to be sorted in. */ 537 if (is_static == 2) { 538 ip->client->alias = lease; 539 return; 540 } 541 542 /* 543 * The new lease may supersede a lease that's not the active 544 * lease but is still on the lease list, so scan the lease list 545 * looking for a lease with the same address, and if we find it, 546 * toss it. 547 */ 548 pl = NULL; 549 for (lp = ip->client->leases; lp; lp = lp->next) { 550 if (lp->address.len == lease->address.len && 551 !memcmp(lp->address.iabuf, lease->address.iabuf, 552 lease->address.len)) { 553 if (pl) 554 pl->next = lp->next; 555 else 556 ip->client->leases = lp->next; 557 free_client_lease(lp); 558 break; 559 } 560 } 561 562 /* 563 * If this is a preloaded lease, just put it on the list of 564 * recorded leases - don't make it the active lease. 565 */ 566 if (is_static) { 567 lease->next = ip->client->leases; 568 ip->client->leases = lease; 569 return; 570 } 571 572 /* 573 * The last lease in the lease file on a particular interface is 574 * the active lease for that interface. Of course, we don't 575 * know what the last lease in the file is until we've parsed 576 * the whole file, so at this point, we assume that the lease we 577 * just parsed is the active lease for its interface. If 578 * there's already an active lease for the interface, and this 579 * lease is for the same ip address, then we just toss the old 580 * active lease and replace it with this one. If this lease is 581 * for a different address, then if the old active lease has 582 * expired, we dump it; if not, we put it on the list of leases 583 * for this interface which are still valid but no longer 584 * active. 585 */ 586 if (ip->client->active) { 587 if (ip->client->active->expiry < cur_time) 588 free_client_lease(ip->client->active); 589 else if (ip->client->active->address.len == 590 lease->address.len && 591 !memcmp(ip->client->active->address.iabuf, 592 lease->address.iabuf, lease->address.len)) 593 free_client_lease(ip->client->active); 594 else { 595 ip->client->active->next = ip->client->leases; 596 ip->client->leases = ip->client->active; 597 } 598 } 599 ip->client->active = lease; 600 601 /* Phew. */ 602 } 603 604 /* 605 * client-lease-declaration :== 606 * BOOTP | 607 * INTERFACE string | 608 * FIXED_ADDR ip_address | 609 * FILENAME string | 610 * SERVER_NAME string | 611 * OPTION option-decl | 612 * RENEW time-decl | 613 * REBIND time-decl | 614 * EXPIRE time-decl 615 */ 616 void 617 parse_client_lease_declaration(FILE *cfile, struct client_lease *lease, 618 struct interface_info **ipp) 619 { 620 int token; 621 char *val; 622 struct interface_info *ip; 623 624 switch (next_token(&val, cfile)) { 625 case BOOTP: 626 lease->is_bootp = 1; 627 break; 628 case INTERFACE: 629 token = next_token(&val, cfile); 630 if (token != STRING) { 631 parse_warn("expecting interface name (in quotes)."); 632 skip_to_semi(cfile); 633 break; 634 } 635 ip = interface_or_dummy(val); 636 *ipp = ip; 637 break; 638 case FIXED_ADDR: 639 if (!parse_ip_addr(cfile, &lease->address)) 640 return; 641 break; 642 case MEDIUM: 643 parse_string_list(cfile, &lease->medium, 0); 644 return; 645 case FILENAME: 646 lease->filename = parse_string(cfile); 647 return; 648 case NEXT_SERVER: 649 if (!parse_ip_addr(cfile, &lease->nextserver)) 650 return; 651 break; 652 case SERVER_NAME: 653 lease->server_name = parse_string(cfile); 654 return; 655 case RENEW: 656 lease->renewal = parse_date(cfile); 657 return; 658 case REBIND: 659 lease->rebind = parse_date(cfile); 660 return; 661 case EXPIRE: 662 lease->expiry = parse_date(cfile); 663 return; 664 case OPTION: 665 parse_option_decl(cfile, lease->options); 666 return; 667 default: 668 parse_warn("expecting lease declaration."); 669 skip_to_semi(cfile); 670 break; 671 } 672 token = next_token(&val, cfile); 673 if (token != SEMI) { 674 parse_warn("expecting semicolon."); 675 skip_to_semi(cfile); 676 } 677 } 678 679 struct option * 680 parse_option_decl(FILE *cfile, struct option_data *options) 681 { 682 char *val; 683 int token; 684 u_int8_t buf[4]; 685 u_int8_t hunkbuf[1024]; 686 int hunkix = 0; 687 char *vendor; 688 char *fmt; 689 struct universe *universe; 690 struct option *option; 691 struct iaddr ip_addr; 692 u_int8_t *dp; 693 int len; 694 int nul_term = 0; 695 696 token = next_token(&val, cfile); 697 if (!is_identifier(token)) { 698 parse_warn("expecting identifier after option keyword."); 699 if (token != SEMI) 700 skip_to_semi(cfile); 701 return (NULL); 702 } 703 if ((vendor = strdup(val)) == NULL) 704 error("no memory for vendor information."); 705 706 token = peek_token(&val, cfile); 707 if (token == DOT) { 708 /* Go ahead and take the DOT token... */ 709 token = next_token(&val, cfile); 710 711 /* The next token should be an identifier... */ 712 token = next_token(&val, cfile); 713 if (!is_identifier(token)) { 714 parse_warn("expecting identifier after '.'"); 715 if (token != SEMI) 716 skip_to_semi(cfile); 717 free(vendor); 718 return (NULL); 719 } 720 721 /* Look up the option name hash table for the specified 722 vendor. */ 723 universe = ((struct universe *)hash_lookup(&universe_hash, 724 (unsigned char *)vendor, 0)); 725 /* If it's not there, we can't parse the rest of the 726 declaration. */ 727 if (!universe) { 728 parse_warn("no vendor named %s.", vendor); 729 skip_to_semi(cfile); 730 free(vendor); 731 return (NULL); 732 } 733 } else { 734 /* Use the default hash table, which contains all the 735 standard dhcp option names. */ 736 val = vendor; 737 universe = &dhcp_universe; 738 } 739 740 /* Look up the actual option info... */ 741 option = (struct option *)hash_lookup(universe->hash, 742 (unsigned char *)val, 0); 743 744 /* If we didn't get an option structure, it's an undefined option. */ 745 if (!option) { 746 if (val == vendor) 747 parse_warn("no option named %s", val); 748 else 749 parse_warn("no option named %s for vendor %s", 750 val, vendor); 751 skip_to_semi(cfile); 752 free(vendor); 753 return (NULL); 754 } 755 756 /* Free the initial identifier token. */ 757 free(vendor); 758 759 /* Parse the option data... */ 760 do { 761 for (fmt = option->format; *fmt; fmt++) { 762 if (*fmt == 'A') 763 break; 764 switch (*fmt) { 765 case 'X': 766 len = parse_X(cfile, &hunkbuf[hunkix], 767 sizeof(hunkbuf) - hunkix); 768 hunkix += len; 769 break; 770 case 't': /* Text string... */ 771 token = next_token(&val, cfile); 772 if (token != STRING) { 773 parse_warn("expecting string."); 774 skip_to_semi(cfile); 775 return (NULL); 776 } 777 len = strlen(val); 778 if (hunkix + len + 1 > sizeof(hunkbuf)) { 779 parse_warn("option data buffer %s", 780 "overflow"); 781 skip_to_semi(cfile); 782 return (NULL); 783 } 784 memcpy(&hunkbuf[hunkix], val, len + 1); 785 nul_term = 1; 786 hunkix += len; 787 break; 788 case 'I': /* IP address. */ 789 if (!parse_ip_addr(cfile, &ip_addr)) 790 return (NULL); 791 len = ip_addr.len; 792 dp = ip_addr.iabuf; 793 alloc: 794 if (hunkix + len > sizeof(hunkbuf)) { 795 parse_warn("option data buffer " 796 "overflow"); 797 skip_to_semi(cfile); 798 return (NULL); 799 } 800 memcpy(&hunkbuf[hunkix], dp, len); 801 hunkix += len; 802 break; 803 case 'L': /* Unsigned 32-bit integer... */ 804 case 'l': /* Signed 32-bit integer... */ 805 token = next_token(&val, cfile); 806 if (token != NUMBER) { 807 need_number: 808 parse_warn("expecting number."); 809 if (token != SEMI) 810 skip_to_semi(cfile); 811 return (NULL); 812 } 813 convert_num(buf, val, 0, 32); 814 len = 4; 815 dp = buf; 816 goto alloc; 817 case 's': /* Signed 16-bit integer. */ 818 case 'S': /* Unsigned 16-bit integer. */ 819 token = next_token(&val, cfile); 820 if (token != NUMBER) 821 goto need_number; 822 convert_num(buf, val, 0, 16); 823 len = 2; 824 dp = buf; 825 goto alloc; 826 case 'b': /* Signed 8-bit integer. */ 827 case 'B': /* Unsigned 8-bit integer. */ 828 token = next_token(&val, cfile); 829 if (token != NUMBER) 830 goto need_number; 831 convert_num(buf, val, 0, 8); 832 len = 1; 833 dp = buf; 834 goto alloc; 835 case 'f': /* Boolean flag. */ 836 token = next_token(&val, cfile); 837 if (!is_identifier(token)) { 838 parse_warn("expecting identifier."); 839 bad_flag: 840 if (token != SEMI) 841 skip_to_semi(cfile); 842 return (NULL); 843 } 844 if (!strcasecmp(val, "true") || 845 !strcasecmp(val, "on")) 846 buf[0] = 1; 847 else if (!strcasecmp(val, "false") || 848 !strcasecmp(val, "off")) 849 buf[0] = 0; 850 else { 851 parse_warn("expecting boolean."); 852 goto bad_flag; 853 } 854 len = 1; 855 dp = buf; 856 goto alloc; 857 default: 858 warning("Bad format %c in parse_option_param.", 859 *fmt); 860 skip_to_semi(cfile); 861 return (NULL); 862 } 863 } 864 token = next_token(&val, cfile); 865 } while (*fmt == 'A' && token == COMMA); 866 867 if (token != SEMI) { 868 parse_warn("semicolon expected."); 869 skip_to_semi(cfile); 870 return (NULL); 871 } 872 873 options[option->code].data = malloc(hunkix + nul_term); 874 if (!options[option->code].data) 875 error("out of memory allocating option data."); 876 memcpy(options[option->code].data, hunkbuf, hunkix + nul_term); 877 options[option->code].len = hunkix; 878 return (option); 879 } 880 881 void 882 parse_string_list(FILE *cfile, struct string_list **lp, int multiple) 883 { 884 int token; 885 char *val; 886 size_t valsize; 887 struct string_list *cur, *tmp; 888 889 /* Find the last medium in the media list. */ 890 if (*lp) 891 for (cur = *lp; cur->next; cur = cur->next) 892 ; /* nothing */ 893 else 894 cur = NULL; 895 896 do { 897 token = next_token(&val, cfile); 898 if (token != STRING) { 899 parse_warn("Expecting media options."); 900 skip_to_semi(cfile); 901 return; 902 } 903 904 valsize = strlen(val) + 1; 905 tmp = new_string_list(valsize); 906 if (tmp == NULL) 907 error("no memory for string list entry."); 908 memcpy(tmp->string, val, valsize); 909 tmp->next = NULL; 910 911 /* Store this medium at the end of the media list. */ 912 if (cur) 913 cur->next = tmp; 914 else 915 *lp = tmp; 916 cur = tmp; 917 918 token = next_token(&val, cfile); 919 } while (multiple && token == COMMA); 920 921 if (token != SEMI) { 922 parse_warn("expecting semicolon."); 923 skip_to_semi(cfile); 924 } 925 } 926 927 void 928 parse_reject_statement(FILE *cfile, struct client_config *config) 929 { 930 int token; 931 char *val; 932 struct iaddr addr; 933 struct iaddrlist *list; 934 935 do { 936 if (!parse_ip_addr(cfile, &addr)) { 937 parse_warn("expecting IP address."); 938 skip_to_semi(cfile); 939 return; 940 } 941 942 list = malloc(sizeof(struct iaddrlist)); 943 if (!list) 944 error("no memory for reject list!"); 945 946 list->addr = addr; 947 list->next = config->reject_list; 948 config->reject_list = list; 949 950 token = next_token(&val, cfile); 951 } while (token == COMMA); 952 953 if (token != SEMI) { 954 parse_warn("expecting semicolon."); 955 skip_to_semi(cfile); 956 } 957 } 958