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