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