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