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