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