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