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