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