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