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 SERVER_NAME: 646 lease->server_name = parse_string(cfile); 647 return; 648 case RENEW: 649 lease->renewal = parse_date(cfile); 650 return; 651 case REBIND: 652 lease->rebind = parse_date(cfile); 653 return; 654 case EXPIRE: 655 lease->expiry = parse_date(cfile); 656 return; 657 case OPTION: 658 parse_option_decl(cfile, lease->options); 659 return; 660 default: 661 parse_warn("expecting lease declaration."); 662 skip_to_semi(cfile); 663 break; 664 } 665 token = next_token(&val, cfile); 666 if (token != SEMI) { 667 parse_warn("expecting semicolon."); 668 skip_to_semi(cfile); 669 } 670 } 671 672 struct option * 673 parse_option_decl(FILE *cfile, struct option_data *options) 674 { 675 char *val; 676 int token; 677 u_int8_t buf[4]; 678 u_int8_t hunkbuf[1024]; 679 int hunkix = 0; 680 char *vendor; 681 char *fmt; 682 struct universe *universe; 683 struct option *option; 684 struct iaddr ip_addr; 685 u_int8_t *dp; 686 int len; 687 int nul_term = 0; 688 689 token = next_token(&val, cfile); 690 if (!is_identifier(token)) { 691 parse_warn("expecting identifier after option keyword."); 692 if (token != SEMI) 693 skip_to_semi(cfile); 694 return (NULL); 695 } 696 if ((vendor = strdup(val)) == NULL) 697 error("no memory for vendor information."); 698 699 token = peek_token(&val, cfile); 700 if (token == DOT) { 701 /* Go ahead and take the DOT token... */ 702 token = next_token(&val, cfile); 703 704 /* The next token should be an identifier... */ 705 token = next_token(&val, cfile); 706 if (!is_identifier(token)) { 707 parse_warn("expecting identifier after '.'"); 708 if (token != SEMI) 709 skip_to_semi(cfile); 710 return (NULL); 711 } 712 713 /* Look up the option name hash table for the specified 714 vendor. */ 715 universe = ((struct universe *)hash_lookup(&universe_hash, 716 (unsigned char *)vendor, 0)); 717 /* If it's not there, we can't parse the rest of the 718 declaration. */ 719 if (!universe) { 720 parse_warn("no vendor named %s.", vendor); 721 skip_to_semi(cfile); 722 return (NULL); 723 } 724 } else { 725 /* Use the default hash table, which contains all the 726 standard dhcp option names. */ 727 val = vendor; 728 universe = &dhcp_universe; 729 } 730 731 /* Look up the actual option info... */ 732 option = (struct option *)hash_lookup(universe->hash, 733 (unsigned char *)val, 0); 734 735 /* If we didn't get an option structure, it's an undefined option. */ 736 if (!option) { 737 if (val == vendor) 738 parse_warn("no option named %s", val); 739 else 740 parse_warn("no option named %s for vendor %s", 741 val, vendor); 742 skip_to_semi(cfile); 743 return (NULL); 744 } 745 746 /* Free the initial identifier token. */ 747 free(vendor); 748 749 /* Parse the option data... */ 750 do { 751 for (fmt = option->format; *fmt; fmt++) { 752 if (*fmt == 'A') 753 break; 754 switch (*fmt) { 755 case 'X': 756 len = parse_X(cfile, &hunkbuf[hunkix], 757 sizeof(hunkbuf) - hunkix); 758 hunkix += len; 759 break; 760 case 't': /* Text string... */ 761 token = next_token(&val, cfile); 762 if (token != STRING) { 763 parse_warn("expecting string."); 764 skip_to_semi(cfile); 765 return (NULL); 766 } 767 len = strlen(val); 768 if (hunkix + len + 1 > sizeof(hunkbuf)) { 769 parse_warn("option data buffer %s", 770 "overflow"); 771 skip_to_semi(cfile); 772 return (NULL); 773 } 774 memcpy(&hunkbuf[hunkix], val, len + 1); 775 nul_term = 1; 776 hunkix += len; 777 break; 778 case 'I': /* IP address. */ 779 if (!parse_ip_addr(cfile, &ip_addr)) 780 return (NULL); 781 len = ip_addr.len; 782 dp = ip_addr.iabuf; 783 alloc: 784 if (hunkix + len > sizeof(hunkbuf)) { 785 parse_warn("option data buffer " 786 "overflow"); 787 skip_to_semi(cfile); 788 return (NULL); 789 } 790 memcpy(&hunkbuf[hunkix], dp, len); 791 hunkix += len; 792 break; 793 case 'L': /* Unsigned 32-bit integer... */ 794 case 'l': /* Signed 32-bit integer... */ 795 token = next_token(&val, cfile); 796 if (token != NUMBER) { 797 need_number: 798 parse_warn("expecting number."); 799 if (token != SEMI) 800 skip_to_semi(cfile); 801 return (NULL); 802 } 803 convert_num(buf, val, 0, 32); 804 len = 4; 805 dp = buf; 806 goto alloc; 807 case 's': /* Signed 16-bit integer. */ 808 case 'S': /* Unsigned 16-bit integer. */ 809 token = next_token(&val, cfile); 810 if (token != NUMBER) 811 goto need_number; 812 convert_num(buf, val, 0, 16); 813 len = 2; 814 dp = buf; 815 goto alloc; 816 case 'b': /* Signed 8-bit integer. */ 817 case 'B': /* Unsigned 8-bit integer. */ 818 token = next_token(&val, cfile); 819 if (token != NUMBER) 820 goto need_number; 821 convert_num(buf, val, 0, 8); 822 len = 1; 823 dp = buf; 824 goto alloc; 825 case 'f': /* Boolean flag. */ 826 token = next_token(&val, cfile); 827 if (!is_identifier(token)) { 828 parse_warn("expecting identifier."); 829 bad_flag: 830 if (token != SEMI) 831 skip_to_semi(cfile); 832 return (NULL); 833 } 834 if (!strcasecmp(val, "true") || 835 !strcasecmp(val, "on")) 836 buf[0] = 1; 837 else if (!strcasecmp(val, "false") || 838 !strcasecmp(val, "off")) 839 buf[0] = 0; 840 else { 841 parse_warn("expecting boolean."); 842 goto bad_flag; 843 } 844 len = 1; 845 dp = buf; 846 goto alloc; 847 default: 848 warning("Bad format %c in parse_option_param.", 849 *fmt); 850 skip_to_semi(cfile); 851 return (NULL); 852 } 853 } 854 token = next_token(&val, cfile); 855 } while (*fmt == 'A' && token == COMMA); 856 857 if (token != SEMI) { 858 parse_warn("semicolon expected."); 859 skip_to_semi(cfile); 860 return (NULL); 861 } 862 863 options[option->code].data = malloc(hunkix + nul_term); 864 if (!options[option->code].data) 865 error("out of memory allocating option data."); 866 memcpy(options[option->code].data, hunkbuf, hunkix + nul_term); 867 options[option->code].len = hunkix; 868 return (option); 869 } 870 871 void 872 parse_string_list(FILE *cfile, struct string_list **lp, int multiple) 873 { 874 int token; 875 char *val; 876 size_t valsize; 877 struct string_list *cur, *tmp; 878 879 /* Find the last medium in the media list. */ 880 if (*lp) 881 for (cur = *lp; cur->next; cur = cur->next) 882 ; /* nothing */ 883 else 884 cur = NULL; 885 886 do { 887 token = next_token(&val, cfile); 888 if (token != STRING) { 889 parse_warn("Expecting media options."); 890 skip_to_semi(cfile); 891 return; 892 } 893 894 valsize = strlen(val) + 1; 895 tmp = new_string_list(valsize); 896 if (tmp == NULL) 897 error("no memory for string list entry."); 898 memcpy(tmp->string, val, valsize); 899 tmp->next = NULL; 900 901 /* Store this medium at the end of the media list. */ 902 if (cur) 903 cur->next = tmp; 904 else 905 *lp = tmp; 906 cur = tmp; 907 908 token = next_token(&val, cfile); 909 } while (multiple && token == COMMA); 910 911 if (token != SEMI) { 912 parse_warn("expecting semicolon."); 913 skip_to_semi(cfile); 914 } 915 } 916 917 void 918 parse_reject_statement(FILE *cfile, struct client_config *config) 919 { 920 int token; 921 char *val; 922 struct iaddr addr; 923 struct iaddrlist *list; 924 925 do { 926 if (!parse_ip_addr(cfile, &addr)) { 927 parse_warn("expecting IP address."); 928 skip_to_semi(cfile); 929 return; 930 } 931 932 list = malloc(sizeof(struct iaddrlist)); 933 if (!list) 934 error("no memory for reject list!"); 935 936 list->addr = addr; 937 list->next = config->reject_list; 938 config->reject_list = list; 939 940 token = next_token(&val, cfile); 941 } while (token == COMMA); 942 943 if (token != SEMI) { 944 parse_warn("expecting semicolon."); 945 skip_to_semi(cfile); 946 } 947 } 948