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