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