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