xref: /openbsd/usr.sbin/dhcpd/confpars.c (revision cecf84d4)
1 /*	$OpenBSD: confpars.c,v 1.23 2014/07/09 13:42:24 yasuoka 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 *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 = calloc(1, sizeof(struct shared_network));
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 			share->name = strdup(n);
251 			if (share->name == NULL)
252 				error("no memory for subnet name");
253 
254 			/* Copy the authoritative parameter from the subnet,
255 			   since there is no opportunity to declare it here. */
256 			share->group->authoritative =
257 				share->subnets->group->authoritative;
258 			enter_shared_network(share);
259 		}
260 		return 1;
261 
262 	case TOK_VENDOR_CLASS:
263 		parse_class_declaration(cfile, group, 0);
264 		return 1;
265 
266 	case TOK_USER_CLASS:
267 		parse_class_declaration(cfile, group, 1);
268 		return 1;
269 
270 	case TOK_DEFAULT_LEASE_TIME:
271 		parse_lease_time(cfile, &group->default_lease_time);
272 		break;
273 
274 	case TOK_MAX_LEASE_TIME:
275 		parse_lease_time(cfile, &group->max_lease_time);
276 		break;
277 
278 	case TOK_DYNAMIC_BOOTP_LEASE_CUTOFF:
279 		group->bootp_lease_cutoff = parse_date(cfile);
280 		break;
281 
282 	case TOK_DYNAMIC_BOOTP_LEASE_LENGTH:
283 		parse_lease_time(cfile, &group->bootp_lease_length);
284 		break;
285 
286 	case TOK_BOOT_UNKNOWN_CLIENTS:
287 		if (type == HOST_DECL)
288 			parse_warn("boot-unknown-clients not allowed here.");
289 		group->boot_unknown_clients = parse_boolean(cfile);
290 		break;
291 
292 	case TOK_GET_LEASE_HOSTNAMES:
293 		if (type == HOST_DECL)
294 			parse_warn("get-lease-hostnames not allowed here.");
295 		group->get_lease_hostnames = parse_boolean(cfile);
296 		break;
297 
298 	case TOK_ALWAYS_REPLY_RFC1048:
299 		group->always_reply_rfc1048 = parse_boolean(cfile);
300 		break;
301 
302 	case TOK_USE_HOST_DECL_NAMES:
303 		if (type == HOST_DECL)
304 			parse_warn("use-host-decl-names not allowed here.");
305 		group->use_host_decl_names = parse_boolean(cfile);
306 		break;
307 
308 	case TOK_USE_LEASE_ADDR_FOR_DEFAULT_ROUTE:
309 		group->use_lease_addr_for_default_route =
310 			parse_boolean(cfile);
311 		break;
312 
313 	case TOK_TOKEN_NOT:
314 		token = next_token(&val, cfile);
315 		switch (token) {
316 		case TOK_AUTHORITATIVE:
317 			if (type == HOST_DECL)
318 			    parse_warn("authority makes no sense here.");
319 			group->authoritative = 0;
320 			parse_semi(cfile);
321 			break;
322 		default:
323 			parse_warn("expecting assertion");
324 			skip_to_semi(cfile);
325 			break;
326 		}
327 		break;
328 
329 	case TOK_AUTHORITATIVE:
330 		if (type == HOST_DECL)
331 		    parse_warn("authority makes no sense here.");
332 		group->authoritative = 1;
333 		parse_semi(cfile);
334 		break;
335 
336 	case TOK_NEXT_SERVER:
337 		tree = parse_ip_addr_or_hostname(cfile, 0);
338 		if (!tree)
339 			break;
340 		cache = tree_cache(tree);
341 		if (!tree_evaluate (cache))
342 			error("next-server is not known");
343 		group->next_server.len = 4;
344 		memcpy(group->next_server.iabuf,
345 		    cache->value, group->next_server.len);
346 		parse_semi(cfile);
347 		break;
348 
349 	case TOK_OPTION:
350 		parse_option_param(cfile, group);
351 		break;
352 
353 	case TOK_SERVER_IDENTIFIER:
354 		tree = parse_ip_addr_or_hostname(cfile, 0);
355 		if (!tree)
356 			return declaration;
357 		group->options[DHO_DHCP_SERVER_IDENTIFIER] = tree_cache(tree);
358 		token = next_token(&val, cfile);
359 		break;
360 
361 	case TOK_FILENAME:
362 		group->filename = parse_string(cfile);
363 		break;
364 
365 	case TOK_SERVER_NAME:
366 		group->server_name = parse_string(cfile);
367 		break;
368 
369 	case TOK_HARDWARE:
370 		parse_hardware_param(cfile, &hardware);
371 		if (host_decl)
372 			host_decl->interface = hardware;
373 		else
374 			parse_warn("hardware address parameter %s",
375 				    "not allowed here.");
376 		break;
377 
378 	case TOK_FIXED_ADDR:
379 		cache = parse_fixed_addr_param(cfile);
380 		if (host_decl)
381 			host_decl->fixed_addr = cache;
382 		else
383 			parse_warn("fixed-address parameter not %s",
384 				    "allowed here.");
385 		break;
386 
387 	case TOK_RANGE:
388 		if (type != SUBNET_DECL || !group->subnet) {
389 			parse_warn("range declaration not allowed here.");
390 			skip_to_semi(cfile);
391 			return declaration;
392 		}
393 		parse_address_range(cfile, group->subnet);
394 		return declaration;
395 
396 	case TOK_ALLOW:
397 		parse_allow_deny(cfile, group, 1);
398 		break;
399 
400 	case TOK_DENY:
401 		parse_allow_deny(cfile, group, 0);
402 		break;
403 
404 	default:
405 		if (declaration)
406 			parse_warn("expecting a declaration.");
407 		else
408 			parse_warn("expecting a parameter or declaration.");
409 		skip_to_semi(cfile);
410 		return declaration;
411 	}
412 
413 	if (declaration) {
414 		parse_warn("parameters not allowed after first declaration.");
415 		return 1;
416 	}
417 
418 	return 0;
419 }
420 
421 /* allow-deny-keyword :== BOOTP
422 			| BOOTING
423 			| DYNAMIC_BOOTP
424 			| UNKNOWN_CLIENTS */
425 
426 void parse_allow_deny(cfile, group, flag)
427 	FILE *cfile;
428 	struct group *group;
429 	int flag;
430 {
431 	int token;
432 	char *val;
433 
434 	token = next_token(&val, cfile);
435 	switch (token) {
436 	case TOK_BOOTP:
437 		group->allow_bootp = flag;
438 		break;
439 
440 	case TOK_BOOTING:
441 		group->allow_booting = flag;
442 		break;
443 
444 	case TOK_DYNAMIC_BOOTP:
445 		group->dynamic_bootp = flag;
446 		break;
447 
448 	case TOK_UNKNOWN_CLIENTS:
449 		group->boot_unknown_clients = flag;
450 		break;
451 
452 	default:
453 		parse_warn("expecting allow/deny key");
454 		skip_to_semi(cfile);
455 		return;
456 	}
457 	parse_semi(cfile);
458 }
459 
460 /* boolean :== ON SEMI | OFF SEMI | TRUE SEMI | FALSE SEMI */
461 
462 int
463 parse_boolean(FILE *cfile)
464 {
465 	char *val;
466 	int rv;
467 
468 	next_token(&val, cfile);
469 	if (!strcasecmp (val, "true") || !strcasecmp (val, "on"))
470 		rv = 1;
471 	else if (!strcasecmp (val, "false") || !strcasecmp (val, "off"))
472 		rv = 0;
473 	else {
474 		parse_warn("boolean value (true/false/on/off) expected");
475 		skip_to_semi(cfile);
476 		return 0;
477 	}
478 	parse_semi(cfile);
479 	return rv;
480 }
481 
482 /* Expect a left brace; if there isn't one, skip over the rest of the
483    statement and return zero; otherwise, return 1. */
484 
485 int
486 parse_lbrace(FILE *cfile)
487 {
488 	int token;
489 	char *val;
490 
491 	token = next_token(&val, cfile);
492 	if (token != '{') {
493 		parse_warn("expecting left brace.");
494 		skip_to_semi(cfile);
495 		return 0;
496 	}
497 	return 1;
498 }
499 
500 
501 /* host-declaration :== hostname '{' parameters declarations '}' */
502 
503 void parse_host_declaration(cfile, group)
504 	FILE *cfile;
505 	struct group *group;
506 {
507 	char *val;
508 	int token;
509 	struct host_decl *host;
510 	char *name = parse_host_name(cfile);
511 	int declaration = 0;
512 
513 	if (!name)
514 		return;
515 
516 	host = calloc(1, sizeof (struct host_decl));
517 	if (!host)
518 		error("can't allocate host decl struct %s.", name);
519 
520 	host->name = name;
521 	host->group = clone_group(group, "parse_host_declaration");
522 
523 	if (!parse_lbrace(cfile)) {
524 		free(host->name);
525 		free(host->group);
526 		free(host);
527 		return;
528 	}
529 
530 	do {
531 		token = peek_token(&val, cfile);
532 		if (token == '}') {
533 			token = next_token(&val, cfile);
534 			break;
535 		}
536 		if (token == EOF) {
537 			token = next_token(&val, cfile);
538 			parse_warn("unexpected end of file");
539 			break;
540 		}
541 		declaration = parse_statement(cfile, host->group,
542 		    HOST_DECL, host, declaration);
543 	} while (1);
544 
545 	if (!host->group->options[DHO_HOST_NAME] &&
546 	    host->group->use_host_decl_names) {
547 		host->group->options[DHO_HOST_NAME] =
548 		    new_tree_cache("parse_host_declaration");
549 		if (!host->group->options[DHO_HOST_NAME])
550 			error("can't allocate a tree cache for hostname.");
551 		host->group->options[DHO_HOST_NAME]->len =
552 			strlen(name);
553 		host->group->options[DHO_HOST_NAME]->value =
554 			(unsigned char *)name;
555 		host->group->options[DHO_HOST_NAME]->buf_size =
556 			host->group->options[DHO_HOST_NAME]->len;
557 		host->group->options[DHO_HOST_NAME]->timeout = -1;
558 		host->group->options[DHO_HOST_NAME]->tree =
559 			NULL;
560 	}
561 
562 	enter_host(host);
563 }
564 
565 /* class-declaration :== STRING '{' parameters declarations '}'
566 */
567 
568 void parse_class_declaration(cfile, group, type)
569 	FILE *cfile;
570 	struct group *group;
571 	int type;
572 {
573 	char *val;
574 	int token;
575 	struct class *class;
576 	int declaration = 0;
577 
578 	token = next_token(&val, cfile);
579 	if (token != TOK_STRING) {
580 		parse_warn("Expecting class name");
581 		skip_to_semi(cfile);
582 		return;
583 	}
584 
585 	class = add_class (type, val);
586 	if (!class)
587 		error("No memory for class %s.", val);
588 	class->group = clone_group(group, "parse_class_declaration");
589 
590 	if (!parse_lbrace(cfile)) {
591 		free(class->name);
592 		free(class->group);
593 		free(class);
594 		return;
595 	}
596 
597 	do {
598 		token = peek_token(&val, cfile);
599 		if (token == '}') {
600 			token = next_token(&val, cfile);
601 			break;
602 		} else if (token == EOF) {
603 			token = next_token(&val, cfile);
604 			parse_warn("unexpected end of file");
605 			break;
606 		} else {
607 			declaration = parse_statement(cfile, class->group,
608 			    CLASS_DECL, NULL, declaration);
609 		}
610 	} while (1);
611 }
612 
613 /* shared-network-declaration :==
614 			hostname LBRACE declarations parameters RBRACE */
615 
616 void parse_shared_net_declaration(cfile, group)
617 	FILE *cfile;
618 	struct group *group;
619 {
620 	char *val;
621 	int token;
622 	struct shared_network *share;
623 	char *name;
624 	int declaration = 0;
625 
626 	share = calloc(1, sizeof(struct shared_network));
627 	if (!share)
628 		error("No memory for shared subnet");
629 	share->leases = NULL;
630 	share->last_lease = NULL;
631 	share->insertion_point = NULL;
632 	share->next = NULL;
633 	share->interface = NULL;
634 	share->group = clone_group(group, "parse_shared_net_declaration");
635 	share->group->shared_network = share;
636 
637 	/* Get the name of the shared network... */
638 	token = peek_token(&val, cfile);
639 	if (token == TOK_STRING) {
640 		token = next_token(&val, cfile);
641 
642 		if (val[0] == 0) {
643 			parse_warn("zero-length shared network name");
644 			val = "<no-name-given>";
645 		}
646 		name = strdup(val);
647 		if (name == NULL)
648 			error("no memory for shared network name");
649 	} else {
650 		name = parse_host_name(cfile);
651 		if (!name) {
652 			free(share->group);
653 			free(share);
654 			return;
655 		}
656 	}
657 	share->name = name;
658 
659 	if (!parse_lbrace(cfile)) {
660 		free(share->group);
661 		free(share->name);
662 		free(share);
663 		return;
664 	}
665 
666 	do {
667 		token = peek_token(&val, cfile);
668 		if (token == '}') {
669 			token = next_token(&val, cfile);
670 			if (!share->subnets) {
671 				free(share->group);
672 				free(share->name);
673 				free(share);
674 				parse_warn("empty shared-network decl");
675 				return;
676 			}
677 			enter_shared_network(share);
678 			return;
679 		} else if (token == EOF) {
680 			token = next_token(&val, cfile);
681 			parse_warn("unexpected end of file");
682 			break;
683 		}
684 
685 		declaration = parse_statement(cfile, share->group,
686 		    SHARED_NET_DECL, NULL, declaration);
687 	} while (1);
688 }
689 
690 /* subnet-declaration :==
691 	net NETMASK netmask RBRACE parameters declarations LBRACE */
692 
693 void parse_subnet_declaration(cfile, share)
694 	FILE *cfile;
695 	struct shared_network *share;
696 {
697 	char *val;
698 	int token;
699 	struct subnet *subnet, *t, *u;
700 	struct iaddr iaddr;
701 	unsigned char addr[4];
702 	int len = sizeof addr;
703 	int declaration = 0;
704 
705 	subnet = calloc(1, sizeof(struct subnet));
706 	if (!subnet)
707 		error("No memory for new subnet");
708 	subnet->shared_network = share;
709 	subnet->group = clone_group(share->group, "parse_subnet_declaration");
710 	subnet->group->subnet = subnet;
711 
712 	/* Get the network number... */
713 	if (!parse_numeric_aggregate(cfile, addr, &len, '.', 10, 8)) {
714 		free(subnet->group);
715 		free(subnet);
716 		return;
717 	}
718 	memcpy(iaddr.iabuf, addr, len);
719 	iaddr.len = len;
720 	subnet->net = iaddr;
721 
722 	token = next_token(&val, cfile);
723 	if (token != TOK_NETMASK) {
724 		free(subnet->group);
725 		free(subnet);
726 		parse_warn("Expecting netmask");
727 		skip_to_semi(cfile);
728 		return;
729 	}
730 
731 	/* Get the netmask... */
732 	if (!parse_numeric_aggregate(cfile, addr, &len, '.', 10, 8)) {
733 		free(subnet->group);
734 		free(subnet);
735 		return;
736 	}
737 	memcpy(iaddr.iabuf, addr, len);
738 	iaddr.len = len;
739 	subnet->netmask = iaddr;
740 
741 	/* Save only the subnet number. */
742 	subnet->net = subnet_number(subnet->net, subnet->netmask);
743 
744 	enter_subnet(subnet);
745 
746 	if (!parse_lbrace(cfile))
747 		return;
748 
749 	do {
750 		token = peek_token(&val, cfile);
751 		if (token == '}') {
752 			token = next_token(&val, cfile);
753 			break;
754 		} else if (token == EOF) {
755 			token = next_token(&val, cfile);
756 			parse_warn("unexpected end of file");
757 			break;
758 		}
759 		declaration = parse_statement(cfile, subnet->group,
760 		    SUBNET_DECL, NULL, declaration);
761 	} while (1);
762 
763 	/* If this subnet supports dynamic bootp, flag it so in the
764 	   shared_network containing it. */
765 	if (subnet->group->dynamic_bootp)
766 		share->group->dynamic_bootp = 1;
767 
768 	/* Add the subnet to the list of subnets in this shared net. */
769 	if (!share->subnets)
770 		share->subnets = subnet;
771 	else {
772 		u = NULL;
773 		for (t = share->subnets; t; t = t->next_sibling) {
774 			if (subnet_inner_than(subnet, t, 0)) {
775 				if (u)
776 					u->next_sibling = subnet;
777 				else
778 					share->subnets = subnet;
779 				subnet->next_sibling = t;
780 				return;
781 			}
782 			u = t;
783 		}
784 		u->next_sibling = subnet;
785 	}
786 }
787 
788 /* group-declaration :== RBRACE parameters declarations LBRACE */
789 
790 void parse_group_declaration(cfile, group)
791 	FILE *cfile;
792 	struct group *group;
793 {
794 	char *val;
795 	int token;
796 	struct group *g;
797 	int declaration = 0;
798 
799 	g = clone_group(group, "parse_group_declaration");
800 
801 	if (!parse_lbrace(cfile)) {
802 		free(g);
803 		return;
804 	}
805 
806 	do {
807 		token = peek_token(&val, cfile);
808 		if (token == '}') {
809 			token = next_token(&val, cfile);
810 			break;
811 		} else if (token == EOF) {
812 			token = next_token(&val, cfile);
813 			parse_warn("unexpected end of file");
814 			break;
815 		}
816 		declaration = parse_statement(cfile, g, GROUP_DECL, NULL,
817 		    declaration);
818 	} while (1);
819 }
820 
821 /* cidr :== ip-address "/" bit-count
822  * ip-address :== NUMBER [ DOT NUMBER [ DOT NUMBER [ DOT NUMBER ] ] ]
823  * bit-count :== 0..32
824  */
825 int
826 parse_cidr(FILE *cfile, unsigned char *addr, unsigned char *prefix)
827 {
828 	char *val;
829 	int token;
830 	int len = 4;
831 
832 	token = peek_token(&val, cfile);
833 
834 	if (!parse_numeric_aggregate(cfile, addr, &len, '.', 10, 8)) {
835 		parse_warn("Expecting CIDR subnet");
836 		goto nocidr;
837 	}
838 
839 	token = next_token(&val, cfile);
840 	if (token != '/') {
841 		parse_warn("Expecting '/'");
842 		goto nocidr;
843 	}
844 
845 	*prefix = 0;
846 	token = next_token(&val, cfile);
847 	if (token == TOK_NUMBER)
848 		convert_num(prefix, val, 10, 8);
849 
850 	if (token != TOK_NUMBER || *prefix > 32) {
851 		parse_warn("Expecting CIDR prefix length, got '%s'", val);
852 		goto nocidr;
853 	}
854 
855 	return 1;
856 
857 nocidr:
858 	if (token != ';')
859 		skip_to_semi(cfile);
860 	return 0;
861 }
862 
863 /* ip-addr-or-hostname :== ip-address | hostname
864    ip-address :== NUMBER DOT NUMBER DOT NUMBER DOT NUMBER
865 
866    Parse an ip address or a hostname.   If uniform is zero, put in
867    a TREE_LIMIT node to catch hostnames that evaluate to more than
868    one IP address. */
869 
870 struct tree *parse_ip_addr_or_hostname(cfile, uniform)
871 	FILE *cfile;
872 	int uniform;
873 {
874 	char *val;
875 	int token;
876 	unsigned char addr[4];
877 	int len = sizeof addr;
878 	char *name;
879 	struct tree *rv;
880 	struct hostent *h;
881 
882 	token = peek_token(&val, cfile);
883 	if (is_identifier(token)) {
884 		name = parse_host_name(cfile);
885 		if (!name)
886 			return NULL;
887 		h = gethostbyname(name);
888 		if (h == NULL) {
889 			parse_warn("%s (%d): could not resolve hostname",
890 			    val, token);
891 			return NULL;
892 		}
893 		rv = tree_const(h->h_addr_list[0], h->h_length);
894 		if (!uniform)
895 			rv = tree_limit(rv, 4);
896 	} else if (token == TOK_NUMBER) {
897 		if (!parse_numeric_aggregate(cfile, addr, &len, '.', 10, 8))
898 			return NULL;
899 		rv = tree_const(addr, len);
900 	} else {
901 		if (token != '{' && token != '}')
902 			token = next_token(&val, cfile);
903 		parse_warn("%s (%d): expecting IP address or hostname",
904 			    val, token);
905 		if (token != ';')
906 			skip_to_semi(cfile);
907 		return NULL;
908 	}
909 
910 	return rv;
911 }
912 
913 
914 /* fixed-addr-parameter :== ip-addrs-or-hostnames SEMI
915    ip-addrs-or-hostnames :== ip-addr-or-hostname
916 			   | ip-addrs-or-hostnames ip-addr-or-hostname */
917 
918 struct tree_cache *parse_fixed_addr_param(cfile)
919 	FILE *cfile;
920 {
921 	char *val;
922 	int token;
923 	struct tree *tree = NULL;
924 	struct tree *tmp;
925 
926 	do {
927 		tmp = parse_ip_addr_or_hostname(cfile, 0);
928 		if (tree)
929 			tree = tree_concat(tree, tmp);
930 		else
931 			tree = tmp;
932 		token = peek_token(&val, cfile);
933 		if (token == ',')
934 			token = next_token(&val, cfile);
935 	} while (token == ',');
936 
937 	if (!parse_semi(cfile))
938 		return NULL;
939 	return tree_cache(tree);
940 }
941 
942 /* option_parameter :== identifier DOT identifier <syntax> SEMI
943 		      | identifier <syntax> SEMI
944 
945    Option syntax is handled specially through format strings, so it
946    would be painful to come up with BNF for it.   However, it always
947    starts as above and ends in a SEMI. */
948 
949 void parse_option_param(cfile, group)
950 	FILE *cfile;
951 	struct group *group;
952 {
953 	char *val;
954 	int token;
955 	unsigned char buf[4];
956 	unsigned char cprefix;
957 	char *vendor;
958 	char *fmt;
959 	struct universe *universe;
960 	struct option *option;
961 	struct tree *tree = NULL;
962 	struct tree *t;
963 
964 	token = next_token(&val, cfile);
965 	if (!is_identifier(token)) {
966 		parse_warn("expecting identifier after option keyword.");
967 		if (token != ';')
968 			skip_to_semi(cfile);
969 		return;
970 	}
971 	vendor = strdup(val);
972 	if (vendor == NULL)
973 		error("no memory for vendor token.");
974 	token = peek_token(&val, cfile);
975 	if (token == '.') {
976 		/* Go ahead and take the DOT token... */
977 		token = next_token(&val, cfile);
978 
979 		/* The next token should be an identifier... */
980 		token = next_token(&val, cfile);
981 		if (!is_identifier(token)) {
982 			parse_warn("expecting identifier after '.'");
983 			if (token != ';')
984 				skip_to_semi(cfile);
985 			free(vendor);
986 			return;
987 		}
988 
989 		/* Look up the option name hash table for the specified
990 		   vendor. */
991 		universe = ((struct universe *)hash_lookup(&universe_hash,
992 		    (unsigned char *)vendor, 0));
993 		/* If it's not there, we can't parse the rest of the
994 		   declaration. */
995 		if (!universe) {
996 			parse_warn("no vendor named %s.", vendor);
997 			skip_to_semi(cfile);
998 			free(vendor);
999 			return;
1000 		}
1001 	} else {
1002 		/* Use the default hash table, which contains all the
1003 		   standard dhcp option names. */
1004 		val = vendor;
1005 		universe = &dhcp_universe;
1006 	}
1007 
1008 	/* Look up the actual option info... */
1009 	option = (struct option *)hash_lookup(universe->hash,
1010 	    (unsigned char *)val, 0);
1011 
1012 	/* If we didn't get an option structure, it's an undefined option. */
1013 	if (!option) {
1014 		if (val == vendor)
1015 			parse_warn("no option named %s", val);
1016 		else
1017 			parse_warn("no option named %s for vendor %s",
1018 				    val, vendor);
1019 		skip_to_semi(cfile);
1020 		free(vendor);
1021 		return;
1022 	}
1023 
1024 	/* Free the initial identifier token. */
1025 	free(vendor);
1026 
1027 	/* Parse the option data... */
1028 	do {
1029 		/* Set a flag if this is an array of a simple type (i.e.,
1030 		   not an array of pairs of IP addresses, or something
1031 		   like that. */
1032 		int uniform = option->format[1] == 'A';
1033 
1034 		for (fmt = option->format; *fmt; fmt++) {
1035 			if (*fmt == 'A')
1036 				break;
1037 			switch (*fmt) {
1038 			case 'X':
1039 				token = peek_token(&val, cfile);
1040 				if (token == TOK_NUMBER_OR_NAME ||
1041 				    token == TOK_NUMBER) {
1042 					do {
1043 						token = next_token
1044 							(&val, cfile);
1045 						if (token != TOK_NUMBER &&
1046 						    token != TOK_NUMBER_OR_NAME) {
1047 							parse_warn("expecting "
1048 							    "number.");
1049 							if (token != ';')
1050 								skip_to_semi(
1051 								    cfile);
1052 							return;
1053 						}
1054 						convert_num(buf, val, 16, 8);
1055 						tree = tree_concat(tree,
1056 						    tree_const(buf, 1));
1057 						token = peek_token(&val, cfile);
1058 						if (token == ':')
1059 							token = next_token(&val,
1060 							    cfile);
1061 					} while (token == ':');
1062 				} else if (token == TOK_STRING) {
1063 					token = next_token(&val, cfile);
1064 					tree = tree_concat(tree,
1065 					    tree_const((unsigned char *)val,
1066 					    strlen(val)));
1067 				} else {
1068 					parse_warn("expecting string %s.",
1069 					    "or hexadecimal data");
1070 					skip_to_semi(cfile);
1071 					return;
1072 				}
1073 				break;
1074 
1075 			case 't': /* Text string... */
1076 				token = next_token(&val, cfile);
1077 				if (token != TOK_STRING
1078 				    && !is_identifier(token)) {
1079 					parse_warn("expecting string.");
1080 					if (token != ';')
1081 						skip_to_semi(cfile);
1082 					return;
1083 				}
1084 				tree = tree_concat(tree,
1085 				    tree_const((unsigned char *)val,
1086 				    strlen(val)));
1087 				break;
1088 
1089 			case 'I': /* IP address or hostname. */
1090 				t = parse_ip_addr_or_hostname(cfile, uniform);
1091 				if (!t)
1092 					return;
1093 				tree = tree_concat(tree, t);
1094 				break;
1095 
1096 			case 'L': /* Unsigned 32-bit integer... */
1097 			case 'l':	/* Signed 32-bit integer... */
1098 				token = next_token(&val, cfile);
1099 				if (token != TOK_NUMBER) {
1100 					parse_warn("expecting number.");
1101 					if (token != ';')
1102 						skip_to_semi(cfile);
1103 					return;
1104 				}
1105 				convert_num(buf, val, 0, 32);
1106 				tree = tree_concat(tree, tree_const(buf, 4));
1107 				break;
1108 			case 's':	/* Signed 16-bit integer. */
1109 			case 'S':	/* Unsigned 16-bit integer. */
1110 				token = next_token(&val, cfile);
1111 				if (token != TOK_NUMBER) {
1112 					parse_warn("expecting number.");
1113 					if (token != ';')
1114 						skip_to_semi(cfile);
1115 					return;
1116 				}
1117 				convert_num(buf, val, 0, 16);
1118 				tree = tree_concat(tree, tree_const(buf, 2));
1119 				break;
1120 			case 'b':	/* Signed 8-bit integer. */
1121 			case 'B':	/* Unsigned 8-bit integer. */
1122 				token = next_token(&val, cfile);
1123 				if (token != TOK_NUMBER) {
1124 					parse_warn("expecting number.");
1125 					if (token != ';')
1126 						skip_to_semi(cfile);
1127 					return;
1128 				}
1129 				convert_num(buf, val, 0, 8);
1130 				tree = tree_concat(tree, tree_const(buf, 1));
1131 				break;
1132 			case 'f': /* Boolean flag. */
1133 				token = next_token(&val, cfile);
1134 				if (!is_identifier(token)) {
1135 					parse_warn("expecting identifier.");
1136 					if (token != ';')
1137 						skip_to_semi(cfile);
1138 					return;
1139 				}
1140 				if (!strcasecmp(val, "true")
1141 				    || !strcasecmp(val, "on"))
1142 					buf[0] = 1;
1143 				else if (!strcasecmp(val, "false")
1144 					 || !strcasecmp(val, "off"))
1145 					buf[0] = 0;
1146 				else {
1147 					parse_warn("expecting boolean.");
1148 					if (token != ';')
1149 						skip_to_semi(cfile);
1150 					return;
1151 				}
1152 				tree = tree_concat(tree, tree_const(buf, 1));
1153 				break;
1154 			case 'C':
1155 				if (!parse_cidr(cfile, buf, &cprefix))
1156 					return;
1157 				tree = tree_concat(tree, tree_const(&cprefix,
1158 				    sizeof(cprefix)));
1159 				if (cprefix > 0)
1160 					tree = tree_concat(tree, tree_const(buf,
1161 					    (cprefix + 7) / 8));
1162 				break;
1163 			default:
1164 				warning("Bad format %c in parse_option_param.",
1165 				    *fmt);
1166 				skip_to_semi(cfile);
1167 				return;
1168 			}
1169 		}
1170 		if (*fmt == 'A') {
1171 			token = peek_token(&val, cfile);
1172 			if (token == ',') {
1173 				token = next_token(&val, cfile);
1174 				continue;
1175 			}
1176 			break;
1177 		}
1178 	} while (*fmt == 'A');
1179 
1180 	token = next_token(&val, cfile);
1181 	if (token != ';') {
1182 		parse_warn("semicolon expected.");
1183 		skip_to_semi(cfile);
1184 		return;
1185 	}
1186 	group->options[option->code] = tree_cache(tree);
1187 }
1188 
1189 /* lease_declaration :== LEASE ip_address LBRACE lease_parameters RBRACE
1190 
1191    lease_parameters :== <nil>
1192 		      | lease_parameter
1193 		      | lease_parameters lease_parameter
1194 
1195    lease_parameter :== STARTS date
1196 		     | ENDS date
1197 		     | TIMESTAMP date
1198 		     | HARDWARE hardware-parameter
1199 		     | UID hex_numbers SEMI
1200 		     | HOSTNAME hostname SEMI
1201 		     | CLIENT_HOSTNAME hostname SEMI
1202 		     | CLASS identifier SEMI
1203 		     | DYNAMIC_BOOTP SEMI */
1204 
1205 struct lease *
1206 parse_lease_declaration(FILE *cfile)
1207 {
1208 	char *val;
1209 	int token;
1210 	unsigned char addr[4];
1211 	int len = sizeof addr;
1212 	int seenmask = 0;
1213 	int seenbit;
1214 	char tbuf[32];
1215 	static struct lease lease;
1216 
1217 	/* Zap the lease structure... */
1218 	memset(&lease, 0, sizeof lease);
1219 
1220 	/* Get the address for which the lease has been issued. */
1221 	if (!parse_numeric_aggregate(cfile, addr, &len, '.', 10, 8))
1222 		return NULL;
1223 	memcpy(lease.ip_addr.iabuf, addr, len);
1224 	lease.ip_addr.len = len;
1225 
1226 	if (!parse_lbrace(cfile))
1227 		return NULL;
1228 
1229 	do {
1230 		token = next_token(&val, cfile);
1231 		if (token == '}')
1232 			break;
1233 		else if (token == EOF) {
1234 			parse_warn("unexpected end of file");
1235 			break;
1236 		}
1237 		strlcpy(tbuf, val, sizeof tbuf);
1238 
1239 		/* Parse any of the times associated with the lease. */
1240 		if (token == TOK_STARTS || token == TOK_ENDS || token == TOK_TIMESTAMP) {
1241 			time_t t;
1242 			t = parse_date(cfile);
1243 			switch (token) {
1244 			case TOK_STARTS:
1245 				seenbit = 1;
1246 				lease.starts = t;
1247 				break;
1248 
1249 			case TOK_ENDS:
1250 				seenbit = 2;
1251 				lease.ends = t;
1252 				break;
1253 
1254 			case TOK_TIMESTAMP:
1255 				seenbit = 4;
1256 				lease.timestamp = t;
1257 				break;
1258 
1259 			default:
1260 				/*NOTREACHED*/
1261 				seenbit = 0;
1262 				break;
1263 			}
1264 		} else {
1265 			switch (token) {
1266 				/* Colon-separated hexadecimal octets... */
1267 			case TOK_UID:
1268 				seenbit = 8;
1269 				token = peek_token(&val, cfile);
1270 				if (token == TOK_STRING) {
1271 					token = next_token(&val, cfile);
1272 					lease.uid_len = strlen(val);
1273 					lease.uid = (unsigned char *)
1274 						malloc(lease.uid_len);
1275 					if (!lease.uid) {
1276 						warning("no space for uid");
1277 						return NULL;
1278 					}
1279 					memcpy(lease.uid, val, lease.uid_len);
1280 					parse_semi(cfile);
1281 				} else {
1282 					lease.uid_len = 0;
1283 					lease.uid =
1284 					    parse_numeric_aggregate(cfile,
1285 					    NULL, &lease.uid_len, ':', 16, 8);
1286 					if (!lease.uid) {
1287 						warning("no space for uid");
1288 						return NULL;
1289 					}
1290 					if (lease.uid_len == 0) {
1291 						lease.uid = NULL;
1292 						parse_warn("zero-length uid");
1293 						seenbit = 0;
1294 						break;
1295 					}
1296 				}
1297 				if (!lease.uid)
1298 					error("No memory for lease uid");
1299 				break;
1300 
1301 			case TOK_CLASS:
1302 				seenbit = 32;
1303 				token = next_token(&val, cfile);
1304 				if (!is_identifier(token)) {
1305 					if (token != ';')
1306 						skip_to_semi(cfile);
1307 					return NULL;
1308 				}
1309 				/* for now, we aren't using this. */
1310 				break;
1311 
1312 			case TOK_HARDWARE:
1313 				seenbit = 64;
1314 				parse_hardware_param(cfile,
1315 				    &lease.hardware_addr);
1316 				break;
1317 
1318 			case TOK_DYNAMIC_BOOTP:
1319 				seenbit = 128;
1320 				lease.flags |= BOOTP_LEASE;
1321 				break;
1322 
1323 			case TOK_ABANDONED:
1324 				seenbit = 256;
1325 				lease.flags |= ABANDONED_LEASE;
1326 				break;
1327 
1328 			case TOK_HOSTNAME:
1329 				seenbit = 512;
1330 				token = peek_token(&val, cfile);
1331 				if (token == TOK_STRING)
1332 					lease.hostname = parse_string(cfile);
1333 				else
1334 					lease.hostname =
1335 					    parse_host_name(cfile);
1336 				if (!lease.hostname) {
1337 					seenbit = 0;
1338 					return NULL;
1339 				}
1340 				break;
1341 
1342 			case TOK_CLIENT_HOSTNAME:
1343 				seenbit = 1024;
1344 				token = peek_token(&val, cfile);
1345 				if (token == TOK_STRING)
1346 					lease.client_hostname =
1347 					    parse_string(cfile);
1348 				else
1349 					lease.client_hostname =
1350 					    parse_host_name(cfile);
1351 				break;
1352 
1353 			default:
1354 				skip_to_semi(cfile);
1355 				seenbit = 0;
1356 				return NULL;
1357 			}
1358 
1359 			if (token != TOK_HARDWARE && token != TOK_STRING) {
1360 				token = next_token(&val, cfile);
1361 				if (token != ';') {
1362 					parse_warn("semicolon expected.");
1363 					skip_to_semi(cfile);
1364 					return NULL;
1365 				}
1366 			}
1367 		}
1368 		if (seenmask & seenbit) {
1369 			parse_warn("Too many %s parameters in lease %s\n",
1370 			    tbuf, piaddr(lease.ip_addr));
1371 		} else
1372 			seenmask |= seenbit;
1373 
1374 	} while (1);
1375 	return &lease;
1376 }
1377 
1378 /*
1379  * address-range-declaration :== ip-address ip-address SEMI
1380  *			       | DYNAMIC_BOOTP ip-address ip-address SEMI
1381  */
1382 void
1383 parse_address_range(FILE *cfile, struct subnet *subnet)
1384 {
1385 	struct iaddr low, high;
1386 	unsigned char addr[4];
1387 	int len = sizeof addr, token, dynamic = 0;
1388 	char *val;
1389 
1390 	if ((token = peek_token(&val, cfile)) == TOK_DYNAMIC_BOOTP) {
1391 		token = next_token(&val, cfile);
1392 		subnet->group->dynamic_bootp = dynamic = 1;
1393 	}
1394 
1395 	/* Get the bottom address in the range... */
1396 	if (!parse_numeric_aggregate(cfile, addr, &len, '.', 10, 8))
1397 		return;
1398 	memcpy(low.iabuf, addr, len);
1399 	low.len = len;
1400 
1401 	/* Only one address? */
1402 	token = peek_token(&val, cfile);
1403 	if (token == ';')
1404 		high = low;
1405 	else {
1406 		/* Get the top address in the range... */
1407 		if (!parse_numeric_aggregate(cfile, addr, &len, '.', 10, 8))
1408 			return;
1409 		memcpy(high.iabuf, addr, len);
1410 		high.len = len;
1411 	}
1412 
1413 	token = next_token(&val, cfile);
1414 	if (token != ';') {
1415 		parse_warn("semicolon expected.");
1416 		skip_to_semi(cfile);
1417 		return;
1418 	}
1419 
1420 	/* Create the new address range... */
1421 	new_address_range(low, high, subnet, dynamic);
1422 }
1423