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