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