1 /* $OpenBSD: confpars.c,v 1.28 2016/08/17 00:55:33 krw Exp $ */ 2 3 /* 4 * Copyright (c) 1995, 1996, 1997 The Internet Software Consortium. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. Neither the name of The Internet Software Consortium nor the names 17 * of its contributors may be used to endorse or promote products derived 18 * from this software without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND 21 * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, 22 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 23 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 24 * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR 25 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 26 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 27 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 28 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 29 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 * 34 * This software has been written for the Internet Software Consortium 35 * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie 36 * Enterprises. To learn more about the Internet Software Consortium, 37 * see ``http://www.vix.com/isc''. To learn more about Vixie 38 * Enterprises, see ``http://www.vix.com''. 39 */ 40 41 #include <sys/types.h> 42 #include <sys/socket.h> 43 44 #include <net/if.h> 45 46 #include <netdb.h> 47 #include <stdio.h> 48 #include <stdlib.h> 49 #include <string.h> 50 51 #include "dhcp.h" 52 #include "tree.h" 53 #include "dhcpd.h" 54 #include "dhctoken.h" 55 56 /* 57 * conf-file :== parameters declarations EOF 58 * parameters :== <nil> | parameter | parameters parameter 59 * declarations :== <nil> | declaration | declarations declaration 60 */ 61 int 62 readconf(void) 63 { 64 FILE *cfile; 65 char *val; 66 int token; 67 int declaration = 0; 68 69 new_parse(path_dhcpd_conf); 70 71 /* Set up the initial dhcp option universe. */ 72 initialize_universes(); 73 74 /* Set up the global defaults. */ 75 root_group.default_lease_time = 43200; /* 12 hours. */ 76 root_group.max_lease_time = 86400; /* 24 hours. */ 77 root_group.bootp_lease_cutoff = MAX_TIME; 78 root_group.boot_unknown_clients = 1; 79 root_group.allow_bootp = 1; 80 root_group.allow_booting = 1; 81 root_group.authoritative = 1; 82 83 if ((cfile = fopen(path_dhcpd_conf, "r")) == NULL) 84 error("Can't open %s: %m", path_dhcpd_conf); 85 86 do { 87 token = peek_token(&val, cfile); 88 if (token == EOF) 89 break; 90 declaration = parse_statement(cfile, &root_group, 91 ROOT_GROUP, 92 NULL, 93 declaration); 94 } while (1); 95 token = next_token(&val, cfile); /* Clear the peek buffer */ 96 fclose(cfile); 97 98 return !warnings_occurred; 99 } 100 101 /* 102 * lease-file :== lease-declarations EOF 103 * lease-statments :== <nil> 104 * | lease-declaration 105 * | lease-declarations lease-declaration 106 */ 107 void 108 read_leases(void) 109 { 110 FILE *cfile; 111 char *val; 112 int token; 113 114 new_parse(path_dhcpd_db); 115 116 /* 117 * Open the lease file. If we can't open it, fail. The reason 118 * for this is that although on initial startup, the absence of 119 * a lease file is perfectly benign, if dhcpd has been running 120 * and this file is absent, it means that dhcpd tried and failed 121 * to rewrite the lease database. If we proceed and the 122 * problem which caused the rewrite to fail has been fixed, but no 123 * human has corrected the database problem, then we are left 124 * thinking that no leases have been assigned to anybody, which 125 * could create severe network chaos. 126 */ 127 if ((cfile = fopen(path_dhcpd_db, "r")) == NULL) { 128 warning("Can't open lease database %s: %m -- %s", 129 path_dhcpd_db, 130 "check for failed database rewrite attempt!"); 131 warning("Please read the dhcpd.leases manual page if you"); 132 error("don't know what to do about this."); 133 } 134 135 do { 136 token = next_token(&val, cfile); 137 if (token == EOF) 138 break; 139 if (token != TOK_LEASE) { 140 warning("Corrupt lease file - possible data loss!"); 141 skip_to_semi(cfile); 142 } else { 143 struct lease *lease; 144 lease = parse_lease_declaration(cfile); 145 if (lease) 146 enter_lease(lease); 147 else 148 parse_warn("possibly corrupt lease file"); 149 } 150 151 } while (1); 152 fclose(cfile); 153 } 154 155 /* 156 * statement :== parameter | declaration 157 * 158 * parameter :== timestamp 159 * | DEFAULT_LEASE_TIME lease_time 160 * | MAX_LEASE_TIME lease_time 161 * | DYNAMIC_BOOTP_LEASE_CUTOFF date 162 * | DYNAMIC_BOOTP_LEASE_LENGTH lease_time 163 * | BOOT_UNKNOWN_CLIENTS boolean 164 * | GET_LEASE_HOSTNAMES boolean 165 * | USE_HOST_DECL_NAME boolean 166 * | NEXT_SERVER ip-addr-or-hostname SEMI 167 * | option_parameter 168 * | SERVER-IDENTIFIER ip-addr-or-hostname SEMI 169 * | FILENAME string-parameter 170 * | SERVER_NAME string-parameter 171 * | hardware-parameter 172 * | fixed-address-parameter 173 * | ALLOW allow-deny-keyword 174 * | DENY allow-deny-keyword 175 * | USE_LEASE_ADDR_FOR_DEFAULT_ROUTE boolean 176 * 177 * declaration :== host-declaration 178 * | group-declaration 179 * | shared-network-declaration 180 * | subnet-declaration 181 * | VENDOR_CLASS class-declaration 182 * | USER_CLASS class-declaration 183 * | RANGE address-range-declaration 184 */ 185 int 186 parse_statement(FILE *cfile, struct group *group, int type, 187 struct host_decl *host_decl, int declaration) 188 { 189 int token; 190 char *val; 191 struct shared_network *share; 192 char *n; 193 struct tree *tree; 194 struct tree_cache *cache; 195 struct hardware hardware; 196 197 switch (next_token(&val, cfile)) { 198 case TOK_HOST: 199 if (type != HOST_DECL) 200 parse_host_declaration(cfile, group); 201 else { 202 parse_warn("host declarations not allowed here."); 203 skip_to_semi(cfile); 204 } 205 return 1; 206 207 case TOK_GROUP: 208 if (type != HOST_DECL) 209 parse_group_declaration(cfile, group); 210 else { 211 parse_warn("host declarations not allowed here."); 212 skip_to_semi(cfile); 213 } 214 return 1; 215 216 case TOK_TIMESTAMP: 217 break; 218 219 case TOK_SHARED_NETWORK: 220 if (type == SHARED_NET_DECL || 221 type == HOST_DECL || 222 type == SUBNET_DECL) { 223 parse_warn("shared-network parameters not %s.", 224 "allowed here"); 225 skip_to_semi(cfile); 226 break; 227 } 228 229 parse_shared_net_declaration(cfile, group); 230 return 1; 231 232 case TOK_SUBNET: 233 if (type == HOST_DECL || type == SUBNET_DECL) { 234 parse_warn("subnet declarations not allowed here."); 235 skip_to_semi(cfile); 236 return 1; 237 } 238 239 /* If we're in a subnet declaration, just do the parse. */ 240 if (group->shared_network) { 241 parse_subnet_declaration(cfile, 242 group->shared_network); 243 break; 244 } 245 246 /* 247 * Otherwise, cons up a fake shared network structure 248 * and populate it with the lone subnet. 249 */ 250 251 share = calloc(1, sizeof(struct shared_network)); 252 if (!share) 253 error("No memory for shared subnet"); 254 share->group = clone_group(group, "parse_statement:subnet"); 255 share->group->shared_network = share; 256 257 parse_subnet_declaration(cfile, share); 258 259 /* share->subnets is the subnet we just parsed. */ 260 if (share->subnets) { 261 share->interface = 262 share->subnets->interface; 263 264 /* Make the shared network name from network number. */ 265 n = piaddr(share->subnets->net); 266 share->name = strdup(n); 267 if (share->name == NULL) 268 error("no memory for subnet name"); 269 270 /* 271 * Copy the authoritative parameter from the subnet, 272 * since there is no opportunity to declare it here. 273 */ 274 share->group->authoritative = 275 share->subnets->group->authoritative; 276 enter_shared_network(share); 277 } 278 return 1; 279 280 case TOK_VENDOR_CLASS: 281 parse_class_declaration(cfile, group, 0); 282 return 1; 283 284 case TOK_USER_CLASS: 285 parse_class_declaration(cfile, group, 1); 286 return 1; 287 288 case TOK_DEFAULT_LEASE_TIME: 289 parse_lease_time(cfile, &group->default_lease_time); 290 break; 291 292 case TOK_MAX_LEASE_TIME: 293 parse_lease_time(cfile, &group->max_lease_time); 294 break; 295 296 case TOK_DYNAMIC_BOOTP_LEASE_CUTOFF: 297 group->bootp_lease_cutoff = parse_date(cfile); 298 break; 299 300 case TOK_DYNAMIC_BOOTP_LEASE_LENGTH: 301 parse_lease_time(cfile, &group->bootp_lease_length); 302 break; 303 304 case TOK_BOOT_UNKNOWN_CLIENTS: 305 if (type == HOST_DECL) 306 parse_warn("boot-unknown-clients not allowed here."); 307 group->boot_unknown_clients = parse_boolean(cfile); 308 break; 309 310 case TOK_GET_LEASE_HOSTNAMES: 311 if (type == HOST_DECL) 312 parse_warn("get-lease-hostnames not allowed here."); 313 group->get_lease_hostnames = parse_boolean(cfile); 314 break; 315 316 case TOK_ALWAYS_REPLY_RFC1048: 317 group->always_reply_rfc1048 = parse_boolean(cfile); 318 break; 319 320 case TOK_USE_HOST_DECL_NAMES: 321 if (type == HOST_DECL) 322 parse_warn("use-host-decl-names not allowed here."); 323 group->use_host_decl_names = parse_boolean(cfile); 324 break; 325 326 case TOK_USE_LEASE_ADDR_FOR_DEFAULT_ROUTE: 327 group->use_lease_addr_for_default_route = 328 parse_boolean(cfile); 329 break; 330 331 case TOK_TOKEN_NOT: 332 token = next_token(&val, cfile); 333 switch (token) { 334 case TOK_AUTHORITATIVE: 335 if (type == HOST_DECL) 336 parse_warn("authority makes no sense here."); 337 group->authoritative = 0; 338 parse_semi(cfile); 339 break; 340 default: 341 parse_warn("expecting assertion"); 342 skip_to_semi(cfile); 343 break; 344 } 345 break; 346 347 case TOK_AUTHORITATIVE: 348 if (type == HOST_DECL) 349 parse_warn("authority makes no sense here."); 350 group->authoritative = 1; 351 parse_semi(cfile); 352 break; 353 354 case TOK_NEXT_SERVER: 355 tree = parse_ip_addr_or_hostname(cfile, 0); 356 if (!tree) 357 break; 358 cache = tree_cache(tree); 359 if (!tree_evaluate (cache)) 360 error("next-server is not known"); 361 group->next_server.len = 4; 362 memcpy(group->next_server.iabuf, 363 cache->value, group->next_server.len); 364 parse_semi(cfile); 365 break; 366 367 case TOK_OPTION: 368 parse_option_param(cfile, group); 369 break; 370 371 case TOK_SERVER_IDENTIFIER: 372 tree = parse_ip_addr_or_hostname(cfile, 0); 373 if (!tree) 374 return declaration; 375 group->options[DHO_DHCP_SERVER_IDENTIFIER] = tree_cache(tree); 376 token = next_token(&val, cfile); 377 break; 378 379 case TOK_FILENAME: 380 group->filename = parse_string(cfile); 381 break; 382 383 case TOK_SERVER_NAME: 384 group->server_name = parse_string(cfile); 385 break; 386 387 case TOK_HARDWARE: 388 parse_hardware_param(cfile, &hardware); 389 if (host_decl) 390 host_decl->interface = hardware; 391 else 392 parse_warn("hardware address parameter %s", 393 "not allowed here."); 394 break; 395 396 case TOK_FIXED_ADDR: 397 cache = parse_fixed_addr_param(cfile); 398 if (host_decl) 399 host_decl->fixed_addr = cache; 400 else 401 parse_warn("fixed-address parameter not %s", 402 "allowed here."); 403 break; 404 405 case TOK_RANGE: 406 if (type != SUBNET_DECL || !group->subnet) { 407 parse_warn("range declaration not allowed here."); 408 skip_to_semi(cfile); 409 return declaration; 410 } 411 parse_address_range(cfile, group->subnet); 412 return declaration; 413 414 case TOK_ALLOW: 415 parse_allow_deny(cfile, group, 1); 416 break; 417 418 case TOK_DENY: 419 parse_allow_deny(cfile, group, 0); 420 break; 421 422 default: 423 if (declaration) 424 parse_warn("expecting a declaration."); 425 else 426 parse_warn("expecting a parameter or declaration."); 427 skip_to_semi(cfile); 428 return declaration; 429 } 430 431 if (declaration) { 432 parse_warn("parameters not allowed after first declaration."); 433 return 1; 434 } 435 436 return 0; 437 } 438 439 /* 440 * allow-deny-keyword :== BOOTP 441 * | BOOTING 442 * | DYNAMIC_BOOTP 443 * | UNKNOWN_CLIENTS 444 */ 445 void 446 parse_allow_deny(FILE *cfile, struct group *group, int flag) 447 { 448 int token; 449 char *val; 450 451 token = next_token(&val, cfile); 452 switch (token) { 453 case TOK_BOOTP: 454 group->allow_bootp = flag; 455 break; 456 457 case TOK_BOOTING: 458 group->allow_booting = flag; 459 break; 460 461 case TOK_DYNAMIC_BOOTP: 462 group->dynamic_bootp = flag; 463 break; 464 465 case TOK_UNKNOWN_CLIENTS: 466 group->boot_unknown_clients = flag; 467 break; 468 469 default: 470 parse_warn("expecting allow/deny key"); 471 skip_to_semi(cfile); 472 return; 473 } 474 parse_semi(cfile); 475 } 476 477 /* 478 * boolean :== ON SEMI | OFF SEMI | TRUE SEMI | FALSE SEMI 479 */ 480 int 481 parse_boolean(FILE *cfile) 482 { 483 char *val; 484 int rv; 485 486 next_token(&val, cfile); 487 if (!strcasecmp (val, "true") || !strcasecmp (val, "on")) 488 rv = 1; 489 else if (!strcasecmp (val, "false") || !strcasecmp (val, "off")) 490 rv = 0; 491 else { 492 parse_warn("boolean value (true/false/on/off) expected"); 493 skip_to_semi(cfile); 494 return 0; 495 } 496 parse_semi(cfile); 497 return rv; 498 } 499 500 /* 501 * Expect a left brace; if there isn't one, skip over the rest of the 502 * statement and return zero; otherwise, return 1. 503 */ 504 int 505 parse_lbrace(FILE *cfile) 506 { 507 int token; 508 char *val; 509 510 token = next_token(&val, cfile); 511 if (token != '{') { 512 parse_warn("expecting left brace."); 513 skip_to_semi(cfile); 514 return 0; 515 } 516 return 1; 517 } 518 519 520 /* 521 * host-declaration :== hostname '{' parameters declarations '}' 522 */ 523 void 524 parse_host_declaration(FILE *cfile, struct group *group) 525 { 526 char *val; 527 int token; 528 struct host_decl *host; 529 char *name = parse_host_name(cfile); 530 int declaration = 0; 531 532 if (!name) 533 return; 534 535 host = calloc(1, sizeof (struct host_decl)); 536 if (!host) 537 error("can't allocate host decl struct %s.", name); 538 539 host->name = name; 540 host->group = clone_group(group, "parse_host_declaration"); 541 542 if (!parse_lbrace(cfile)) { 543 free(host->name); 544 free(host->group); 545 free(host); 546 return; 547 } 548 549 do { 550 token = peek_token(&val, cfile); 551 if (token == '}') { 552 token = next_token(&val, cfile); 553 break; 554 } 555 if (token == EOF) { 556 token = next_token(&val, cfile); 557 parse_warn("unexpected end of file"); 558 break; 559 } 560 declaration = parse_statement(cfile, host->group, 561 HOST_DECL, host, declaration); 562 } while (1); 563 564 if (!host->group->options[DHO_HOST_NAME] && 565 host->group->use_host_decl_names) { 566 host->group->options[DHO_HOST_NAME] = 567 new_tree_cache("parse_host_declaration"); 568 if (!host->group->options[DHO_HOST_NAME]) 569 error("can't allocate a tree cache for hostname."); 570 host->group->options[DHO_HOST_NAME]->len = 571 strlen(name); 572 host->group->options[DHO_HOST_NAME]->value = 573 (unsigned char *)name; 574 host->group->options[DHO_HOST_NAME]->buf_size = 575 host->group->options[DHO_HOST_NAME]->len; 576 host->group->options[DHO_HOST_NAME]->timeout = -1; 577 host->group->options[DHO_HOST_NAME]->tree = 578 NULL; 579 } 580 581 enter_host(host); 582 } 583 584 /* 585 * class-declaration :== STRING '{' parameters declarations '}' 586 */ 587 void 588 parse_class_declaration(FILE *cfile, struct group *group, int type) 589 { 590 char *val; 591 int token; 592 struct class *class; 593 int declaration = 0; 594 595 token = next_token(&val, cfile); 596 if (token != TOK_STRING) { 597 parse_warn("Expecting class name"); 598 skip_to_semi(cfile); 599 return; 600 } 601 602 class = add_class (type, val); 603 if (!class) 604 error("No memory for class %s.", val); 605 class->group = clone_group(group, "parse_class_declaration"); 606 607 if (!parse_lbrace(cfile)) { 608 free(class->name); 609 free(class->group); 610 free(class); 611 return; 612 } 613 614 do { 615 token = peek_token(&val, cfile); 616 if (token == '}') { 617 token = next_token(&val, cfile); 618 break; 619 } else if (token == EOF) { 620 token = next_token(&val, cfile); 621 parse_warn("unexpected end of file"); 622 break; 623 } else { 624 declaration = parse_statement(cfile, class->group, 625 CLASS_DECL, NULL, declaration); 626 } 627 } while (1); 628 } 629 630 /* 631 * shared-network-declaration :== 632 * hostname LBRACE declarations parameters RBRACE 633 */ 634 void 635 parse_shared_net_declaration(FILE *cfile, struct group *group) 636 { 637 char *val; 638 int token; 639 struct shared_network *share; 640 char *name; 641 int declaration = 0; 642 643 share = calloc(1, sizeof(struct shared_network)); 644 if (!share) 645 error("No memory for shared subnet"); 646 share->leases = NULL; 647 share->last_lease = NULL; 648 share->insertion_point = NULL; 649 share->next = NULL; 650 share->interface = NULL; 651 share->group = clone_group(group, "parse_shared_net_declaration"); 652 share->group->shared_network = share; 653 654 /* Get the name of the shared network. */ 655 token = peek_token(&val, cfile); 656 if (token == TOK_STRING) { 657 token = next_token(&val, cfile); 658 659 if (val[0] == 0) { 660 parse_warn("zero-length shared network name"); 661 val = "<no-name-given>"; 662 } 663 name = strdup(val); 664 if (name == NULL) 665 error("no memory for shared network name"); 666 } else { 667 name = parse_host_name(cfile); 668 if (!name) { 669 free(share->group); 670 free(share); 671 return; 672 } 673 } 674 share->name = name; 675 676 if (!parse_lbrace(cfile)) { 677 free(share->group); 678 free(share->name); 679 free(share); 680 return; 681 } 682 683 do { 684 token = peek_token(&val, cfile); 685 if (token == '}') { 686 token = next_token(&val, cfile); 687 if (!share->subnets) { 688 free(share->group); 689 free(share->name); 690 free(share); 691 parse_warn("empty shared-network decl"); 692 return; 693 } 694 enter_shared_network(share); 695 return; 696 } else if (token == EOF) { 697 token = next_token(&val, cfile); 698 parse_warn("unexpected end of file"); 699 break; 700 } 701 702 declaration = parse_statement(cfile, share->group, 703 SHARED_NET_DECL, NULL, declaration); 704 } while (1); 705 } 706 707 /* 708 * subnet-declaration :== 709 * net NETMASK netmask RBRACE parameters declarations LBRACE 710 */ 711 void 712 parse_subnet_declaration(FILE *cfile, struct shared_network *share) 713 { 714 char *val; 715 int token; 716 struct subnet *subnet, *t, *u; 717 struct iaddr iaddr; 718 unsigned char addr[4]; 719 int len = sizeof addr; 720 int declaration = 0; 721 722 subnet = calloc(1, sizeof(struct subnet)); 723 if (!subnet) 724 error("No memory for new subnet"); 725 subnet->shared_network = share; 726 subnet->group = clone_group(share->group, "parse_subnet_declaration"); 727 subnet->group->subnet = subnet; 728 729 /* Get the network number. */ 730 if (!parse_numeric_aggregate(cfile, addr, &len, '.', 10, 8)) { 731 free(subnet->group); 732 free(subnet); 733 return; 734 } 735 memcpy(iaddr.iabuf, addr, len); 736 iaddr.len = len; 737 subnet->net = iaddr; 738 739 token = next_token(&val, cfile); 740 if (token != TOK_NETMASK) { 741 free(subnet->group); 742 free(subnet); 743 parse_warn("Expecting netmask"); 744 skip_to_semi(cfile); 745 return; 746 } 747 748 /* Get the netmask. */ 749 if (!parse_numeric_aggregate(cfile, addr, &len, '.', 10, 8)) { 750 free(subnet->group); 751 free(subnet); 752 return; 753 } 754 memcpy(iaddr.iabuf, addr, len); 755 iaddr.len = len; 756 subnet->netmask = iaddr; 757 758 /* Save only the subnet number. */ 759 subnet->net = subnet_number(subnet->net, subnet->netmask); 760 761 enter_subnet(subnet); 762 763 if (!parse_lbrace(cfile)) 764 return; 765 766 do { 767 token = peek_token(&val, cfile); 768 if (token == '}') { 769 token = next_token(&val, cfile); 770 break; 771 } else if (token == EOF) { 772 token = next_token(&val, cfile); 773 parse_warn("unexpected end of file"); 774 break; 775 } 776 declaration = parse_statement(cfile, subnet->group, 777 SUBNET_DECL, NULL, declaration); 778 } while (1); 779 780 /* 781 * If this subnet supports dynamic bootp, flag it so in the 782 * shared_network containing it. 783 */ 784 if (subnet->group->dynamic_bootp) 785 share->group->dynamic_bootp = 1; 786 787 /* Add the subnet to the list of subnets in this shared net. */ 788 if (!share->subnets) 789 share->subnets = subnet; 790 else { 791 u = NULL; 792 for (t = share->subnets; t; t = t->next_sibling) { 793 if (subnet_inner_than(subnet, t, 0)) { 794 if (u) 795 u->next_sibling = subnet; 796 else 797 share->subnets = subnet; 798 subnet->next_sibling = t; 799 return; 800 } 801 u = t; 802 } 803 u->next_sibling = subnet; 804 } 805 } 806 807 /* 808 * group-declaration :== RBRACE parameters declarations LBRACE 809 */ 810 void 811 parse_group_declaration(FILE *cfile, struct group *group) 812 { 813 char *val; 814 int token; 815 struct group *g; 816 int declaration = 0; 817 818 g = clone_group(group, "parse_group_declaration"); 819 820 if (!parse_lbrace(cfile)) { 821 free(g); 822 return; 823 } 824 825 do { 826 token = peek_token(&val, cfile); 827 if (token == '}') { 828 token = next_token(&val, cfile); 829 break; 830 } else if (token == EOF) { 831 token = next_token(&val, cfile); 832 parse_warn("unexpected end of file"); 833 break; 834 } 835 declaration = parse_statement(cfile, g, GROUP_DECL, NULL, 836 declaration); 837 } while (1); 838 } 839 840 /* 841 * cidr :== ip-address "/" bit-count 842 * ip-address :== NUMBER [ DOT NUMBER [ DOT NUMBER [ DOT NUMBER ] ] ] 843 * bit-count :== 0..32 844 */ 845 int 846 parse_cidr(FILE *cfile, unsigned char *addr, unsigned char *prefix) 847 { 848 const char *errstr; 849 char *val; 850 int token; 851 int len = 4; 852 853 token = peek_token(&val, cfile); 854 855 if (!parse_numeric_aggregate(cfile, addr, &len, '.', 10, 8)) { 856 parse_warn("Expecting CIDR subnet"); 857 goto nocidr; 858 } 859 860 token = next_token(&val, cfile); 861 if (token != '/') { 862 parse_warn("Expecting '/'"); 863 goto nocidr; 864 } 865 866 token = next_token(&val, cfile); 867 868 if (token == TOK_NUMBER_OR_NAME) 869 *prefix = strtonum(val, 0, 32, &errstr); 870 871 if (token != TOK_NUMBER_OR_NAME || errstr) { 872 *prefix = 0; 873 parse_warn("Expecting CIDR prefix length, got '%s'", val); 874 goto nocidr; 875 } 876 877 return 1; 878 879 nocidr: 880 if (token != ';') 881 skip_to_semi(cfile); 882 return 0; 883 } 884 885 /* 886 * ip-addr-or-hostname :== ip-address | hostname 887 * ip-address :== NUMBER DOT NUMBER DOT NUMBER DOT NUMBER 888 * 889 * Parse an ip address or a hostname. If uniform is zero, put in 890 * a TREE_LIMIT node to catch hostnames that evaluate to more than 891 * one IP address. 892 */ 893 struct tree * 894 parse_ip_addr_or_hostname(FILE *cfile, int uniform) 895 { 896 char *val; 897 int token; 898 unsigned char addr[4]; 899 int len = sizeof addr; 900 char *name; 901 struct tree *rv; 902 struct hostent *h; 903 904 name = NULL; 905 h = NULL; 906 907 token = peek_token(&val, cfile); 908 if (is_identifier(token)) { 909 name = parse_host_name(cfile); 910 if (name) 911 h = gethostbyname(name); 912 if (name && h) { 913 rv = tree_const(h->h_addr_list[0], h->h_length); 914 if (!uniform) 915 rv = tree_limit(rv, 4); 916 return rv; 917 } 918 } 919 920 if (token == TOK_NUMBER_OR_NAME) { 921 if (!parse_numeric_aggregate(cfile, addr, &len, '.', 10, 8)) { 922 parse_warn("%s (%d): expecting IP address or hostname", 923 val, token); 924 return NULL; 925 } 926 rv = tree_const(addr, len); 927 } else { 928 if (token != '{' && token != '}') 929 token = next_token(&val, cfile); 930 parse_warn("%s (%d): expecting IP address or hostname", 931 val, token); 932 if (token != ';') 933 skip_to_semi(cfile); 934 return NULL; 935 } 936 937 return rv; 938 } 939 940 941 /* 942 * fixed-addr-parameter :== ip-addrs-or-hostnames SEMI 943 * ip-addrs-or-hostnames :== ip-addr-or-hostname 944 * | ip-addrs-or-hostnames ip-addr-or-hostname 945 */ 946 struct tree_cache * 947 parse_fixed_addr_param(FILE *cfile) 948 { 949 char *val; 950 int token; 951 struct tree *tree = NULL; 952 struct tree *tmp; 953 954 do { 955 tmp = parse_ip_addr_or_hostname(cfile, 0); 956 if (tree) 957 tree = tree_concat(tree, tmp); 958 else 959 tree = tmp; 960 token = peek_token(&val, cfile); 961 if (token == ',') 962 token = next_token(&val, cfile); 963 } while (token == ','); 964 965 if (!parse_semi(cfile)) 966 return NULL; 967 return tree_cache(tree); 968 } 969 970 /* 971 * option_parameter :== identifier DOT identifier <syntax> SEMI 972 * | identifier <syntax> SEMI 973 * 974 * Option syntax is handled specially through format strings, so it 975 * would be painful to come up with BNF for it. However, it always 976 * starts as above and ends in a SEMI. 977 */ 978 void 979 parse_option_param(FILE *cfile, struct group *group) 980 { 981 char *val; 982 int token; 983 unsigned char buf[4]; 984 unsigned char cprefix; 985 char *vendor; 986 char *fmt; 987 struct universe *universe; 988 struct option *option; 989 struct tree *tree = NULL; 990 struct tree *t; 991 992 token = next_token(&val, cfile); 993 if (!is_identifier(token)) { 994 parse_warn("expecting identifier after option keyword."); 995 if (token != ';') 996 skip_to_semi(cfile); 997 return; 998 } 999 vendor = strdup(val); 1000 if (vendor == NULL) 1001 error("no memory for vendor token."); 1002 token = peek_token(&val, cfile); 1003 if (token == '.') { 1004 /* Go ahead and take the DOT token. */ 1005 token = next_token(&val, cfile); 1006 1007 /* The next token should be an identifier. */ 1008 token = next_token(&val, cfile); 1009 if (!is_identifier(token)) { 1010 parse_warn("expecting identifier after '.'"); 1011 if (token != ';') 1012 skip_to_semi(cfile); 1013 free(vendor); 1014 return; 1015 } 1016 1017 /* 1018 * Look up the option name hash table for the specified 1019 * vendor. 1020 */ 1021 universe = ((struct universe *)hash_lookup(&universe_hash, 1022 (unsigned char *)vendor, 0)); 1023 /* 1024 * If it's not there, we can't parse the rest of the 1025 * declaration. 1026 */ 1027 if (!universe) { 1028 parse_warn("no vendor named %s.", vendor); 1029 skip_to_semi(cfile); 1030 free(vendor); 1031 return; 1032 } 1033 } else { 1034 /* 1035 * Use the default hash table, which contains all the 1036 * standard dhcp option names. 1037 */ 1038 val = vendor; 1039 universe = &dhcp_universe; 1040 } 1041 1042 /* Look up the actual option info. */ 1043 option = (struct option *)hash_lookup(universe->hash, 1044 (unsigned char *)val, 0); 1045 1046 /* If we didn't get an option structure, it's an undefined option. */ 1047 if (!option) { 1048 if (val == vendor) 1049 parse_warn("no option named %s", val); 1050 else 1051 parse_warn("no option named %s for vendor %s", 1052 val, vendor); 1053 skip_to_semi(cfile); 1054 free(vendor); 1055 return; 1056 } 1057 1058 /* Free the initial identifier token. */ 1059 free(vendor); 1060 1061 /* Parse the option data. */ 1062 do { 1063 /* 1064 * Set a flag if this is an array of a simple type (i.e., 1065 * not an array of pairs of IP addresses, or something 1066 * like that. 1067 */ 1068 int uniform = option->format[1] == 'A'; 1069 1070 for (fmt = option->format; *fmt; fmt++) { 1071 if (*fmt == 'A') 1072 break; 1073 switch (*fmt) { 1074 case 'X': 1075 token = peek_token(&val, cfile); 1076 if (token == TOK_NUMBER_OR_NAME) { 1077 do { 1078 token = next_token 1079 (&val, cfile); 1080 if (token != TOK_NUMBER_OR_NAME) { 1081 parse_warn("expecting " 1082 "hex number."); 1083 if (token != ';') 1084 skip_to_semi( 1085 cfile); 1086 return; 1087 } 1088 convert_num(buf, val, 16, 8); 1089 tree = tree_concat(tree, 1090 tree_const(buf, 1)); 1091 token = peek_token(&val, cfile); 1092 if (token == ':') 1093 token = next_token(&val, 1094 cfile); 1095 } while (token == ':'); 1096 } else if (token == TOK_STRING) { 1097 token = next_token(&val, cfile); 1098 tree = tree_concat(tree, 1099 tree_const((unsigned char *)val, 1100 strlen(val))); 1101 } else { 1102 parse_warn("expecting string %s.", 1103 "or hexadecimal data"); 1104 skip_to_semi(cfile); 1105 return; 1106 } 1107 break; 1108 1109 case 't': /* Text string. */ 1110 token = next_token(&val, cfile); 1111 if (token != TOK_STRING 1112 && !is_identifier(token)) { 1113 parse_warn("expecting string."); 1114 if (token != ';') 1115 skip_to_semi(cfile); 1116 return; 1117 } 1118 tree = tree_concat(tree, 1119 tree_const((unsigned char *)val, 1120 strlen(val))); 1121 break; 1122 1123 case 'I': /* IP address or hostname. */ 1124 t = parse_ip_addr_or_hostname(cfile, uniform); 1125 if (!t) 1126 return; 1127 tree = tree_concat(tree, t); 1128 break; 1129 1130 case 'L': /* Unsigned 32-bit integer. */ 1131 case 'l': /* Signed 32-bit integer. */ 1132 token = next_token(&val, cfile); 1133 if (token != TOK_NUMBER && token != 1134 TOK_NUMBER_OR_NAME) { 1135 parse_warn("expecting number."); 1136 if (token != ';') 1137 skip_to_semi(cfile); 1138 return; 1139 } 1140 convert_num(buf, val, 0, 32); 1141 tree = tree_concat(tree, tree_const(buf, 4)); 1142 break; 1143 case 's': /* Signed 16-bit integer. */ 1144 case 'S': /* Unsigned 16-bit integer. */ 1145 token = next_token(&val, cfile); 1146 if (token != TOK_NUMBER && token != 1147 TOK_NUMBER_OR_NAME) { 1148 parse_warn("expecting number."); 1149 if (token != ';') 1150 skip_to_semi(cfile); 1151 return; 1152 } 1153 convert_num(buf, val, 0, 16); 1154 tree = tree_concat(tree, tree_const(buf, 2)); 1155 break; 1156 case 'b': /* Signed 8-bit integer. */ 1157 case 'B': /* Unsigned 8-bit integer. */ 1158 token = next_token(&val, cfile); 1159 if (token != TOK_NUMBER && token != 1160 TOK_NUMBER_OR_NAME) { 1161 parse_warn("expecting number."); 1162 if (token != ';') 1163 skip_to_semi(cfile); 1164 return; 1165 } 1166 convert_num(buf, val, 0, 8); 1167 tree = tree_concat(tree, tree_const(buf, 1)); 1168 break; 1169 case 'f': /* Boolean flag. */ 1170 token = next_token(&val, cfile); 1171 if (!is_identifier(token)) { 1172 parse_warn("expecting identifier."); 1173 if (token != ';') 1174 skip_to_semi(cfile); 1175 return; 1176 } 1177 if (!strcasecmp(val, "true") 1178 || !strcasecmp(val, "on")) 1179 buf[0] = 1; 1180 else if (!strcasecmp(val, "false") 1181 || !strcasecmp(val, "off")) 1182 buf[0] = 0; 1183 else { 1184 parse_warn("expecting boolean."); 1185 if (token != ';') 1186 skip_to_semi(cfile); 1187 return; 1188 } 1189 tree = tree_concat(tree, tree_const(buf, 1)); 1190 break; 1191 case 'C': 1192 if (!parse_cidr(cfile, buf, &cprefix)) 1193 return; 1194 tree = tree_concat(tree, tree_const(&cprefix, 1195 sizeof(cprefix))); 1196 if (cprefix > 0) 1197 tree = tree_concat(tree, tree_const(buf, 1198 (cprefix + 7) / 8)); 1199 break; 1200 default: 1201 warning("Bad format %c in parse_option_param.", 1202 *fmt); 1203 skip_to_semi(cfile); 1204 return; 1205 } 1206 } 1207 if (*fmt == 'A') { 1208 token = peek_token(&val, cfile); 1209 if (token == ',') { 1210 token = next_token(&val, cfile); 1211 continue; 1212 } 1213 break; 1214 } 1215 } while (*fmt == 'A'); 1216 1217 token = next_token(&val, cfile); 1218 if (token != ';') { 1219 parse_warn("semicolon expected."); 1220 skip_to_semi(cfile); 1221 return; 1222 } 1223 group->options[option->code] = tree_cache(tree); 1224 } 1225 1226 /* 1227 * lease_declaration :== LEASE ip_address LBRACE lease_parameters RBRACE 1228 * 1229 * lease_parameters :== <nil> 1230 * | lease_parameter 1231 * | lease_parameters lease_parameter 1232 * 1233 * lease_parameter :== STARTS date 1234 * | ENDS date 1235 * | TIMESTAMP date 1236 * | HARDWARE hardware-parameter 1237 * | UID hex_numbers SEMI 1238 * | HOSTNAME hostname SEMI 1239 * | CLIENT_HOSTNAME hostname SEMI 1240 * | CLASS identifier SEMI 1241 * | DYNAMIC_BOOTP SEMI 1242 */ 1243 struct lease * 1244 parse_lease_declaration(FILE *cfile) 1245 { 1246 char *val; 1247 int token; 1248 unsigned char addr[4]; 1249 int len = sizeof addr; 1250 int seenmask = 0; 1251 int seenbit; 1252 char tbuf[32]; 1253 static struct lease lease; 1254 1255 /* Zap the lease structure. */ 1256 memset(&lease, 0, sizeof lease); 1257 1258 /* Get the address for which the lease has been issued. */ 1259 if (!parse_numeric_aggregate(cfile, addr, &len, '.', 10, 8)) 1260 return NULL; 1261 memcpy(lease.ip_addr.iabuf, addr, len); 1262 lease.ip_addr.len = len; 1263 1264 if (!parse_lbrace(cfile)) 1265 return NULL; 1266 1267 do { 1268 token = next_token(&val, cfile); 1269 if (token == '}') 1270 break; 1271 else if (token == EOF) { 1272 parse_warn("unexpected end of file"); 1273 break; 1274 } 1275 strlcpy(tbuf, val, sizeof tbuf); 1276 1277 /* Parse any of the times associated with the lease. */ 1278 if (token == TOK_STARTS || token == TOK_ENDS || token == TOK_TIMESTAMP) { 1279 time_t t; 1280 t = parse_date(cfile); 1281 switch (token) { 1282 case TOK_STARTS: 1283 seenbit = 1; 1284 lease.starts = t; 1285 break; 1286 1287 case TOK_ENDS: 1288 seenbit = 2; 1289 lease.ends = t; 1290 break; 1291 1292 case TOK_TIMESTAMP: 1293 seenbit = 4; 1294 lease.timestamp = t; 1295 break; 1296 1297 default: 1298 /*NOTREACHED*/ 1299 seenbit = 0; 1300 break; 1301 } 1302 } else { 1303 switch (token) { 1304 /* Colon-separated hexadecimal octets. */ 1305 case TOK_UID: 1306 seenbit = 8; 1307 token = peek_token(&val, cfile); 1308 if (token == TOK_STRING) { 1309 token = next_token(&val, cfile); 1310 lease.uid_len = strlen(val); 1311 lease.uid = malloc(lease.uid_len); 1312 if (!lease.uid) { 1313 warning("no space for uid"); 1314 return NULL; 1315 } 1316 memcpy(lease.uid, val, lease.uid_len); 1317 parse_semi(cfile); 1318 } else { 1319 lease.uid_len = 0; 1320 lease.uid = 1321 parse_numeric_aggregate(cfile, 1322 NULL, &lease.uid_len, ':', 16, 8); 1323 if (!lease.uid) { 1324 warning("no space for uid"); 1325 return NULL; 1326 } 1327 if (lease.uid_len == 0) { 1328 lease.uid = NULL; 1329 parse_warn("zero-length uid"); 1330 seenbit = 0; 1331 break; 1332 } 1333 } 1334 if (!lease.uid) 1335 error("No memory for lease uid"); 1336 break; 1337 1338 case TOK_CLASS: 1339 seenbit = 32; 1340 token = next_token(&val, cfile); 1341 if (!is_identifier(token)) { 1342 if (token != ';') 1343 skip_to_semi(cfile); 1344 return NULL; 1345 } 1346 /* for now, we aren't using this. */ 1347 break; 1348 1349 case TOK_HARDWARE: 1350 seenbit = 64; 1351 parse_hardware_param(cfile, 1352 &lease.hardware_addr); 1353 break; 1354 1355 case TOK_DYNAMIC_BOOTP: 1356 seenbit = 128; 1357 lease.flags |= BOOTP_LEASE; 1358 break; 1359 1360 case TOK_ABANDONED: 1361 seenbit = 256; 1362 lease.flags |= ABANDONED_LEASE; 1363 break; 1364 1365 case TOK_HOSTNAME: 1366 seenbit = 512; 1367 token = peek_token(&val, cfile); 1368 if (token == TOK_STRING) 1369 lease.hostname = parse_string(cfile); 1370 else 1371 lease.hostname = 1372 parse_host_name(cfile); 1373 if (!lease.hostname) { 1374 seenbit = 0; 1375 return NULL; 1376 } 1377 break; 1378 1379 case TOK_CLIENT_HOSTNAME: 1380 seenbit = 1024; 1381 token = peek_token(&val, cfile); 1382 if (token == TOK_STRING) 1383 lease.client_hostname = 1384 parse_string(cfile); 1385 else 1386 lease.client_hostname = 1387 parse_host_name(cfile); 1388 break; 1389 1390 default: 1391 skip_to_semi(cfile); 1392 seenbit = 0; 1393 return NULL; 1394 } 1395 1396 if (token != TOK_HARDWARE && token != TOK_STRING) { 1397 token = next_token(&val, cfile); 1398 if (token != ';') { 1399 parse_warn("semicolon expected."); 1400 skip_to_semi(cfile); 1401 return NULL; 1402 } 1403 } 1404 } 1405 if (seenmask & seenbit) { 1406 parse_warn("Too many %s parameters in lease %s\n", 1407 tbuf, piaddr(lease.ip_addr)); 1408 } else 1409 seenmask |= seenbit; 1410 1411 } while (1); 1412 return &lease; 1413 } 1414 1415 /* 1416 * address-range-declaration :== ip-address ip-address SEMI 1417 * | DYNAMIC_BOOTP ip-address ip-address SEMI 1418 */ 1419 void 1420 parse_address_range(FILE *cfile, struct subnet *subnet) 1421 { 1422 struct iaddr low, high; 1423 unsigned char addr[4]; 1424 int len = sizeof addr, token, dynamic = 0; 1425 char *val; 1426 1427 if ((token = peek_token(&val, cfile)) == TOK_DYNAMIC_BOOTP) { 1428 token = next_token(&val, cfile); 1429 subnet->group->dynamic_bootp = dynamic = 1; 1430 } 1431 1432 /* Get the bottom address in the range. */ 1433 if (!parse_numeric_aggregate(cfile, addr, &len, '.', 10, 8)) 1434 return; 1435 memcpy(low.iabuf, addr, len); 1436 low.len = len; 1437 1438 /* Only one address? */ 1439 token = peek_token(&val, cfile); 1440 if (token == ';') 1441 high = low; 1442 else { 1443 /* Get the top address in the range. */ 1444 if (!parse_numeric_aggregate(cfile, addr, &len, '.', 10, 8)) 1445 return; 1446 memcpy(high.iabuf, addr, len); 1447 high.len = len; 1448 } 1449 1450 token = next_token(&val, cfile); 1451 if (token != ';') { 1452 parse_warn("semicolon expected."); 1453 skip_to_semi(cfile); 1454 return; 1455 } 1456 1457 /* Create the new address range. */ 1458 new_address_range(low, high, subnet, dynamic); 1459 } 1460