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