1147072Sbrooks/*	$OpenBSD: clparse.c,v 1.18 2004/09/15 18:15:18 henning Exp $	*/
2147072Sbrooks
3147072Sbrooks/* Parser for dhclient config and lease files... */
4147072Sbrooks
5331722Seadler/*
6147072Sbrooks * Copyright (c) 1997 The Internet Software Consortium.
7147072Sbrooks * All rights reserved.
8147072Sbrooks *
9147072Sbrooks * Redistribution and use in source and binary forms, with or without
10147072Sbrooks * modification, are permitted provided that the following conditions
11147072Sbrooks * are met:
12147072Sbrooks *
13147072Sbrooks * 1. Redistributions of source code must retain the above copyright
14147072Sbrooks *    notice, this list of conditions and the following disclaimer.
15147072Sbrooks * 2. Redistributions in binary form must reproduce the above copyright
16147072Sbrooks *    notice, this list of conditions and the following disclaimer in the
17147072Sbrooks *    documentation and/or other materials provided with the distribution.
18147072Sbrooks * 3. Neither the name of The Internet Software Consortium nor the names
19147072Sbrooks *    of its contributors may be used to endorse or promote products derived
20147072Sbrooks *    from this software without specific prior written permission.
21147072Sbrooks *
22147072Sbrooks * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
23147072Sbrooks * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
24147072Sbrooks * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
25147072Sbrooks * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
26147072Sbrooks * DISCLAIMED.  IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
27147072Sbrooks * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28147072Sbrooks * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
29147072Sbrooks * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
30147072Sbrooks * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
31147072Sbrooks * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
32147072Sbrooks * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
33147072Sbrooks * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34147072Sbrooks * SUCH DAMAGE.
35147072Sbrooks *
36147072Sbrooks * This software has been written for the Internet Software Consortium
37147072Sbrooks * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
38147072Sbrooks * Enterprises.  To learn more about the Internet Software Consortium,
39147072Sbrooks * see ``http://www.vix.com/isc''.  To learn more about Vixie
40147072Sbrooks * Enterprises, see ``http://www.vix.com''.
41147072Sbrooks */
42147072Sbrooks
43149399Sbrooks#include <sys/cdefs.h>
44149399Sbrooks__FBSDID("$FreeBSD: stable/11/sbin/dhclient/clparse.c 332602 2018-04-16 16:23:32Z asomers $");
45149399Sbrooks
46147072Sbrooks#include "dhcpd.h"
47147072Sbrooks#include "dhctoken.h"
48147072Sbrooks
49147072Sbrooksstruct client_config top_level_config;
50147072Sbrooksstruct interface_info *dummy_interfaces;
51147072Sbrooksextern struct interface_info *ifi;
52147072Sbrooks
53147072Sbrookschar client_script_name[] = "/sbin/dhclient-script";
54147072Sbrooks
55147072Sbrooks/*
56147072Sbrooks * client-conf-file :== client-declarations EOF
57147072Sbrooks * client-declarations :== <nil>
58147072Sbrooks *			 | client-declaration
59147072Sbrooks *			 | client-declarations client-declaration
60147072Sbrooks */
61147072Sbrooksint
62147072Sbrooksread_client_conf(void)
63147072Sbrooks{
64147072Sbrooks	FILE			*cfile;
65147072Sbrooks	char			*val;
66147072Sbrooks	int			 token;
67147072Sbrooks	struct client_config	*config;
68147072Sbrooks
69147072Sbrooks	new_parse(path_dhclient_conf);
70147072Sbrooks
71147072Sbrooks	/* Set up the initial dhcp option universe. */
72147072Sbrooks	initialize_universes();
73147072Sbrooks
74147072Sbrooks	/* Initialize the top level client configuration. */
75147072Sbrooks	memset(&top_level_config, 0, sizeof(top_level_config));
76147072Sbrooks
77147072Sbrooks	/* Set some defaults... */
78147072Sbrooks	top_level_config.timeout = 60;
79147072Sbrooks	top_level_config.select_interval = 0;
80147072Sbrooks	top_level_config.reboot_timeout = 10;
81147072Sbrooks	top_level_config.retry_interval = 300;
82147072Sbrooks	top_level_config.backoff_cutoff = 15;
83147072Sbrooks	top_level_config.initial_interval = 3;
84147072Sbrooks	top_level_config.bootp_policy = ACCEPT;
85147072Sbrooks	top_level_config.script_name = client_script_name;
86147072Sbrooks	top_level_config.requested_options
87147072Sbrooks	    [top_level_config.requested_option_count++] = DHO_SUBNET_MASK;
88147072Sbrooks	top_level_config.requested_options
89147072Sbrooks	    [top_level_config.requested_option_count++] = DHO_BROADCAST_ADDRESS;
90147072Sbrooks	top_level_config.requested_options
91147072Sbrooks	    [top_level_config.requested_option_count++] = DHO_TIME_OFFSET;
92147072Sbrooks	top_level_config.requested_options
93166602Semaste	    [top_level_config.requested_option_count++] = DHO_CLASSLESS_ROUTES;
94166602Semaste	top_level_config.requested_options
95147072Sbrooks	    [top_level_config.requested_option_count++] = DHO_ROUTERS;
96147072Sbrooks	top_level_config.requested_options
97147072Sbrooks	    [top_level_config.requested_option_count++] = DHO_DOMAIN_NAME;
98147072Sbrooks	top_level_config.requested_options
99147072Sbrooks	    [top_level_config.requested_option_count++] =
100147072Sbrooks	    DHO_DOMAIN_NAME_SERVERS;
101147072Sbrooks	top_level_config.requested_options
102147072Sbrooks	    [top_level_config.requested_option_count++] = DHO_HOST_NAME;
103228259Sdumbbell	top_level_config.requested_options
104228259Sdumbbell	    [top_level_config.requested_option_count++] = DHO_DOMAIN_SEARCH;
105331179Seadler	top_level_config.requested_options
106331179Seadler	    [top_level_config.requested_option_count++] = DHO_INTERFACE_MTU;
107147072Sbrooks
108147072Sbrooks	if ((cfile = fopen(path_dhclient_conf, "r")) != NULL) {
109147072Sbrooks		do {
110147072Sbrooks			token = peek_token(&val, cfile);
111147072Sbrooks			if (token == EOF)
112147072Sbrooks				break;
113147072Sbrooks			parse_client_statement(cfile, NULL, &top_level_config);
114147072Sbrooks		} while (1);
115147072Sbrooks		token = next_token(&val, cfile); /* Clear the peek buffer */
116147072Sbrooks		fclose(cfile);
117147072Sbrooks	}
118147072Sbrooks
119147072Sbrooks	/*
120147072Sbrooks	 * Set up state and config structures for clients that don't
121147072Sbrooks	 * have per-interface configuration declarations.
122147072Sbrooks	 */
123147072Sbrooks	config = NULL;
124147072Sbrooks	if (!ifi->client) {
125147072Sbrooks		ifi->client = malloc(sizeof(struct client_state));
126147072Sbrooks		if (!ifi->client)
127147072Sbrooks			error("no memory for client state.");
128147072Sbrooks		memset(ifi->client, 0, sizeof(*(ifi->client)));
129147072Sbrooks	}
130147072Sbrooks	if (!ifi->client->config) {
131147072Sbrooks		if (!config) {
132147072Sbrooks			config = malloc(sizeof(struct client_config));
133147072Sbrooks			if (!config)
134147072Sbrooks				error("no memory for client config.");
135147072Sbrooks			memcpy(config, &top_level_config,
136147072Sbrooks				sizeof(top_level_config));
137147072Sbrooks		}
138147072Sbrooks		ifi->client->config = config;
139147072Sbrooks	}
140147072Sbrooks
141147072Sbrooks	return (!warnings_occurred);
142147072Sbrooks}
143147072Sbrooks
144147072Sbrooks/*
145147072Sbrooks * lease-file :== client-lease-statements EOF
146147072Sbrooks * client-lease-statements :== <nil>
147147072Sbrooks *		     | client-lease-statements LEASE client-lease-statement
148147072Sbrooks */
149147072Sbrooksvoid
150147072Sbrooksread_client_leases(void)
151147072Sbrooks{
152147072Sbrooks	FILE	*cfile;
153147072Sbrooks	char	*val;
154147072Sbrooks	int	 token;
155147072Sbrooks
156147072Sbrooks	new_parse(path_dhclient_db);
157147072Sbrooks
158147072Sbrooks	/* Open the lease file.   If we can't open it, just return -
159147072Sbrooks	   we can safely trust the server to remember our state. */
160147072Sbrooks	if ((cfile = fopen(path_dhclient_db, "r")) == NULL)
161147072Sbrooks		return;
162147072Sbrooks	do {
163147072Sbrooks		token = next_token(&val, cfile);
164147072Sbrooks		if (token == EOF)
165147072Sbrooks			break;
166147072Sbrooks		if (token != LEASE) {
167147072Sbrooks			warning("Corrupt lease file - possible data loss!");
168147072Sbrooks			skip_to_semi(cfile);
169147072Sbrooks			break;
170147072Sbrooks		} else
171147072Sbrooks			parse_client_lease_statement(cfile, 0);
172147072Sbrooks
173147072Sbrooks	} while (1);
174147072Sbrooks	fclose(cfile);
175147072Sbrooks}
176147072Sbrooks
177147072Sbrooks/*
178147072Sbrooks * client-declaration :==
179147072Sbrooks *	SEND option-decl |
180147072Sbrooks *	DEFAULT option-decl |
181147072Sbrooks *	SUPERSEDE option-decl |
182147072Sbrooks *	PREPEND option-decl |
183147072Sbrooks *	APPEND option-decl |
184147072Sbrooks *	hardware-declaration |
185147072Sbrooks *	REQUEST option-list |
186147072Sbrooks *	REQUIRE option-list |
187147072Sbrooks *	TIMEOUT number |
188147072Sbrooks *	RETRY number |
189147072Sbrooks *	REBOOT number |
190147072Sbrooks *	SELECT_TIMEOUT number |
191147072Sbrooks *	SCRIPT string |
192147072Sbrooks *	interface-declaration |
193147072Sbrooks *	LEASE client-lease-statement |
194147072Sbrooks *	ALIAS client-lease-statement
195147072Sbrooks */
196147072Sbrooksvoid
197147072Sbrooksparse_client_statement(FILE *cfile, struct interface_info *ip,
198147072Sbrooks    struct client_config *config)
199147072Sbrooks{
200147072Sbrooks	int		 token;
201147072Sbrooks	char		*val;
202147072Sbrooks	struct option	*option;
203147072Sbrooks
204147072Sbrooks	switch (next_token(&val, cfile)) {
205147072Sbrooks	case SEND:
206147072Sbrooks		parse_option_decl(cfile, &config->send_options[0]);
207147072Sbrooks		return;
208147072Sbrooks	case DEFAULT:
209147072Sbrooks		option = parse_option_decl(cfile, &config->defaults[0]);
210147072Sbrooks		if (option)
211147072Sbrooks			config->default_actions[option->code] = ACTION_DEFAULT;
212147072Sbrooks		return;
213147072Sbrooks	case SUPERSEDE:
214147072Sbrooks		option = parse_option_decl(cfile, &config->defaults[0]);
215147072Sbrooks		if (option)
216147072Sbrooks			config->default_actions[option->code] =
217147072Sbrooks			    ACTION_SUPERSEDE;
218147072Sbrooks		return;
219147072Sbrooks	case APPEND:
220147072Sbrooks		option = parse_option_decl(cfile, &config->defaults[0]);
221147072Sbrooks		if (option)
222147072Sbrooks			config->default_actions[option->code] = ACTION_APPEND;
223147072Sbrooks		return;
224147072Sbrooks	case PREPEND:
225147072Sbrooks		option = parse_option_decl(cfile, &config->defaults[0]);
226147072Sbrooks		if (option)
227147072Sbrooks			config->default_actions[option->code] = ACTION_PREPEND;
228147072Sbrooks		return;
229147072Sbrooks	case MEDIA:
230147072Sbrooks		parse_string_list(cfile, &config->media, 1);
231147072Sbrooks		return;
232147072Sbrooks	case HARDWARE:
233147072Sbrooks		if (ip)
234147072Sbrooks			parse_hardware_param(cfile, &ip->hw_address);
235147072Sbrooks		else {
236147072Sbrooks			parse_warn("hardware address parameter %s",
237147072Sbrooks				    "not allowed here.");
238147072Sbrooks			skip_to_semi(cfile);
239147072Sbrooks		}
240147072Sbrooks		return;
241147072Sbrooks	case REQUEST:
242147072Sbrooks		config->requested_option_count =
243147072Sbrooks			parse_option_list(cfile, config->requested_options);
244147072Sbrooks		return;
245147072Sbrooks	case REQUIRE:
246147072Sbrooks		memset(config->required_options, 0,
247147072Sbrooks		    sizeof(config->required_options));
248147072Sbrooks		parse_option_list(cfile, config->required_options);
249147072Sbrooks		return;
250147072Sbrooks	case TIMEOUT:
251147072Sbrooks		parse_lease_time(cfile, &config->timeout);
252147072Sbrooks		return;
253147072Sbrooks	case RETRY:
254147072Sbrooks		parse_lease_time(cfile, &config->retry_interval);
255147072Sbrooks		return;
256147072Sbrooks	case SELECT_TIMEOUT:
257147072Sbrooks		parse_lease_time(cfile, &config->select_interval);
258147072Sbrooks		return;
259147072Sbrooks	case REBOOT:
260147072Sbrooks		parse_lease_time(cfile, &config->reboot_timeout);
261147072Sbrooks		return;
262147072Sbrooks	case BACKOFF_CUTOFF:
263147072Sbrooks		parse_lease_time(cfile, &config->backoff_cutoff);
264147072Sbrooks		return;
265147072Sbrooks	case INITIAL_INTERVAL:
266147072Sbrooks		parse_lease_time(cfile, &config->initial_interval);
267147072Sbrooks		return;
268147072Sbrooks	case SCRIPT:
269147072Sbrooks		config->script_name = parse_string(cfile);
270147072Sbrooks		return;
271147072Sbrooks	case INTERFACE:
272147072Sbrooks		if (ip)
273147072Sbrooks			parse_warn("nested interface declaration.");
274147072Sbrooks		parse_interface_declaration(cfile, config);
275147072Sbrooks		return;
276147072Sbrooks	case LEASE:
277147072Sbrooks		parse_client_lease_statement(cfile, 1);
278147072Sbrooks		return;
279147072Sbrooks	case ALIAS:
280147072Sbrooks		parse_client_lease_statement(cfile, 2);
281147072Sbrooks		return;
282147072Sbrooks	case REJECT:
283147072Sbrooks		parse_reject_statement(cfile, config);
284147072Sbrooks		return;
285147072Sbrooks	default:
286147072Sbrooks		parse_warn("expecting a statement.");
287147072Sbrooks		skip_to_semi(cfile);
288147072Sbrooks		break;
289147072Sbrooks	}
290147072Sbrooks	token = next_token(&val, cfile);
291147072Sbrooks	if (token != SEMI) {
292147072Sbrooks		parse_warn("semicolon expected.");
293147072Sbrooks		skip_to_semi(cfile);
294147072Sbrooks	}
295147072Sbrooks}
296147072Sbrooks
297327854Sasomersunsigned
298327854Sasomersparse_X(FILE *cfile, u_int8_t *buf, unsigned max)
299147072Sbrooks{
300147072Sbrooks	int	 token;
301147072Sbrooks	char	*val;
302327854Sasomers	unsigned len;
303147072Sbrooks
304147072Sbrooks	token = peek_token(&val, cfile);
305147072Sbrooks	if (token == NUMBER_OR_NAME || token == NUMBER) {
306147072Sbrooks		len = 0;
307147072Sbrooks		do {
308147072Sbrooks			token = next_token(&val, cfile);
309147072Sbrooks			if (token != NUMBER && token != NUMBER_OR_NAME) {
310147072Sbrooks				parse_warn("expecting hexadecimal constant.");
311147072Sbrooks				skip_to_semi(cfile);
312147072Sbrooks				return (0);
313147072Sbrooks			}
314147072Sbrooks			convert_num(&buf[len], val, 16, 8);
315147072Sbrooks			if (len++ > max) {
316147072Sbrooks				parse_warn("hexadecimal constant too long.");
317147072Sbrooks				skip_to_semi(cfile);
318147072Sbrooks				return (0);
319147072Sbrooks			}
320147072Sbrooks			token = peek_token(&val, cfile);
321147072Sbrooks			if (token == COLON)
322147072Sbrooks				token = next_token(&val, cfile);
323147072Sbrooks		} while (token == COLON);
324147072Sbrooks		val = (char *)buf;
325147072Sbrooks	} else if (token == STRING) {
326147072Sbrooks		token = next_token(&val, cfile);
327147072Sbrooks		len = strlen(val);
328147072Sbrooks		if (len + 1 > max) {
329147072Sbrooks			parse_warn("string constant too long.");
330147072Sbrooks			skip_to_semi(cfile);
331147072Sbrooks			return (0);
332147072Sbrooks		}
333147072Sbrooks		memcpy(buf, val, len + 1);
334147072Sbrooks	} else {
335147072Sbrooks		parse_warn("expecting string or hexadecimal data");
336147072Sbrooks		skip_to_semi(cfile);
337147072Sbrooks		return (0);
338147072Sbrooks	}
339147072Sbrooks	return (len);
340147072Sbrooks}
341147072Sbrooks
342147072Sbrooks/*
343147072Sbrooks * option-list :== option_name |
344147072Sbrooks *		   option_list COMMA option_name
345147072Sbrooks */
346147072Sbrooksint
347147072Sbrooksparse_option_list(FILE *cfile, u_int8_t *list)
348147072Sbrooks{
349147072Sbrooks	int	 ix, i;
350147072Sbrooks	int	 token;
351147072Sbrooks	char	*val;
352147072Sbrooks
353147072Sbrooks	ix = 0;
354147072Sbrooks	do {
355147072Sbrooks		token = next_token(&val, cfile);
356147072Sbrooks		if (!is_identifier(token)) {
357147072Sbrooks			parse_warn("expected option name.");
358147072Sbrooks			skip_to_semi(cfile);
359147072Sbrooks			return (0);
360147072Sbrooks		}
361147072Sbrooks		for (i = 0; i < 256; i++)
362147072Sbrooks			if (!strcasecmp(dhcp_options[i].name, val))
363147072Sbrooks				break;
364147072Sbrooks
365147072Sbrooks		if (i == 256) {
366147072Sbrooks			parse_warn("%s: unexpected option name.", val);
367147072Sbrooks			skip_to_semi(cfile);
368147072Sbrooks			return (0);
369147072Sbrooks		}
370147072Sbrooks		list[ix++] = i;
371147072Sbrooks		if (ix == 256) {
372147072Sbrooks			parse_warn("%s: too many options.", val);
373147072Sbrooks			skip_to_semi(cfile);
374147072Sbrooks			return (0);
375147072Sbrooks		}
376147072Sbrooks		token = next_token(&val, cfile);
377147072Sbrooks	} while (token == COMMA);
378147072Sbrooks	if (token != SEMI) {
379147072Sbrooks		parse_warn("expecting semicolon.");
380147072Sbrooks		skip_to_semi(cfile);
381147072Sbrooks		return (0);
382147072Sbrooks	}
383147072Sbrooks	return (ix);
384147072Sbrooks}
385147072Sbrooks
386147072Sbrooks/*
387147072Sbrooks * interface-declaration :==
388147072Sbrooks *	INTERFACE string LBRACE client-declarations RBRACE
389147072Sbrooks */
390147072Sbrooksvoid
391147072Sbrooksparse_interface_declaration(FILE *cfile, struct client_config *outer_config)
392147072Sbrooks{
393147072Sbrooks	int			 token;
394147072Sbrooks	char			*val;
395147072Sbrooks	struct interface_info	*ip;
396147072Sbrooks
397147072Sbrooks	token = next_token(&val, cfile);
398147072Sbrooks	if (token != STRING) {
399147072Sbrooks		parse_warn("expecting interface name (in quotes).");
400147072Sbrooks		skip_to_semi(cfile);
401147072Sbrooks		return;
402147072Sbrooks	}
403147072Sbrooks
404147072Sbrooks	ip = interface_or_dummy(val);
405147072Sbrooks
406147072Sbrooks	if (!ip->client)
407147072Sbrooks		make_client_state(ip);
408147072Sbrooks
409147072Sbrooks	if (!ip->client->config)
410147072Sbrooks		make_client_config(ip, outer_config);
411147072Sbrooks
412147072Sbrooks	token = next_token(&val, cfile);
413147072Sbrooks	if (token != LBRACE) {
414147072Sbrooks		parse_warn("expecting left brace.");
415147072Sbrooks		skip_to_semi(cfile);
416147072Sbrooks		return;
417147072Sbrooks	}
418147072Sbrooks
419147072Sbrooks	do {
420147072Sbrooks		token = peek_token(&val, cfile);
421147072Sbrooks		if (token == EOF) {
422147072Sbrooks			parse_warn("unterminated interface declaration.");
423147072Sbrooks			return;
424147072Sbrooks		}
425147072Sbrooks		if (token == RBRACE)
426147072Sbrooks			break;
427147072Sbrooks		parse_client_statement(cfile, ip, ip->client->config);
428147072Sbrooks	} while (1);
429147072Sbrooks	token = next_token(&val, cfile);
430147072Sbrooks}
431147072Sbrooks
432147072Sbrooksstruct interface_info *
433147072Sbrooksinterface_or_dummy(char *name)
434147072Sbrooks{
435147072Sbrooks	struct interface_info	*ip;
436147072Sbrooks
437147072Sbrooks	/* Find the interface (if any) that matches the name. */
438147072Sbrooks	if (!strcmp(ifi->name, name))
439147072Sbrooks		return (ifi);
440147072Sbrooks
441147072Sbrooks	/* If it's not a real interface, see if it's on the dummy list. */
442147072Sbrooks	for (ip = dummy_interfaces; ip; ip = ip->next)
443147072Sbrooks		if (!strcmp(ip->name, name))
444147072Sbrooks			return (ip);
445147072Sbrooks
446147072Sbrooks	/*
447147072Sbrooks	 * If we didn't find an interface, make a dummy interface as a
448147072Sbrooks	 * placeholder.
449147072Sbrooks	 */
450147072Sbrooks	ip = malloc(sizeof(*ip));
451147072Sbrooks	if (!ip)
452147072Sbrooks		error("Insufficient memory to record interface %s", name);
453147072Sbrooks	memset(ip, 0, sizeof(*ip));
454147072Sbrooks	strlcpy(ip->name, name, IFNAMSIZ);
455147072Sbrooks	ip->next = dummy_interfaces;
456147072Sbrooks	dummy_interfaces = ip;
457147072Sbrooks	return (ip);
458147072Sbrooks}
459147072Sbrooks
460147072Sbrooksvoid
461147072Sbrooksmake_client_state(struct interface_info *ip)
462147072Sbrooks{
463147072Sbrooks	ip->client = malloc(sizeof(*(ip->client)));
464147072Sbrooks	if (!ip->client)
465147072Sbrooks		error("no memory for state on %s", ip->name);
466147072Sbrooks	memset(ip->client, 0, sizeof(*(ip->client)));
467147072Sbrooks}
468147072Sbrooks
469147072Sbrooksvoid
470147072Sbrooksmake_client_config(struct interface_info *ip, struct client_config *config)
471147072Sbrooks{
472147072Sbrooks	ip->client->config = malloc(sizeof(struct client_config));
473147072Sbrooks	if (!ip->client->config)
474147072Sbrooks		error("no memory for config for %s", ip->name);
475147072Sbrooks	memset(ip->client->config, 0, sizeof(*(ip->client->config)));
476147072Sbrooks	memcpy(ip->client->config, config, sizeof(*config));
477147072Sbrooks}
478147072Sbrooks
479147072Sbrooks/*
480147072Sbrooks * client-lease-statement :==
481147072Sbrooks *	RBRACE client-lease-declarations LBRACE
482147072Sbrooks *
483147072Sbrooks *	client-lease-declarations :==
484147072Sbrooks *		<nil> |
485147072Sbrooks *		client-lease-declaration |
486147072Sbrooks *		client-lease-declarations client-lease-declaration
487147072Sbrooks */
488147072Sbrooksvoid
489147072Sbrooksparse_client_lease_statement(FILE *cfile, int is_static)
490147072Sbrooks{
491147072Sbrooks	struct client_lease	*lease, *lp, *pl;
492147072Sbrooks	struct interface_info	*ip;
493147072Sbrooks	int			 token;
494147072Sbrooks	char			*val;
495147072Sbrooks
496147072Sbrooks	token = next_token(&val, cfile);
497147072Sbrooks	if (token != LBRACE) {
498147072Sbrooks		parse_warn("expecting left brace.");
499147072Sbrooks		skip_to_semi(cfile);
500147072Sbrooks		return;
501147072Sbrooks	}
502147072Sbrooks
503147072Sbrooks	lease = malloc(sizeof(struct client_lease));
504147072Sbrooks	if (!lease)
505147072Sbrooks		error("no memory for lease.");
506147072Sbrooks	memset(lease, 0, sizeof(*lease));
507147072Sbrooks	lease->is_static = is_static;
508147072Sbrooks
509147072Sbrooks	ip = NULL;
510147072Sbrooks
511147072Sbrooks	do {
512147072Sbrooks		token = peek_token(&val, cfile);
513147072Sbrooks		if (token == EOF) {
514147072Sbrooks			parse_warn("unterminated lease declaration.");
515315609Sngie			free_client_lease(lease);
516147072Sbrooks			return;
517147072Sbrooks		}
518147072Sbrooks		if (token == RBRACE)
519147072Sbrooks			break;
520147072Sbrooks		parse_client_lease_declaration(cfile, lease, &ip);
521147072Sbrooks	} while (1);
522147072Sbrooks	token = next_token(&val, cfile);
523147072Sbrooks
524147072Sbrooks	/* If the lease declaration didn't include an interface
525147072Sbrooks	 * declaration that we recognized, it's of no use to us.
526147072Sbrooks	 */
527147072Sbrooks	if (!ip) {
528147072Sbrooks		free_client_lease(lease);
529147072Sbrooks		return;
530147072Sbrooks	}
531147072Sbrooks
532147072Sbrooks	/* Make sure there's a client state structure... */
533147072Sbrooks	if (!ip->client)
534147072Sbrooks		make_client_state(ip);
535147072Sbrooks
536147072Sbrooks	/* If this is an alias lease, it doesn't need to be sorted in. */
537147072Sbrooks	if (is_static == 2) {
538147072Sbrooks		ip->client->alias = lease;
539147072Sbrooks		return;
540147072Sbrooks	}
541147072Sbrooks
542147072Sbrooks	/*
543147072Sbrooks	 * The new lease may supersede a lease that's not the active
544147072Sbrooks	 * lease but is still on the lease list, so scan the lease list
545147072Sbrooks	 * looking for a lease with the same address, and if we find it,
546147072Sbrooks	 * toss it.
547147072Sbrooks	 */
548147072Sbrooks	pl = NULL;
549147072Sbrooks	for (lp = ip->client->leases; lp; lp = lp->next) {
550147072Sbrooks		if (lp->address.len == lease->address.len &&
551147072Sbrooks		    !memcmp(lp->address.iabuf, lease->address.iabuf,
552147072Sbrooks		    lease->address.len)) {
553147072Sbrooks			if (pl)
554147072Sbrooks				pl->next = lp->next;
555147072Sbrooks			else
556147072Sbrooks				ip->client->leases = lp->next;
557147072Sbrooks			free_client_lease(lp);
558147072Sbrooks			break;
559147072Sbrooks		}
560147072Sbrooks	}
561147072Sbrooks
562147072Sbrooks	/*
563147072Sbrooks	 * If this is a preloaded lease, just put it on the list of
564147072Sbrooks	 * recorded leases - don't make it the active lease.
565147072Sbrooks	 */
566147072Sbrooks	if (is_static) {
567147072Sbrooks		lease->next = ip->client->leases;
568147072Sbrooks		ip->client->leases = lease;
569147072Sbrooks		return;
570147072Sbrooks	}
571147072Sbrooks
572147072Sbrooks	/*
573147072Sbrooks	 * The last lease in the lease file on a particular interface is
574147072Sbrooks	 * the active lease for that interface.    Of course, we don't
575147072Sbrooks	 * know what the last lease in the file is until we've parsed
576147072Sbrooks	 * the whole file, so at this point, we assume that the lease we
577147072Sbrooks	 * just parsed is the active lease for its interface.   If
578147072Sbrooks	 * there's already an active lease for the interface, and this
579147072Sbrooks	 * lease is for the same ip address, then we just toss the old
580147072Sbrooks	 * active lease and replace it with this one.   If this lease is
581147072Sbrooks	 * for a different address, then if the old active lease has
582147072Sbrooks	 * expired, we dump it; if not, we put it on the list of leases
583147072Sbrooks	 * for this interface which are still valid but no longer
584147072Sbrooks	 * active.
585147072Sbrooks	 */
586147072Sbrooks	if (ip->client->active) {
587147072Sbrooks		if (ip->client->active->expiry < cur_time)
588147072Sbrooks			free_client_lease(ip->client->active);
589147072Sbrooks		else if (ip->client->active->address.len ==
590147072Sbrooks		    lease->address.len &&
591147072Sbrooks		    !memcmp(ip->client->active->address.iabuf,
592147072Sbrooks		    lease->address.iabuf, lease->address.len))
593147072Sbrooks			free_client_lease(ip->client->active);
594147072Sbrooks		else {
595147072Sbrooks			ip->client->active->next = ip->client->leases;
596147072Sbrooks			ip->client->leases = ip->client->active;
597147072Sbrooks		}
598147072Sbrooks	}
599147072Sbrooks	ip->client->active = lease;
600147072Sbrooks
601147072Sbrooks	/* Phew. */
602147072Sbrooks}
603147072Sbrooks
604147072Sbrooks/*
605147072Sbrooks * client-lease-declaration :==
606147072Sbrooks *	BOOTP |
607147072Sbrooks *	INTERFACE string |
608147072Sbrooks *	FIXED_ADDR ip_address |
609147072Sbrooks *	FILENAME string |
610147072Sbrooks *	SERVER_NAME string |
611147072Sbrooks *	OPTION option-decl |
612147072Sbrooks *	RENEW time-decl |
613147072Sbrooks *	REBIND time-decl |
614147072Sbrooks *	EXPIRE time-decl
615147072Sbrooks */
616147072Sbrooksvoid
617147072Sbrooksparse_client_lease_declaration(FILE *cfile, struct client_lease *lease,
618147072Sbrooks    struct interface_info **ipp)
619147072Sbrooks{
620147072Sbrooks	int			 token;
621147072Sbrooks	char			*val;
622147072Sbrooks	struct interface_info	*ip;
623147072Sbrooks
624147072Sbrooks	switch (next_token(&val, cfile)) {
625147072Sbrooks	case BOOTP:
626147072Sbrooks		lease->is_bootp = 1;
627147072Sbrooks		break;
628147072Sbrooks	case INTERFACE:
629147072Sbrooks		token = next_token(&val, cfile);
630147072Sbrooks		if (token != STRING) {
631147072Sbrooks			parse_warn("expecting interface name (in quotes).");
632147072Sbrooks			skip_to_semi(cfile);
633147072Sbrooks			break;
634147072Sbrooks		}
635147072Sbrooks		ip = interface_or_dummy(val);
636147072Sbrooks		*ipp = ip;
637147072Sbrooks		break;
638147072Sbrooks	case FIXED_ADDR:
639147072Sbrooks		if (!parse_ip_addr(cfile, &lease->address))
640147072Sbrooks			return;
641147072Sbrooks		break;
642147072Sbrooks	case MEDIUM:
643147072Sbrooks		parse_string_list(cfile, &lease->medium, 0);
644147072Sbrooks		return;
645147072Sbrooks	case FILENAME:
646147072Sbrooks		lease->filename = parse_string(cfile);
647147072Sbrooks		return;
648252506Sbms	case NEXT_SERVER:
649252506Sbms		if (!parse_ip_addr(cfile, &lease->nextserver))
650252506Sbms			return;
651252506Sbms		break;
652147072Sbrooks	case SERVER_NAME:
653147072Sbrooks		lease->server_name = parse_string(cfile);
654147072Sbrooks		return;
655147072Sbrooks	case RENEW:
656147072Sbrooks		lease->renewal = parse_date(cfile);
657147072Sbrooks		return;
658147072Sbrooks	case REBIND:
659147072Sbrooks		lease->rebind = parse_date(cfile);
660147072Sbrooks		return;
661147072Sbrooks	case EXPIRE:
662147072Sbrooks		lease->expiry = parse_date(cfile);
663147072Sbrooks		return;
664147072Sbrooks	case OPTION:
665147072Sbrooks		parse_option_decl(cfile, lease->options);
666147072Sbrooks		return;
667147072Sbrooks	default:
668147072Sbrooks		parse_warn("expecting lease declaration.");
669147072Sbrooks		skip_to_semi(cfile);
670147072Sbrooks		break;
671147072Sbrooks	}
672147072Sbrooks	token = next_token(&val, cfile);
673147072Sbrooks	if (token != SEMI) {
674147072Sbrooks		parse_warn("expecting semicolon.");
675147072Sbrooks		skip_to_semi(cfile);
676147072Sbrooks	}
677147072Sbrooks}
678147072Sbrooks
679147072Sbrooksstruct option *
680147072Sbrooksparse_option_decl(FILE *cfile, struct option_data *options)
681147072Sbrooks{
682147072Sbrooks	char		*val;
683147072Sbrooks	int		 token;
684147072Sbrooks	u_int8_t	 buf[4];
685147072Sbrooks	u_int8_t	 hunkbuf[1024];
686327854Sasomers	unsigned	 hunkix = 0;
687147072Sbrooks	char		*vendor;
688332602Sasomers	const char	*fmt;
689147072Sbrooks	struct universe	*universe;
690147072Sbrooks	struct option	*option;
691147072Sbrooks	struct iaddr	 ip_addr;
692147072Sbrooks	u_int8_t	*dp;
693327854Sasomers	unsigned	 len;
694147072Sbrooks	int		 nul_term = 0;
695147072Sbrooks
696147072Sbrooks	token = next_token(&val, cfile);
697147072Sbrooks	if (!is_identifier(token)) {
698147072Sbrooks		parse_warn("expecting identifier after option keyword.");
699147072Sbrooks		if (token != SEMI)
700147072Sbrooks			skip_to_semi(cfile);
701147072Sbrooks		return (NULL);
702147072Sbrooks	}
703147072Sbrooks	if ((vendor = strdup(val)) == NULL)
704147072Sbrooks		error("no memory for vendor information.");
705147072Sbrooks
706147072Sbrooks	token = peek_token(&val, cfile);
707147072Sbrooks	if (token == DOT) {
708147072Sbrooks		/* Go ahead and take the DOT token... */
709147072Sbrooks		token = next_token(&val, cfile);
710147072Sbrooks
711147072Sbrooks		/* The next token should be an identifier... */
712147072Sbrooks		token = next_token(&val, cfile);
713147072Sbrooks		if (!is_identifier(token)) {
714147072Sbrooks			parse_warn("expecting identifier after '.'");
715147072Sbrooks			if (token != SEMI)
716147072Sbrooks				skip_to_semi(cfile);
717315609Sngie			free(vendor);
718147072Sbrooks			return (NULL);
719147072Sbrooks		}
720147072Sbrooks
721147072Sbrooks		/* Look up the option name hash table for the specified
722147072Sbrooks		   vendor. */
723147072Sbrooks		universe = ((struct universe *)hash_lookup(&universe_hash,
724147072Sbrooks		    (unsigned char *)vendor, 0));
725147072Sbrooks		/* If it's not there, we can't parse the rest of the
726147072Sbrooks		   declaration. */
727147072Sbrooks		if (!universe) {
728147072Sbrooks			parse_warn("no vendor named %s.", vendor);
729147072Sbrooks			skip_to_semi(cfile);
730315609Sngie			free(vendor);
731147072Sbrooks			return (NULL);
732147072Sbrooks		}
733147072Sbrooks	} else {
734147072Sbrooks		/* Use the default hash table, which contains all the
735147072Sbrooks		   standard dhcp option names. */
736147072Sbrooks		val = vendor;
737147072Sbrooks		universe = &dhcp_universe;
738147072Sbrooks	}
739147072Sbrooks
740147072Sbrooks	/* Look up the actual option info... */
741147072Sbrooks	option = (struct option *)hash_lookup(universe->hash,
742147072Sbrooks	    (unsigned char *)val, 0);
743147072Sbrooks
744147072Sbrooks	/* If we didn't get an option structure, it's an undefined option. */
745147072Sbrooks	if (!option) {
746147072Sbrooks		if (val == vendor)
747147072Sbrooks			parse_warn("no option named %s", val);
748147072Sbrooks		else
749147072Sbrooks			parse_warn("no option named %s for vendor %s",
750147072Sbrooks				    val, vendor);
751147072Sbrooks		skip_to_semi(cfile);
752315609Sngie		free(vendor);
753147072Sbrooks		return (NULL);
754147072Sbrooks	}
755147072Sbrooks
756147072Sbrooks	/* Free the initial identifier token. */
757147072Sbrooks	free(vendor);
758147072Sbrooks
759147072Sbrooks	/* Parse the option data... */
760147072Sbrooks	do {
761147072Sbrooks		for (fmt = option->format; *fmt; fmt++) {
762147072Sbrooks			if (*fmt == 'A')
763147072Sbrooks				break;
764147072Sbrooks			switch (*fmt) {
765147072Sbrooks			case 'X':
766147072Sbrooks				len = parse_X(cfile, &hunkbuf[hunkix],
767147072Sbrooks				    sizeof(hunkbuf) - hunkix);
768147072Sbrooks				hunkix += len;
769147072Sbrooks				break;
770147072Sbrooks			case 't': /* Text string... */
771147072Sbrooks				token = next_token(&val, cfile);
772147072Sbrooks				if (token != STRING) {
773147072Sbrooks					parse_warn("expecting string.");
774147072Sbrooks					skip_to_semi(cfile);
775147072Sbrooks					return (NULL);
776147072Sbrooks				}
777147072Sbrooks				len = strlen(val);
778147072Sbrooks				if (hunkix + len + 1 > sizeof(hunkbuf)) {
779147072Sbrooks					parse_warn("option data buffer %s",
780147072Sbrooks					    "overflow");
781147072Sbrooks					skip_to_semi(cfile);
782147072Sbrooks					return (NULL);
783147072Sbrooks				}
784147072Sbrooks				memcpy(&hunkbuf[hunkix], val, len + 1);
785147072Sbrooks				nul_term = 1;
786147072Sbrooks				hunkix += len;
787147072Sbrooks				break;
788147072Sbrooks			case 'I': /* IP address. */
789147072Sbrooks				if (!parse_ip_addr(cfile, &ip_addr))
790147072Sbrooks					return (NULL);
791147072Sbrooks				len = ip_addr.len;
792147072Sbrooks				dp = ip_addr.iabuf;
793147072Sbrooksalloc:
794147072Sbrooks				if (hunkix + len > sizeof(hunkbuf)) {
795147072Sbrooks					parse_warn("option data buffer "
796147072Sbrooks					    "overflow");
797147072Sbrooks					skip_to_semi(cfile);
798147072Sbrooks					return (NULL);
799147072Sbrooks				}
800147072Sbrooks				memcpy(&hunkbuf[hunkix], dp, len);
801147072Sbrooks				hunkix += len;
802147072Sbrooks				break;
803147072Sbrooks			case 'L':	/* Unsigned 32-bit integer... */
804147072Sbrooks			case 'l':	/* Signed 32-bit integer... */
805147072Sbrooks				token = next_token(&val, cfile);
806147072Sbrooks				if (token != NUMBER) {
807147072Sbrooksneed_number:
808147072Sbrooks					parse_warn("expecting number.");
809147072Sbrooks					if (token != SEMI)
810147072Sbrooks						skip_to_semi(cfile);
811147072Sbrooks					return (NULL);
812147072Sbrooks				}
813147072Sbrooks				convert_num(buf, val, 0, 32);
814147072Sbrooks				len = 4;
815147072Sbrooks				dp = buf;
816147072Sbrooks				goto alloc;
817147072Sbrooks			case 's':	/* Signed 16-bit integer. */
818147072Sbrooks			case 'S':	/* Unsigned 16-bit integer. */
819147072Sbrooks				token = next_token(&val, cfile);
820147072Sbrooks				if (token != NUMBER)
821147072Sbrooks					goto need_number;
822147072Sbrooks				convert_num(buf, val, 0, 16);
823147072Sbrooks				len = 2;
824147072Sbrooks				dp = buf;
825147072Sbrooks				goto alloc;
826147072Sbrooks			case 'b':	/* Signed 8-bit integer. */
827147072Sbrooks			case 'B':	/* Unsigned 8-bit integer. */
828147072Sbrooks				token = next_token(&val, cfile);
829147072Sbrooks				if (token != NUMBER)
830147072Sbrooks					goto need_number;
831147072Sbrooks				convert_num(buf, val, 0, 8);
832147072Sbrooks				len = 1;
833147072Sbrooks				dp = buf;
834147072Sbrooks				goto alloc;
835147072Sbrooks			case 'f': /* Boolean flag. */
836147072Sbrooks				token = next_token(&val, cfile);
837147072Sbrooks				if (!is_identifier(token)) {
838147072Sbrooks					parse_warn("expecting identifier.");
839147072Sbrooksbad_flag:
840147072Sbrooks					if (token != SEMI)
841147072Sbrooks						skip_to_semi(cfile);
842147072Sbrooks					return (NULL);
843147072Sbrooks				}
844147072Sbrooks				if (!strcasecmp(val, "true") ||
845147072Sbrooks				    !strcasecmp(val, "on"))
846147072Sbrooks					buf[0] = 1;
847147072Sbrooks				else if (!strcasecmp(val, "false") ||
848147072Sbrooks				    !strcasecmp(val, "off"))
849147072Sbrooks					buf[0] = 0;
850147072Sbrooks				else {
851147072Sbrooks					parse_warn("expecting boolean.");
852147072Sbrooks					goto bad_flag;
853147072Sbrooks				}
854147072Sbrooks				len = 1;
855147072Sbrooks				dp = buf;
856147072Sbrooks				goto alloc;
857147072Sbrooks			default:
858147072Sbrooks				warning("Bad format %c in parse_option_param.",
859147072Sbrooks				    *fmt);
860147072Sbrooks				skip_to_semi(cfile);
861147072Sbrooks				return (NULL);
862147072Sbrooks			}
863147072Sbrooks		}
864147072Sbrooks		token = next_token(&val, cfile);
865147072Sbrooks	} while (*fmt == 'A' && token == COMMA);
866147072Sbrooks
867147072Sbrooks	if (token != SEMI) {
868147072Sbrooks		parse_warn("semicolon expected.");
869147072Sbrooks		skip_to_semi(cfile);
870147072Sbrooks		return (NULL);
871147072Sbrooks	}
872147072Sbrooks
873147072Sbrooks	options[option->code].data = malloc(hunkix + nul_term);
874147072Sbrooks	if (!options[option->code].data)
875147072Sbrooks		error("out of memory allocating option data.");
876147072Sbrooks	memcpy(options[option->code].data, hunkbuf, hunkix + nul_term);
877147072Sbrooks	options[option->code].len = hunkix;
878147072Sbrooks	return (option);
879147072Sbrooks}
880147072Sbrooks
881147072Sbrooksvoid
882147072Sbrooksparse_string_list(FILE *cfile, struct string_list **lp, int multiple)
883147072Sbrooks{
884147072Sbrooks	int			 token;
885147072Sbrooks	char			*val;
886228614Sdim	size_t			 valsize;
887147072Sbrooks	struct string_list	*cur, *tmp;
888147072Sbrooks
889147072Sbrooks	/* Find the last medium in the media list. */
890147072Sbrooks	if (*lp)
891147072Sbrooks		for (cur = *lp; cur->next; cur = cur->next)
892147072Sbrooks			;	/* nothing */
893147072Sbrooks	else
894147072Sbrooks		cur = NULL;
895147072Sbrooks
896147072Sbrooks	do {
897147072Sbrooks		token = next_token(&val, cfile);
898147072Sbrooks		if (token != STRING) {
899147072Sbrooks			parse_warn("Expecting media options.");
900147072Sbrooks			skip_to_semi(cfile);
901147072Sbrooks			return;
902147072Sbrooks		}
903147072Sbrooks
904228614Sdim		valsize = strlen(val) + 1;
905228614Sdim		tmp = new_string_list(valsize);
906147072Sbrooks		if (tmp == NULL)
907147072Sbrooks			error("no memory for string list entry.");
908228615Sdim		memcpy(tmp->string, val, valsize);
909147072Sbrooks		tmp->next = NULL;
910147072Sbrooks
911147072Sbrooks		/* Store this medium at the end of the media list. */
912147072Sbrooks		if (cur)
913147072Sbrooks			cur->next = tmp;
914147072Sbrooks		else
915147072Sbrooks			*lp = tmp;
916147072Sbrooks		cur = tmp;
917147072Sbrooks
918147072Sbrooks		token = next_token(&val, cfile);
919147072Sbrooks	} while (multiple && token == COMMA);
920147072Sbrooks
921147072Sbrooks	if (token != SEMI) {
922147072Sbrooks		parse_warn("expecting semicolon.");
923147072Sbrooks		skip_to_semi(cfile);
924147072Sbrooks	}
925147072Sbrooks}
926147072Sbrooks
927147072Sbrooksvoid
928147072Sbrooksparse_reject_statement(FILE *cfile, struct client_config *config)
929147072Sbrooks{
930147072Sbrooks	int			 token;
931147072Sbrooks	char			*val;
932147072Sbrooks	struct iaddr		 addr;
933147072Sbrooks	struct iaddrlist	*list;
934147072Sbrooks
935147072Sbrooks	do {
936147072Sbrooks		if (!parse_ip_addr(cfile, &addr)) {
937147072Sbrooks			parse_warn("expecting IP address.");
938147072Sbrooks			skip_to_semi(cfile);
939147072Sbrooks			return;
940147072Sbrooks		}
941147072Sbrooks
942147072Sbrooks		list = malloc(sizeof(struct iaddrlist));
943147072Sbrooks		if (!list)
944147072Sbrooks			error("no memory for reject list!");
945147072Sbrooks
946147072Sbrooks		list->addr = addr;
947147072Sbrooks		list->next = config->reject_list;
948147072Sbrooks		config->reject_list = list;
949147072Sbrooks
950147072Sbrooks		token = next_token(&val, cfile);
951147072Sbrooks	} while (token == COMMA);
952147072Sbrooks
953147072Sbrooks	if (token != SEMI) {
954147072Sbrooks		parse_warn("expecting semicolon.");
955147072Sbrooks		skip_to_semi(cfile);
956147072Sbrooks	}
957147072Sbrooks}
958