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