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