xref: /netbsd-src/external/mpl/dhcp/dist/keama/confparse.c (revision f407d9293b6650aa8c33d6a995f797bb6aaefd90)
1 /*	$NetBSD: confparse.c,v 1.3 2022/04/03 01:10:59 christos Exp $	*/
2 
3 /*
4  * Copyright (C) 2017-2022 Internet Systems Consortium, Inc. ("ISC")
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
16  * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  *
18  *   Internet Systems Consortium, Inc.
19  *   PO Box 360
20  *   Newmarket, NH 03857 USA
21  *   <info@isc.org>
22  *   https://www.isc.org/
23  *
24  */
25 
26 #include <sys/cdefs.h>
27 __RCSID("$NetBSD: confparse.c,v 1.3 2022/04/03 01:10:59 christos Exp $");
28 
29 /* From server/confpars.c */
30 
31 #include "keama.h"
32 
33 #include <sys/errno.h>
34 #include <arpa/inet.h>
35 #include <assert.h>
36 #include <ctype.h>
37 #include <fcntl.h>
38 #include <time.h>
39 #include <stdlib.h>
40 #include <string.h>
41 
42 /* Print failover stuff once */
43 isc_boolean_t failover_once = ISC_TRUE;
44 
45 /* To manage host-reservation-identifiers */
46 isc_boolean_t use_client_id = ISC_FALSE;
47 isc_boolean_t use_flex_id = ISC_FALSE;
48 isc_boolean_t use_hw_address = ISC_FALSE;
49 
50 /* option and relays used for flexible host identifier */
51 const struct option *host_id_option = NULL;
52 int host_id_relays = 0;
53 
54 /* Simple or complex config */
55 unsigned subnet_counter = 0;
56 
57 /* For subclass name generation */
58 unsigned subclass_counter = 0;
59 
60 /* To map reservations to declared subnets */
61 struct subnet {
62 	struct element *subnet;
63 	struct element *share;
64 	struct string *addr;
65 	struct string *mask;
66 	TAILQ_ENTRY(subnet) next;
67 };
68 
69 TAILQ_HEAD(subnets, subnet) known_subnets;
70 
71 /* To map pools to subnets inside a shared-network */
72 struct range {
73 	struct element *pool;
74 	struct element *share;
75 	struct string *low;
76 	TAILQ_ENTRY(range) next;
77 };
78 
79 TAILQ_HEAD(ranges, range) known_ranges;
80 
81 static void post_process_lifetimes(struct parse *);
82 static size_t post_process_reservations(struct parse *);
83 static void post_process_classes(struct parse *);
84 static void post_process_generated_classes(struct parse *);
85 static void check_depend(struct element *, struct element *);
86 static void post_process_option_definitions(struct parse *);
87 static void add_host_reservation_identifiers(struct parse *, const char *);
88 static void add_host_id_option(struct parse *, const struct option *, int);
89 static void subclass_inherit(struct parse *, struct element *,
90 			     struct element *);
91 static void add_match_class(struct parse *, struct element *,
92 			    struct element *);
93 static void option_data_derive(struct parse *, struct handle *,
94 			       struct handle *);
95 static void derive_classes(struct parse *, struct handle *, struct handle *);
96 static isc_boolean_t is_hexa_only(const char *, unsigned len);
97 static void new_network_interface(struct parse *, struct element *);
98 static struct string *addrmask(const struct string *, const struct string *);
99 static struct element *find_match(struct parse *, struct element *,
100 				  isc_boolean_t *);
101 static struct element *find_location(struct element *, struct range *);
102 static int get_prefix_length(const char *, const char *);
103 static struct element *get_class(struct parse *, struct element *);
104 static void concat_classes(struct parse *, struct element *, struct element *);
105 static void generate_class(struct parse *, struct element *, struct element *,
106 			   struct element *);
107 
108 static struct string *CLASS_ALL;
109 static struct string *CLASS_KNOWN;
110 
111 /* Add head config file comments to the DHCP server map */
112 
113 size_t
conf_file_parse(struct parse * cfile)114 conf_file_parse(struct parse *cfile)
115 {
116 	struct element *top;
117 	struct element *dhcp;
118 	size_t issues;
119 
120 	TAILQ_INIT(&known_subnets);
121 	TAILQ_INIT(&known_ranges);
122 	CLASS_ALL = makeString(-1, "ALL");
123 	CLASS_KNOWN = makeString(-1, "KNOWN");
124 
125 	top = createMap();
126 	top->kind = TOPLEVEL;
127 	TAILQ_CONCAT(&top->comments, &cfile->comments);
128 
129 	dhcp = createMap();
130 	dhcp->kind = ROOT_GROUP;
131 	(void) peek_token(NULL, NULL, cfile);
132 	TAILQ_CONCAT(&dhcp->comments, &cfile->comments);
133 	stackPush(cfile, dhcp);
134 	assert(cfile->stack_top == 1);
135 	cfile->stack[0] = top;
136 
137 	if (local_family == AF_INET)
138 		mapSet(top, dhcp, "Dhcp4");
139 	else if (local_family == AF_INET6)
140 		mapSet(top, dhcp, "Dhcp6");
141 	else
142 		parse_error(cfile, "address family is not set");
143 
144 	issues = conf_file_subparse(cfile, ROOT_GROUP);
145 
146 	/* Add a warning when interfaces-config is not present */
147 	if (subnet_counter > 0) {
148 		struct element *ifconf;
149 
150 		ifconf = mapGet(cfile->stack[1], "interfaces-config");
151 		if (ifconf == NULL) {
152 			struct comment *comment;
153 
154 			comment = createComment("/// This configuration "
155 						"declares some subnets but "
156 						"has no interfaces-config");
157 			TAILQ_INSERT_TAIL(&cfile->stack[1]->comments, comment);
158 			comment = createComment("/// Reference Kea #245");
159 			TAILQ_INSERT_TAIL(&cfile->stack[1]->comments, comment);
160 		}
161 	}
162 
163 	post_process_lifetimes(cfile);
164 	if (!global_hr)
165 		issues += post_process_reservations(cfile);
166 	post_process_classes(cfile);
167 	post_process_generated_classes(cfile);
168 	post_process_option_definitions(cfile);
169 
170 	return issues;
171 }
172 
173 /* Lifetime post-processing */
174 static void
post_process_lifetimes(struct parse * cfile)175 post_process_lifetimes(struct parse *cfile)
176 {
177 	struct element *entry;
178 
179 	entry = mapGet(cfile->stack[1], "valid-lifetime");
180 	if ((entry == NULL) && use_isc_lifetimes) {
181 		struct comment *comment;
182 
183 		/* DEFAULT_DEFAULT_LEASE_TIME is 43200 */
184 		entry = createInt(43200);
185 		comment = createComment("/// Use ISC DHCP default lifetime");
186 		TAILQ_INSERT_TAIL(&entry->comments, comment);
187 		mapSet(cfile->stack[1], entry, "valid-lifetime");
188 	}
189 
190 	entry = mapGet(cfile->stack[1], "min-valid-lifetime");
191 	if ((entry == NULL) && use_isc_lifetimes) {
192 		struct comment *comment;
193 
194 		/* DEFAULT_MIN_LEASE_TIME is 300 */
195 		entry = createInt(300);
196 		comment = createComment("/// Use ISC DHCP min lifetime");
197 		TAILQ_INSERT_TAIL(&entry->comments, comment);
198 		mapSet(cfile->stack[1], entry, "min-valid-lifetime");
199 	}
200 
201 	entry = mapGet(cfile->stack[1], "max-valid-lifetime");
202 	if ((entry == NULL) && use_isc_lifetimes) {
203 		struct comment *comment;
204 
205 		/* DEFAULT_MAX_LEASE_TIME is 86400 */
206 		entry = createInt(86400);
207 		comment = createComment("/// Use ISC DHCP max lifetime");
208 		TAILQ_INSERT_TAIL(&entry->comments, comment);
209 		mapSet(cfile->stack[1], entry, "max-valid-lifetime");
210 	}
211 
212 	/* Done for DHCPv4 */
213 	if (local_family == AF_INET)
214 		return;
215 
216 	/* There is no builtin default for preferred-lifetime,
217 	   nor min/max values in ISC DHCP. */
218 }
219 
220 /* Reservation post-processing */
221 
222 static size_t
post_process_reservations(struct parse * cfile)223 post_process_reservations(struct parse *cfile)
224 {
225 	struct element *hosts;
226 	struct element *orphans;
227 	struct element *host;
228 	struct element *where;
229 	struct element *dest;
230 	isc_boolean_t used_heuristic;
231 	size_t issues;
232 
233 	issues = 0;
234 	hosts = mapGet(cfile->stack[1], "reservations");
235 	if ((hosts == NULL) || global_hr)
236 		return issues;
237 	mapRemove(cfile->stack[1], "reservations");
238 	orphans = createList();
239 	orphans->kind = HOST_DECL;
240 	while (listSize(hosts) > 0) {
241 		host = listGet(hosts, 0);
242 		listRemove(hosts, 0);
243 		used_heuristic = ISC_FALSE;
244 		where = find_match(cfile, host, &used_heuristic);
245 		if (where == cfile->stack[1])
246 			dest = orphans;
247 		else
248 			dest = mapGet(where, "reservations");
249 		if (dest == NULL) {
250 			dest = createList();
251 			dest->kind = HOST_DECL;
252 			mapSet(where, dest, "reservations");
253 		}
254 		listPush(dest, host);
255 	}
256 	if (listSize(orphans) > 0) {
257 		struct comment *comment;
258 
259 		comment = createComment("/// Orphan reservations");
260 		TAILQ_INSERT_TAIL(&orphans->comments, comment);
261 		comment = createComment("/// Kea reservations are per subnet");
262 		TAILQ_INSERT_TAIL(&orphans->comments, comment);
263 		comment = createComment("/// Reference Kea #231");
264 		TAILQ_INSERT_TAIL(&orphans->comments, comment);
265 		orphans->skip = ISC_TRUE;
266 		issues++;
267 		mapSet(cfile->stack[1], orphans, "reservations");
268 	}
269 	return issues;
270 }
271 
272 /* Cleanup classes */
273 
274 static void
post_process_classes(struct parse * cfile)275 post_process_classes(struct parse *cfile)
276 {
277 	struct element *classes;
278 	struct element *class;
279 	struct element *name;
280 	struct element *entry;
281 	struct element *reduced;
282 	struct string *msg;
283 	struct comment *comment;
284 	isc_boolean_t lose;
285 	size_t i;
286 
287 	classes = mapGet(cfile->stack[1], "client-classes");
288 	if ((classes == NULL) || (listSize(classes) == 0))
289 		return;
290 	for (i = 0; i < listSize(classes); i++) {
291 		class = listGet(classes, i);
292 		if ((class == NULL) || (class->type != ELEMENT_MAP))
293 			parse_error(cfile, "null global class at %i",
294 				    (unsigned)i);
295 		name = mapGet(class, "name");
296 		if ((name == NULL) || (name->type != ELEMENT_STRING))
297 			parse_error(cfile, "global class at %u "
298 				    "without a name", (unsigned)i);
299 		if (!mapContains(class, "super"))
300 			goto cleanup_superclass;
301 
302 		/* cleanup subclass */
303 		mapRemove(class,"super");
304 		entry = mapGet(class, "string");
305 		if (entry != NULL) {
306 			if (entry->type != ELEMENT_STRING)
307 				parse_error(cfile, "subclass %s has "
308 					    "a bad string selector",
309 					    stringValue(name)->content);
310 			msg = makeString(-1, "/// subclass selector ");
311 			appendString(msg, "'");
312 			concatString(msg, stringValue(entry));
313 			appendString(msg, "'");
314 			comment = createComment(msg->content);
315 			TAILQ_INSERT_TAIL(&class->comments, comment);
316 			mapRemove(class, "string");
317 			continue;
318 		}
319 		entry = mapGet(class, "binary");
320 		if (entry == NULL)
321 			parse_error(cfile, "subclass %s has no selector",
322 				    stringValue(name)->content);
323 		msg = makeString(-1, "/// subclass selector 0x");
324 		concatString(msg, stringValue(entry));
325 		comment = createComment(msg->content);
326 		TAILQ_INSERT_TAIL(&class->comments, comment);
327 		mapRemove(class, "binary");
328 
329 	cleanup_superclass:
330 		/* cleanup superclass */
331 		entry = mapGet(class, "spawning");
332 		if (entry == NULL)
333 			goto cleanup_class;
334 		if (entry->type != ELEMENT_BOOLEAN)
335 			parse_error(cfile, "superclass %s has bad "
336 				    "spawning flag",
337 				    stringValue(name)->content);
338 		if (boolValue(entry)) {
339 			msg = makeString(-1, "/// Spawning classes "
340 					 "are not supported by Kea");
341 			comment = createComment(msg->content);
342 			TAILQ_INSERT_TAIL(&class->comments, comment);
343 			msg = makeString(-1, "/// Reference Kea #248");
344 			comment = createComment(msg->content);
345 			TAILQ_INSERT_TAIL(&class->comments, comment);
346 			msg = makeString(-1, "/// spawn with: ");
347 		} else
348 			msg = makeString(-1, "/// match: ");
349 		entry = mapGet(class, "submatch");
350 
351 		if (entry == NULL)
352 			parse_error(cfile, "superclass %s has no submatch",
353 				    stringValue(name)->content);
354 		lose = ISC_FALSE;
355 		appendString(msg, print_data_expression(entry, &lose));
356 		if (!lose) {
357 			comment = createComment(msg->content);
358 			TAILQ_INSERT_TAIL(&class->comments, comment);
359 			mapRemove(class, "spawning");
360 			mapRemove(class, "submatch");
361 		}
362 
363 	cleanup_class:
364 		/* cleanup class */
365 		entry = mapGet(class, "match-if");
366 		if (entry == NULL)
367 			continue;
368 		reduced = mapGet(class, "test");
369 		lose = ISC_FALSE;
370 		if (reduced != NULL)
371 			msg = makeString(-1, "/// from: match if ");
372 		else
373 			msg = makeString(-1, "/// match if ");
374 		appendString(msg, print_boolean_expression(entry, &lose));
375 		if (!lose) {
376 			comment = createComment(msg->content);
377 			if (reduced != NULL) {
378 				TAILQ_INSERT_TAIL(&reduced->comments, comment);
379 				mapRemove(class, "match-if");
380 				continue;
381 			}
382 			TAILQ_INSERT_TAIL(&entry->comments, comment);
383 		}
384 	}
385 }
386 
387 /* Move generated client classes to the end of client class list */
388 
389 static void
post_process_generated_classes(struct parse * cfile)390 post_process_generated_classes(struct parse *cfile)
391 {
392 	struct element *generated;
393 	struct element *classes;
394 	struct element *class;
395 
396 	generated = mapGet(cfile->stack[1], "generated-classes");
397 	if (generated == NULL)
398 		return;
399 	mapRemove(cfile->stack[1], "generated-classes");
400 	if (listSize(generated) == 0)
401 		return;
402 	classes = mapGet(cfile->stack[1], "client-classes");
403 	if (classes == NULL) {
404 		classes = createList();
405 		mapSet(cfile->stack[1], classes, "client-classes");
406 	}
407 
408 	while (listSize(generated) > 0) {
409 		class = listGet(generated, 0);
410 		listRemove(generated, 0);
411 		check_depend(class, classes);
412 		listPush(classes, class);
413 	}
414 }
415 
416 static void
check_depend(struct element * class,struct element * classes)417 check_depend(struct element *class, struct element *classes)
418 {
419 	struct element *list;
420 
421 	if (!mapContains(class, "depend"))
422 		return;
423 	list = mapGet(class, "depend");
424 	mapRemove(class, "depend");
425 	while (listSize(list) > 0) {
426 		struct element *depend;
427 		struct string *dname;
428 		struct string *msg;
429 		struct comment *comment;
430 		isc_boolean_t found;
431 		size_t i;
432 
433 		depend = listGet(list, 0);
434 		listRemove(list, 0);
435 		assert(depend != NULL);
436 		assert(depend->type == ELEMENT_STRING);
437 		dname = stringValue(depend);
438 		if (eqString(dname, CLASS_ALL) ||
439 		    eqString(dname, CLASS_KNOWN))
440 			continue;
441 		found = ISC_FALSE;
442 		for (i = 0; i < listSize(classes); i++) {
443 			struct element *item;
444 			struct element *name;
445 
446 			item = listGet(classes, i);
447 			assert(item != NULL);
448 			assert(item->type == ELEMENT_MAP);
449 			name = mapGet(item, "name");
450 			if (name == NULL)
451 				continue;
452 			assert(name->type == ELEMENT_STRING);
453 			if (eqString(stringValue(name), dname)) {
454 				found = ISC_TRUE;
455 				break;
456 			}
457 		}
458 		if (found)
459 			continue;
460 		msg = makeString(-1, "/// Depend on missing '");
461 		concatString(msg, dname);
462 		appendString(msg, "' class");
463 		comment = createComment(msg->content);
464 		TAILQ_INSERT_TAIL(&class->comments, comment);
465 		class->skip = ISC_TRUE;
466 	}
467 }
468 
469 static void
post_process_option_definitions(struct parse * cfile)470 post_process_option_definitions(struct parse *cfile)
471 {
472 	struct element *opt_def;
473 	struct element *def, *ndef;
474 
475 	opt_def = mapGet(cfile->stack[1], "option-def");
476 	if (opt_def == NULL)
477 		return;
478 	TAILQ_FOREACH_SAFE(def, &opt_def->value.list_value, ndef) {
479 		if (mapContains(def, "no-export"))
480 			TAILQ_REMOVE(&opt_def->value.list_value, def);
481 	}
482 }
483 
484 void
read_conf_file(struct parse * parent,const char * filename,int group_type)485 read_conf_file(struct parse *parent, const char *filename, int group_type)
486 {
487 	int file;
488 	struct parse *cfile;
489 	struct string *msg;
490 	struct comment *comment;
491 	size_t amount = parent->stack_size * sizeof(struct element *);
492 
493 	if ((file = open (filename, O_RDONLY)) < 0)
494 
495 		parse_error(parent, "Can't open %s: %s",
496 			    filename, strerror(errno));
497 
498 	cfile = new_parse(file, NULL, 0, filename, 0);
499 	if (cfile == NULL)
500 		parse_error(parent, "Can't create new parse structure");
501 
502 	cfile->stack = (struct element **)malloc(amount);
503 	if (cfile->stack == NULL)
504 		parse_error(parent, "Can't create new element stack");
505 	memcpy(cfile->stack, parent->stack, amount);
506 	cfile->stack_size = parent->stack_size;
507 	cfile->stack_top = parent->stack_top;
508 	cfile->issue_counter = parent->issue_counter;
509 
510 	msg = makeString(-1, "/// Begin file ");
511 	concatString(msg, makeString(-1, filename));
512 	comment = createComment(msg->content);
513 	TAILQ_INSERT_TAIL(&cfile->comments, comment);
514 
515 	conf_file_subparse(cfile, group_type);
516 
517 	amount = cfile->stack_size * sizeof(struct element *);
518 	if (cfile->stack_size > parent->stack_size) {
519 		parent->stack =
520 			(struct element **)realloc(parent->stack, amount);
521 		if (parent->stack == NULL)
522 			parse_error(cfile, "can't resize element stack");
523 	}
524 	memcpy(parent->stack, cfile->stack, amount);
525 	parent->stack_size = cfile->stack_size;
526 	parent->stack_top = cfile->stack_top;
527 	parent->issue_counter = cfile->issue_counter;
528 	msg = makeString(-1, "/// End file ");
529 	concatString(msg, makeString(-1, filename));
530 	comment= createComment(msg->content);
531 	TAILQ_INSERT_TAIL(&parent->comments, comment);
532 	end_parse(cfile);
533 }
534 
535 /* conf-file :== parameters declarations END_OF_FILE
536    parameters :== <nil> | parameter | parameters parameter
537    declarations :== <nil> | declaration | declarations declaration */
538 
539 size_t
conf_file_subparse(struct parse * cfile,int type)540 conf_file_subparse(struct parse *cfile, int type)
541 {
542 	const char *val;
543 	enum dhcp_token token;
544 	isc_boolean_t declaration = ISC_FALSE;
545 
546 	for (;;) {
547 		token = peek_token(&val, NULL, cfile);
548 		if (token == END_OF_FILE)
549 			break;
550 		declaration = parse_statement(cfile, type, declaration);
551 	}
552 	skip_token(&val, NULL, cfile);
553 
554 	return cfile->issue_counter;
555 }
556 
557 /* statement :== parameter | declaration | PERCENT directive
558 
559    parameter :== DEFAULT_LEASE_TIME lease_time
560 	       | MAX_LEASE_TIME lease_time
561 	       | DYNAMIC_BOOTP_LEASE_CUTOFF date
562 	       | DYNAMIC_BOOTP_LEASE_LENGTH lease_time
563 	       | BOOT_UNKNOWN_CLIENTS boolean
564 	       | ONE_LEASE_PER_CLIENT boolean
565 	       | GET_LEASE_HOSTNAMES boolean
566 	       | USE_HOST_DECL_NAME boolean
567 	       | NEXT_SERVER ip-addr-or-hostname SEMI
568 	       | option_parameter
569 	       | SERVER-IDENTIFIER ip-addr-or-hostname SEMI
570 	       | FILENAME string-parameter
571 	       | SERVER_NAME string-parameter
572 	       | hardware-parameter
573 	       | fixed-address-parameter
574 	       | ALLOW allow-deny-keyword
575 	       | DENY allow-deny-keyword
576 	       | USE_LEASE_ADDR_FOR_DEFAULT_ROUTE boolean
577 	       | AUTHORITATIVE
578 	       | NOT AUTHORITATIVE
579 
580    declaration :== host-declaration
581 		 | group-declaration
582 		 | shared-network-declaration
583 		 | subnet-declaration
584 		 | VENDOR_CLASS class-declaration
585 		 | USER_CLASS class-declaration
586 		 | RANGE address-range-declaration */
587 
588 isc_boolean_t
parse_statement(struct parse * cfile,int type,isc_boolean_t declaration)589 parse_statement(struct parse *cfile, int type, isc_boolean_t declaration)
590 {
591 	enum dhcp_token token;
592 	const char *val;
593 	struct element *hardware;
594 	struct element *cache;
595 	struct element *et;
596 	isc_boolean_t lose;
597 	isc_boolean_t known;
598 	isc_boolean_t authoritative;
599 	struct option *option;
600 	size_t host_decl = 0;
601 	size_t subnet = 0;
602 	size_t i;
603 
604 	token = peek_token(&val, NULL, cfile);
605 
606 	switch (token) {
607 	case INCLUDE:
608 		skip_token(&val, NULL, cfile);
609 		token = next_token(&val, NULL, cfile);
610 		if (token != STRING)
611 			parse_error(cfile, "filename string expected.");
612 		read_conf_file(cfile, val, type);
613 		parse_semi(cfile);
614 		return 1;
615 
616 	case HOST:
617 		skip_token(&val, NULL, cfile);
618 		if (type != HOST_DECL && type != CLASS_DECL)
619 			parse_host_declaration(cfile);
620 		else
621 			parse_error(cfile,
622 				    "host declarations not allowed here.");
623 		return 1;
624 
625 	case GROUP:
626 		skip_token(&val, NULL, cfile);
627 		if (type != HOST_DECL && type != CLASS_DECL)
628 			parse_group_declaration(cfile);
629 		else
630 			parse_error(cfile,
631 				    "group declarations not allowed here.");
632 		return 1;
633 
634 	case SHARED_NETWORK:
635 		skip_token(&val, NULL, cfile);
636 		if (type == SHARED_NET_DECL ||
637 		    type == HOST_DECL ||
638 		    type == SUBNET_DECL ||
639 		    type == CLASS_DECL)
640 			parse_error(cfile, "shared-network parameters not %s.",
641 				    "allowed here");
642 		parse_shared_net_declaration(cfile);
643 		return 1;
644 
645 	case SUBNET:
646 	case SUBNET6:
647 		skip_token(&val, NULL, cfile);
648 		if (type == HOST_DECL || type == SUBNET_DECL ||
649 		    type == CLASS_DECL)
650 			parse_error(cfile,
651 				    "subnet declarations not allowed here.");
652 
653 		if (token == SUBNET)
654 			parse_subnet_declaration(cfile);
655 		else
656 			parse_subnet6_declaration(cfile);
657 		return 1;
658 
659 	case VENDOR_CLASS:
660 	case USER_CLASS:
661 	case CLASS:
662 	case SUBCLASS:
663 		skip_token(&val, NULL, cfile);
664 		if (token == VENDOR_CLASS)
665 			parse_error(cfile, "obsolete 'vendor-class' "
666 				    "declaration");
667 		if (token == USER_CLASS)
668 			parse_error(cfile, "obsolete 'user-class' "
669 				    "declaration");
670 		if (type == CLASS_DECL)
671 			parse_error(cfile,
672 				    "class declarations not allowed here.");
673 		parse_class_declaration(cfile, token == CLASS
674 					       ? CLASS_TYPE_CLASS
675 					       : CLASS_TYPE_SUBCLASS);
676 		return 1;
677 
678 	case HARDWARE:
679 		if (!use_hw_address) {
680 			add_host_reservation_identifiers(cfile,
681 							 "hw-address");
682 			use_hw_address = ISC_TRUE;
683 		}
684 
685 		skip_token(&val, NULL, cfile);
686 		if (!host_decl) {
687 			for (i = cfile->stack_top; i > 0; --i) {
688 				if (cfile->stack[i]->kind == HOST_DECL) {
689 					host_decl = i;
690 					break;
691 				}
692 			}
693 		}
694 		if (!host_decl)
695 			parse_error(cfile, "hardware address parameter %s",
696 				    "not allowed here.");
697 		if (mapContains(cfile->stack[host_decl], "hw-address"))
698 			parse_error(cfile, "Host hardware address already "
699 				    "configured.");
700 		hardware = parse_hardware_param(cfile);
701 		mapSet(cfile->stack[host_decl], hardware, "hw-address");
702 		if (hardware->skip)
703 			cfile->stack[host_decl]->skip = ISC_TRUE;
704 		break;
705 
706 	case FIXED_ADDR:
707 	case FIXED_ADDR6:
708 		skip_token(&val, NULL, cfile);
709 		if (!host_decl) {
710 			for (i = cfile->stack_top; i > 0; --i) {
711 				if (cfile->stack[i]->kind == HOST_DECL) {
712 					host_decl = i;
713 					break;
714 				}
715 			}
716 		}
717 		if (!host_decl)
718 			parse_error(cfile,
719 				   "fixed-address parameter not "
720 				   "allowed here.");
721 		cache = parse_fixed_addr_param(cfile, token);
722 		if (token == FIXED_ADDR) {
723 			struct element *addr;
724 
725 			if (mapContains(cfile->stack[host_decl], "ip-address"))
726 				parse_error(cfile, "Only one fixed address "
727 					    "declaration per host.");
728 			addr = listGet(cache, 0);
729 			listRemove(cache, 0);
730 			mapSet(cfile->stack[host_decl], addr, "ip-address");
731 			if (listSize(cache) > 0) {
732 				cache->skip = ISC_TRUE;
733 				cfile->issue_counter++;
734 				mapSet(cfile->stack[host_decl],
735 				       cache, "extra-ip-addresses");
736 			}
737 		} else {
738 			if (mapContains(cfile->stack[host_decl],
739 					"ip-addresses"))
740 				parse_error(cfile, "Only one fixed address "
741 					    "declaration per host.");
742 			mapSet(cfile->stack[host_decl], cache, "ip-addresses");
743 		}
744 		break;
745 
746 	case POOL:
747 		skip_token(&val, NULL, cfile);
748 		if (type == POOL_DECL)
749 			parse_error(cfile, "pool declared within pool.");
750 		if (type != SUBNET_DECL && type != SHARED_NET_DECL)
751 			parse_error(cfile, "pool declared outside of network");
752 		parse_pool_statement(cfile, type);
753 
754 		return declaration;
755 
756 	case RANGE:
757 		skip_token(&val, NULL, cfile);
758 		if (!subnet) {
759 			for (i = cfile->stack_top; i > 0; --i) {
760 				if (cfile->stack[i]->kind == SUBNET_DECL) {
761 					subnet = i;
762 					break;
763 				}
764 			}
765 		}
766 		if (type != SUBNET_DECL || !subnet)
767 			parse_error(cfile,
768 				    "range declaration not allowed here.");
769 		parse_address_range(cfile, type, subnet);
770 		return declaration;
771 
772 	case RANGE6:
773 		if (local_family != AF_INET6)
774 			goto unknown;
775 		skip_token(NULL, NULL, cfile);
776 		if (!subnet) {
777 			for (i = cfile->stack_top; i > 0; --i) {
778 				if (cfile->stack[i]->kind == SUBNET_DECL) {
779 					subnet = i;
780 					break;
781 				}
782 			}
783 		}
784 	        if ((type != SUBNET_DECL) || !subnet)
785 			parse_error(cfile,
786 				    "range6 declaration not allowed here.");
787 	      	parse_address_range6(cfile, type, subnet);
788 		return declaration;
789 
790 	case PREFIX6:
791 		if (local_family != AF_INET6)
792 			goto unknown;
793 		skip_token(NULL, NULL, cfile);
794 		if (!subnet) {
795 			for (i = cfile->stack_top; i > 0; --i) {
796 				if (cfile->stack[i]->kind == SUBNET_DECL) {
797 					subnet = i;
798 					break;
799 				}
800 			}
801 		}
802 		if ((type != SUBNET_DECL) || !subnet)
803 			parse_error(cfile,
804 				    "prefix6 declaration not allowed here.");
805 	      	parse_prefix6(cfile, type, subnet);
806 		return declaration;
807 
808 	case FIXED_PREFIX6:
809 		if (local_family != AF_INET6)
810 			goto unknown;
811 		skip_token(&val, NULL, cfile);
812 		if (!host_decl) {
813 			for (i = cfile->stack_top; i > 0; --i) {
814 				if (cfile->stack[i]->kind == HOST_DECL) {
815 					host_decl = i;
816 					break;
817 				}
818 			}
819 		}
820 		if (!host_decl)
821 			parse_error(cfile,
822 				    "fixed-prefix6 declaration not "
823 				    "allowed here.");
824 		parse_fixed_prefix6(cfile, host_decl);
825 		break;
826 
827 	case POOL6:
828 		if (local_family != AF_INET6)
829 			goto unknown;
830 		skip_token(&val, NULL, cfile);
831 		if (type == POOL_DECL)
832 			parse_error(cfile, "pool6 declared within pool.");
833 		if (type != SUBNET_DECL)
834 			parse_error(cfile,
835 				    "pool6 declared outside of network");
836 		parse_pool6_statement(cfile, type);
837 
838 		return declaration;
839 
840 	case TOKEN_NOT:
841 		skip_token(&val, NULL, cfile);
842 		token = next_token(&val, NULL, cfile);
843 		switch (token) {
844 		case AUTHORITATIVE:
845 			authoritative = ISC_FALSE;
846 			goto authoritative;
847 		default:
848 			parse_error(cfile, "expecting assertion");
849 		}
850 		break;
851 
852 	case AUTHORITATIVE:
853 		skip_token(&val, NULL, cfile);
854 		authoritative = ISC_TRUE;
855 	authoritative:
856 		if (type == HOST_DECL)
857 			parse_error(cfile, "authority makes no sense here.");
858 		if (!subnet) {
859 			for (i = cfile->stack_top; i > 0; --i) {
860 				int kind;
861 
862 				kind = cfile->stack[i]->kind;
863 				if ((kind == SUBNET_DECL) ||
864 				    (kind == SHARED_NET_DECL) ||
865 				    (kind == ROOT_GROUP)) {
866 					subnet = i;
867 					break;
868 				}
869 			}
870 		}
871 		if (!subnet)
872 			parse_error(cfile, "can't find root group");
873 		if (local_family == AF_INET) {
874 			cache = createBool(authoritative);
875 			TAILQ_CONCAT(&cache->comments, &cfile->comments);
876 			mapSet(cfile->stack[subnet], cache, "authoritative");
877 		}
878 		parse_semi(cfile);
879 		break;
880 
881 		/* "server-identifier" is a special hack, equivalent to
882 		   "option dhcp-server-identifier". */
883 	case SERVER_IDENTIFIER:
884 		option = option_lookup_code("dhcp",
885 					    DHO_DHCP_SERVER_IDENTIFIER);
886 		assert(option);
887 		skip_token(&val, NULL, cfile);
888 		goto finish_option;
889 
890 	case OPTION:
891 		skip_token(&val, NULL, cfile);
892 		token = peek_token(&val, NULL, cfile);
893 		if (token == SPACE) {
894 			if (type != ROOT_GROUP)
895 				parse_error(cfile,
896 					    "option space definitions %s",
897 					    "may not be scoped.");
898 			parse_option_space_decl(cfile);
899 			return declaration;
900 		}
901 
902 		known = ISC_FALSE;
903 		option = parse_option_name(cfile, ISC_TRUE, &known);
904 		token = peek_token(&val, NULL, cfile);
905 		if (token == CODE) {
906 			if (type != ROOT_GROUP)
907 				parse_error(cfile,
908 					    "option definitions%s",
909 					    " may not be scoped.");
910 			skip_token(&val, NULL, cfile);
911 
912 			/* next function must deal with redefinitions */
913 			parse_option_code_definition(cfile, option);
914 			return declaration;
915 		}
916 		/* If this wasn't an option code definition, don't
917 		   allow an unknown option. */
918 		if (!known)
919 			parse_error(cfile, "unknown option %s.%s",
920 				    option->space->old, option->old);
921 	finish_option:
922 		parse_option_statement(NULL, cfile, option,
923 				       supersede_option_statement);
924 		return declaration;
925 
926 	case FAILOVER:
927 		if (failover_once)
928 			fprintf(stderr, "ignoring failover\n");
929 		failover_once = ISC_FALSE;
930 		skip_to_semi(cfile);
931 		break;
932 
933 	case SERVER_DUID:
934 		if (local_family != AF_INET6)
935 			goto unknown;
936 		parse_server_duid_conf(cfile);
937 		break;
938 
939 	case LEASE_ID_FORMAT:
940 		token = next_token(&val, NULL, cfile);
941 		/* ignore: ISC DHCP specific */
942 		break;
943 
944 	case PERCENT:
945 		skip_token(&val, NULL, cfile);
946 		if (type != ROOT_GROUP)
947 			parse_error(cfile, "directives are only supported "
948 				    "at toplevel");
949 		parse_directive(cfile);
950 		return declaration;
951 
952 	unknown:
953 		skip_token(&val, NULL, cfile);
954 
955 	default:
956 		et = createMap();
957 		TAILQ_CONCAT(&et->comments, &cfile->comments);
958 		lose = ISC_FALSE;
959 		if (!parse_executable_statement(et, cfile, &lose,
960 						context_any, ISC_TRUE)) {
961 			if (!lose) {
962 				if (declaration)
963 					parse_error(cfile,
964 						    "expecting a declaration");
965 				else
966 					parse_error(cfile,
967 						    "expecting a parameter %s",
968 						    "or declaration");
969 			}
970 			return declaration;
971 		}
972 		if (mapSize(et) == 0)
973 			return declaration;
974 
975 		et->skip = ISC_TRUE;
976 		cfile->issue_counter++;
977 		mapSet(cfile->stack[cfile->stack_top], et, "statement");
978 	}
979 
980 	return 0;
981 }
982 
983 /*!
984  *
985  * \brief Parse allow and deny statements
986  *
987  * This function handles the common processing code for permit and deny
988  * statements in the parse_pool_statement and parse_pool6_statement functions.
989  *
990  * The allow or deny token should already be consumed, this function expects
991  * one of the following:
992  *   known-clients;
993  *   unknown-clients;
994  *   known clients;
995  *   unknown clients;
996  *   authenticated clients;
997  *   unauthenticated clients;
998  *   all clients;
999  *   dynamic bootp clients;
1000  *   members of <class name>;
1001  *   after <date>;
1002  *
1003  * \param[in] cfile       = the configuration file being parsed
1004  * \param[in] permit_head = the head of the permit list (permit or prohibit)
1005  *			    to which to attach the newly created  permit structure
1006  */
1007 
1008 void
get_permit(struct parse * cfile,struct element * permit_head)1009 get_permit(struct parse *cfile, struct element *permit_head)
1010 {
1011 	enum dhcp_token token;
1012 	const char *val;
1013 	struct string *permit;
1014 	struct string *alias = NULL;
1015 	struct comment *comment = NULL;
1016 	struct element *member;
1017 	isc_boolean_t need_clients = ISC_TRUE;
1018 	isc_boolean_t negative = ISC_FALSE;
1019 
1020 	token = next_token(&val, NULL, cfile);
1021 	switch (token) {
1022 	case UNKNOWN:
1023 		permit = CLASS_KNOWN;
1024 		negative = ISC_TRUE;
1025 		alias = makeString(-1, "unknown clients");
1026 		break;
1027 
1028 	case KNOWN_CLIENTS:
1029 		need_clients = ISC_FALSE;
1030 		permit = CLASS_KNOWN;
1031 		alias = makeString(-1, "known-clients");
1032 		break;
1033 
1034 	case UNKNOWN_CLIENTS:
1035 		need_clients = ISC_FALSE;
1036 		permit = CLASS_KNOWN;
1037 		negative = ISC_TRUE;
1038 		alias = makeString(-1, "unknown-clients");
1039 		break;
1040 
1041 	case KNOWN:
1042 		permit = CLASS_KNOWN;
1043 		alias = makeString(-1, "known clients");
1044 		break;
1045 
1046 	case AUTHENTICATED:
1047 		permit = CLASS_ALL;
1048 		alias = makeString(-1, "authenticated clients");
1049 		negative = ISC_TRUE;
1050 	authenticated_clients:
1051 		comment = createComment("/// [un]authenticated-clients is "
1052 					"not supported by ISC DHCP and Kea");
1053 		break;
1054 
1055 	case UNAUTHENTICATED:
1056 		permit = CLASS_ALL;
1057 		alias = makeString(-1, "unauthenticated clients");
1058 		goto authenticated_clients;
1059 		break;
1060 
1061 	case ALL:
1062 		permit = CLASS_ALL;
1063 		alias = makeString(-1, "all clients");
1064 		break;
1065 
1066 	case DYNAMIC:
1067 		/* bootp is not supported by Kea so the dynamic bootp
1068 		 * client set is the empty set. */
1069 		if (next_token(&val, NULL, cfile) != TOKEN_BOOTP)
1070 			parse_error(cfile, "expecting \"bootp\"");
1071 		permit = CLASS_ALL;
1072 		negative = ISC_TRUE;
1073 		alias = makeString(-1, "dynamic bootp clients");
1074 		cfile->issue_counter++;
1075 		comment = createComment("/// dynamic-bootp-client is not "
1076 					"supported by Kea");
1077 		break;
1078 
1079 	case MEMBERS:
1080 		/* we don't check the class... */
1081 		need_clients = ISC_FALSE;
1082 		if (next_token(&val, NULL, cfile) != OF)
1083 			parse_error(cfile, "expecting \"of\"");
1084 		if (next_token(&val, NULL, cfile) != STRING)
1085 			parse_error(cfile, "expecting class name.");
1086 		permit = makeString(-1, val);
1087 		break;
1088 
1089 	case AFTER:
1090 		/* don't use parse_date_code() */
1091 		need_clients = ISC_FALSE;
1092 		permit = makeString(-1, "AFTER_");
1093 		alias = makeString(-1, "after ");
1094 		while (peek_raw_token(NULL, NULL, cfile) != SEMI) {
1095 			next_raw_token(&val, NULL, cfile);
1096 			appendString(permit, val);
1097 			appendString(alias, val);
1098 		}
1099 		permit_head->skip = ISC_TRUE;
1100 		cfile->issue_counter++;
1101 		comment = createComment("/// after <date> is not yet "
1102 					"supported by Kea");
1103 		break;
1104 
1105 	default:
1106 		parse_error(cfile, "expecting permit type.");
1107 	}
1108 
1109 	/*
1110 	 * The need_clients flag is set if we are expecting the
1111 	 * CLIENTS token
1112 	 */
1113 	if (need_clients && (next_token(&val, NULL, cfile) != CLIENTS))
1114 		parse_error(cfile, "expecting \"clients\"");
1115 	member = createMap();
1116 	mapSet(member, createString(permit), "class");
1117 	mapSet(member, createBool(!negative), "way");
1118 	if (alias != NULL)
1119 		mapSet(member, createString(alias), "real");
1120 	if (comment != NULL)
1121 		TAILQ_INSERT_TAIL(&permit_head->comments, comment);
1122 	listPush(permit_head, member);
1123 	parse_semi(cfile);
1124 
1125 	return;
1126 }
1127 
1128 /*!
1129  *
1130  * \brief Parse a pool statement
1131  *
1132  * Pool statements are used to group declarations and permit & deny information
1133  * with a specific address range.  They must be declared within a shared network
1134  * or subnet and there may be multiple pools withing a shared network or subnet.
1135  * Each pool may have a different set of permit or deny options.
1136  *
1137  * \param[in] cfile = the configuration file being parsed
1138  * \param[in] type  = the type of the enclosing statement.  This must be
1139  *		      SHARED_NET_DECL or SUBNET_DECL for this function.
1140  *
1141  * \return
1142  * void - This function either parses the statement and updates the structures
1143  *        or it generates an error message and possible halts the program if
1144  *        it encounters a problem.
1145  */
1146 void
parse_pool_statement(struct parse * cfile,int type)1147 parse_pool_statement(struct parse *cfile, int type)
1148 {
1149 	enum dhcp_token token;
1150 	const char *val;
1151 	isc_boolean_t done = ISC_FALSE;
1152 	struct element *pool;
1153 	struct element *pools;
1154 	struct element *permit;
1155 	struct element *prohibit;
1156 	int declaration = 0;
1157 	unsigned range_counter = 0;
1158 
1159 	pool = createMap();
1160 	pool->kind = POOL_DECL;
1161 	TAILQ_CONCAT(&pool->comments, &cfile->comments);
1162 
1163 	if (type != SUBNET_DECL && type != SHARED_NET_DECL)
1164 		parse_error(cfile, "Dynamic pools are only valid inside "
1165 			    "subnet or shared-network statements.");
1166 	parse_lbrace(cfile);
1167 
1168 	stackPush(cfile, pool);
1169 	type = POOL_DECL;
1170 
1171 	permit = createList();
1172 	prohibit = createList();
1173 
1174 	do {
1175 		token = peek_token(&val, NULL, cfile);
1176 		switch (token) {
1177 		case TOKEN_NO:
1178 		case FAILOVER:
1179 			if (failover_once)
1180 				fprintf(stderr, "ignoring failover\n");
1181 			failover_once = ISC_FALSE;
1182 			skip_to_semi(cfile);
1183 			break;
1184 
1185 		case RANGE:
1186 			skip_token(&val, NULL, cfile);
1187 			parse_address_range(cfile, type, cfile->stack_top);
1188 			range_counter++;
1189 			break;
1190 
1191 		case ALLOW:
1192 			skip_token(&val, NULL, cfile);
1193 			get_permit(cfile, permit);
1194 			break;
1195 
1196 		case DENY:
1197 			skip_token(&val, NULL, cfile);
1198 			get_permit(cfile, prohibit);
1199 			break;
1200 
1201 		case RBRACE:
1202 			skip_token(&val, NULL, cfile);
1203 			done = ISC_TRUE;
1204 			break;
1205 
1206 		case END_OF_FILE:
1207 			/*
1208 			 * We can get to END_OF_FILE if, for instance,
1209 			 * the parse_statement() reads all available tokens
1210 			 * and leaves us at the end.
1211 			 */
1212 			parse_error(cfile, "unexpected end of file");
1213 
1214 		default:
1215 			declaration = parse_statement(cfile, type,
1216 						      declaration);
1217 			break;
1218 		}
1219 	} while (!done);
1220 
1221 	cfile->stack_top--;
1222 
1223 	generate_class(cfile, pool, permit, prohibit);
1224 
1225 	pools = mapGet(cfile->stack[cfile->stack_top], "pools");
1226 	if (pools == NULL) {
1227 		pools = createList();
1228 		pools->kind = POOL_DECL;
1229 		mapSet(cfile->stack[cfile->stack_top], pools, "pools");
1230 	}
1231 	if (range_counter == 0) {
1232 		struct comment *comment;
1233 
1234 		/* no range */
1235 		comment = createComment("empty pool");
1236 		TAILQ_INSERT_TAIL(&pool->comments, comment);
1237 		pool->skip = ISC_TRUE;
1238 		cfile->issue_counter++;
1239 		listPush(pools, pool);
1240 		return;
1241 	}
1242 	/* spread extra ranges into pool copies */
1243 	while (--range_counter != 0) {
1244 		struct handle *handle;
1245 		struct element *first;
1246 		struct element *saved;
1247 		isc_boolean_t seen = ISC_FALSE;
1248 
1249 		first = createMap();
1250 		saved = copy(pool);
1251 		TAILQ_CONCAT(&first->comments, &pool->comments);
1252 		while (mapSize(pool) > 0) {
1253 			handle = mapPop(pool);
1254 			if ((handle == NULL) || (handle->key == NULL) ||
1255 			    (handle->value == NULL))
1256 				parse_error(cfile, "bad pool entry");
1257 			if (strcmp(handle->key, "pool") != 0)
1258 				mapSet(first, handle->value, handle->key);
1259 			else if (!seen) {
1260 				mapSet(first, handle->value, handle->key);
1261 				mapRemove(saved, "pool");
1262 				seen = ISC_TRUE;
1263 			}
1264 		}
1265 		listPush(pools, first);
1266 		pool = saved;
1267 	}
1268 	listPush(pools, pool);
1269 }
1270 
1271 /* Expect a left brace */
1272 
1273 void
parse_lbrace(struct parse * cfile)1274 parse_lbrace(struct parse *cfile)
1275 {
1276 	enum dhcp_token token;
1277 	const char *val;
1278 
1279 	token = next_token(&val, NULL, cfile);
1280 	if (token != LBRACE)
1281 		parse_error(cfile, "expecting left brace.");
1282 }
1283 
1284 /* host-declaration :== hostname RBRACE parameters declarations LBRACE */
1285 
1286 void
parse_host_declaration(struct parse * cfile)1287 parse_host_declaration(struct parse *cfile)
1288 {
1289 	const char *val;
1290 	enum dhcp_token token;
1291 	struct element *host;
1292 	struct string *name;
1293 	struct element *where;
1294 	struct element *hosts = NULL;
1295 	int declaration = 0;
1296 	isc_boolean_t used_heuristic = ISC_FALSE;
1297 
1298 	host = createMap();
1299 	host->kind = HOST_DECL;
1300 	TAILQ_CONCAT(&host->comments, &cfile->comments);
1301 
1302 	name = parse_host_name(cfile);
1303 	if (!name)
1304 		parse_error(cfile, "expecting a name for host declaration.");
1305 
1306 	mapSet(host, createString(name), "hostname");
1307 
1308 	parse_lbrace(cfile);
1309 
1310 	stackPush(cfile, host);
1311 
1312 	for (;;) {
1313 		token = peek_token(&val, NULL, cfile);
1314 		if (token == RBRACE) {
1315 			skip_token(&val, NULL, cfile);
1316 			break;
1317 		}
1318 		if (token == END_OF_FILE)
1319 			parse_error(cfile, "unexpected end of file");
1320 		/* If the host declaration was created by the server,
1321 		   remember to save it. */
1322 		if (token == DYNAMIC) {
1323 			skip_token(&val, NULL, cfile);
1324 			parse_error(cfile, "dynamic hosts don't exist "
1325 				    "in the config file");
1326 		}
1327 		/* If the host declaration was created by the server,
1328 		   remember to save it. */
1329 		if (token == TOKEN_DELETED) {
1330 			skip_token(&val, NULL, cfile);
1331 			parse_error(cfile, "deleted hosts don't exist "
1332 				    "in the config file");
1333 		}
1334 
1335 		if (token == GROUP) {
1336 			struct element *group;
1337 			struct comment *comment;
1338 
1339 			skip_token(&val, NULL, cfile);
1340 			token = next_token(&val, NULL, cfile);
1341 			if (token != STRING && !is_identifier(token))
1342 				parse_error(cfile,
1343 					    "expecting string or identifier.");
1344 			group = createString(makeString(-1, val));
1345 			group->skip = ISC_TRUE;
1346 			cfile->issue_counter++;
1347 			comment = createComment("/// Unsupported group in "
1348 						"host reservations");
1349 			TAILQ_INSERT_TAIL(&group->comments, comment);
1350 			comment = createComment("/// Reference Kea #233");
1351 			TAILQ_INSERT_TAIL(&group->comments, comment);
1352 			mapSet(host, group, "group");
1353 			parse_semi(cfile);
1354 			continue;
1355 		}
1356 
1357 		if (token == UID) {
1358 			struct string *client_id;
1359 
1360 			if (!use_client_id) {
1361 				add_host_reservation_identifiers(cfile,
1362 								 "client-id");
1363 				use_client_id = ISC_TRUE;
1364 			}
1365 
1366 			skip_token(&val, NULL, cfile);
1367 
1368 			if (mapContains(host, "client-id"))
1369 				parse_error(cfile, "Host %s already has a "
1370 						   "client identifier.",
1371 					    name->content);
1372 
1373 			/* See if it's a string or a cshl. */
1374 			token = peek_token(&val, NULL, cfile);
1375 			if (token == STRING) {
1376 				skip_token(&val, NULL, cfile);
1377 				client_id = makeString(-1, val);
1378 			} else {
1379 				struct string *bin;
1380 				unsigned len = 0;
1381 
1382 				bin = parse_numeric_aggregate
1383 					(cfile, NULL, &len, ':', 16, 8);
1384 				if (!bin)
1385 					parse_error(cfile,
1386 						    "expecting hex list.");
1387 				client_id = makeStringExt(bin->length,
1388 							  bin->content, 'H');
1389 			}
1390 			mapSet(host, createString(client_id), "client-id");
1391 
1392 			parse_semi(cfile);
1393 			continue;
1394 		}
1395 
1396 		if (token == HOST_IDENTIFIER) {
1397 			struct string *host_id;
1398 			isc_boolean_t known;
1399 			struct option *option;
1400 			struct element *expr;
1401 			struct string *data;
1402 			int relays = 0;
1403 
1404 			if (!use_flex_id) {
1405 				add_host_reservation_identifiers(cfile,
1406 								 "flex-id");
1407 				use_flex_id = ISC_TRUE;
1408 			}
1409 
1410 			if (mapContains(host, "host-identifier") ||
1411 			    mapContains(host, "flex-id"))
1412 				parse_error(cfile,
1413 					    "only one host-identifier allowed "
1414 					    "per host");
1415 	      		skip_token(&val, NULL, cfile);
1416 			token = next_token(&val, NULL, cfile);
1417 			host_id = makeString(-1, val);
1418 			appendString(host_id, " ");
1419 			if (token == V6RELOPT) {
1420 				token = next_token(&val, NULL, cfile);
1421 
1422 				if (token != NUMBER)
1423 					parse_error(cfile,
1424 						    "host-identifier v6relopt "
1425 						    "must have a number");
1426 				appendString(host_id, val);
1427 				appendString(host_id, " ");
1428 				relays = atoi(val);
1429 				if (relays < 0)
1430 					parse_error(cfile,
1431 						    "host-identifier v6relopt "
1432 						    "must have a number >= 0");
1433 				if (relays > MAX_V6RELAY_HOPS)
1434 					relays = MAX_V6RELAY_HOPS + 1;
1435 			} else if (token != OPTION)
1436 				parse_error(cfile,
1437 					    "host-identifier must be an option"
1438 					    " or v6relopt");
1439 			known = ISC_FALSE;
1440 			option = parse_option_name(cfile, ISC_TRUE, &known);
1441 			if (!known)
1442 				parse_error(cfile, "unknown option %s.%s",
1443 					    option->space->old, option->old);
1444 			appendString(host_id, option->space->name);
1445 			appendString(host_id, ".");
1446 			appendString(host_id, option->name);
1447 			appendString(host_id, " ");
1448 
1449 			data = parse_option_textbin(cfile, option);
1450 			parse_semi(cfile);
1451 
1452 			if (data == NULL)
1453 				parse_error(cfile, "can't get option data");
1454 			concatString(host_id, data);
1455 			expr = createString(host_id);
1456 			expr->skip = ISC_TRUE;
1457 			cfile->issue_counter++;
1458 			mapSet(host, expr, "host-identifier");
1459 
1460 			if (host_id_option == NULL)
1461 				add_host_id_option(cfile, option, relays);
1462 			else if ((host_id_option != option) ||
1463 				 (host_id_relays != relays)) {
1464 				struct string *msg;
1465 				struct comment *comment;
1466 
1467 				msg = allocString();
1468 				appendString(msg, "/// Another option (");
1469 				appendString(msg, host_id_option->name);
1470 				appendString(msg, ") is already used as ");
1471 				appendString(msg, "host-identifier");
1472 				comment = createComment(msg->content);
1473 				TAILQ_INSERT_TAIL(&expr->comments, comment);
1474 				continue;
1475 			}
1476 
1477 			/*
1478 			 * Everything good: set a flex-id and remove
1479 			 * the host-identifier entry.
1480 			 */
1481 			mapSet(host, createString(data), "flex-id");
1482 			mapRemove(host, "host-identifier");
1483 			continue;
1484 		}
1485 
1486 		declaration = parse_statement(cfile, HOST_DECL, declaration);
1487 	}
1488 
1489 	cfile->stack_top--;
1490 
1491 	where = find_match(cfile, host, &used_heuristic);
1492 	hosts = mapGet(where, "reservations");
1493 	if (hosts == NULL) {
1494 		hosts = createList();
1495 		hosts->kind = HOST_DECL;
1496 		mapSet(where, hosts, "reservations");
1497 		if (used_heuristic) {
1498 			struct comment *comment;
1499 
1500 			comment = createComment("/// Host reservations "
1501 						"without fixed addresses "
1502 						"were put in the last "
1503 						"declared subnet");
1504 			TAILQ_INSERT_TAIL(&hosts->comments, comment);
1505 			comment = createComment("/// Reference Kea #231");
1506 			TAILQ_INSERT_TAIL(&hosts->comments, comment);
1507 		}
1508 	}
1509 	listPush(hosts, host);
1510 }
1511 
1512 /* Simple tool to declare used (and only used) reservation identifiers */
1513 static void
add_host_reservation_identifiers(struct parse * cfile,const char * id)1514 add_host_reservation_identifiers(struct parse *cfile, const char *id)
1515 {
1516 	struct element *ids;
1517 
1518 	ids = mapGet(cfile->stack[1], "host-reservation-identifiers");
1519 	if (ids == NULL) {
1520 		ids = createList();
1521 		mapSet(cfile->stack[1], ids, "host-reservation-identifiers");
1522 	}
1523 	listPush(ids, createString(makeString(-1, id)));
1524 }
1525 
1526 /* Add the flexible host identifier glue */
1527 static void
add_host_id_option(struct parse * cfile,const struct option * option,int relays)1528 add_host_id_option(struct parse *cfile,
1529 		   const struct option *option, int relays)
1530 {
1531 	struct string *path;
1532 	struct string *expr;
1533 	struct element *params;
1534 	struct element *entry;
1535 	struct element *hooks;
1536 	struct comment *comment;
1537 	char buf[40];
1538 
1539 	host_id_option = option;
1540 	host_id_relays = relays;
1541 
1542 	/*
1543 	 * Using the example from the Kea Administrator Reference Manual
1544 	 * as recommended by Tomek
1545 	 */
1546 	hooks = createList();
1547 	mapSet(cfile->stack[1], hooks, "hooks-libraries");
1548 	comment = createComment("/// The flexible host identifier "
1549 				"is a premium feature");
1550 	TAILQ_INSERT_TAIL(&hooks->comments, comment);
1551 	entry = createMap();
1552 	listPush(hooks, entry);
1553 	if (hook_library_path != NULL)
1554 		path = makeString(-1, hook_library_path);
1555 	else
1556 		path = makeString(-1, "/path/");
1557 	appendString(path, "libdhcp_flex_id.so");
1558 	params = createString(path);
1559 	if (hook_library_path == NULL) {
1560 		comment = createComment("/// Please update the path here");
1561 		TAILQ_INSERT_TAIL(&params->comments, comment);
1562 	}
1563 	mapSet(entry, params, "library");
1564 	params = createMap();
1565 	mapSet(entry, params, "parameters");
1566 
1567 	snprintf(buf, sizeof(buf), "%soption[%u].hex",
1568 		 relays > 0 ? "relay[0]." : "", option->code);
1569 	expr = makeString(-1, buf);
1570 	mapSet(params, createString(expr), "identifier-expression");
1571 }
1572 
1573 static void add_host_reservation_identifiers(struct parse *, const char *);
1574 /* class-declaration :== STRING LBRACE parameters declarations RBRACE
1575  *
1576  * in fact:
1577  * (CLASS) NAME(STRING) LBRACE ... RBRACE
1578  * (SUBCLASS) SUPER(STRING) DATA/HASH(STRING | <hexa>) [BRACE ... RBRACE]
1579  *
1580  * class "name" { MATCH IF <boolean-expr> }: direct: belong when true
1581  * class "name" { MATCH <data-expr> }: indirect: use subclasses
1582  * class "name" { MATCH <data-expr> SPAWN WITH <data-expr> }: indirect:
1583  *  create dynamically a subclass
1584  * subclass "super" <data-expr = string or binary aka hash>: belongs when
1585  *  super <data-expr> == <hash>
1586  */
1587 
1588 void
parse_class_declaration(struct parse * cfile,int type)1589 parse_class_declaration(struct parse *cfile, int type)
1590 {
1591 	const char *val = NULL;
1592 	enum dhcp_token token;
1593 	size_t group = 0;
1594 	size_t i = 0;
1595 	struct element *group_classes = NULL;
1596 	struct element *classes = NULL;
1597 	struct element *class = NULL;
1598 	struct element *pc = NULL; /* p(arent)c(lass) */
1599 	struct element *tmp = NULL;
1600 	struct element *expr = NULL;
1601 	struct element *data = NULL;
1602 	isc_boolean_t binary = ISC_FALSE;
1603 	int declaration = 0;
1604 	struct string *name = NULL;
1605 	isc_boolean_t lose = ISC_FALSE;
1606 	isc_boolean_t matchedonce = ISC_FALSE;
1607 	isc_boolean_t submatchedonce = ISC_FALSE;
1608 
1609 	token = next_token(&val, NULL, cfile);
1610 	if (token != STRING)
1611 		parse_error(cfile, "Expecting class name");
1612 
1613 	/* Find group and root classes */
1614 	classes = mapGet(cfile->stack[1], "client-classes");
1615 	if (classes == NULL) {
1616 		classes = createList();
1617 		classes->kind = CLASS_DECL;
1618 		mapSet(cfile->stack[1], classes, "client-classes");
1619 	}
1620 	for (group = cfile->stack_top; group > 0; --group) {
1621 		int kind;
1622 
1623 		kind = cfile->stack[group]->kind;
1624 		if (kind == CLASS_DECL)
1625 			parse_error(cfile, "class in class");
1626 		if ((kind == GROUP_DECL) || (kind == ROOT_GROUP))
1627 			break;
1628 	}
1629 	if (!group)
1630 		parse_error(cfile, "can't find root group");
1631 	if (cfile->stack[group]->kind == GROUP_DECL) {
1632 		group_classes = mapGet(cfile->stack[group], "client-classes");
1633 		if (group_classes == NULL) {
1634 			group_classes = createList();
1635 			group_classes->kind = CLASS_DECL;
1636 			mapSet(cfile->stack[group], group_classes,
1637 			       "client-classes");
1638 		}
1639 	} else
1640 		group_classes = classes;
1641 
1642 	/* See if there's already a class with the specified name. */
1643 	for (i = 0; i < listSize(classes); i++) {
1644 		struct element *name;
1645 
1646 		tmp = listGet(classes, i);
1647 		name = mapGet(tmp, "name");
1648 		if (name == NULL)
1649 			continue;
1650 		if (strcmp(stringValue(name)->content, val) == 0) {
1651 			pc = tmp;
1652 			break;
1653 		}
1654 	}
1655 
1656 	/* If it is a class, we're updating it.  If it's any of the other
1657 	 * types (subclass, vendor or user class), the named class is a
1658 	 * reference to the parent class so its mandatory.
1659 	 */
1660 	if ((pc != NULL) && (type == CLASS_TYPE_CLASS)) {
1661 		class = pc;
1662 		pc = NULL;
1663 	} else if (type != CLASS_TYPE_CLASS) {
1664 		if (pc == NULL)
1665 			parse_error(cfile, "no class named %s", val);
1666 		if (!mapContains(pc, "spawning") ||
1667 		    !mapContains(pc, "submatch"))
1668 			parse_error(cfile, "found class name %s but it is "
1669 				    "not a suitable superclass", val);
1670 	}
1671 
1672 	name = makeString(-1, val);
1673 	/* If this is a straight subclass, parse the hash string. */
1674 	if (type == CLASS_TYPE_SUBCLASS) {
1675 		token = peek_token(&val, NULL, cfile);
1676 		if (token == STRING) {
1677 			unsigned len;
1678 
1679 			skip_token(&val, &len, cfile);
1680 			data = createString(makeString(len, val));
1681 		} else if (token == NUMBER_OR_NAME || token == NUMBER) {
1682 			data = createHexa(parse_hexa(cfile));
1683 			binary = ISC_TRUE;
1684 		} else {
1685 			skip_token(&val, NULL, cfile);
1686 			parse_error(cfile, "Expecting string or hex list.");
1687 		}
1688 	}
1689 
1690 	/* See if there's already a class in the hash table matching the
1691 	   hash data. */
1692 	if (type != CLASS_TYPE_CLASS) {
1693 		for (i = 0; i < listSize(classes); i++) {
1694 			struct element *super;
1695 			struct element *selector;
1696 
1697 			tmp = listGet(classes, i);
1698 			super = mapGet(tmp, "super");
1699 			if (super == NULL)
1700 				continue;
1701 			if (!eqString(stringValue(super), name))
1702 				continue;
1703 			if (binary)
1704 				selector = mapGet(tmp, "binary");
1705 			else
1706 				selector = mapGet(tmp, "string");
1707 			if (selector == NULL)
1708 				continue;
1709 			if (eqString(stringValue(selector),
1710 				     stringValue(data))) {
1711 				class = tmp;
1712 				break;
1713 			}
1714 		}
1715 	}
1716 
1717 	/* Note the class declaration in the enclosing group */
1718 	if (group_classes != classes) {
1719 		struct element *gc;
1720 
1721 		gc = createMap();
1722 		gc->kind = CLASS_DECL;
1723 		tmp = createString(name);
1724 		if (type == CLASS_TYPE_CLASS)
1725 			mapSet(gc, tmp, "name");
1726 		else {
1727 			tmp->skip = ISC_TRUE;
1728 			mapSet(gc, tmp, "super");
1729 			data->skip = ISC_TRUE;
1730 			if (binary)
1731 				mapSet(gc, data, "binary");
1732 			else
1733 				mapSet(gc, data, "string");
1734 		}
1735 		listPush(group_classes, gc);
1736 	}
1737 
1738 	/* If we didn't find an existing class, allocate a new one. */
1739 	if (!class) {
1740 		/* Allocate the class structure... */
1741 		class = createMap();
1742 		class->kind = CLASS_DECL;
1743 		TAILQ_CONCAT(&class->comments, &cfile->comments);
1744 		if (type == CLASS_TYPE_SUBCLASS) {
1745 			struct string *subname;
1746 			char buf[40];
1747 
1748 			cfile->issue_counter++;
1749 			tmp = createString(name);
1750 			tmp->skip = ISC_TRUE;
1751 			mapSet(class, tmp, "super");
1752 			data->skip = ISC_TRUE;
1753 			if (binary)
1754 				mapSet(class, data, "binary");
1755 			else
1756 				mapSet(class, data, "string");
1757 			subname = makeString(-1, "sub#");
1758 			concatString(subname, name);
1759 			snprintf(buf, sizeof(buf),
1760 				 "#%u", subclass_counter++);
1761 			appendString(subname, buf);
1762 			mapSet(class, createString(subname), "name");
1763 		} else
1764 			/* Save the name, if there is one. */
1765 			mapSet(class, createString(name), "name");
1766 		listPush(classes, class);
1767 	}
1768 
1769 	/* Spawned classes don't have to have their own settings. */
1770 	if (type == CLASS_TYPE_SUBCLASS) {
1771 		token = peek_token(&val, NULL, cfile);
1772 		if (token == SEMI) {
1773 			skip_token(&val, NULL, cfile);
1774 			subclass_inherit(cfile, class, copy(pc));
1775 			return;
1776 		}
1777 	}
1778 
1779 	parse_lbrace(cfile);
1780 
1781 	stackPush(cfile, class);
1782 
1783 	for (;;) {
1784 		token = peek_token(&val, NULL, cfile);
1785 		if (token == RBRACE) {
1786 			skip_token(&val, NULL, cfile);
1787 			break;
1788 		} else if (token == END_OF_FILE) {
1789 			skip_token(&val, NULL, cfile);
1790 			parse_error(cfile, "unexpected end of file");
1791 		} else if (token == DYNAMIC) {
1792 			skip_token(&val, NULL, cfile);
1793 			parse_error(cfile, "dynamic classes don't exist "
1794 				    "in the config file");
1795 		} else if (token == TOKEN_DELETED) {
1796 			skip_token(&val, NULL, cfile);
1797 			parse_error(cfile, "deleted hosts don't exist "
1798 				    "in the config file");
1799 		} else if (token == MATCH) {
1800 			skip_token(&val, NULL, cfile);
1801 			if (pc)
1802 				parse_error(cfile,
1803 					    "invalid match in subclass.");
1804 			token = peek_token(&val, NULL, cfile);
1805 			if (token != IF) {
1806 				expr = createBool(ISC_FALSE);
1807 				expr->skip = 1;
1808 				mapSet(class, expr, "spawning");
1809 				goto submatch;
1810 			}
1811 
1812 			skip_token(&val, NULL, cfile);
1813 			if (matchedonce)
1814 				parse_error(cfile,
1815 					    "A class may only have "
1816 					    "one 'match if' clause.");
1817 			matchedonce = ISC_TRUE;
1818 			expr = createMap();
1819 			if (!parse_boolean_expression(expr, cfile, &lose)) {
1820 				if (!lose)
1821 					parse_error(cfile,
1822 						    "expecting boolean expr.");
1823 			} else {
1824 				expr->skip = ISC_TRUE;
1825 				mapSet(class, expr, "match-if");
1826 				add_match_class(cfile, class, copy(expr));
1827 				parse_semi(cfile);
1828 			}
1829 		} else if (token == SPAWN) {
1830 			skip_token(&val, NULL, cfile);
1831 			if (pc)
1832 				parse_error(cfile,
1833 					    "invalid spawn in subclass.");
1834 			expr = createBool(ISC_TRUE);
1835 			expr->skip = ISC_TRUE;
1836 			cfile->issue_counter++;
1837 			mapSet(class, expr, "spawning");
1838 			token = next_token(&val, NULL, cfile);
1839 			if (token != WITH)
1840 				parse_error(cfile,
1841 					    "expecting with after spawn");
1842 		submatch:
1843 			if (submatchedonce)
1844 				parse_error(cfile,
1845 					    "can't override existing "
1846 					    "submatch/spawn");
1847 			submatchedonce = ISC_TRUE;
1848 			expr = createMap();
1849 			if (!parse_data_expression(expr, cfile, &lose)) {
1850 				if (!lose)
1851 					parse_error(cfile,
1852 						    "expecting data expr.");
1853 			} else {
1854 				expr->skip = ISC_TRUE;
1855 				cfile->issue_counter++;
1856 				mapSet(class, expr, "submatch");
1857 				parse_semi(cfile);
1858 			}
1859 		} else if (token == LEASE) {
1860 			struct comment *comment;
1861 
1862 			skip_token(&val, NULL, cfile);
1863 			token = next_token(&val, NULL, cfile);
1864 			if (token != LIMIT)
1865 				parse_error(cfile, "expecting \"limit\"");
1866 			token = next_token(&val, NULL, cfile);
1867 			if (token != NUMBER)
1868 				parse_error(cfile, "expecting a number");
1869 			tmp = createInt(atoll(val));
1870 			tmp->skip = ISC_TRUE;
1871 			cfile->issue_counter++;
1872 			comment = createComment("/// Per-class limit is not "
1873 						"supported by Kea");
1874 			TAILQ_INSERT_TAIL(&tmp->comments, comment);
1875 			comment = createComment("/// Reference Kea #237");
1876 			TAILQ_INSERT_TAIL(&tmp->comments, comment);
1877 			mapSet(class, tmp, "lease-limit");
1878 			parse_semi(cfile);
1879 		} else
1880 			declaration = parse_statement(cfile, CLASS_DECL,
1881 						      declaration);
1882 	}
1883 
1884 	cfile->stack_top--;
1885 
1886 	if (type == CLASS_TYPE_SUBCLASS)
1887 		subclass_inherit(cfile, class, copy(pc));
1888 }
1889 
1890 /*
1891  * Inherit entries:
1892  *  - first copy entries from the current superclass to the subclass
1893  *  - second try to reduce the subclass matching condition
1894  */
1895 
1896 static void
subclass_inherit(struct parse * cfile,struct element * class,struct element * superclass)1897 subclass_inherit(struct parse *cfile,
1898 		 struct element *class,
1899 		 struct element *superclass)
1900 {
1901 	struct string *name;
1902 	struct element *guard;
1903 	struct element *submatch;
1904 	struct handle *handle;
1905 	struct string *gmsg;
1906 	struct string *mmsg;
1907 	struct string *dmsg;
1908 	struct element *expr;
1909 	struct element *data;
1910 	struct element *match;
1911 	struct element *reduced;
1912 	unsigned order = 0;
1913 	struct comment *comment;
1914 	isc_boolean_t marked = ISC_FALSE;
1915 	isc_boolean_t lose = ISC_FALSE;
1916 	isc_boolean_t modified = ISC_FALSE;
1917 
1918 	expr = mapGet(superclass, "name");
1919 	if (expr == NULL)
1920 		parse_error(cfile, "can't get superclass name");
1921 	name = stringValue(expr);
1922 	guard = mapGet(superclass, "match-if");
1923 	submatch = mapGet(superclass, "submatch");
1924 	if (submatch == NULL)
1925 		parse_error(cfile, "can't get superclass submatch");
1926 
1927 	/* Iterates on (copy of) superclass entries */
1928 	while (mapSize(superclass) > 0) {
1929 		handle = mapPop(superclass);
1930 		if ((handle == NULL) || (handle->key == NULL) ||
1931 		    (handle->value == NULL))
1932 			parse_error(cfile, "can't get superclass %s item at "
1933 				    "%u", name->content, order);
1934 		handle->order = order++;
1935 		/* Superclass specific entries */
1936 		if ((strcmp(handle->key, "name") == 0) ||
1937 		    (strcmp(handle->key, "spawning") == 0) ||
1938 		    (strcmp(handle->key, "match-if") == 0) ||
1939 		    (strcmp(handle->key, "test") == 0) ||
1940 		    (strcmp(handle->key, "submatch") == 0))
1941 			continue;
1942 		/* Subclass specific so impossible entries */
1943 		if ((strcmp(handle->key, "super") == 0) ||
1944 		    (strcmp(handle->key, "binary") == 0) ||
1945 		    (strcmp(handle->key, "string") == 0))
1946 			parse_error(cfile, "superclass %s has unexpected %s "
1947 				    "at %u",
1948 				    name->content, handle->key, order);
1949 		/* Special entries */
1950 		if (strcmp(handle->key, "option-data") == 0) {
1951 			struct element *opt_list;
1952 
1953 			opt_list = mapGet(class, handle->key);
1954 			if (opt_list != NULL)
1955 				merge_option_data(handle->value, opt_list);
1956 			else
1957 				mapSet(class, handle->value, handle->key);
1958 			continue;
1959 		}
1960 		/* Just copy */
1961 		if ((strcmp(handle->key, "lease-limit") == 0) ||
1962 		    (strcmp(handle->key, "boot-file-name") == 0) ||
1963 		    (strcmp(handle->key, "serverhostname") == 0) ||
1964 		    (strcmp(handle->key, "next-server") == 0)) {
1965 			mapSet(class, handle->value, handle->key);
1966 			continue;
1967 		}
1968 		/* Unknown */
1969 		if (!marked) {
1970 			marked = ISC_TRUE;
1971 			comment = createComment("/// copied from superclass");
1972 			TAILQ_INSERT_TAIL(&handle->value->comments, comment);
1973 		}
1974 		comment = createComment("/// unhandled entry");
1975 		TAILQ_INSERT_TAIL(&handle->value->comments, comment);
1976 		if (!handle->value->skip) {
1977 			handle->value->skip = ISC_TRUE;
1978 			cfile->issue_counter++;
1979 		}
1980 		mapSet(class, handle->value, handle->key);
1981 	}
1982 
1983 	/* build [guard and] submatch = data */
1984 	expr = mapGet(class, "binary");
1985 	if (expr != NULL) {
1986 		data = createMap();
1987 		mapSet(data, copy(expr), "const-data");
1988 	} else
1989 		data = mapGet(class, "string");
1990 	if (data == NULL)
1991 		parse_error(cfile, "can't get subclass %s data",
1992 			    name->content);
1993 	match = createMap();
1994 	mapSet(match, copy(submatch), "left");
1995 	mapSet(match, copy(data), "right");
1996 	expr = createMap();
1997 	mapSet(expr, match, "equal");
1998 
1999 	if (guard != NULL) {
2000 		match = createMap();
2001 		mapSet(match, copy(guard), "left");
2002 		mapSet(match, expr, "right");
2003 		expr = createMap();
2004 		mapSet(expr, match, "and");
2005 
2006 		gmsg = makeString(-1, "/// from: match-if ");
2007 		appendString(gmsg, print_boolean_expression(guard, &lose));
2008 		mmsg = makeString(-1, "/// match: ");
2009 	} else {
2010 		gmsg = NULL;
2011 		mmsg = makeString(-1, "/// from: match ");
2012 	}
2013 
2014 	appendString(mmsg, print_data_expression(submatch, &lose));
2015 	dmsg = makeString(-1, "/// data: ");
2016 	appendString(dmsg, print_data_expression(data, &lose));
2017 
2018 	/* evaluate the expression and try to reduce it */
2019 	reduced = eval_boolean_expression(expr, &modified);
2020 	reduced = reduce_boolean_expression(reduced);
2021 	if ((reduced != NULL) && (reduced->type == ELEMENT_BOOLEAN))
2022 		parse_error(cfile, "class matching rule evaluated to a "
2023 			    "constant boolean expression: %s = %s",
2024 			    print_data_expression(submatch, &lose),
2025 			    print_data_expression(data, &lose));
2026 	if ((reduced == NULL) || (reduced->type != ELEMENT_STRING))
2027 		return;
2028 	if (!lose) {
2029 		if (gmsg != NULL) {
2030 			comment = createComment(gmsg->content);
2031 			TAILQ_INSERT_TAIL(&reduced->comments, comment);
2032 		}
2033 		comment = createComment(mmsg->content);
2034 		TAILQ_INSERT_TAIL(&reduced->comments, comment);
2035 		comment = createComment(dmsg->content);
2036 		TAILQ_INSERT_TAIL(&reduced->comments, comment);
2037 	}
2038 	mapSet(class, reduced, "test");
2039 }
2040 
2041 /*
2042  * Try to reduce a match-if condition into a Kea evaluate bool "test"
2043  */
2044 
2045 static void
add_match_class(struct parse * cfile,struct element * class,struct element * expr)2046 add_match_class(struct parse *cfile,
2047 		struct element *class,
2048 		struct element *expr)
2049 {
2050 	struct element *reduced;
2051 	isc_boolean_t modified = ISC_FALSE;
2052 	isc_boolean_t lose = ISC_FALSE;
2053 
2054 	/* evaluate the expression and try to reduce it */
2055 	reduced = eval_boolean_expression(expr, &modified);
2056 	reduced = reduce_boolean_expression(reduced);
2057 	if ((reduced != NULL) && (reduced->type == ELEMENT_BOOLEAN))
2058 		parse_error(cfile, "'match if' with a constant boolean "
2059 			    "expression %s",
2060 			    print_boolean_expression(expr, &lose));
2061 	if ((reduced != NULL) && (reduced->type == ELEMENT_STRING))
2062 		mapSet(class, reduced, "test");
2063 	else
2064 		cfile->issue_counter++;
2065 }
2066 
2067 /* Move pools to subnets */
2068 
2069 static void
relocate_pools(struct element * share)2070 relocate_pools(struct element *share)
2071 {
2072 	struct element *srcs;
2073 	struct element *dsts;
2074 	struct element *subnet;
2075 	struct range *range;
2076 	size_t i;
2077 
2078 	srcs = mapGet(share, "pools");
2079 	if (srcs == NULL)
2080 		return;
2081 	if (listSize(srcs) == 0)
2082 		return;
2083 	TAILQ_FOREACH(range, &known_ranges) {
2084 		if (range->share != share)
2085 			continue;
2086 		subnet = find_location(share, range);
2087 		if (subnet == NULL)
2088 			continue;
2089 		for (i = 0; i < listSize(srcs); i++) {
2090 			struct element *pool;
2091 
2092 			pool = listGet(srcs, i);
2093 			if (range->pool != pool)
2094 				continue;
2095 			listRemove(srcs, i);
2096 			dsts = mapGet(subnet, "pools");
2097 			if (dsts == NULL) {
2098 				dsts = createList();
2099 				mapSet(subnet, dsts, "pools");
2100 			}
2101 			listPush(dsts, pool);
2102 		}
2103 	}
2104 }
2105 
2106 /* shared-network-declaration :==
2107 			hostname LBRACE declarations parameters RBRACE */
2108 
2109 void
parse_shared_net_declaration(struct parse * cfile)2110 parse_shared_net_declaration(struct parse *cfile)
2111 {
2112 	const char *val;
2113 	enum dhcp_token token;
2114 	struct element *share;
2115 	struct element *subnets;
2116 	struct element *interface;
2117 	struct element *subnet;
2118 	struct string *name;
2119 	int declaration = 0;
2120 
2121 	share = createMap();
2122 	share->kind = SHARED_NET_DECL;
2123 	TAILQ_CONCAT(&share->comments, &cfile->comments);
2124 
2125 	/* Get the name of the shared network... */
2126 	token = peek_token(&val, NULL, cfile);
2127 	if (token == STRING) {
2128 		skip_token(&val, NULL, cfile);
2129 
2130 		if (val[0] == 0)
2131 			parse_error(cfile, "zero-length shared network name");
2132 		name = makeString(-1, val);
2133 	} else {
2134 		name = parse_host_name(cfile);
2135 		if (!name)
2136 			parse_error(cfile,
2137 				    "expecting a name for shared-network");
2138 	}
2139 	mapSet(share, createString(name), "name");
2140 
2141 	subnets = createList();
2142 	mapSet(share, subnets,
2143 	       local_family == AF_INET ? "subnet4" : "subnet6");
2144 
2145 	parse_lbrace(cfile);
2146 
2147 	stackPush(cfile, share);
2148 
2149 	for (;;) {
2150 		token = peek_token(&val, NULL, cfile);
2151 		if (token == RBRACE) {
2152 			skip_token(&val, NULL, cfile);
2153 			break;
2154 		} else if (token == END_OF_FILE) {
2155 			skip_token(&val, NULL, cfile);
2156 			parse_error(cfile, "unexpected end of file");
2157 		} else if (token == INTERFACE) {
2158 			skip_token(&val, NULL, cfile);
2159 			token = next_token(&val, NULL, cfile);
2160 			if (mapContains(share, "interface"))
2161 				parse_error(cfile,
2162 					    "A shared network can't be "
2163 					    "connected to two interfaces.");
2164 			interface = createString(makeString(-1, val));
2165 			mapSet(share, interface, "interface");
2166 			new_network_interface(cfile, interface);
2167 			parse_semi(cfile);
2168 			continue;
2169 		}
2170 
2171 		declaration = parse_statement(cfile, SHARED_NET_DECL,
2172 					      declaration);
2173 	}
2174 
2175 	cfile->stack_top--;
2176 
2177 	if (listSize(subnets) == 0)
2178 		parse_error(cfile, "empty shared-network decl");
2179 	if (listSize(subnets) > 1) {
2180 		struct element *shares;
2181 		struct element *pools;
2182 
2183 		shares = mapGet(cfile->stack[cfile->stack_top],
2184 				"shared-networks");
2185 		if (shares == NULL) {
2186 			struct comment *comment;
2187 
2188 			shares = createList();
2189 			shares->kind = SHARED_NET_DECL;
2190 			mapSet(cfile->stack[cfile->stack_top],
2191 			       shares, "shared-networks");
2192 			comment = createComment("/// Kea shared-networks "
2193 						"are different, cf Kea #236");
2194 			TAILQ_INSERT_TAIL(&shares->comments, comment);
2195 		}
2196 		listPush(shares, share);
2197 
2198 		/* Pools are forbidden at shared-network level in Kea */
2199 		relocate_pools(share);
2200 		pools = mapGet(share, "pools");
2201 		if ((pools != NULL) && (listSize(pools) == 0)) {
2202 			mapRemove(share, "pools");
2203 			pools = NULL;
2204 		}
2205 		if (pools != NULL) {
2206 			struct comment *comment;
2207 
2208 			pools->skip = ISC_TRUE;
2209 			cfile->issue_counter++;
2210 			comment = createComment("/// Kea pools must be "
2211 						"in a subnet");
2212 			TAILQ_INSERT_TAIL(&pools->comments, comment);
2213 			comment = createComment("/// Reference Kea #249");
2214 			TAILQ_INSERT_TAIL(&pools->comments, comment);
2215 		}
2216 		pools = mapGet(share, "pd-pools");
2217 		if ((pools != NULL) && (listSize(pools) == 0)) {
2218 			mapRemove(share, "pd-pools");
2219 			pools = NULL;
2220 		}
2221 		if (pools != NULL) {
2222 			struct comment *comment;
2223 
2224 			pools->skip = ISC_TRUE;
2225 			cfile->issue_counter++;
2226 			comment = createComment("/// Kea pools must be "
2227 						"in a subnet");
2228 			TAILQ_INSERT_TAIL(&pools->comments, comment);
2229 			comment = createComment("/// Reference Kea #249");
2230 			TAILQ_INSERT_TAIL(&pools->comments, comment);
2231 		}
2232 		return;
2233 	}
2234 
2235 	/* There is one subnet so the shared network is useless */
2236 	subnet = listGet(subnets, 0);
2237 	listRemove(subnets, 0);
2238 	mapRemove(share, "name");
2239 	mapRemove(share, local_family == AF_INET ? "subnet4" : "subnet6");
2240 	/* specific case before calling generic merge */
2241 	if (mapContains(share, "pools") &&
2242 	    mapContains(subnet, "pools")) {
2243 		struct element *pools;
2244 		struct element *sub;
2245 
2246 		pools = mapGet(share, "pools");
2247 		mapRemove(share, "pools");
2248 		sub = mapGet(subnet, "pools");
2249 		concat(sub, pools);
2250 	}
2251 	if (mapContains(share, "pd-pools") &&
2252 	    mapContains(subnet, "pd-pools")) {
2253 		struct element *pools;
2254 		struct element *sub;
2255 
2256 		pools = mapGet(share, "pd-pools");
2257 		mapRemove(share, "pd-pools");
2258 		sub = mapGet(subnet, "pd-pools");
2259 		concat(sub, pools);
2260 	}
2261 	if (mapContains(share, "option-data") &&
2262 	    mapContains(subnet, "option-data")) {
2263 		struct element *opt_list;
2264 		struct element *sub;
2265 
2266 		opt_list = mapGet(share, "option-data");
2267 		mapRemove(share, "option-data");
2268 		sub = mapGet(subnet, "option-data");
2269 		merge_option_data(opt_list, sub);
2270 	}
2271 	merge(subnet, share);
2272 
2273 	if (local_family == AF_INET) {
2274 		subnets = mapGet(cfile->stack[1], "subnet4");
2275 		if (subnets == NULL) {
2276 			subnets = createList();
2277 			subnets->kind = SUBNET_DECL;
2278 			mapSet(cfile->stack[1], subnets, "subnet4");
2279 		}
2280 	} else {
2281 		subnets = mapGet(cfile->stack[1], "subnet6");
2282 		if (subnets == NULL) {
2283 			subnets = createList();
2284 			subnets->kind = SUBNET_DECL;
2285 			mapSet(cfile->stack[1], subnets, "subnet6");
2286 		}
2287 	}
2288 	listPush(subnets, subnet);
2289 }
2290 
2291 static void
common_subnet_parsing(struct parse * cfile,struct element * subnets,struct element * subnet)2292 common_subnet_parsing(struct parse *cfile,
2293 		      struct element *subnets,
2294 		      struct element *subnet)
2295 {
2296 	enum dhcp_token token;
2297 	const char *val;
2298 	struct element *interface;
2299 	int declaration = 0;
2300 
2301 	parse_lbrace(cfile);
2302 
2303 	stackPush(cfile, subnet);
2304 
2305 	for (;;) {
2306 		token = peek_token(&val, NULL, cfile);
2307 		if (token == RBRACE) {
2308 			skip_token(&val, NULL, cfile);
2309 			break;
2310 		} else if (token == END_OF_FILE) {
2311 			skip_token(&val, NULL, cfile);
2312 			parse_error(cfile, "unexpected end of file");
2313 			break;
2314 		} else if (token == INTERFACE) {
2315 			skip_token(&val, NULL, cfile);
2316 			token = next_token(&val, NULL, cfile);
2317 			if (mapContains(subnet, "interface"))
2318 				parse_error(cfile,
2319 					    "A subnet can't be connected "
2320 					    "to two interfaces.");
2321 			interface = createString(makeString(-1, val));
2322 			mapSet(subnet, interface, "interface");
2323 			new_network_interface(cfile, interface);
2324 			parse_semi(cfile);
2325 			continue;
2326 		}
2327 		declaration = parse_statement(cfile, SUBNET_DECL, declaration);
2328 	}
2329 
2330 	cfile->stack_top--;
2331 
2332 	/* Add the subnet to the list of subnets in this shared net. */
2333 	listPush(subnets, subnet);
2334 
2335 	return;
2336 }
2337 
2338 /* subnet-declaration :==
2339 	net NETMASK netmask RBRACE parameters declarations LBRACE */
2340 
2341 void
parse_subnet_declaration(struct parse * cfile)2342 parse_subnet_declaration(struct parse *cfile)
2343 {
2344 	const char *val;
2345 	enum dhcp_token token;
2346 	struct element *subnet;
2347 	struct subnet *chain;
2348 	struct element *subnets;
2349 	struct string *address;
2350 	struct string *netmask;
2351 	struct string *prefix;
2352 	unsigned char addr[4];
2353 	unsigned len = sizeof(addr);
2354 	size_t parent = 0;
2355 	size_t i;
2356 	int kind = 0;
2357 
2358 	subnet = createMap();
2359 	subnet->kind = SUBNET_DECL;
2360 	TAILQ_CONCAT(&subnet->comments, &cfile->comments);
2361 
2362 	subnet_counter++;
2363 	mapSet(subnet, createInt(subnet_counter), "id");
2364 
2365 	chain = (struct subnet *)malloc(sizeof(*chain));
2366 	if (chain == NULL)
2367 		parse_error(cfile, "can't allocate subnet");
2368 	memset(chain, 0, sizeof(*chain));
2369 	chain->subnet = subnet;
2370 	TAILQ_INSERT_TAIL(&known_subnets, chain);
2371 
2372 	/* Find parent */
2373 	for (i = cfile->stack_top; i > 0; --i) {
2374 		kind = cfile->stack[i]->kind;
2375 		if ((kind == SHARED_NET_DECL) ||
2376 		    (kind == GROUP_DECL) ||
2377 		    (kind == ROOT_GROUP)) {
2378 			parent = i;
2379 			break;
2380 		}
2381 	}
2382 	if (kind == 0)
2383 		parse_error(cfile, "can't find a place to put subnet");
2384 	if (kind == SHARED_NET_DECL)
2385 		chain->share = cfile->stack[parent];
2386 	subnets = mapGet(cfile->stack[parent], "subnet4");
2387 	if (subnets == NULL) {
2388 		if (kind == SHARED_NET_DECL)
2389 			parse_error(cfile, "shared network without subnets");
2390 		subnets = createList();
2391 		subnets->kind = SUBNET_DECL;
2392 		mapSet(cfile->stack[parent], subnets, "subnet4");
2393 	}
2394 
2395 	/* Get the network number... */
2396 	address = parse_numeric_aggregate(cfile, addr, &len, DOT, 10, 8);
2397 	if (address == NULL)
2398 		parse_error(cfile, "can't decode network number");
2399 	if (address->length != 4)
2400 		parse_error(cfile, "bad IPv4 address length");
2401 	chain->addr = address;
2402 
2403 	token = next_token(&val, NULL, cfile);
2404 	if (token != NETMASK)
2405 		parse_error(cfile, "Expecting netmask");
2406 
2407 	/* Get the netmask... */
2408 	netmask = parse_numeric_aggregate(cfile, addr, &len, DOT, 10, 8);
2409 	if (netmask == NULL)
2410 		parse_error(cfile, "can't decode network mask");
2411 	if (netmask->length != address->length)
2412 		parse_error(cfile, "bad IPv4 mask length");
2413 	chain->mask = netmask;
2414 
2415 	prefix = addrmask(address, netmask);
2416 	if (prefix == NULL) {
2417 		char bufa[INET_ADDRSTRLEN];
2418 		char bufm[INET_ADDRSTRLEN];
2419 
2420 		inet_ntop(AF_INET, address->content, bufa, INET_ADDRSTRLEN);
2421 		inet_ntop(AF_INET, netmask->content, bufm, INET_ADDRSTRLEN);
2422 		parse_error(cfile, "can't get a prefix from %s mask %s",
2423 			    bufa, bufm);
2424 	}
2425 	mapSet(subnet, createString(prefix), "subnet");
2426 
2427 	common_subnet_parsing(cfile, subnets, subnet);
2428 }
2429 
2430 /* subnet6-declaration :==
2431 	net / bits RBRACE parameters declarations LBRACE */
2432 
2433 void
parse_subnet6_declaration(struct parse * cfile)2434 parse_subnet6_declaration(struct parse *cfile)
2435 {
2436 	enum dhcp_token token;
2437 	const char *val;
2438 	struct element *subnet;
2439 	struct subnet *chain;
2440 	struct element *subnets;
2441 	struct string *address;
2442 	struct string *prefix;
2443 	struct string *netmask;
2444 	size_t parent = 0;
2445         size_t i;
2446         int kind = 0;
2447 	char *p;
2448 
2449         if (local_family != AF_INET6)
2450                 parse_error(cfile, "subnet6 statement is only supported "
2451 				   "in DHCPv6 mode.");
2452 
2453 	subnet = createMap();
2454 	subnet->kind = SUBNET_DECL;
2455 	TAILQ_CONCAT(&subnet->comments, &cfile->comments);
2456 
2457 	subnet_counter++;
2458 	mapSet(subnet, createInt(subnet_counter), "id");
2459 
2460 	chain = (struct subnet *)malloc(sizeof(*chain));
2461 	if (chain == NULL)
2462 		parse_error(cfile, "can't allocate subnet");
2463 	memset(chain, 0, sizeof(*chain));
2464 	chain->subnet = subnet;
2465 	TAILQ_INSERT_TAIL(&known_subnets, chain);
2466 
2467 	/* Find parent */
2468 	for (i = cfile->stack_top; i > 0; --i) {
2469 		kind = cfile->stack[i]->kind;
2470 		if ((kind == SHARED_NET_DECL) ||
2471 		    (kind == GROUP_DECL) ||
2472 		    (kind == ROOT_GROUP)) {
2473 			parent = i;
2474 			break;
2475 		}
2476 	}
2477 	if (kind == 0)
2478 		parse_error(cfile, "can't find a place to put subnet");
2479 	if (kind == SHARED_NET_DECL)
2480 		chain->share = cfile->stack[parent];
2481 	subnets = mapGet(cfile->stack[parent], "subnet6");
2482 	if (subnets == NULL) {
2483 		if (kind == SHARED_NET_DECL)
2484 			parse_error(cfile, "shared network without subnets");
2485 		subnets = createList();
2486 		subnets->kind = SUBNET_DECL;
2487 		mapSet(cfile->stack[parent], subnets, "subnet6");
2488 	}
2489 
2490 	address = parse_ip6_addr(cfile);
2491 	if (address == NULL)
2492 		parse_error(cfile, "can't decode network number");
2493 	if (address->length != 16)
2494 		parse_error(cfile, "bad IPv6 address length");
2495 	chain->addr = address;
2496 
2497 	prefix = makeStringExt(address->length, address->content, '6');
2498 
2499 	token = next_token(&val, NULL, cfile);
2500 	if (token != SLASH)
2501 		parse_error(cfile, "Expecting a '/'.");
2502 	appendString(prefix, val);
2503 
2504 	token = next_token(&val, NULL, cfile);
2505 	if (token != NUMBER)
2506 		parse_error(cfile, "Expecting a number.");
2507 	appendString(prefix, val);
2508 
2509 	netmask = makeString(16, "0123456789abcdef");
2510 	memset(netmask->content, 0, 16);
2511 	p = netmask->content;
2512 	for (i = atoi(val); i >= 8; i -= 8)
2513 		*p++ = 0xff;
2514 	*p = 0xff << (8 - i);
2515 	chain->mask = netmask;
2516 
2517 	mapSet(subnet, createString(prefix), "subnet");
2518 
2519 	common_subnet_parsing(cfile, subnets, subnet);
2520 }
2521 
2522 /* group-declaration :== RBRACE parameters declarations LBRACE */
2523 
2524 void
parse_group_declaration(struct parse * cfile)2525 parse_group_declaration(struct parse *cfile)
2526 {
2527 	const char *val;
2528 	enum dhcp_token token;
2529 	struct element *group;
2530 	int declaration = 0;
2531 	struct string *name = NULL;
2532 
2533 	if (mapContains(cfile->stack[cfile->stack_top], "group"))
2534 		parse_error(cfile, "another group is already open");
2535 	group = createMap();
2536 	group->skip = ISC_TRUE;
2537 	group->kind = GROUP_DECL;
2538 	TAILQ_CONCAT(&group->comments, &cfile->comments);
2539 	mapSet(cfile->stack[cfile->stack_top], group, "group");
2540 
2541 	token = peek_token(&val, NULL, cfile);
2542 	if (is_identifier(token) || token == STRING) {
2543 		skip_token(&val, NULL, cfile);
2544 
2545 		name = makeString(-1, val);
2546 		if (!name)
2547 			parse_error(cfile, "no memory for group decl name %s",
2548 				    val);
2549 	}
2550 
2551 	parse_lbrace(cfile);
2552 
2553 	stackPush(cfile, group);
2554 
2555 	for (;;) {
2556 		token = peek_token(&val, NULL, cfile);
2557 		if (token == RBRACE) {
2558 			skip_token(&val, NULL, cfile);
2559 			break;
2560 		} else if (token == END_OF_FILE) {
2561 			skip_token(&val, NULL, cfile);
2562 			parse_error(cfile, "unexpected end of file");
2563 			break;
2564 		} else if (token == TOKEN_DELETED) {
2565 			skip_token(&val, NULL, cfile);
2566 			parse_error(cfile, "deleted groups don't exist "
2567 				    "in the config file");
2568 		} else if (token == DYNAMIC) {
2569 			skip_token(&val, NULL, cfile);
2570 			parse_error(cfile, "dynamic groups don't exist "
2571 				    "in the config file");
2572 		} else if (token == STATIC) {
2573 			skip_token(&val, NULL, cfile);
2574 			parse_error(cfile, "static groups don't exist "
2575 				    "in the config file");
2576 		}
2577 		declaration = parse_statement(cfile, GROUP_DECL, declaration);
2578 	}
2579 
2580 	cfile->stack_top--;
2581 
2582 	if (name != NULL)
2583 		mapSet(group, createString(name), "name");
2584 	close_group(cfile, group);
2585 }
2586 
2587 /*
2588  * Close a group. Called when a group is closed.
2589  *  - spread parameters to children
2590  *  - attach declarations at an upper level
2591  */
2592 
2593 void
close_group(struct parse * cfile,struct element * group)2594 close_group(struct parse *cfile, struct element *group)
2595 {
2596 	struct handle *handle;
2597 	struct handle *nh;
2598 	struct element *parent;
2599 	struct element *item;
2600 	struct element *param;
2601 	struct handle *hosts = NULL;
2602 	struct handle *shares = NULL;
2603 	struct handle *subnets = NULL;
2604 	struct handle *classes = NULL;
2605 	struct handle *pdpools = NULL;
2606 	struct handle *pools = NULL;
2607 	struct handles downs;
2608 	struct comment *comment;
2609 	const char *key = NULL;
2610 	const char *name = NULL;
2611 	unsigned order = 0;
2612 	isc_boolean_t marked = ISC_FALSE;
2613 
2614 	TAILQ_INIT(&downs);
2615 
2616 	/* check that group is in its parent */
2617 	parent = cfile->stack[cfile->stack_top];
2618 	if (parent->kind == PARAMETER)
2619 		parse_error(cfile, "unexpected kind for group parent %d",
2620 			    parent->kind);
2621 	item = mapGet(parent, "group");
2622 	if (item == NULL)
2623 		parse_error(cfile, "no group in parent");
2624 	if (item != group)
2625 		parse_error(cfile, "got a different group from parent");
2626 	mapRemove(parent, "group");
2627 
2628 	/* classify content */
2629 	while (mapSize(group) > 0) {
2630 		handle = mapPop(group);
2631 		if ((handle == NULL) || (handle->key == NULL) ||
2632 		    (handle->value == NULL))
2633 		    parse_error(cfile, "can't get group item at %u",
2634 				order);
2635 		handle->order = order++;
2636 		switch (handle->value->kind) {
2637 		case TOPLEVEL:
2638 		case ROOT_GROUP:
2639 		case GROUP_DECL:
2640 		badkind:
2641 			parse_error(cfile, "impossible group item (kind %d) "
2642 				    "for %s at order %u",
2643 				    handle->value->kind, handle->key, order);
2644 
2645 		case HOST_DECL:
2646 			if (strcmp(handle->key, "reservations") != 0)
2647 				parse_error(cfile, "expected reservations "
2648 					    "got %s at %u",
2649 					    handle->key, order);
2650 			if (hosts != NULL)
2651 				parse_error(cfile, "got reservations twice "
2652 					    "at %u and %u",
2653 					    hosts->order, order);
2654 			if ((parent->kind == HOST_DECL) ||
2655 			    (parent->kind == CLASS_DECL))
2656 				parse_error(cfile, "host declarations not "
2657 					    "allowed here.");
2658 			hosts = handle;
2659 			handle = NULL;
2660 			break;
2661 
2662 		case SHARED_NET_DECL:
2663 			if (strcmp(handle->key, "shared-networks") != 0)
2664 				parse_error(cfile, "expected shared-networks "
2665 					    "got %s at %u",
2666 					    handle->key, order);
2667 			if ((parent->kind == SHARED_NET_DECL) ||
2668 			    (parent->kind == HOST_DECL) ||
2669 			    (parent->kind == SUBNET_DECL) ||
2670 			    (parent->kind == CLASS_DECL))
2671 				parse_error(cfile, "shared-network parameters "
2672 					    "not allowed here.");
2673 			shares = handle;
2674 			handle = NULL;
2675 			break;
2676 
2677 		case SUBNET_DECL:
2678 			key = local_family == AF_INET ? "subnet4" : "subnet6";
2679 			if (strcmp(handle->key, key) != 0)
2680 				parse_error(cfile, "expected %s got %s at %u",
2681 					    key, handle->key, order);
2682 			if (subnets != NULL)
2683 				parse_error(cfile, "got %s twice at %u and %u",
2684 					    key, subnets->order, order);
2685 			if ((parent->kind == HOST_DECL) ||
2686 			    (parent->kind == SUBNET_DECL) ||
2687 			    (parent->kind == CLASS_DECL))
2688 				parse_error(cfile, "subnet declarations not "
2689 					    "allowed here.");
2690 			subnets = handle;
2691 			handle = NULL;
2692 			break;
2693 
2694 		case CLASS_DECL:
2695 			if (strcmp(handle->key, "client-classes") != 0)
2696 				parse_error(cfile, "expected client-classes "
2697 					    "got %s at %u",
2698 					    handle->key, order);
2699 			if (classes != NULL)
2700 				parse_error(cfile, "got %s twice at %u and %u",
2701 					    key, classes->order, order);
2702 			if (parent->kind == CLASS_DECL)
2703 				parse_error(cfile, "class declarations not "
2704 					    "allowed here.");
2705 			classes = handle;
2706 			handle = NULL;
2707                         break;
2708 
2709 		case POOL_DECL:
2710 			if (strcmp(handle->key, "pd-pools") == 0) {
2711                                 if (pdpools != NULL)
2712                                         parse_error(cfile, "got pd-pools "
2713 						    "twice at %u and %u",
2714                                                     pdpools->order, order);
2715                                 pdpools = handle;
2716 			} else if (strcmp(handle->key, "pools") == 0) {
2717 				if (pools != NULL)
2718 					parse_error(cfile, "got pools twice "
2719 						    "at %u and %u",
2720 						    pools->order, order);
2721 				pools = handle;
2722 			} else
2723 				parse_error(cfile, "expecyed [pd-]pools got "
2724 					    "%s at %u",
2725 					    handle->key, order);
2726 			if (parent->kind == POOL_DECL)
2727 				parse_error(cfile, "pool declared within "
2728 					    "pool.");
2729 			if ((parent->kind == HOST_DECL) ||
2730 			    (parent->kind == CLASS_DECL))
2731 				parse_error(cfile, "pool declared outside "
2732 					    "of network");
2733 			handle = NULL;
2734 			break;
2735 		default:
2736 			if (handle->value->kind != PARAMETER)
2737 				goto badkind;
2738 		}
2739 		if (handle == NULL)
2740 			continue;
2741 
2742 		/* we have a parameter */
2743 		param = handle->value;
2744 		/* group name */
2745 		if (strcmp(handle->key, "name") == 0) {
2746 			name = stringValue(param)->content;
2747 			continue;
2748 		}
2749 		/* unexpected values */
2750 		if ((strcmp(handle->key, "reservations") == 0) ||
2751 		    (strcmp(handle->key, "group") == 0) ||
2752 		    (strcmp(handle->key, "shared-networks") == 0) ||
2753 		    (strcmp(handle->key, "subnet4") == 0) ||
2754 		    (strcmp(handle->key, "subnet6") == 0) ||
2755 		    (strcmp(handle->key, "subnet") == 0) ||
2756 		    (strcmp(handle->key, "client-classes") == 0) ||
2757 		    (strcmp(handle->key, "hw-address") == 0) ||
2758 		    (strcmp(handle->key, "ip-address") == 0) ||
2759 		    (strcmp(handle->key, "extra-ip-addresses") == 0) ||
2760 		    (strcmp(handle->key, "ip-addresses") == 0) ||
2761 		    (strcmp(handle->key, "prefixes") == 0) ||
2762 		    (strcmp(handle->key, "pool") == 0) ||
2763 		    (strcmp(handle->key, "prefix") == 0) ||
2764 		    (strcmp(handle->key, "delegated-len") == 0) ||
2765 		    (strcmp(handle->key, "prefix-len") == 0) ||
2766 		    (strcmp(handle->key, "prefix-highest") == 0) ||
2767 		    (strcmp(handle->key, "option-def") == 0) ||
2768 		    (strcmp(handle->key, "hostname") == 0) ||
2769 		    (strcmp(handle->key, "client-id") == 0) ||
2770 		    (strcmp(handle->key, "host-identifier") == 0) ||
2771 		    (strcmp(handle->key, "flex-id") == 0) ||
2772 		    (strcmp(handle->key, "test") == 0) ||
2773 		    (strcmp(handle->key, "authoritative") == 0) ||
2774 		    (strcmp(handle->key, "dhcp-ddns") == 0) ||
2775 		    (strcmp(handle->key, "host-reservation-identifiers") == 0))
2776 			parse_error(cfile, "unexpected parameter %s "
2777 				    "in group at %u",
2778 				    handle->key, order);
2779 
2780 		/* to parent at group position */
2781 		if ((strcmp(handle->key, "option-space") == 0) ||
2782 		    (strcmp(handle->key, "server-duid") == 0) ||
2783 		    (strcmp(handle->key, "statement") == 0) ||
2784 		    (strcmp(handle->key, "config") == 0) ||
2785 		    (strcmp(handle->key, "ddns-update-style") == 0) ||
2786 		    (strcmp(handle->key, "echo-client-id") == 0)) {
2787 			if (!marked) {
2788 				struct string *msg;
2789 
2790 				marked = ISC_TRUE;
2791 				msg = makeString(-1, "/// moved from group");
2792 				if (name != NULL)
2793 					appendString(msg, " ");
2794 				appendString(msg, name);
2795 				comment = createComment(msg->content);
2796 				TAILQ_INSERT_TAIL(&param->comments, comment);
2797 			}
2798 			mapSet(parent, param, handle->key);
2799 			free(handle);
2800 			continue;
2801 		}
2802 		/* To reconsider: qualifying-suffix, enable-updates */
2803 		if ((strcmp(handle->key, "option-data") == 0) ||
2804 		    (strcmp(handle->key, "allow") == 0) ||
2805 		    (strcmp(handle->key, "deny") == 0) ||
2806 		    (strcmp(handle->key, "interface") == 0) ||
2807 		    (strcmp(handle->key, "valid-lifetime") == 0) ||
2808 		    (strcmp(handle->key, "preferred-lifetime") == 0) ||
2809 		    (strcmp(handle->key, "renew-timer") == 0) ||
2810 		    (strcmp(handle->key, "rebind-timer") == 0) ||
2811 		    (strcmp(handle->key, "boot-file-name") == 0) ||
2812 		    (strcmp(handle->key, "server-hostname") == 0) ||
2813 		    (strcmp(handle->key, "next-server") == 0) ||
2814 		    (strcmp(handle->key, "match-client-id") == 0)) {
2815 			TAILQ_INSERT_TAIL(&downs, handle);
2816 			continue;
2817 		}
2818 		/* unknown */
2819 		if (!marked) {
2820 			struct string *msg;
2821 
2822 			marked = ISC_TRUE;
2823 			msg = makeString(-1, "/// moved from group");
2824 			if (name != NULL)
2825 				appendString(msg, " ");
2826 			appendString(msg, name);
2827 			comment = createComment(msg->content);
2828 			TAILQ_INSERT_TAIL(&param->comments, comment);
2829 		}
2830 		comment = createComment("/// unhandled parameter");
2831 		TAILQ_INSERT_TAIL(&param->comments, comment);
2832 		param->skip = ISC_TRUE;
2833 		cfile->issue_counter++;
2834 		mapSet(parent, param, handle->key);
2835 		free(handle);
2836 	}
2837 	TAILQ_FOREACH_SAFE(handle, &downs, nh) {
2838 		if (strcmp(handle->key, "option-data") == 0) {
2839 			option_data_derive(cfile, handle, hosts);
2840 			option_data_derive(cfile, handle, shares);
2841 			option_data_derive(cfile, handle, subnets);
2842 			derive_classes(cfile, handle, classes);
2843 			option_data_derive(cfile, handle, pdpools);
2844 			option_data_derive(cfile, handle, pools);
2845 		} else if ((strcmp(handle->key, "allow") == 0) ||
2846 			   (strcmp(handle->key, "deny") == 0)) {
2847 			derive(handle, pdpools);
2848 			derive(handle, pools);
2849 		} else if ((strcmp(handle->key, "interface") == 0) ||
2850 			   (strcmp(handle->key, "valid-lifetime") == 0) ||
2851 			   (strcmp(handle->key, "preferred-lifetime") == 0) ||
2852 			   (strcmp(handle->key, "renew-timer") == 0) ||
2853 			   (strcmp(handle->key, "rebind-timer") == 0) ||
2854 			   (strcmp(handle->key, "match-client-id") == 0)) {
2855 			derive(handle, shares);
2856 			derive(handle, subnets);
2857 		} else if ((strcmp(handle->key, "boot-file-name") == 0) ||
2858 			   (strcmp(handle->key, "server-hostname") == 0)) {
2859 			derive(handle, hosts);
2860 			derive_classes(cfile, handle, classes);
2861 		} else if (strcmp(handle->key, "next-server") == 0) {
2862 			derive(handle, hosts);
2863 			derive(handle, subnets);
2864 			derive_classes(cfile, handle, classes);
2865 		} else
2866 			parse_error(cfile, "unexpected parameter %s to derive",
2867 				    handle->key);
2868 	}
2869 	if (hosts != NULL) {
2870 		struct element *root;
2871 
2872 		root = mapGet(cfile->stack[1], "reservations");
2873 		if (root == NULL)
2874 			mapSet(cfile->stack[1], hosts->value, "reservations");
2875 		else
2876 			concat(root, hosts->value);
2877 	}
2878 	if (shares != NULL) {
2879 		struct element *upper;
2880 
2881 		upper = mapGet(parent, "shared-networks");
2882 		if (upper == NULL)
2883 			mapSet(parent, shares->value, "shared-networks");
2884 		else
2885 			concat(upper, shares->value);
2886 	}
2887 	key = local_family == AF_INET ? "subnet4" : "subnet6";
2888 	if (subnets != NULL) {
2889 		struct element *upper;
2890 
2891 		upper = mapGet(parent, key);
2892 		if (upper == NULL)
2893 			mapSet(parent, subnets->value, key);
2894 		else
2895 			concat(upper, subnets->value);
2896 	}
2897 	if (classes != NULL) {
2898 		struct element *upper;
2899 		size_t where;
2900 		int kind = 0;
2901 
2902 		for (where = cfile->stack_top; where > 0; --where) {
2903 			kind = cfile->stack[where]->kind;
2904 			if ((kind == GROUP_DECL) || (kind == ROOT_GROUP))
2905 				break;
2906 		}
2907 		if (kind == GROUP_DECL) {
2908 			upper = mapGet(cfile->stack[where], "client-classes");
2909 			if (upper == NULL)
2910 				mapSet(cfile->stack[where],
2911 				       classes->value,
2912 				       "client-classes");
2913 			else
2914 				concat_classes(cfile, upper, classes->value);
2915 		}
2916 	}
2917 	if (pdpools != NULL) {
2918 		struct element *upper;
2919 
2920 		upper = mapGet(parent, "pd-pools");
2921 		if (upper == NULL)
2922                         mapSet(parent, pdpools->value, "pools");
2923                 else
2924                         concat(upper, pdpools->value);
2925 	}
2926 	if (pools != NULL) {
2927 		struct element *upper;
2928 
2929 		upper = mapGet(parent, "pools");
2930 		if (upper == NULL)
2931                         mapSet(parent, pools->value, "pools");
2932                 else
2933                         concat(upper, pools->value);
2934 	}
2935 }
2936 
2937 /*
2938  * Specialized derivation routine for option-data
2939  * (options are identified by space + name and/or code
2940  */
2941 
2942 static void
option_data_derive(struct parse * cfile,struct handle * src,struct handle * dst)2943 option_data_derive(struct parse *cfile, struct handle *src, struct handle *dst)
2944 {
2945 	struct element *list;
2946 	struct element *item;
2947 	struct element *opt_list;
2948 	size_t i;
2949 
2950 	if (dst == NULL)
2951 		return;
2952 	list = dst->value;
2953 	assert(list != NULL);
2954 	assert(list->type == ELEMENT_LIST);
2955 	for (i = 0; i < listSize(list); i++) {
2956 		item = listGet(list, i);
2957 		assert(item != NULL);
2958 		assert(item->type == ELEMENT_MAP);
2959 		opt_list = mapGet(item, src->key);
2960 		if (opt_list != NULL) {
2961 			merge_option_data(src->value, opt_list);
2962 			continue;
2963 		}
2964 		opt_list = copy(src->value);
2965 		mapSet(item, opt_list, src->key);
2966 	}
2967 }
2968 
2969 /*
2970  * Specialized derivation routine for classes
2971  * (which are by reference so a resolution step is needed)
2972  */
2973 static void
derive_classes(struct parse * cfile,struct handle * src,struct handle * dst)2974 derive_classes(struct parse *cfile, struct handle *src, struct handle *dst)
2975 {
2976 	struct element *list;
2977 	struct element *item;
2978 	size_t i;
2979 
2980 	if (dst == NULL)
2981 		return;
2982 	list = dst->value;
2983 	assert(list != NULL);
2984 	assert(list->type == ELEMENT_LIST);
2985 	for (i = 0; i < listSize(list); i++) {
2986 		item = listGet(list, i);
2987 		assert(item != NULL);
2988 		assert(item->type == ELEMENT_MAP);
2989 		item = get_class(cfile, item);
2990 		if (item == NULL)
2991 			parse_error(cfile, "dangling class reference");
2992 		if (strcmp(src->key, "option-data") == 0) {
2993 			struct element *opt_list;
2994 
2995 			opt_list = mapGet(item, "option-data");
2996 			if (opt_list != NULL)
2997 				merge_option_data(src->value, opt_list);
2998 			else
2999 				mapSet(item, copy(src->value), "option-data");
3000 			continue;
3001 		}
3002 		if (mapContains(item, src->key))
3003 			continue;
3004 		mapSet(item, copy(src->value), src->key);
3005 	}
3006 }
3007 
3008 /* fixed-addr-parameter :== ip-addrs-or-hostnames SEMI
3009    ip-addrs-or-hostnames :== ip-addr-or-hostname
3010 			   | ip-addrs-or-hostnames ip-addr-or-hostname */
3011 
3012 struct element *
parse_fixed_addr_param(struct parse * cfile,enum dhcp_token type)3013 parse_fixed_addr_param(struct parse *cfile, enum dhcp_token type) {
3014 	const char *val;
3015 	enum dhcp_token token;
3016 	struct element *addr;
3017 	struct element *addresses;
3018 	struct string *address;
3019 
3020 	addresses = createList();
3021 	TAILQ_CONCAT(&addresses->comments, &cfile->comments);
3022 
3023 	do {
3024 		address = NULL;
3025 		if (type == FIXED_ADDR)
3026 			address = parse_ip_addr_or_hostname(cfile, ISC_TRUE);
3027 		else if (type == FIXED_ADDR6)
3028 			address = parse_ip6_addr_txt(cfile);
3029 		else
3030 			parse_error(cfile, "requires FIXED_ADDR[6]");
3031 		if (address == NULL)
3032 			parse_error(cfile, "can't parse fixed address");
3033 		addr = createString(address);
3034 		/* Take the comment for resolution into multiple addresses */
3035 		TAILQ_CONCAT(&addr->comments, &cfile->comments);
3036 		listPush(addresses, addr);
3037 		token = peek_token(&val, NULL, cfile);
3038 		if (token == COMMA)
3039 			token = next_token(&val, NULL, cfile);
3040 	} while (token == COMMA);
3041 
3042 	parse_semi(cfile);
3043 
3044 	/* Sanity */
3045 	if (listSize(addresses) == 0)
3046 		parse_error(cfile, "can't get fixed address");
3047 
3048 	return addresses;
3049 
3050 }
3051 
3052 #ifdef notyet
3053 /* Parse the right side of a 'binding value'.
3054  *
3055  * set foo = "bar"; is a string
3056  * set foo = false; is a boolean
3057  * set foo = %31; is a numeric value.
3058  */
3059 static struct element *
parse_binding_value(struct parse * cfile)3060 parse_binding_value(struct parse *cfile)
3061 {
3062 	struct element *value = NULL;
3063 	struct string *data;
3064 	const char *val;
3065 	unsigned buflen;
3066 	int token;
3067 
3068 	token = peek_token(&val, NULL, cfile);
3069 	if (token == STRING) {
3070 		skip_token(&val, &buflen, cfile);
3071 		data = makeString(buflen, val);
3072 		value = createString(data);
3073 	} else if (token == NUMBER_OR_NAME) {
3074 		value = createMap();
3075 		data = parse_hexa(cfile);
3076 		mapSet(value, createHexa(data), "const-data");
3077 	} else if (token == PERCENT) {
3078 		skip_token(&val, NULL, cfile);
3079 		token = next_token(&val, NULL, cfile);
3080 		if (token != NUMBER)
3081 			parse_error(cfile, "expecting decimal number.");
3082 		value = createInt(atol(val));
3083 	} else if (token == NAME) {
3084 		token = next_token(&val, NULL, cfile);
3085 		if (!strcasecmp(val, "true"))
3086 			value = createBool(ISC_TRUE);
3087 		else if (!strcasecmp(val, "false"))
3088 			value = createBool(ISC_FALSE);
3089 		else
3090 			parse_error(cfile, "expecting true or false");
3091 	} else
3092 		parse_error(cfile, "expecting a constant value.");
3093 
3094 	return value;
3095 }
3096 #endif
3097 
3098 /* address-range-declaration :== ip-address ip-address SEMI
3099 			       | DYNAMIC_BOOTP ip-address ip-address SEMI */
3100 
3101 void
parse_address_range(struct parse * cfile,int type,size_t where)3102 parse_address_range(struct parse *cfile, int type, size_t where)
3103 {
3104 	struct string *low, *high, *range;
3105 	unsigned char addr[4];
3106 	unsigned len = sizeof(addr);
3107 	enum dhcp_token token;
3108 	const char *val;
3109 	struct element *pool;
3110 	struct element *r;
3111 	struct range *chain;
3112 	size_t i;
3113 	int kind;
3114 
3115 	if ((token = peek_token(&val, NULL, cfile)) == DYNAMIC_BOOTP) {
3116 		skip_token(&val, NULL, cfile);
3117 	}
3118 
3119 	/* Get the bottom address in the range... */
3120 	low = parse_numeric_aggregate(cfile, addr, &len, DOT, 10, 8);
3121 	if (low == NULL)
3122 		parse_error(cfile, "can't parse range (low)");
3123 
3124 	/* Only one address? */
3125 	token = peek_token(&val, NULL, cfile);
3126 	if (token == SEMI)
3127 		high = low;
3128 	else {
3129 		/* Get the top address in the range... */
3130 		high = parse_numeric_aggregate(cfile, addr, &len, DOT, 10, 8);
3131 		if (high ==  NULL)
3132 			parse_error(cfile, "can't parse range (high)");
3133 	}
3134 
3135 	token = next_token(&val, NULL, cfile);
3136 	if (token != SEMI)
3137 		parse_error(cfile, "semicolon expected.");
3138 
3139 	if (type != POOL_DECL) {
3140 		struct element *group;
3141 		struct element *pools;
3142 #ifdef want_bootp
3143 		struct element *permit;
3144 #endif
3145 
3146 		group = cfile->stack[where];
3147 		pool = createMap();
3148 #ifdef want_bootp
3149 		permit = createList();
3150 		permit->skip = ISC_TRUE;
3151 
3152 		/* Dynamic pools permit all clients.   Otherwise
3153 		   we prohibit BOOTP clients. */
3154 		if (dynamic) {
3155 			struct string *all;
3156 
3157 			all = makeString(-1, "all clients");
3158 			listPush(permit, createString(all));
3159 			mapSet(pool, permit, "allow");
3160 		} else {
3161 			struct string *dyn_bootp;
3162 
3163 			dyn_bootp = makeString(-1, "dynamic bootp clients");
3164 			listPush(permit, createString(dyn_bootp));
3165 			mapSet(pool, permit, "deny");
3166 		}
3167 #endif
3168 
3169 		pools = mapGet(group, "pools");
3170 		if (pools == NULL) {
3171 			pools = createList();
3172 			pools->kind = POOL_DECL;
3173 			mapSet(group, pools, "pools");
3174 		}
3175 		listPush(pools, pool);
3176 	} else
3177 		pool = cfile->stack[where];
3178 
3179 	/* Create the new address range... */
3180 	if (memcmp(high->content, low->content, high->length) < 0) {
3181 		struct string *swap;
3182 
3183 		swap = low;
3184 		low = high;
3185 		high = swap;
3186 	}
3187 	range = makeStringExt(low->length, low->content, 'I');
3188 	appendString(range, " - ");
3189 	concatString(range, makeStringExt(high->length, high->content, 'I'));
3190 
3191 	r = createString(range);
3192 	TAILQ_CONCAT(&r->comments, &cfile->comments);
3193 
3194 	mapSet(pool, r, "pool");
3195 
3196 	chain = (struct range *)malloc(sizeof(*chain));
3197 	if (chain == NULL)
3198 		parse_error(cfile, "can't allocate range");
3199 	memset(chain, 0, sizeof(*chain));
3200 	chain->pool = pool;
3201 	for (i = where; i > 0; --i) {
3202 		kind = cfile->stack[i]->kind;
3203 		if (kind == SHARED_NET_DECL) {
3204 			chain->share = cfile->stack[i];
3205 			break;
3206 		}
3207 	}
3208 	chain->low = low;
3209 	TAILQ_INSERT_TAIL(&known_ranges, chain);
3210 }
3211 
3212 /* address-range6-declaration :== ip-address6 ip-address6 SEMI
3213 			       | ip-address6 SLASH number SEMI
3214 			       | ip-address6 [SLASH number] TEMPORARY SEMI */
3215 
3216 void
parse_address_range6(struct parse * cfile,int type,size_t where)3217 parse_address_range6(struct parse *cfile, int type, size_t where)
3218 {
3219 	struct string *low, *high, *range;
3220 	enum dhcp_token token;
3221 	const char *val;
3222 	isc_boolean_t is_temporary = ISC_FALSE;
3223 	struct element *pool;
3224 	struct element *r;
3225 	struct range *chain;
3226 	size_t i;
3227 	int kind;
3228 
3229         if (local_family != AF_INET6)
3230                 parse_error(cfile, "range6 statement is only supported "
3231 			    "in DHCPv6 mode.");
3232 
3233 	/*
3234 	 * Read starting address as text.
3235 	 */
3236 	low = parse_ip6_addr_txt(cfile);
3237 	if (low == NULL)
3238 		parse_error(cfile, "can't parse range6 address (low)");
3239 	range = allocString();
3240 	concatString(range, low);
3241 
3242 	/*
3243 	 * See if we we're using range or CIDR notation or TEMPORARY
3244 	 */
3245 	token = peek_token(&val, NULL, cfile);
3246 	if (token == SLASH) {
3247 		appendString(range, val);
3248 		/*
3249 		 * '/' means CIDR notation, so read the bits we want.
3250 		 */
3251 		skip_token(NULL, NULL, cfile);
3252 		token = next_token(&val, NULL, cfile);
3253 		if (token != NUMBER)
3254 			parse_error(cfile, "expecting number");
3255 		/*
3256 		 * no sanity checks
3257 		 */
3258 		appendString(range, val);
3259 		/*
3260 		 * can be temporary (RFC 4941 like)
3261 		 */
3262 		token = peek_token(&val, NULL, cfile);
3263 		if (token == TEMPORARY) {
3264 			is_temporary = ISC_TRUE;
3265 			appendString(range, " ");
3266 			appendString(range, val);
3267 			skip_token(NULL, NULL, cfile);
3268 		}
3269 	} else if (token == TEMPORARY) {
3270 		/*
3271 		 * temporary (RFC 4941)
3272 		 */
3273 		is_temporary = ISC_TRUE;
3274 		appendString(range, "/64 ");
3275 		appendString(range, val);
3276 		skip_token(NULL, NULL, cfile);
3277 	} else {
3278 		/*
3279 		 * No '/', so we are looking for the end address of
3280 		 * the IPv6 pool.
3281 		 */
3282 		high = parse_ip6_addr_txt(cfile);
3283 		if (high == NULL)
3284 			parse_error(cfile,
3285 				    "can't parse range6 address (high)");
3286 		/* No sanity checks */
3287 		appendString(range, " - ");
3288 		appendString(range, high->content);
3289 	}
3290 
3291 	token = next_token(NULL, NULL, cfile);
3292 	if (token != SEMI)
3293 		parse_error(cfile, "semicolon expected.");
3294 
3295 	if (type != POOL_DECL) {
3296 		struct element *group;
3297 		struct element *pools;
3298 
3299 		group = cfile->stack[where];
3300 		pool = createMap();
3301 		pools = mapGet(group, "pools");
3302 		if (pools == NULL) {
3303 			pools = createList();
3304 			pools->kind = POOL_DECL;
3305 			mapSet(group, pools, "pools");
3306 		}
3307 		listPush(pools, pool);
3308 	} else
3309 		pool = cfile->stack[where];
3310 
3311 	r = createString(range);
3312 	TAILQ_CONCAT(&r->comments, &cfile->comments);
3313 	if (is_temporary) {
3314 		pool->skip = ISC_TRUE;
3315 		cfile->issue_counter++;
3316 	}
3317 	mapSet(pool, r, "pool");
3318 
3319 	chain = (struct range *)malloc(sizeof(*chain));
3320 	if (chain == NULL)
3321 		parse_error(cfile, "can't allocate range");
3322 	memset(chain, 0, sizeof(*chain));
3323 	chain->pool = pool;
3324 	for (i = where; i > 0; --i) {
3325 		kind = cfile->stack[i]->kind;
3326 		if (kind == SHARED_NET_DECL) {
3327 			chain->share = cfile->stack[i];
3328 			break;
3329 		}
3330 	}
3331 	chain->low = low;
3332 	TAILQ_INSERT_TAIL(&known_ranges, chain);
3333 }
3334 
3335 /* prefix6-declaration :== ip-address6 ip-address6 SLASH number SEMI */
3336 
3337 void
parse_prefix6(struct parse * cfile,int type,size_t where)3338 parse_prefix6(struct parse *cfile, int type, size_t where)
3339 {
3340 	struct string *lo, *hi;
3341 	int plen;
3342 	int bits;
3343 	enum dhcp_token token;
3344 	const char *val;
3345 	struct element *pool;
3346 	struct element *prefix;
3347 
3348 	if (local_family != AF_INET6)
3349 		parse_error(cfile, "prefix6 statement is only supported "
3350 			    "in DHCPv6 mode.");
3351 
3352 	/*
3353 	 * Read starting and ending address as text.
3354 	 */
3355 	lo = parse_ip6_addr_txt(cfile);
3356 	if (lo == NULL)
3357 		parse_error(cfile, "can't parse prefix6 address (low)");
3358 
3359 	hi = parse_ip6_addr_txt(cfile);
3360 	if (hi == NULL)
3361 		parse_error(cfile, "can't parse prefix6 address (high)");
3362 
3363 	/*
3364 	 * Next is '/' number ';'.
3365 	 */
3366 	token = next_token(NULL, NULL, cfile);
3367 	if (token != SLASH)
3368 		parse_error(cfile, "expecting '/'");
3369 	token = next_token(&val, NULL, cfile);
3370 	if (token != NUMBER)
3371 		parse_error(cfile, "expecting number");
3372 	bits = atoi(val);
3373 	if ((bits <= 0) || (bits >= 128))
3374 		parse_error(cfile, "networks have 0 to 128 bits (exclusive)");
3375 
3376 	token = next_token(NULL, NULL, cfile);
3377 	if (token != SEMI)
3378 		parse_error(cfile, "semicolon expected.");
3379 
3380 	if (type != POOL_DECL) {
3381 		struct element *group;
3382 		struct element *pools;
3383 
3384 		group = cfile->stack[where];
3385 		pool = createMap();
3386 		pools = mapGet(group, "pd-pools");
3387 		if (pools == NULL) {
3388 			pools = createList();
3389 			pools->kind = POOL_DECL;
3390 			mapSet(group, pools, "pd-pools");
3391 		}
3392 		listPush(pools, pool);
3393 	} else
3394 		pool = cfile->stack[where];
3395 
3396 	prefix = createString(lo);
3397 	TAILQ_CONCAT(&prefix->comments, &cfile->comments);
3398 	mapSet(pool, prefix, "prefix");
3399 	mapSet(pool, createInt(bits), "delegated-len");
3400 	plen = get_prefix_length(lo->content, hi->content);
3401 	if (plen >= 0)
3402 		mapSet(pool, createInt(plen), "prefix-len");
3403 	else {
3404 		if (!pool->skip)
3405 			cfile->issue_counter++;
3406 		pool->skip = ISC_TRUE;
3407 		mapSet(pool, createString(hi), "prefix-highest");
3408 	}
3409 }
3410 
3411 /* fixed-prefix6 :== ip6-address SLASH number SEMI */
3412 
3413 void
parse_fixed_prefix6(struct parse * cfile,size_t host_decl)3414 parse_fixed_prefix6(struct parse *cfile, size_t host_decl)
3415 {
3416 	struct string *ia;
3417 	enum dhcp_token token;
3418 	const char *val;
3419 	struct element *host;
3420 	struct element *prefixes;
3421 	struct element *prefix;
3422 
3423 	if (local_family != AF_INET6)
3424 		parse_error(cfile, "fixed-prefix6 statement is only "
3425 			    "supported in DHCPv6 mode.");
3426 
3427 	/*
3428 	 * Get the fixed-prefix list.
3429 	 */
3430 	host = cfile->stack[host_decl];
3431 	prefixes = mapGet(host, "prefixes");
3432 	if (prefixes == NULL) {
3433 		prefixes = createList();
3434 		mapSet(host, prefixes, "prefixes");
3435 	}
3436 
3437 	ia = parse_ip6_addr_txt(cfile);
3438 	if (ia == NULL)
3439 		parse_error(cfile, "can't parse fixed-prefix6 address");
3440 	token = next_token(&val, NULL, cfile);
3441 	if (token != SLASH)
3442 		parse_error(cfile, "expecting '/'");
3443 	appendString(ia, val);
3444 	token = next_token(&val, NULL, cfile);
3445 	if (token != NUMBER)
3446 		parse_error(cfile, "expecting number");
3447 	appendString(ia, val);
3448 	token = next_token(NULL, NULL, cfile);
3449 	if (token != SEMI)
3450 		parse_error(cfile, "semicolon expected.");
3451 
3452 	prefix = createString(ia);
3453 	TAILQ_CONCAT(&prefix->comments, &cfile->comments);
3454 	listPush(prefixes, prefix);
3455 }
3456 
3457 /*!
3458  *
3459  * \brief Parse a pool6 statement
3460  *
3461  * Pool statements are used to group declarations and permit & deny information
3462  * with a specific address range.  They must be declared within a shared network
3463  * or subnet and there may be multiple pools withing a shared network or subnet.
3464  * Each pool may have a different set of permit or deny options.
3465  *
3466  * \param[in] cfile = the configuration file being parsed
3467  * \param[in] type  = the type of the enclosing statement.  This must be
3468  *		      SUBNET_DECL for this function.
3469  *
3470  * \return
3471  * void - This function either parses the statement and updates the structures
3472  *        or it generates an error message and possible halts the program if
3473  *        it encounters a problem.
3474  */
3475 
3476 void
parse_pool6_statement(struct parse * cfile,int type)3477 parse_pool6_statement(struct parse *cfile, int type)
3478 {
3479 	enum dhcp_token token;
3480 	const char *val;
3481 	isc_boolean_t done = ISC_FALSE;
3482 	struct element *pool;
3483 	struct element *pools;
3484 	struct element *pdpool;
3485 	struct element *pdpools;
3486 	struct element *permit;
3487 	struct element *prohibit;
3488 	int declaration = 0;
3489 	unsigned range_counter = 0;
3490 	unsigned prefix_counter = 0;
3491 
3492 	if (local_family != AF_INET6)
3493 		parse_error(cfile, "pool6 statement is only supported "
3494 			    "in DHCPv6 mode.");
3495 
3496 	pool = createMap();
3497 	pool->kind = POOL_DECL;
3498 	TAILQ_CONCAT(&pool->comments, &cfile->comments);
3499 
3500 	if (type != SUBNET_DECL)
3501 		parse_error(cfile, "pool6s are only valid inside "
3502 			    "subnet statements.");
3503 	parse_lbrace(cfile);
3504 
3505 	stackPush(cfile, pool);
3506 	type = POOL_DECL;
3507 
3508 	permit = createList();
3509 	prohibit = createList();
3510 
3511 	do {
3512 		token = peek_token(&val, NULL, cfile);
3513 		switch (token) {
3514 		case RANGE6:
3515 			skip_token(NULL, NULL, cfile);
3516 			parse_address_range6(cfile, type, cfile->stack_top);
3517 			range_counter++;
3518 			break;
3519 
3520 		case PREFIX6:
3521 			skip_token(NULL, NULL, cfile);
3522 			parse_prefix6(cfile, type, cfile->stack_top);
3523 			mapSet(pool, createNull(), "***mark***");
3524 			prefix_counter++;
3525 			break;
3526 
3527 		case ALLOW:
3528 			skip_token(NULL, NULL, cfile);
3529 			get_permit(cfile, permit);
3530 			break;
3531 
3532 		case DENY:
3533 			skip_token(NULL, NULL, cfile);
3534 			get_permit(cfile, prohibit);
3535 			break;
3536 
3537 		case RBRACE:
3538 			skip_token(&val, NULL, cfile);
3539 			done = ISC_TRUE;
3540 			break;
3541 
3542 		case END_OF_FILE:
3543 			/*
3544 			 * We can get to END_OF_FILE if, for instance,
3545 			 * the parse_statement() reads all available tokens
3546 			 * and leaves us at the end.
3547 			 */
3548 			parse_error(cfile, "unexpected end of file");
3549 
3550 		default:
3551 			declaration = parse_statement(cfile, POOL_DECL,
3552 						      declaration);
3553 			break;
3554 		}
3555 	} while (!done);
3556 
3557 	cfile->stack_top--;
3558 
3559 	generate_class(cfile, pool, permit, prohibit);
3560 
3561 	/*
3562 	 * Spread and eventually split between pools and pd-pools
3563 	 */
3564 	if (prefix_counter == 0) {
3565 		/* we need pools list */
3566 		pools = mapGet(cfile->stack[cfile->stack_top], "pools");
3567 		if (pools == NULL) {
3568 			pools = createList();
3569 			pools->kind = POOL_DECL;
3570 			mapSet(cfile->stack[cfile->stack_top], pools, "pools");
3571 		}
3572 
3573 		/* no address or prefix range */
3574 		if (range_counter == 0) {
3575 			struct comment *comment;
3576 
3577 			comment = createComment("empty pool6");
3578 			TAILQ_INSERT_TAIL(&pool->comments, comment);
3579 			pool->skip = ISC_TRUE;
3580 			cfile->issue_counter++;
3581 			listPush(pools, pool);
3582 			return;
3583 		}
3584 	} else {
3585 		/* we need pd-pools list */
3586 		pdpools = mapGet(cfile->stack[cfile->stack_top], "pd-pools");
3587 		if (pdpools == NULL) {
3588 			pdpools = createList();
3589 			pdpools->kind = POOL_DECL;
3590 			mapSet(cfile->stack[cfile->stack_top],
3591 			       pdpools, "pd-pools");
3592 		}
3593 
3594 		/* split and purge copies */
3595 		pdpool = copy(pool);
3596 		while (mapContains(pdpool, "pool"))
3597 			mapRemove(pdpool, "pool");
3598 		while (mapContains(pool, "prefix"))
3599 			mapRemove(pool, "prefix");
3600 		while (mapContains(pool, "prefix-len"))
3601 			mapRemove(pool, "prefix-len");
3602 		while (mapContains(pool, "delegated-len"))
3603 			mapRemove(pool, "delegated-len");
3604 		while (mapContains(pool, "excluded-prefix"))
3605 			mapRemove(pool, "excluded-prefix");
3606 		while (mapContains(pool, "excluded-prefix-len"))
3607 			mapRemove(pool, "excluded-prefix-len");
3608 		while (mapContains(pool, "***mark***"))
3609 			mapRemove(pool, "***mark***");
3610 
3611 		/* spread extra prefixes into pdpool copies */
3612 		while (--prefix_counter != 0) {
3613 			struct handle *handle;
3614 			struct element *first;
3615 			struct element *saved;
3616 			isc_boolean_t seen = ISC_FALSE;
3617 
3618 			first = createMap();
3619 			saved = copy(pdpool);
3620 			while (mapSize(pdpool) > 0) {
3621 				handle = mapPop(pdpool);
3622 				if ((handle == NULL) ||
3623 				    (handle->key == NULL) ||
3624 				    (handle->value == NULL))
3625 					parse_error(cfile, "bad pdpool entry");
3626 				if (strcmp(handle->key, "***mark***") == 0) {
3627 					if (!seen) {
3628 						mapRemove(saved, handle->key);
3629 						seen = ISC_TRUE;
3630 					}
3631 					continue;
3632 				}
3633 				if ((strcmp(handle->key, "prefix") != 0) &&
3634 				    (strcmp(handle->key, "prefix-len") != 0) &&
3635 				    (strcmp(handle->key,
3636 					    "delegated-len") != 0) &&
3637 				    (strcmp(handle->key,
3638 					    "excluded-prefix") != 0) &&
3639 				    (strcmp(handle->key,
3640 					    "excluded-prefix-len") != 0))
3641 					mapSet(first, handle->value,
3642 					       handle->key);
3643 				else if (!seen) {
3644 					mapSet(first, handle->value,
3645 					       handle->key);
3646 					mapRemove(saved, handle->key);
3647 				}
3648 			}
3649 			listPush(pdpools, first);
3650 			pdpool = saved;
3651 		}
3652 		if (!mapContains(pdpool, "***mark***"))
3653 			parse_error(cfile, "can't find prefix marker");
3654 		mapRemove(pdpool, "***mark***");
3655 		if (mapContains(pdpool, "***mark***"))
3656 			parse_error(cfile, "unexpected prefix marker");
3657 		listPush(pdpools, pdpool);
3658 	}
3659 
3660 	/* Do pools now */
3661 	if (range_counter != 0) {
3662 		/* we need pools list */
3663 		pools = mapGet(cfile->stack[cfile->stack_top], "pools");
3664 		if (pools == NULL) {
3665 			pools = createList();
3666 			pools->kind = POOL_DECL;
3667 			mapSet(cfile->stack[cfile->stack_top], pools, "pools");
3668 		}
3669 
3670 		/* spread extra prefixes into pool copies */
3671 		while (--range_counter != 0) {
3672 			struct handle *handle;
3673 			struct element *first;
3674 			struct element *saved;
3675 			isc_boolean_t seen = ISC_FALSE;
3676 
3677 			first = createMap();
3678 			saved = copy(pool);
3679 			while (mapSize(pool) > 0) {
3680 				handle = mapPop(pool);
3681 				if ((handle == NULL) ||
3682 				    (handle->key == NULL) ||
3683 				    (handle->value == NULL))
3684 					parse_error(cfile, "bad pool entry");
3685 				if (strcmp(handle->key, "pool") != 0)
3686 					mapSet(first, handle->value,
3687 					       handle->key);
3688 				else if (!seen) {
3689 					mapSet(first, handle->value,
3690 					       handle->key);
3691 					mapRemove(saved, "pool");
3692 					seen = ISC_TRUE;
3693 				}
3694 			}
3695 			listPush(pools, first);
3696 			pool = saved;
3697 		}
3698 		listPush(pools, pool);
3699 	}
3700 }
3701 
3702 /* allow-deny-keyword :== BOOTP
3703    			| BOOTING
3704 			| DYNAMIC_BOOTP
3705 			| UNKNOWN_CLIENTS */
3706 
3707 struct element *
parse_allow_deny(struct parse * cfile,int flag)3708 parse_allow_deny(struct parse *cfile, int flag)
3709 {
3710 	enum dhcp_token token;
3711 	const char *val;
3712 	const char *value;
3713 	const char *name;
3714 	struct element *config;
3715 	struct option *option;
3716 
3717 	switch (flag) {
3718 	case 0:
3719 		value = "deny";
3720 		break;
3721 	case 1:
3722 		value = "allow";
3723 		break;
3724 	case 2:
3725 		value = "ignore";
3726 		break;
3727 	default:
3728 		value = "unknown?";
3729 		break;
3730 	}
3731 
3732 	token = next_token(&val, NULL, cfile);
3733 	switch (token) {
3734 	case TOKEN_BOOTP:
3735 		name = "allow-bootp";
3736 		break;
3737 
3738 	case BOOTING:
3739 		name = "allow-booting";
3740 		break;
3741 
3742 	case DYNAMIC_BOOTP:
3743 		name = "dynamic-bootp";
3744 		break;
3745 
3746 	case UNKNOWN_CLIENTS:
3747 		name = "boot-unknown-clients";
3748 		break;
3749 
3750 	case DUPLICATES:
3751 		name = "duplicates";
3752 		break;
3753 
3754 	case DECLINES:
3755 		name = "declines";
3756 		break;
3757 
3758 	case CLIENT_UPDATES:
3759 		name = "client-updates";
3760 		break;
3761 
3762 	case LEASEQUERY:
3763 		name = "leasequery";
3764 		break;
3765 
3766 	default:
3767 		parse_error(cfile, "expecting allow/deny key");
3768 	}
3769 	parse_semi(cfile);
3770 
3771 	config = createMap();
3772 	mapSet(config, createString(makeString(-1, value)), "value");
3773 	mapSet(config, createString(makeString(-1, name)), "name");
3774 	option = option_lookup_name("server", name);
3775 	if (option == NULL)
3776 		parse_error(cfile, "unknown allow/deny keyword (%s)", name);
3777 	mapSet(config, createInt(option->code), "code");
3778 	config->skip = ISC_TRUE;
3779 	cfile->issue_counter++;
3780 	return config;
3781 }
3782 
3783 /*
3784  * When we parse a server-duid statement in a config file, we will
3785  * have the type of the server DUID to generate, and possibly the
3786  * actual value defined.
3787  *
3788  * server-duid llt;
3789  * server-duid llt ethernet|ieee802|fddi 213982198 00:16:6F:49:7D:9B;
3790  * server-duid ll;
3791  * server-duid ll ethernet|ieee802|fddi 00:16:6F:49:7D:9B;
3792  * server-duid en 2495 "enterprise-specific-identifier-1234";
3793  */
3794 void
parse_server_duid_conf(struct parse * cfile)3795 parse_server_duid_conf(struct parse *cfile) {
3796 	enum dhcp_token token;
3797 	const char *val;
3798 	unsigned int len;
3799 	struct string *ll_addr;
3800 	struct element *duid;
3801 	struct element *item;
3802 	int ll_type;
3803 
3804 	duid = createMap();
3805 	TAILQ_CONCAT(&duid->comments, &cfile->comments);
3806 
3807 	/*
3808 	 * Consume the SERVER_DUID token.
3809 	 */
3810 	next_token(&val, NULL, cfile);
3811 
3812 	/*
3813 	 * Obtain the DUID type.
3814 	 */
3815 	token = next_token(&val, NULL, cfile);
3816 
3817 	/*
3818 	 * Enterprise is the easiest - enterprise number and raw data
3819 	 * are required.
3820 	 */
3821 	if (token == EN) {
3822 		item = createString(makeString(-1, "EN"));
3823 		mapSet(duid, item, "type");
3824 
3825 		/*
3826 		 * Get enterprise number and identifier.
3827 		 */
3828 		token = next_token(&val, NULL, cfile);
3829 		if (token != NUMBER)
3830 			parse_error(cfile, "enterprise number expected");
3831 		item = createInt(atoi(val));
3832 		mapSet(duid, item, "enterprise-id");
3833 
3834 		token = next_token(&val, &len, cfile);
3835 		if (token != STRING)
3836 			parse_error(cfile, "identifier expected");
3837 		/* Kea requires a hexadecimal identifier */
3838 		if (is_hexa_only(val, len))
3839 			item = createString(makeString(len, val));
3840 		else
3841 			item = createString(makeStringExt(len, val, 'X'));
3842 		mapSet(duid, item, "identifier");
3843 	}
3844 
3845 	/*
3846 	 * Next easiest is the link-layer DUID. It consists only of
3847 	 * the LL directive, or optionally the specific value to use.
3848 	 *
3849 	 * If we have LL only, then we set the type. If we have the
3850 	 * value, then we set the actual DUID.
3851 	 */
3852 	else if (token == LL) {
3853 		item = createString(makeString(-1, "LL"));
3854 		mapSet(duid, item, "type");
3855 
3856 		if (peek_token(NULL, NULL, cfile) != SEMI) {
3857 			/*
3858 			 * Get our hardware type and address.
3859 			 */
3860 			token = next_token(NULL, NULL, cfile);
3861 			switch (token) {
3862 			case ETHERNET:
3863 				ll_type = HTYPE_ETHER;
3864 				break;
3865 			case TOKEN_RING:
3866 				ll_type = HTYPE_IEEE802;
3867 				break;
3868 			case TOKEN_FDDI:
3869 				ll_type = HTYPE_FDDI;
3870 				break;
3871 			default:
3872 				parse_error(cfile, "hardware type expected");
3873 			}
3874 			item = createInt(ll_type);
3875 			mapSet(duid, item, "htype");
3876 
3877 			ll_addr = parse_hexa(cfile);
3878 			if (ll_addr == NULL)
3879 				parse_error(cfile,
3880 					    "can't get hardware address");
3881 			item = createString(ll_addr);
3882 			mapSet(duid, item, "identifier");
3883 		}
3884 	}
3885 
3886 	/*
3887 	 * Finally the link-layer DUID plus time. It consists only of
3888 	 * the LLT directive, or optionally the specific value to use.
3889 	 *
3890 	 * If we have LLT only, then we set the type. If we have the
3891 	 * value, then we set the actual DUID.
3892 	 */
3893 	else if (token == LLT) {
3894 		item = createString(makeString(-1, "LLT"));
3895 		mapSet(duid, item, "type");
3896 
3897 		if (peek_token(NULL, NULL, cfile) != SEMI) {
3898 			/*
3899 			 * Get our hardware type, timestamp, and address.
3900 			 */
3901 			token = next_token(NULL, NULL, cfile);
3902 			switch (token) {
3903 			case ETHERNET:
3904 				ll_type = HTYPE_ETHER;
3905 				break;
3906 			case TOKEN_RING:
3907 				ll_type = HTYPE_IEEE802;
3908 				break;
3909 			case TOKEN_FDDI:
3910 				ll_type = HTYPE_FDDI;
3911 				break;
3912 			default:
3913 				parse_error(cfile, "hardware type expected");
3914 			}
3915 			item = createInt(ll_type);
3916 			mapSet(duid, item, "htype");
3917 
3918 			token = next_token(&val, NULL, cfile);
3919 			if (token != NUMBER)
3920 				parse_error(cfile, "timestamp expected");
3921 			item = createInt(atoi(val));
3922 			mapSet(duid, item, "time");
3923 
3924 			ll_addr = parse_hexa(cfile);
3925 			if (ll_addr == NULL)
3926 				parse_error(cfile,
3927 					    "can't get hardware address");
3928 			item = createString(ll_addr);
3929 			mapSet(duid, item, "identifier");
3930 		}
3931 	}
3932 
3933 	/*
3934 	 * If users want they can use a number for DUID types.
3935 	 * This is useful for supporting future, not-yet-defined
3936 	 * DUID types.
3937 	 *
3938 	 * In this case, they have to put in the complete value.
3939 	 *
3940 	 * This also works for existing DUID types of course.
3941 	 */
3942 	else if (token == NUMBER) {
3943 		item = createString(makeString(-1, val));
3944 		item->skip = ISC_TRUE;
3945 		/* Kea wants EN, LL or LLT so skip the whole thing */
3946 		duid->skip = ISC_TRUE;
3947 		cfile->issue_counter++;
3948 		mapSet(duid, item, "type");
3949 
3950 		token = next_token(&val, &len, cfile);
3951 		if (token != STRING)
3952 			parse_error(cfile, "identifier expected");
3953 		item = createString(makeString(len, val));
3954 		mapSet(duid, item, "identifier");
3955 	}
3956 
3957 	/*
3958 	 * Anything else is an error.
3959 	 */
3960 	else
3961 		parse_error(cfile, "DUID type of LLT, EN, or LL expected");
3962 
3963 	/*
3964 	 * Finally consume our trailing semicolon.
3965 	 */
3966 	token = next_token(NULL, NULL, cfile);
3967 	if (token != SEMI)
3968 		parse_error(cfile, "semicolon expected");
3969 
3970 	/* server-id is a global parameter */
3971 	if (mapContains(cfile->stack[1], "server-id"))
3972 		parse_error(cfile, "there is already a server-id");
3973 	/* DHCPv6 only but not fatal */
3974 	if ((local_family != AF_INET6) && !duid->skip) {
3975 		duid->skip = ISC_TRUE;
3976 		cfile->issue_counter++;
3977 	}
3978 	mapSet(cfile->stack[1], duid, "server-id");
3979 }
3980 
3981 /* Check whether the argument is encoded in hexadecimal or not */
3982 static isc_boolean_t
is_hexa_only(const char * s,unsigned l)3983 is_hexa_only(const char *s, unsigned l)
3984 {
3985 	unsigned i;
3986 
3987 	for (i = 0; i < l; i++)
3988 		if (!isxdigit((int)s[i]))
3989 			return ISC_FALSE;
3990 	return ISC_TRUE;
3991 }
3992 
3993 /*!
3994  *
3995  * \brief Parse (and execute) a directive (extension)
3996  *
3997  * OPTION SPACE <name> [ALIAS <kea-name>] [KNOWN*2|UNKNOWN*2|DYNAMIC]
3998  * OPTION <universe>.<name> [CHECK]
3999  *                          [ALIAS <name>]
4000  *                          [CODE <code> = "<format>"]
4001  *                          [KNOWN*2|UNKNOWN*2|DYNAMIC]
4002  *                          [LOCAL|DEFINE]
4003  */
4004 
4005 void
parse_directive(struct parse * cfile)4006 parse_directive(struct parse *cfile)
4007 {
4008 	enum dhcp_token token;
4009 	const char *val;
4010 	isc_boolean_t known;
4011 	struct option *option;
4012 
4013 	token = peek_token(&val, NULL, cfile);
4014 
4015 	switch (token) {
4016 	case OPTION:
4017 		skip_token(&val, NULL, cfile);
4018 		token = peek_token(&val, NULL, cfile);
4019 		if (token == SPACE) {
4020 			parse_option_space_dir(cfile);
4021 			return;
4022 		}
4023 
4024 		known = ISC_FALSE;
4025 		option = parse_option_name(cfile, ISC_TRUE, &known);
4026 		token = next_token(&val, NULL, cfile);
4027 		if (token == CHECK) {
4028 			struct string *datatype;
4029 			isc_boolean_t is_array = ISC_FALSE;
4030 			isc_boolean_t encapsulate = ISC_FALSE;
4031 
4032 			datatype = convert_format(option->format,
4033 						  &is_array,
4034 						  &encapsulate);
4035 			printf("option ISC DHCP (Kea)\n"
4036 			       " %s.%s (%s.%s)\n"
4037 			       " format \"%s\" (type \"%s\" "
4038 			       "array %s encap %s)\n"
4039 			       " status %s\n",
4040 			       option->space->old, option->old,
4041 			       option->space->name, option->name,
4042 			       option->format, datatype->content,
4043 			       is_array ? "true" : "false",
4044 			       encapsulate ? "true" : "false",
4045 			       display_status(option->status));
4046 			parse_semi(cfile);
4047 			return;
4048 		}
4049 		if (option->space->status == special)
4050 			parse_error(cfile, "attempt to modify config %s.%s",
4051 				    option->space->old, option->name);
4052 		if (token == ALIAS) {
4053 			token = next_token(&val, NULL, cfile);
4054 			if (!is_identifier(token))
4055 				parse_error(cfile,
4056 					    "expecting identifier after "
4057 					    "alias keyword.");
4058 			if (option->status != dynamic)
4059 				parse_error(cfile,
4060 					    "attempt to rename %s.%s to %s",
4061 					    option->space->name,
4062 					    option->name, val);
4063 			option->name = strdup(val);
4064 			parse_semi(cfile);
4065 			return;
4066 		}
4067 		if (token == CODE) {
4068 			parse_option_code_dir(cfile, option);
4069 			return;
4070 		}
4071 		if ((token == KNOWN) || (token == UNKNOWN) ||
4072 		    (token == DYNAMIC)) {
4073 			parse_option_status_dir(cfile, option, token);
4074 			return;
4075 		}
4076 		if (token == LOCAL) {
4077 			parse_option_local_dir(cfile, option);
4078 			parse_semi(cfile);
4079 			return;
4080 		}
4081 		if (token == DEFINE) {
4082 			parse_option_define_dir(cfile, option);
4083 			parse_semi(cfile);
4084 			return;
4085 		}
4086 		parse_error(cfile, "unknown option directive %s", val);
4087 
4088 	default:
4089 		parse_error(cfile, "unknown directive %s", val);
4090 	}
4091 }
4092 
4093 /* Set alias and status for option spaces */
4094 
4095 void
parse_option_space_dir(struct parse * cfile)4096 parse_option_space_dir(struct parse *cfile)
4097 {
4098 	enum dhcp_token token;
4099 	const char *val;
4100 	struct space *space;
4101 
4102 	skip_token(NULL, NULL, cfile);	/* Discard SPACE */
4103 	token = next_token(&val, NULL, cfile);
4104 	if (!is_identifier(token))
4105 		parse_error(cfile, "expecting identifier.");
4106 	space = space_lookup(val);
4107 	if (space == NULL)
4108 		parse_error(cfile, "can't find space '%s", val);
4109 
4110 	token = next_token(&val, NULL, cfile);
4111 	if (token == CHECK) {
4112 		printf("space ISC DHCP (kea)\n"
4113 		       " %s (%s)\n status %s\n%s",
4114 		       space->old, space->name,
4115 		       display_status(space->status),
4116 		       space->vendor != NULL ? " vendor\n" : "");
4117 		parse_semi(cfile);
4118 		return;
4119 	}
4120 	if (token == ALIAS) {
4121 		token = next_token(&val, NULL, cfile);
4122 		if (!is_identifier(token))
4123 			parse_error(cfile,
4124 				    "expecting identifier after "
4125 				    "alias keyword.");
4126 		if (space->status != dynamic)
4127 			parse_error(cfile,
4128 				    "attempt to rename %s to %s",
4129 				    space->name, val);
4130 		space->name = strdup(val);
4131 		parse_semi(cfile);
4132 		return;
4133 	}
4134 	if (token == DYNAMIC)
4135 		space->status = dynamic;
4136 	else if (token == UNKNOWN) {
4137 		token = next_token(NULL, NULL, cfile);
4138 		if (token == KNOWN)
4139 			space->status = known;
4140 		else if (token == UNKNOWN)
4141 			space->status = kea_unknown;
4142 		else
4143 			parse_error(cfile, "expected KNOW or UNKNOWN");
4144 	} else if (token != UNKNOWN)
4145 		parse_error(cfile, "expected KNOW or UNKNOWN or DYNAMIC");
4146 	else {
4147                 if (token == KNOWN)
4148 			space->status = isc_dhcp_unknown;
4149                 else if (token == UNKNOWN)
4150                         parse_error(cfile, "illicit combination: space "
4151                                     "%s is known by nobody", space->name);
4152 		else
4153 			parse_error(cfile, "expected KNOW or UNKNOWN");
4154 	}
4155 	parse_semi(cfile);
4156 }
4157 
4158 /* Alternative to parse_option_code_decl using the raw ISC DHCP format */
4159 
4160 void
parse_option_code_dir(struct parse * cfile,struct option * option)4161 parse_option_code_dir(struct parse *cfile, struct option *option)
4162 {
4163 	const char *val;
4164 	enum dhcp_token token;
4165 	unsigned code;
4166 	struct element *def;
4167 	struct element *optdef;
4168 	struct string *datatype;
4169 	isc_boolean_t is_array = ISC_FALSE;
4170 	isc_boolean_t encapsulate = ISC_FALSE;
4171 
4172 	def = createMap();
4173 	mapSet(def,
4174 	       createString(makeString(-1, option->space->name)),
4175 	       "space");
4176 	mapSet(def, createString(makeString(-1, option->name)), "name");
4177 
4178 	/* Parse the option code. */
4179 	token = next_token(&val, NULL, cfile);
4180 	if (token != NUMBER)
4181 		parse_error(cfile, "expecting option code number.");
4182 	code = atoi(val);
4183 	mapSet(def, createInt(code), "code");
4184 
4185 	/* We have the code so we can get the real option now */
4186 	if (option->code == 0) {
4187 		struct option *from_code;
4188 
4189 		option->code = code;
4190 		from_code = option_lookup_code(option->space->old, code);
4191 		if (from_code != NULL)
4192 			option = from_code;
4193 	}
4194 
4195 	/* Redefinitions are not allowed */
4196 	if ((option->status != dynamic) ||
4197 	    (strcmp(option->format, "u") != 0))
4198 		parse_error(cfile, "attempt to redefine %s.%s code %u",
4199 			    option->space->name, option->name, code);
4200 
4201 	token = next_token(&val, NULL, cfile);
4202 	if (token != EQUAL)
4203 		parse_error(cfile, "expecting \"=\"");
4204 	token = next_token(&val, NULL, cfile);
4205 	if (token != STRING)
4206 		parse_error(cfile, "expecting format string");
4207 	option->format = strdup(val);
4208 	parse_semi(cfile);
4209 
4210 	datatype = convert_format(val, &is_array, &encapsulate);
4211 
4212 	if ((datatype == NULL) && (strchr(datatype->content, '?') != NULL))
4213 		parse_error(cfile, "failed to convert format \"%s\" for "
4214 			    "option %s.%s code %u",
4215 			    val, option->space->name, option->name, code);
4216 	/* todo */
4217 	if (encapsulate)
4218 		parse_error(cfile, "option %s.%s code %u encapsulate?",
4219 			    option->space->name, option->name, code);
4220 
4221 	if (strchr(datatype->content, ',') == NULL)
4222 		mapSet(def, createString(datatype), "type");
4223 	else {
4224 		mapSet(def, createString(datatype), "record-types");
4225 		mapSet(def, createString(makeString(-1, "record")), "type");
4226 	}
4227 	if (is_array)
4228 		mapSet(def, createBool(ISC_TRUE), "array");
4229 
4230 	optdef = mapGet(cfile->stack[1], "option-def");
4231 	if (optdef == NULL) {
4232 		optdef = createList();
4233 		mapSet(cfile->stack[1], optdef, "option-def");
4234 	}
4235 	listPush(optdef, def);
4236 }
4237 
4238 /* Update the option status for instance to add standard options */
4239 
4240 void
parse_option_status_dir(struct parse * cfile,struct option * option,enum dhcp_token token)4241 parse_option_status_dir(struct parse *cfile, struct option *option,
4242 			enum dhcp_token token)
4243 {
4244 	if (token == DYNAMIC)
4245 		option->status = dynamic;
4246 	else if (token == KNOWN) {
4247 		token = next_token(NULL, NULL, cfile);
4248 		if (token == KNOWN)
4249 			option->status = known;
4250 		else if (token == UNKNOWN)
4251 			option->status = kea_unknown;
4252 		else
4253 			parse_error(cfile, "expected KNOW or UNKNOWN");
4254 	} else if (token != UNKNOWN)
4255 		parse_error(cfile, "expected KNOW or UNKNOWN or DYNAMIC");
4256 	else {
4257 		if (token == KNOWN)
4258 			option->status = isc_dhcp_unknown;
4259 		else if (token == UNKNOWN)
4260 			parse_error(cfile, "illicit combination: option "
4261 				    "%s.%s code %u is known by nobody",
4262 				    option->space->name, option->name,
4263 				    option->code);
4264 		else
4265 			parse_error(cfile, "expected KNOW or UNKNOWN");
4266 	}
4267 	parse_semi(cfile);
4268 }
4269 
4270 /* Make the option definition not exported to Kea */
4271 
4272 void
parse_option_local_dir(struct parse * cfile,struct option * option)4273 parse_option_local_dir(struct parse *cfile, struct option *option)
4274 {
4275 	struct element *optdef;
4276 	struct element *def;
4277 	struct element *elem;
4278 	size_t i;
4279 
4280 	def = NULL;
4281 	if (option->code == 0)
4282 		parse_error(cfile, "unknown code for option %s.%s",
4283 			    option->space->name, option->name);
4284 
4285 	optdef = mapGet(cfile->stack[1], "option-def");
4286 	if (optdef == NULL) {
4287 		optdef = createList();
4288 		mapSet(cfile->stack[1], optdef, "option-def");
4289 		goto not_found;
4290 	}
4291 	for (i = 0; i < listSize(optdef); i++) {
4292 		def = listGet(optdef, i);
4293 		elem = mapGet(def, "space");
4294 		if ((elem == NULL) || (elem->type != ELEMENT_STRING))
4295 			parse_error(cfile, "got an option definition "
4296 				    "without space at %u", (unsigned)i);
4297 		if (strcmp(option->space->name,
4298 			   stringValue(elem)->content) != 0)
4299 			continue;
4300 		elem = mapGet(def, "code");
4301 		if ((elem == NULL) || (elem->type != ELEMENT_INTEGER))
4302 			parse_error(cfile, "got an option definition "
4303 				    "without code at %u", (unsigned)i);
4304 		if (intValue(elem) == option->code)
4305 			break;
4306 	}
4307 	if (def == NULL)
4308 		goto not_found;
4309 	def->skip = ISC_TRUE;
4310 	mapSet(def, createNull(), "no-export");
4311 	return;
4312 
4313 not_found:
4314 	parse_error(cfile, "can't find option %s.%s code %u in definitions",
4315 		    option->space->name, option->name, option->code);
4316 }
4317 
4318 /* Make the opposite: force the definition */
4319 
4320 void
parse_option_define_dir(struct parse * cfile,struct option * option)4321 parse_option_define_dir(struct parse *cfile, struct option *option)
4322 {
4323 	struct element *optdef;
4324 	struct element *def;
4325 	struct element *elem;
4326 	struct string *datatype;
4327 	isc_boolean_t is_array = ISC_FALSE;
4328 	isc_boolean_t encapsulate = ISC_FALSE;
4329 	size_t i;
4330 
4331 	def = NULL;
4332 	if (option->code == 0)
4333 		parse_error(cfile, "unknown code for option %s.%s",
4334 			    option->space->name, option->name);
4335 
4336 	optdef = mapGet(cfile->stack[1], "option-def");
4337 	if (optdef == NULL) {
4338 		optdef = createList();
4339 		mapSet(cfile->stack[1], optdef, "option-def");
4340 		goto no_search;
4341 	}
4342 	for (i = 0; i < listSize(optdef); i++) {
4343 		def = listGet(optdef, i);
4344 		elem = mapGet(def, "space");
4345 		if ((elem == NULL) || (elem->type != ELEMENT_STRING))
4346 			parse_error(cfile, "got an option definition "
4347 				    "without space at %u", (unsigned)i);
4348 		if (strcmp(option->space->name,
4349 			   stringValue(elem)->content) != 0)
4350 			continue;
4351 		elem = mapGet(def, "code");
4352 		if ((elem == NULL) || (elem->type != ELEMENT_INTEGER))
4353 			parse_error(cfile, "got an option definition "
4354 				    "without code at %u", (unsigned)i);
4355 		if (intValue(elem) == option->code)
4356 			parse_error(cfile, "unexpected definition for "
4357 				    "option %s.%s code %u",
4358 				    option->space->name, option->name,
4359 				    option->code);
4360 	}
4361 no_search:
4362 	def = createMap();
4363 	mapSet(def,
4364 	       createString(makeString(-1, option->space->name)),
4365 	       "space");
4366 	mapSet(def, createString(makeString(-1, option->name)), "name");
4367 	mapSet(def, createInt(option->code), "code");
4368 
4369 	datatype = convert_format(option->format, &is_array, &encapsulate);
4370 
4371 	if ((datatype == NULL) && (strchr(datatype->content, '?') != NULL))
4372 		parse_error(cfile, "failed to convert format \"%s\" for "
4373 			    "option %s.%s code %u",
4374 			    option->format, option->space->name,
4375 			    option->name, option->code);
4376 	/* todo */
4377 	if (encapsulate)
4378 		parse_error(cfile, "option %s.%s code %u encapsulate?",
4379 			    option->space->name, option->name, option->code);
4380 
4381 	if (strchr(datatype->content, ',') == NULL)
4382 		mapSet(def, createString(datatype), "type");
4383 	else {
4384 		mapSet(def, createString(datatype), "record-types");
4385 		mapSet(def, createString(makeString(-1, "record")), "type");
4386 	}
4387 	if (is_array)
4388 		mapSet(def, createBool(ISC_TRUE), "array");
4389 
4390 	listPush(optdef, def);
4391 
4392 	return;
4393 }
4394 
4395 /*
4396  * Push new interface on the interface list when it is not already.
4397  */
4398 
4399 static void
new_network_interface(struct parse * cfile,struct element * iface)4400 new_network_interface(struct parse *cfile, struct element *iface)
4401 {
4402 	struct element *ifconf;
4403 	struct element *iflist;
4404 	struct string *name = stringValue(iface);
4405 	int i;
4406 
4407 	ifconf = mapGet(cfile->stack[1], "interfaces-config");
4408 	if (ifconf == NULL) {
4409 		ifconf = createMap();
4410 		mapSet(cfile->stack[1], ifconf, "interfaces-config");
4411 	}
4412 
4413 	iflist = mapGet(ifconf, "interfaces");
4414 	if (iflist == NULL) {
4415 		iflist = createList();
4416 		mapSet(ifconf, iflist, "interfaces");
4417 	}
4418 
4419 	for (i = 0; i < listSize(iflist); i++) {
4420 		struct element *item;
4421 
4422 		item = listGet(iflist, i);
4423 		if ((item != NULL) &&
4424 		    (item->type == ELEMENT_STRING) &&
4425 		    eqString(stringValue(item), name))
4426 			return;
4427 	}
4428 
4429 	listPush(iflist, createString(name));
4430 }
4431 
4432 /* Convert address and mask in binary into address/len text */
4433 
4434 static const uint32_t bitmasks[32 + 1] = {
4435 	0xffffffff, 0x7fffffff, 0x3fffffff, 0x1fffffff,
4436 	0x0fffffff, 0x07ffffff, 0x03ffffff, 0x01ffffff,
4437 	0x00ffffff, 0x007fffff, 0x003fffff, 0x001fffff,
4438 	0x000fffff, 0x0007ffff, 0x0003ffff, 0x0001ffff,
4439 	0x0000ffff, 0x00007fff, 0x00003fff, 0x00001fff,
4440 	0x00000fff, 0x000007ff, 0x000003ff, 0x000001ff,
4441 	0x000000ff, 0x0000007f, 0x0000003f, 0x0000001f,
4442 	0x0000000f, 0x00000007, 0x00000003, 0x00000001,
4443 	0x00000000 };
4444 
4445 static struct string *
addrmask(const struct string * address,const struct string * netmask)4446 addrmask(const struct string *address, const struct string *netmask)
4447 {
4448 	struct string *result;
4449 	uint8_t plen;
4450 	uint32_t mask;
4451 
4452 	result = makeStringExt(address->length, address->content, 'I');
4453 
4454 	memcpy(&mask, netmask->content, 4);
4455 	mask = ntohl(mask);
4456 	for (plen = 0; plen <= 32; ++plen)
4457 		if (~mask == bitmasks[plen])
4458 			break;
4459 	if (plen > 32)
4460 		return NULL;
4461 
4462 	appendString(result, "/");
4463 	concatString(result, makeStringExt(1, (char *)&plen, 'B'));
4464 	return result;
4465 }
4466 
4467 /*
4468  * find a place where to put a reservation
4469  * (reservations aka hosts must be in a subnet in Kea < 1.5)
4470  * (defaulting to the last defined subnet (e.g. for reservations
4471  *  without any address).
4472  * (first step is to find an enclosing group).
4473  */
4474 
4475 static struct element *
find_match(struct parse * cfile,struct element * host,isc_boolean_t * used_heuristicp)4476 find_match(struct parse *cfile, struct element *host,
4477 	   isc_boolean_t *used_heuristicp)
4478 {
4479 	struct element *address;
4480 	struct subnet *subnet;
4481 	char addr[16];
4482 	size_t group;
4483 	size_t i, len;
4484 	int kind;
4485 
4486 	if (global_hr) {
4487 		struct element *hosts;
4488 
4489 		hosts = mapGet(cfile->stack[1], "reservations");
4490 		if (!hosts) {
4491 			mapSet(cfile->stack[1],
4492 			       createString(makeString(-1, "global")),
4493 			       "reservation-mode");
4494 			hosts = createList();
4495 			mapSet(cfile->stack[1], hosts, "reservations");
4496 		}
4497 		*used_heuristicp = ISC_FALSE;
4498 		return cfile->stack[1];
4499 	}
4500 
4501 	for (group = cfile->stack_top; group > 0; --group) {
4502 		kind = cfile->stack[group]->kind;
4503 		if ((kind == GROUP_DECL) || (kind == ROOT_GROUP))
4504 			break;
4505 	}
4506 	if (!group)
4507 		parse_error(cfile, "can't find root group");
4508 	if (kind == GROUP_DECL)
4509 		return cfile->stack[group];
4510 
4511 	if (local_family == AF_INET) {
4512 		address = mapGet(host, "ip-address");
4513 		if (address == NULL) {
4514 			if (TAILQ_EMPTY(&known_subnets))
4515 				return cfile->stack[1];
4516 			if (used_heuristicp)
4517 				*used_heuristicp = ISC_TRUE;
4518 			return TAILQ_LAST(&known_subnets, subnets)->subnet;
4519 		}
4520 		len = 4;
4521 	} else {
4522 		address = mapGet(host, "ip-addresses");
4523 		if (address == NULL) {
4524 			if (TAILQ_EMPTY(&known_subnets))
4525 				return cfile->stack[1];
4526 			if (used_heuristicp)
4527 				*used_heuristicp = ISC_TRUE;
4528 			return TAILQ_LAST(&known_subnets, subnets)->subnet;
4529 		}
4530 		address = listGet(address, 0);
4531 		if (address == NULL)
4532 			return TAILQ_LAST(&known_subnets, subnets)->subnet;
4533 		len = 16;
4534 	}
4535 
4536 	if (inet_pton(local_family, stringValue(address)->content, addr) != 1)
4537 		parse_error(cfile, "bad address %s",
4538 			    stringValue(address)->content);
4539 	TAILQ_FOREACH(subnet, &known_subnets) {
4540 		isc_boolean_t matching = ISC_TRUE;
4541 
4542 		if (subnet->mask->length != len)
4543 			continue;
4544 		for (i = 0; i < len; i++)
4545 			if ((addr[i] & subnet->mask->content[i]) !=
4546 					subnet->addr->content[i]) {
4547 				matching = ISC_FALSE;
4548 				break;
4549 			}
4550 		if (matching)
4551 			return subnet->subnet;
4552 	}
4553 	return cfile->stack[1];
4554 }
4555 
4556 /*
4557  * find a subnet where to put a pool
4558  * (pools are not allowed at shared-network level in Kea)
4559  */
4560 
4561 static struct element *
find_location(struct element * share,struct range * range)4562 find_location(struct element *share, struct range *range)
4563 {
4564 	struct subnet *subnet;
4565 	size_t i;
4566 
4567 	TAILQ_FOREACH(subnet, &known_subnets) {
4568 		isc_boolean_t matching = ISC_TRUE;
4569 
4570 		if (subnet->share != share)
4571 			continue;
4572 		if (subnet->mask->length != range->low->length)
4573 			continue;
4574 		for (i = 0; i < range->low->length; i++)
4575 			if ((range->low->content[i] &
4576 			     subnet->mask->content[i]) !=
4577 			    subnet->addr->content[i]) {
4578 				matching = ISC_FALSE;
4579 				break;
4580 			}
4581 		if (matching)
4582 			return subnet->subnet;
4583 	}
4584 	return NULL;
4585 }
4586 
4587 /*
4588  * Compute a prefix length from lower - higher IPv6 addresses.
4589  */
4590 
4591 static const uint8_t bytemasks[8] = {
4592 	0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff
4593 };
4594 
4595 static int
get_prefix_length(const char * low,const char * high)4596 get_prefix_length(const char *low, const char *high)
4597 {
4598 	uint8_t lo[16];
4599 	uint8_t hi[16];
4600 	uint8_t xor[16];
4601 	int i, plen;
4602 
4603 	if ((inet_pton(AF_INET6, low, lo) != 1) ||
4604 	    (inet_pton(AF_INET6, high, hi) != 1))
4605 		return -100;
4606 
4607 	for (i = 0; i < 16; i++)
4608 		xor[i] = lo[i] ^ hi[i];
4609 	for (plen = 0; plen < 128; plen += 8)
4610 		if (xor[plen / 8] != 0)
4611 			break;
4612 	if (plen == 128)
4613 		return plen;
4614 	for (i = (plen / 8) + 1; i < 16; i++)
4615 		if (xor[i] != 0)
4616 			return -2;
4617 	for (i = 0; i < 8; i++) {
4618 		uint8_t msk =  ~xor[plen / 8];
4619 
4620 		if (msk == bytemasks[i])
4621 			return plen + i + 1;
4622 	}
4623 	return -1;
4624 }
4625 
4626 /*
4627  * Get a (global) class from its reference, i.e.:
4628  * - name for a (super)class
4629  * - super, and binary or string for a subclass
4630  */
4631 static struct element *
get_class(struct parse * cfile,struct element * ref)4632 get_class(struct parse *cfile, struct element *ref)
4633 {
4634 	struct element *classes;
4635 	struct element *class;
4636 	struct element *name;
4637 	struct element *selector;
4638 	struct element *param;
4639 	size_t i;
4640 
4641 	classes = mapGet(cfile->stack[1], "client-classes");
4642 	if ((classes == NULL) || (listSize(classes) == 0))
4643 		return NULL;
4644 
4645 	name = mapGet(ref, "super");
4646 	if (name == NULL) {
4647 		name = mapGet(ref, "name");
4648 		if (name == NULL)
4649 			return NULL;
4650 		for (i = 0; i < listSize(classes); i++) {
4651 			class = listGet(classes, i);
4652 			if (mapContains(ref, "super"))
4653 				continue;
4654 			param = mapGet(class, "name");
4655 			if (param == NULL)
4656 				continue;
4657 			if (eqString(stringValue(name), stringValue(param)))
4658 				return class;
4659 		}
4660 		return NULL;
4661 	}
4662 	selector = mapGet(ref, "string");
4663 	if (selector == NULL) {
4664 		selector = mapGet(ref, "binary");
4665 		if (selector == NULL)
4666 			return NULL;
4667 		for (i = 0; i <listSize(classes); i++) {
4668 			class = listGet(classes, i);
4669 			param = mapGet(class, "super");
4670 			if (param == NULL)
4671 				continue;
4672 			if (!eqString(stringValue(name), stringValue(param)))
4673 				continue;
4674 			param = mapGet(class, "binary");
4675 			if (param == NULL)
4676 				continue;
4677 			if (eqString(stringValue(selector),
4678 				     stringValue(param)))
4679 				return class;
4680 		}
4681 		return NULL;
4682 	}
4683 	for (i = 0; i <listSize(classes); i++) {
4684 		class = listGet(classes, i);
4685 		param = mapGet(class, "super");
4686 		if (param == NULL)
4687 			continue;
4688 		if (!eqString(stringValue(name), stringValue(param)))
4689 			continue;
4690 		param = mapGet(class, "string");
4691 		if (param == NULL)
4692 			continue;
4693 		if (eqString(stringValue(selector), stringValue(param)))
4694 			return class;
4695 	}
4696 	return NULL;
4697 }
4698 
4699 /*
4700  * Concatenate two class reference lists eliminating duplicates
4701  * (complexity is bad: if this becomes a performance pig, use a hash table)
4702  */
4703 
4704 static void
concat_classes(struct parse * cfile,struct element * dst,struct element * src)4705 concat_classes(struct parse *cfile, struct element *dst, struct element *src)
4706 {
4707 	struct element *class;
4708 	struct element *sitem;
4709 	struct element *ditem;
4710 	size_t i;
4711 	isc_boolean_t dup;
4712 
4713 	while (listSize(src) > 0) {
4714 		sitem = listGet(src, 0);
4715 		listRemove(src, 0);
4716 		class = get_class(cfile, sitem);
4717 		if (class == NULL)
4718 			/* just ignore */
4719 			continue;
4720 		dup = ISC_FALSE;
4721 		for (i = 0; i < listSize(dst); i++) {
4722 			ditem = listGet(dst, i);
4723 			if (class == get_class(cfile, ditem)) {
4724 				dup = ISC_TRUE;
4725 				break;
4726 			}
4727 		}
4728 		if (dup)
4729 			continue;
4730 		listPush(dst, sitem);
4731 	}
4732 }
4733 
4734 /* Generate a class from allow/deny member lists */
4735 
4736 static void
generate_class(struct parse * cfile,struct element * pool,struct element * allow,struct element * deny)4737 generate_class(struct parse *cfile, struct element *pool,
4738 	       struct element *allow, struct element *deny)
4739 {
4740 	struct element *classes;
4741 	struct element *class;
4742 	struct element *elem;
4743 	struct element *prop;
4744 	struct element *depend;
4745 	struct element *result = NULL;
4746 	struct string *name;
4747 	struct string *expr;
4748 	struct string *msg;
4749 	struct comments comments;
4750 	struct comment *comment;
4751 	isc_boolean_t rescan;
4752 	size_t i;
4753 
4754 	if ((listSize(allow) == 0) && (listSize(deny) == 0))
4755 		return;
4756 
4757 	classes = mapGet(cfile->stack[1], "generated-classes");
4758 	if (classes == NULL) {
4759 		classes = createList();
4760 		mapSet(cfile->stack[1], classes, "generated-classes");
4761 	}
4762 
4763 	/* Create comments */
4764 	TAILQ_INIT(&comments);
4765 	comment = createComment("/// From:");
4766 	TAILQ_INSERT_TAIL(&comments, comment);
4767 	for (i = 0; i < listSize(allow); i++) {
4768 		struct element *alias;
4769 
4770 		elem = listGet(allow, i);
4771 		assert(elem != NULL);
4772 		prop = mapGet(elem, "class");
4773 		assert(prop != NULL);
4774 		assert(prop->type == ELEMENT_STRING);
4775 		alias = mapGet(elem, "real");
4776 		msg = makeString(-1, "///   allow ");
4777 		concatString(msg, stringValue(alias ? alias : prop));
4778 		comment = createComment(msg->content);
4779 		TAILQ_INSERT_TAIL(&comments, comment);
4780 	}
4781 	for (i = 0; i < listSize(deny); i++) {
4782 		struct element *alias;
4783 
4784 		elem = listGet(deny, i);
4785 		assert(elem != NULL);
4786 		prop = mapGet(elem, "class");
4787 		assert(prop != NULL);
4788 		assert(prop->type == ELEMENT_STRING);
4789 		alias = mapGet(elem, "real");
4790 		msg = makeString(-1, "///   deny ");
4791 		concatString(msg, stringValue(alias ? alias : prop));
4792 		comment = createComment(msg->content);
4793 		TAILQ_INSERT_TAIL(&comments, comment);
4794 	}
4795 	TAILQ_CONCAT(&comments, &allow->comments);
4796 	TAILQ_CONCAT(&comments, &deny->comments);
4797 
4798 	/* Deal with special cases */
4799 	for (;;) {
4800 		rescan = ISC_FALSE;
4801 		for (i = 0; i < listSize(allow); i++) {
4802 			elem = listGet(allow, i);
4803 			assert(elem != NULL);
4804 			prop = mapGet(elem, "way");
4805 			assert(prop != NULL);
4806 			assert(prop->type == ELEMENT_BOOLEAN);
4807 			class = mapGet(elem, "class");
4808 			assert(class != NULL);
4809 			assert(class->type == ELEMENT_STRING);
4810 			/* allow !ALL and other */
4811 			if (eqString(stringValue(class), CLASS_ALL) &&
4812 			    !boolValue(prop) && (listSize(allow) > 1)) {
4813 				listRemove(allow, i);
4814 				rescan = ISC_TRUE;
4815 				break;
4816 			}
4817 			/* allow ALL alone */
4818 			if (eqString(stringValue(class), CLASS_ALL) &&
4819 			    boolValue(prop) && (listSize(allow) == 1)) {
4820 				resetList(allow);
4821 				rescan = ISC_TRUE;
4822 				break;
4823 			}
4824 		}
4825 		if (!rescan)
4826 			break;
4827 	}
4828 
4829 	for (;;) {
4830 		rescan = ISC_FALSE;
4831 		for (i = 0; i < listSize(deny); i++) {
4832 			elem = listGet(deny, i);
4833 			assert(elem != NULL);
4834 			prop = mapGet(elem, "way");
4835 			assert(prop != NULL);
4836 			assert(prop->type == ELEMENT_BOOLEAN);
4837 			class = mapGet(elem, "class");
4838 			assert(class != NULL);
4839 			assert(class->type == ELEMENT_STRING);
4840 			/* DENY !ALL */
4841 			if (eqString(stringValue(class), CLASS_ALL) &&
4842 			    !boolValue(prop)) {
4843 				listRemove(deny, i);
4844 				rescan = ISC_TRUE;
4845 				break;
4846 			}
4847 			/* DENY ALL */
4848 			if (eqString(stringValue(class), CLASS_ALL) &&
4849 			    boolValue(prop)) {
4850 				resetList(allow);
4851 				if (listSize(deny) > 1) {
4852 					listRemove(deny, i);
4853 					resetList(deny);
4854 					listPush(deny, elem);
4855 				}
4856 				break;
4857 			}
4858 		}
4859 		if (!rescan)
4860 			break;
4861 	}
4862 
4863 	/* Fully cleaned? */
4864 	if ((listSize(allow) == 0) && (listSize(deny) == 0)) {
4865 		if (result != NULL)
4866 			TAILQ_CONCAT(&result->comments, &comments);
4867 		else
4868 			TAILQ_CONCAT(&pool->comments, &comments);
4869 		return;
4870 	}
4871 
4872 	/* Unique allow member short cut */
4873 	if ((listSize(allow) == 1) && (listSize(deny) == 0) &&
4874 	    !allow->skip && !deny->skip) {
4875 		elem = listGet(allow, 0);
4876 		assert(elem != NULL);
4877 		prop = mapGet(elem, "way");
4878 		assert(prop != NULL);
4879 		assert(prop->type == ELEMENT_BOOLEAN);
4880 		class = mapGet(elem, "class");
4881 		assert(class != NULL);
4882 		assert(class->type == ELEMENT_STRING);
4883 		if (boolValue(prop)) {
4884 			result = createString(stringValue(class));
4885 			TAILQ_CONCAT(&result->comments, &comments);
4886 			mapSet(pool, result, "client-class");
4887 			return;
4888 		}
4889 	}
4890 
4891 	/* Build name */
4892 	name = makeString(-1, "gen#");
4893 	for (i = 0; i < listSize(allow); i++) {
4894 		elem = listGet(allow, i);
4895 		assert(elem != NULL);
4896 		prop = mapGet(elem, "way");
4897 		assert(prop != NULL);
4898 		assert(prop->type == ELEMENT_BOOLEAN);
4899 		if (!boolValue(prop))
4900 			appendString(name, "!");
4901 		prop = mapGet(elem, "class");
4902 		assert(prop != NULL);
4903 		assert(prop->type == ELEMENT_STRING);
4904 		concatString(name, stringValue(prop));
4905 		appendString(name, "#");
4906 	}
4907 	if (listSize(deny) > 0) {
4908 		appendString(name, "_AND_#");
4909 		for (i = 0; i < listSize(deny); i++) {
4910 			elem = listGet(deny, i);
4911 			assert(elem != NULL);
4912 			prop = mapGet(elem, "way");
4913 			assert(prop != NULL);
4914 			assert(prop->type == ELEMENT_BOOLEAN);
4915 			if (boolValue(prop))
4916 				appendString(name, "!");
4917 			prop = mapGet(elem, "class");
4918 			assert(prop != NULL);
4919 			assert(prop->type == ELEMENT_STRING);
4920 			concatString(name, stringValue(prop));
4921 			appendString(name, "#");
4922 		}
4923 	}
4924 
4925 	/* Check if it already exists */
4926 	for (i = 0; i < listSize(classes); i++) {
4927 		struct element *descr;
4928 
4929 		class = listGet(classes, i);
4930 		assert(class != NULL);
4931 		descr = mapGet(class, "name");
4932 		assert(descr != NULL);
4933 		assert(descr->type == ELEMENT_STRING);
4934 		if (!eqString(name, stringValue(descr)))
4935 			continue;
4936 		result = createString(name);
4937 		TAILQ_CONCAT(&result->comments, &comments);
4938 		mapSet(pool, result, "client-class");
4939 		return;
4940 	}
4941 
4942 	/* Create expression */
4943 	class = createMap();
4944 	depend = createList();
4945 	expr = allocString();
4946 
4947 	if ((listSize(allow) > 0) && (listSize(deny) > 0))
4948 		appendString(expr, "(");
4949 
4950 	for (i = 0; i < listSize(allow); i++) {
4951 		isc_boolean_t negative;
4952 
4953 		if (i > 0)
4954 			appendString(expr, " or ");
4955 		elem = listGet(allow, i);
4956 		prop = mapGet(elem, "way");
4957 		negative = !boolValue(prop);
4958 		prop = mapGet(elem, "class");
4959 		if (negative)
4960 			appendString(expr, "not ");
4961 		appendString(expr, "member('");
4962 		concatString(expr, stringValue(prop));
4963 		appendString(expr, "')");
4964 		listPush(depend, createString(stringValue(prop)));
4965 	}
4966 
4967 	if ((listSize(allow) > 0) && (listSize(deny) > 0))
4968 		appendString(expr, ") and ");
4969 
4970 	for (i = 0; i < listSize(deny); i++) {
4971 		isc_boolean_t negative;
4972 
4973 		if (i > 0)
4974 			appendString(expr, " and ");
4975 		elem = listGet(deny, i);
4976 		prop = mapGet(elem, "way");
4977 		negative = boolValue(prop);
4978 		prop = mapGet(elem, "class");
4979 		if (negative)
4980 			appendString(expr, "not ");
4981 		appendString(expr, "member('");
4982 		concatString(expr, stringValue(prop));
4983 		appendString(expr, "')");
4984 		listPush(depend, createString(stringValue(prop)));
4985 	}
4986 
4987 	mapSet(class, createString(name), "name");
4988 	mapSet(class, createString(expr), "test");
4989 	mapSet(class, depend, "depend");
4990 	/* inherit untranslatable cases */
4991 	class->skip |= allow->skip || deny->skip;
4992 	listPush(classes, class);
4993 
4994 	result = createString(name);
4995 	TAILQ_CONCAT(&result->comments, &comments);
4996 	mapSet(pool, result, "client-class");
4997 }
4998