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 */
43isc_boolean_t failover_once = ISC_TRUE;
44
45/* To manage host-reservation-identifiers */
46isc_boolean_t use_client_id = ISC_FALSE;
47isc_boolean_t use_flex_id = ISC_FALSE;
48isc_boolean_t use_hw_address = ISC_FALSE;
49
50/* option and relays used for flexible host identifier */
51const struct option *host_id_option = NULL;
52int host_id_relays = 0;
53
54/* Simple or complex config */
55unsigned subnet_counter = 0;
56
57/* For subclass name generation */
58unsigned subclass_counter = 0;
59
60/* To map reservations to declared subnets */
61struct subnet {
62	struct element *subnet;
63	struct element *share;
64	struct string *addr;
65	struct string *mask;
66	TAILQ_ENTRY(subnet) next;
67};
68
69TAILQ_HEAD(subnets, subnet) known_subnets;
70
71/* To map pools to subnets inside a shared-network */
72struct range {
73	struct element *pool;
74	struct element *share;
75	struct string *low;
76	TAILQ_ENTRY(range) next;
77};
78
79TAILQ_HEAD(ranges, range) known_ranges;
80
81static void post_process_lifetimes(struct parse *);
82static size_t post_process_reservations(struct parse *);
83static void post_process_classes(struct parse *);
84static void post_process_generated_classes(struct parse *);
85static void check_depend(struct element *, struct element *);
86static void post_process_option_definitions(struct parse *);
87static void add_host_reservation_identifiers(struct parse *, const char *);
88static void add_host_id_option(struct parse *, const struct option *, int);
89static void subclass_inherit(struct parse *, struct element *,
90			     struct element *);
91static void add_match_class(struct parse *, struct element *,
92			    struct element *);
93static void option_data_derive(struct parse *, struct handle *,
94			       struct handle *);
95static void derive_classes(struct parse *, struct handle *, struct handle *);
96static isc_boolean_t is_hexa_only(const char *, unsigned len);
97static void new_network_interface(struct parse *, struct element *);
98static struct string *addrmask(const struct string *, const struct string *);
99static struct element *find_match(struct parse *, struct element *,
100				  isc_boolean_t *);
101static struct element *find_location(struct element *, struct range *);
102static int get_prefix_length(const char *, const char *);
103static struct element *get_class(struct parse *, struct element *);
104static void concat_classes(struct parse *, struct element *, struct element *);
105static void generate_class(struct parse *, struct element *, struct element *,
106			   struct element *);
107
108static struct string *CLASS_ALL;
109static struct string *CLASS_KNOWN;
110
111/* Add head config file comments to the DHCP server map */
112
113size_t
114conf_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 */
174static void
175post_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
222static size_t
223post_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
274static void
275post_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
389static void
390post_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
416static void
417check_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
469static void
470post_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
484void
485read_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
539size_t
540conf_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
588isc_boolean_t
589parse_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
1008void
1009get_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 */
1146void
1147parse_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
1273void
1274parse_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
1286void
1287parse_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 */
1513static void
1514add_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 */
1527static void
1528add_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
1573static 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
1588void
1589parse_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
1896static void
1897subclass_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
2045static void
2046add_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
2069static void
2070relocate_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
2109void
2110parse_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
2291static void
2292common_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
2341void
2342parse_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
2433void
2434parse_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
2524void
2525parse_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
2593void
2594close_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
2942static void
2943option_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 */
2973static void
2974derive_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
3012struct element *
3013parse_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 */
3059static struct element *
3060parse_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
3101void
3102parse_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
3216void
3217parse_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
3337void
3338parse_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
3413void
3414parse_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
3476void
3477parse_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
3707struct element *
3708parse_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 */
3794void
3795parse_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 */
3982static isc_boolean_t
3983is_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
4005void
4006parse_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
4095void
4096parse_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
4160void
4161parse_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
4240void
4241parse_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
4272void
4273parse_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
4313not_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
4320void
4321parse_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	}
4361no_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
4399static void
4400new_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
4434static 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
4445static struct string *
4446addrmask(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
4475static struct element *
4476find_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
4561static struct element *
4562find_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
4591static const uint8_t bytemasks[8] = {
4592	0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff
4593};
4594
4595static int
4596get_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 */
4631static struct element *
4632get_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
4704static void
4705concat_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
4736static void
4737generate_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