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
5147072Sbrooks/*
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/10/sbin/dhclient/clparse.c 315610 2017-03-20 03:06:41Z ngie $");
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;
105147072Sbrooks
106147072Sbrooks	if ((cfile = fopen(path_dhclient_conf, "r")) != NULL) {
107147072Sbrooks		do {
108147072Sbrooks			token = peek_token(&val, cfile);
109147072Sbrooks			if (token == EOF)
110147072Sbrooks				break;
111147072Sbrooks			parse_client_statement(cfile, NULL, &top_level_config);
112147072Sbrooks		} while (1);
113147072Sbrooks		token = next_token(&val, cfile); /* Clear the peek buffer */
114147072Sbrooks		fclose(cfile);
115147072Sbrooks	}
116147072Sbrooks
117147072Sbrooks	/*
118147072Sbrooks	 * Set up state and config structures for clients that don't
119147072Sbrooks	 * have per-interface configuration declarations.
120147072Sbrooks	 */
121147072Sbrooks	config = NULL;
122147072Sbrooks	if (!ifi->client) {
123147072Sbrooks		ifi->client = malloc(sizeof(struct client_state));
124147072Sbrooks		if (!ifi->client)
125147072Sbrooks			error("no memory for client state.");
126147072Sbrooks		memset(ifi->client, 0, sizeof(*(ifi->client)));
127147072Sbrooks	}
128147072Sbrooks	if (!ifi->client->config) {
129147072Sbrooks		if (!config) {
130147072Sbrooks			config = malloc(sizeof(struct client_config));
131147072Sbrooks			if (!config)
132147072Sbrooks				error("no memory for client config.");
133147072Sbrooks			memcpy(config, &top_level_config,
134147072Sbrooks				sizeof(top_level_config));
135147072Sbrooks		}
136147072Sbrooks		ifi->client->config = config;
137147072Sbrooks	}
138147072Sbrooks
139147072Sbrooks	return (!warnings_occurred);
140147072Sbrooks}
141147072Sbrooks
142147072Sbrooks/*
143147072Sbrooks * lease-file :== client-lease-statements EOF
144147072Sbrooks * client-lease-statements :== <nil>
145147072Sbrooks *		     | client-lease-statements LEASE client-lease-statement
146147072Sbrooks */
147147072Sbrooksvoid
148147072Sbrooksread_client_leases(void)
149147072Sbrooks{
150147072Sbrooks	FILE	*cfile;
151147072Sbrooks	char	*val;
152147072Sbrooks	int	 token;
153147072Sbrooks
154147072Sbrooks	new_parse(path_dhclient_db);
155147072Sbrooks
156147072Sbrooks	/* Open the lease file.   If we can't open it, just return -
157147072Sbrooks	   we can safely trust the server to remember our state. */
158147072Sbrooks	if ((cfile = fopen(path_dhclient_db, "r")) == NULL)
159147072Sbrooks		return;
160147072Sbrooks	do {
161147072Sbrooks		token = next_token(&val, cfile);
162147072Sbrooks		if (token == EOF)
163147072Sbrooks			break;
164147072Sbrooks		if (token != LEASE) {
165147072Sbrooks			warning("Corrupt lease file - possible data loss!");
166147072Sbrooks			skip_to_semi(cfile);
167147072Sbrooks			break;
168147072Sbrooks		} else
169147072Sbrooks			parse_client_lease_statement(cfile, 0);
170147072Sbrooks
171147072Sbrooks	} while (1);
172147072Sbrooks	fclose(cfile);
173147072Sbrooks}
174147072Sbrooks
175147072Sbrooks/*
176147072Sbrooks * client-declaration :==
177147072Sbrooks *	SEND option-decl |
178147072Sbrooks *	DEFAULT option-decl |
179147072Sbrooks *	SUPERSEDE option-decl |
180147072Sbrooks *	PREPEND option-decl |
181147072Sbrooks *	APPEND option-decl |
182147072Sbrooks *	hardware-declaration |
183147072Sbrooks *	REQUEST option-list |
184147072Sbrooks *	REQUIRE option-list |
185147072Sbrooks *	TIMEOUT number |
186147072Sbrooks *	RETRY number |
187147072Sbrooks *	REBOOT number |
188147072Sbrooks *	SELECT_TIMEOUT number |
189147072Sbrooks *	SCRIPT string |
190147072Sbrooks *	interface-declaration |
191147072Sbrooks *	LEASE client-lease-statement |
192147072Sbrooks *	ALIAS client-lease-statement
193147072Sbrooks */
194147072Sbrooksvoid
195147072Sbrooksparse_client_statement(FILE *cfile, struct interface_info *ip,
196147072Sbrooks    struct client_config *config)
197147072Sbrooks{
198147072Sbrooks	int		 token;
199147072Sbrooks	char		*val;
200147072Sbrooks	struct option	*option;
201147072Sbrooks
202147072Sbrooks	switch (next_token(&val, cfile)) {
203147072Sbrooks	case SEND:
204147072Sbrooks		parse_option_decl(cfile, &config->send_options[0]);
205147072Sbrooks		return;
206147072Sbrooks	case DEFAULT:
207147072Sbrooks		option = parse_option_decl(cfile, &config->defaults[0]);
208147072Sbrooks		if (option)
209147072Sbrooks			config->default_actions[option->code] = ACTION_DEFAULT;
210147072Sbrooks		return;
211147072Sbrooks	case SUPERSEDE:
212147072Sbrooks		option = parse_option_decl(cfile, &config->defaults[0]);
213147072Sbrooks		if (option)
214147072Sbrooks			config->default_actions[option->code] =
215147072Sbrooks			    ACTION_SUPERSEDE;
216147072Sbrooks		return;
217147072Sbrooks	case APPEND:
218147072Sbrooks		option = parse_option_decl(cfile, &config->defaults[0]);
219147072Sbrooks		if (option)
220147072Sbrooks			config->default_actions[option->code] = ACTION_APPEND;
221147072Sbrooks		return;
222147072Sbrooks	case PREPEND:
223147072Sbrooks		option = parse_option_decl(cfile, &config->defaults[0]);
224147072Sbrooks		if (option)
225147072Sbrooks			config->default_actions[option->code] = ACTION_PREPEND;
226147072Sbrooks		return;
227147072Sbrooks	case MEDIA:
228147072Sbrooks		parse_string_list(cfile, &config->media, 1);
229147072Sbrooks		return;
230147072Sbrooks	case HARDWARE:
231147072Sbrooks		if (ip)
232147072Sbrooks			parse_hardware_param(cfile, &ip->hw_address);
233147072Sbrooks		else {
234147072Sbrooks			parse_warn("hardware address parameter %s",
235147072Sbrooks				    "not allowed here.");
236147072Sbrooks			skip_to_semi(cfile);
237147072Sbrooks		}
238147072Sbrooks		return;
239147072Sbrooks	case REQUEST:
240147072Sbrooks		config->requested_option_count =
241147072Sbrooks			parse_option_list(cfile, config->requested_options);
242147072Sbrooks		return;
243147072Sbrooks	case REQUIRE:
244147072Sbrooks		memset(config->required_options, 0,
245147072Sbrooks		    sizeof(config->required_options));
246147072Sbrooks		parse_option_list(cfile, config->required_options);
247147072Sbrooks		return;
248147072Sbrooks	case TIMEOUT:
249147072Sbrooks		parse_lease_time(cfile, &config->timeout);
250147072Sbrooks		return;
251147072Sbrooks	case RETRY:
252147072Sbrooks		parse_lease_time(cfile, &config->retry_interval);
253147072Sbrooks		return;
254147072Sbrooks	case SELECT_TIMEOUT:
255147072Sbrooks		parse_lease_time(cfile, &config->select_interval);
256147072Sbrooks		return;
257147072Sbrooks	case REBOOT:
258147072Sbrooks		parse_lease_time(cfile, &config->reboot_timeout);
259147072Sbrooks		return;
260147072Sbrooks	case BACKOFF_CUTOFF:
261147072Sbrooks		parse_lease_time(cfile, &config->backoff_cutoff);
262147072Sbrooks		return;
263147072Sbrooks	case INITIAL_INTERVAL:
264147072Sbrooks		parse_lease_time(cfile, &config->initial_interval);
265147072Sbrooks		return;
266147072Sbrooks	case SCRIPT:
267147072Sbrooks		config->script_name = parse_string(cfile);
268147072Sbrooks		return;
269147072Sbrooks	case INTERFACE:
270147072Sbrooks		if (ip)
271147072Sbrooks			parse_warn("nested interface declaration.");
272147072Sbrooks		parse_interface_declaration(cfile, config);
273147072Sbrooks		return;
274147072Sbrooks	case LEASE:
275147072Sbrooks		parse_client_lease_statement(cfile, 1);
276147072Sbrooks		return;
277147072Sbrooks	case ALIAS:
278147072Sbrooks		parse_client_lease_statement(cfile, 2);
279147072Sbrooks		return;
280147072Sbrooks	case REJECT:
281147072Sbrooks		parse_reject_statement(cfile, config);
282147072Sbrooks		return;
283147072Sbrooks	default:
284147072Sbrooks		parse_warn("expecting a statement.");
285147072Sbrooks		skip_to_semi(cfile);
286147072Sbrooks		break;
287147072Sbrooks	}
288147072Sbrooks	token = next_token(&val, cfile);
289147072Sbrooks	if (token != SEMI) {
290147072Sbrooks		parse_warn("semicolon expected.");
291147072Sbrooks		skip_to_semi(cfile);
292147072Sbrooks	}
293147072Sbrooks}
294147072Sbrooks
295147072Sbrooksint
296147072Sbrooksparse_X(FILE *cfile, u_int8_t *buf, int max)
297147072Sbrooks{
298147072Sbrooks	int	 token;
299147072Sbrooks	char	*val;
300147072Sbrooks	int	 len;
301147072Sbrooks
302147072Sbrooks	token = peek_token(&val, cfile);
303147072Sbrooks	if (token == NUMBER_OR_NAME || token == NUMBER) {
304147072Sbrooks		len = 0;
305147072Sbrooks		do {
306147072Sbrooks			token = next_token(&val, cfile);
307147072Sbrooks			if (token != NUMBER && token != NUMBER_OR_NAME) {
308147072Sbrooks				parse_warn("expecting hexadecimal constant.");
309147072Sbrooks				skip_to_semi(cfile);
310147072Sbrooks				return (0);
311147072Sbrooks			}
312147072Sbrooks			convert_num(&buf[len], val, 16, 8);
313147072Sbrooks			if (len++ > max) {
314147072Sbrooks				parse_warn("hexadecimal constant too long.");
315147072Sbrooks				skip_to_semi(cfile);
316147072Sbrooks				return (0);
317147072Sbrooks			}
318147072Sbrooks			token = peek_token(&val, cfile);
319147072Sbrooks			if (token == COLON)
320147072Sbrooks				token = next_token(&val, cfile);
321147072Sbrooks		} while (token == COLON);
322147072Sbrooks		val = (char *)buf;
323147072Sbrooks	} else if (token == STRING) {
324147072Sbrooks		token = next_token(&val, cfile);
325147072Sbrooks		len = strlen(val);
326147072Sbrooks		if (len + 1 > max) {
327147072Sbrooks			parse_warn("string constant too long.");
328147072Sbrooks			skip_to_semi(cfile);
329147072Sbrooks			return (0);
330147072Sbrooks		}
331147072Sbrooks		memcpy(buf, val, len + 1);
332147072Sbrooks	} else {
333147072Sbrooks		parse_warn("expecting string or hexadecimal data");
334147072Sbrooks		skip_to_semi(cfile);
335147072Sbrooks		return (0);
336147072Sbrooks	}
337147072Sbrooks	return (len);
338147072Sbrooks}
339147072Sbrooks
340147072Sbrooks/*
341147072Sbrooks * option-list :== option_name |
342147072Sbrooks *		   option_list COMMA option_name
343147072Sbrooks */
344147072Sbrooksint
345147072Sbrooksparse_option_list(FILE *cfile, u_int8_t *list)
346147072Sbrooks{
347147072Sbrooks	int	 ix, i;
348147072Sbrooks	int	 token;
349147072Sbrooks	char	*val;
350147072Sbrooks
351147072Sbrooks	ix = 0;
352147072Sbrooks	do {
353147072Sbrooks		token = next_token(&val, cfile);
354147072Sbrooks		if (!is_identifier(token)) {
355147072Sbrooks			parse_warn("expected option name.");
356147072Sbrooks			skip_to_semi(cfile);
357147072Sbrooks			return (0);
358147072Sbrooks		}
359147072Sbrooks		for (i = 0; i < 256; i++)
360147072Sbrooks			if (!strcasecmp(dhcp_options[i].name, val))
361147072Sbrooks				break;
362147072Sbrooks
363147072Sbrooks		if (i == 256) {
364147072Sbrooks			parse_warn("%s: unexpected option name.", val);
365147072Sbrooks			skip_to_semi(cfile);
366147072Sbrooks			return (0);
367147072Sbrooks		}
368147072Sbrooks		list[ix++] = i;
369147072Sbrooks		if (ix == 256) {
370147072Sbrooks			parse_warn("%s: too many options.", val);
371147072Sbrooks			skip_to_semi(cfile);
372147072Sbrooks			return (0);
373147072Sbrooks		}
374147072Sbrooks		token = next_token(&val, cfile);
375147072Sbrooks	} while (token == COMMA);
376147072Sbrooks	if (token != SEMI) {
377147072Sbrooks		parse_warn("expecting semicolon.");
378147072Sbrooks		skip_to_semi(cfile);
379147072Sbrooks		return (0);
380147072Sbrooks	}
381147072Sbrooks	return (ix);
382147072Sbrooks}
383147072Sbrooks
384147072Sbrooks/*
385147072Sbrooks * interface-declaration :==
386147072Sbrooks *	INTERFACE string LBRACE client-declarations RBRACE
387147072Sbrooks */
388147072Sbrooksvoid
389147072Sbrooksparse_interface_declaration(FILE *cfile, struct client_config *outer_config)
390147072Sbrooks{
391147072Sbrooks	int			 token;
392147072Sbrooks	char			*val;
393147072Sbrooks	struct interface_info	*ip;
394147072Sbrooks
395147072Sbrooks	token = next_token(&val, cfile);
396147072Sbrooks	if (token != STRING) {
397147072Sbrooks		parse_warn("expecting interface name (in quotes).");
398147072Sbrooks		skip_to_semi(cfile);
399147072Sbrooks		return;
400147072Sbrooks	}
401147072Sbrooks
402147072Sbrooks	ip = interface_or_dummy(val);
403147072Sbrooks
404147072Sbrooks	if (!ip->client)
405147072Sbrooks		make_client_state(ip);
406147072Sbrooks
407147072Sbrooks	if (!ip->client->config)
408147072Sbrooks		make_client_config(ip, outer_config);
409147072Sbrooks
410147072Sbrooks	token = next_token(&val, cfile);
411147072Sbrooks	if (token != LBRACE) {
412147072Sbrooks		parse_warn("expecting left brace.");
413147072Sbrooks		skip_to_semi(cfile);
414147072Sbrooks		return;
415147072Sbrooks	}
416147072Sbrooks
417147072Sbrooks	do {
418147072Sbrooks		token = peek_token(&val, cfile);
419147072Sbrooks		if (token == EOF) {
420147072Sbrooks			parse_warn("unterminated interface declaration.");
421147072Sbrooks			return;
422147072Sbrooks		}
423147072Sbrooks		if (token == RBRACE)
424147072Sbrooks			break;
425147072Sbrooks		parse_client_statement(cfile, ip, ip->client->config);
426147072Sbrooks	} while (1);
427147072Sbrooks	token = next_token(&val, cfile);
428147072Sbrooks}
429147072Sbrooks
430147072Sbrooksstruct interface_info *
431147072Sbrooksinterface_or_dummy(char *name)
432147072Sbrooks{
433147072Sbrooks	struct interface_info	*ip;
434147072Sbrooks
435147072Sbrooks	/* Find the interface (if any) that matches the name. */
436147072Sbrooks	if (!strcmp(ifi->name, name))
437147072Sbrooks		return (ifi);
438147072Sbrooks
439147072Sbrooks	/* If it's not a real interface, see if it's on the dummy list. */
440147072Sbrooks	for (ip = dummy_interfaces; ip; ip = ip->next)
441147072Sbrooks		if (!strcmp(ip->name, name))
442147072Sbrooks			return (ip);
443147072Sbrooks
444147072Sbrooks	/*
445147072Sbrooks	 * If we didn't find an interface, make a dummy interface as a
446147072Sbrooks	 * placeholder.
447147072Sbrooks	 */
448147072Sbrooks	ip = malloc(sizeof(*ip));
449147072Sbrooks	if (!ip)
450147072Sbrooks		error("Insufficient memory to record interface %s", name);
451147072Sbrooks	memset(ip, 0, sizeof(*ip));
452147072Sbrooks	strlcpy(ip->name, name, IFNAMSIZ);
453147072Sbrooks	ip->next = dummy_interfaces;
454147072Sbrooks	dummy_interfaces = ip;
455147072Sbrooks	return (ip);
456147072Sbrooks}
457147072Sbrooks
458147072Sbrooksvoid
459147072Sbrooksmake_client_state(struct interface_info *ip)
460147072Sbrooks{
461147072Sbrooks	ip->client = malloc(sizeof(*(ip->client)));
462147072Sbrooks	if (!ip->client)
463147072Sbrooks		error("no memory for state on %s", ip->name);
464147072Sbrooks	memset(ip->client, 0, sizeof(*(ip->client)));
465147072Sbrooks}
466147072Sbrooks
467147072Sbrooksvoid
468147072Sbrooksmake_client_config(struct interface_info *ip, struct client_config *config)
469147072Sbrooks{
470147072Sbrooks	ip->client->config = malloc(sizeof(struct client_config));
471147072Sbrooks	if (!ip->client->config)
472147072Sbrooks		error("no memory for config for %s", ip->name);
473147072Sbrooks	memset(ip->client->config, 0, sizeof(*(ip->client->config)));
474147072Sbrooks	memcpy(ip->client->config, config, sizeof(*config));
475147072Sbrooks}
476147072Sbrooks
477147072Sbrooks/*
478147072Sbrooks * client-lease-statement :==
479147072Sbrooks *	RBRACE client-lease-declarations LBRACE
480147072Sbrooks *
481147072Sbrooks *	client-lease-declarations :==
482147072Sbrooks *		<nil> |
483147072Sbrooks *		client-lease-declaration |
484147072Sbrooks *		client-lease-declarations client-lease-declaration
485147072Sbrooks */
486147072Sbrooksvoid
487147072Sbrooksparse_client_lease_statement(FILE *cfile, int is_static)
488147072Sbrooks{
489147072Sbrooks	struct client_lease	*lease, *lp, *pl;
490147072Sbrooks	struct interface_info	*ip;
491147072Sbrooks	int			 token;
492147072Sbrooks	char			*val;
493147072Sbrooks
494147072Sbrooks	token = next_token(&val, cfile);
495147072Sbrooks	if (token != LBRACE) {
496147072Sbrooks		parse_warn("expecting left brace.");
497147072Sbrooks		skip_to_semi(cfile);
498147072Sbrooks		return;
499147072Sbrooks	}
500147072Sbrooks
501147072Sbrooks	lease = malloc(sizeof(struct client_lease));
502147072Sbrooks	if (!lease)
503147072Sbrooks		error("no memory for lease.");
504147072Sbrooks	memset(lease, 0, sizeof(*lease));
505147072Sbrooks	lease->is_static = is_static;
506147072Sbrooks
507147072Sbrooks	ip = NULL;
508147072Sbrooks
509147072Sbrooks	do {
510147072Sbrooks		token = peek_token(&val, cfile);
511147072Sbrooks		if (token == EOF) {
512147072Sbrooks			parse_warn("unterminated lease declaration.");
513315610Sngie			free_client_lease(lease);
514147072Sbrooks			return;
515147072Sbrooks		}
516147072Sbrooks		if (token == RBRACE)
517147072Sbrooks			break;
518147072Sbrooks		parse_client_lease_declaration(cfile, lease, &ip);
519147072Sbrooks	} while (1);
520147072Sbrooks	token = next_token(&val, cfile);
521147072Sbrooks
522147072Sbrooks	/* If the lease declaration didn't include an interface
523147072Sbrooks	 * declaration that we recognized, it's of no use to us.
524147072Sbrooks	 */
525147072Sbrooks	if (!ip) {
526147072Sbrooks		free_client_lease(lease);
527147072Sbrooks		return;
528147072Sbrooks	}
529147072Sbrooks
530147072Sbrooks	/* Make sure there's a client state structure... */
531147072Sbrooks	if (!ip->client)
532147072Sbrooks		make_client_state(ip);
533147072Sbrooks
534147072Sbrooks	/* If this is an alias lease, it doesn't need to be sorted in. */
535147072Sbrooks	if (is_static == 2) {
536147072Sbrooks		ip->client->alias = lease;
537147072Sbrooks		return;
538147072Sbrooks	}
539147072Sbrooks
540147072Sbrooks	/*
541147072Sbrooks	 * The new lease may supersede a lease that's not the active
542147072Sbrooks	 * lease but is still on the lease list, so scan the lease list
543147072Sbrooks	 * looking for a lease with the same address, and if we find it,
544147072Sbrooks	 * toss it.
545147072Sbrooks	 */
546147072Sbrooks	pl = NULL;
547147072Sbrooks	for (lp = ip->client->leases; lp; lp = lp->next) {
548147072Sbrooks		if (lp->address.len == lease->address.len &&
549147072Sbrooks		    !memcmp(lp->address.iabuf, lease->address.iabuf,
550147072Sbrooks		    lease->address.len)) {
551147072Sbrooks			if (pl)
552147072Sbrooks				pl->next = lp->next;
553147072Sbrooks			else
554147072Sbrooks				ip->client->leases = lp->next;
555147072Sbrooks			free_client_lease(lp);
556147072Sbrooks			break;
557147072Sbrooks		}
558147072Sbrooks	}
559147072Sbrooks
560147072Sbrooks	/*
561147072Sbrooks	 * If this is a preloaded lease, just put it on the list of
562147072Sbrooks	 * recorded leases - don't make it the active lease.
563147072Sbrooks	 */
564147072Sbrooks	if (is_static) {
565147072Sbrooks		lease->next = ip->client->leases;
566147072Sbrooks		ip->client->leases = lease;
567147072Sbrooks		return;
568147072Sbrooks	}
569147072Sbrooks
570147072Sbrooks	/*
571147072Sbrooks	 * The last lease in the lease file on a particular interface is
572147072Sbrooks	 * the active lease for that interface.    Of course, we don't
573147072Sbrooks	 * know what the last lease in the file is until we've parsed
574147072Sbrooks	 * the whole file, so at this point, we assume that the lease we
575147072Sbrooks	 * just parsed is the active lease for its interface.   If
576147072Sbrooks	 * there's already an active lease for the interface, and this
577147072Sbrooks	 * lease is for the same ip address, then we just toss the old
578147072Sbrooks	 * active lease and replace it with this one.   If this lease is
579147072Sbrooks	 * for a different address, then if the old active lease has
580147072Sbrooks	 * expired, we dump it; if not, we put it on the list of leases
581147072Sbrooks	 * for this interface which are still valid but no longer
582147072Sbrooks	 * active.
583147072Sbrooks	 */
584147072Sbrooks	if (ip->client->active) {
585147072Sbrooks		if (ip->client->active->expiry < cur_time)
586147072Sbrooks			free_client_lease(ip->client->active);
587147072Sbrooks		else if (ip->client->active->address.len ==
588147072Sbrooks		    lease->address.len &&
589147072Sbrooks		    !memcmp(ip->client->active->address.iabuf,
590147072Sbrooks		    lease->address.iabuf, lease->address.len))
591147072Sbrooks			free_client_lease(ip->client->active);
592147072Sbrooks		else {
593147072Sbrooks			ip->client->active->next = ip->client->leases;
594147072Sbrooks			ip->client->leases = ip->client->active;
595147072Sbrooks		}
596147072Sbrooks	}
597147072Sbrooks	ip->client->active = lease;
598147072Sbrooks
599147072Sbrooks	/* Phew. */
600147072Sbrooks}
601147072Sbrooks
602147072Sbrooks/*
603147072Sbrooks * client-lease-declaration :==
604147072Sbrooks *	BOOTP |
605147072Sbrooks *	INTERFACE string |
606147072Sbrooks *	FIXED_ADDR ip_address |
607147072Sbrooks *	FILENAME string |
608147072Sbrooks *	SERVER_NAME string |
609147072Sbrooks *	OPTION option-decl |
610147072Sbrooks *	RENEW time-decl |
611147072Sbrooks *	REBIND time-decl |
612147072Sbrooks *	EXPIRE time-decl
613147072Sbrooks */
614147072Sbrooksvoid
615147072Sbrooksparse_client_lease_declaration(FILE *cfile, struct client_lease *lease,
616147072Sbrooks    struct interface_info **ipp)
617147072Sbrooks{
618147072Sbrooks	int			 token;
619147072Sbrooks	char			*val;
620147072Sbrooks	struct interface_info	*ip;
621147072Sbrooks
622147072Sbrooks	switch (next_token(&val, cfile)) {
623147072Sbrooks	case BOOTP:
624147072Sbrooks		lease->is_bootp = 1;
625147072Sbrooks		break;
626147072Sbrooks	case INTERFACE:
627147072Sbrooks		token = next_token(&val, cfile);
628147072Sbrooks		if (token != STRING) {
629147072Sbrooks			parse_warn("expecting interface name (in quotes).");
630147072Sbrooks			skip_to_semi(cfile);
631147072Sbrooks			break;
632147072Sbrooks		}
633147072Sbrooks		ip = interface_or_dummy(val);
634147072Sbrooks		*ipp = ip;
635147072Sbrooks		break;
636147072Sbrooks	case FIXED_ADDR:
637147072Sbrooks		if (!parse_ip_addr(cfile, &lease->address))
638147072Sbrooks			return;
639147072Sbrooks		break;
640147072Sbrooks	case MEDIUM:
641147072Sbrooks		parse_string_list(cfile, &lease->medium, 0);
642147072Sbrooks		return;
643147072Sbrooks	case FILENAME:
644147072Sbrooks		lease->filename = parse_string(cfile);
645147072Sbrooks		return;
646252506Sbms	case NEXT_SERVER:
647252506Sbms		if (!parse_ip_addr(cfile, &lease->nextserver))
648252506Sbms			return;
649252506Sbms		break;
650147072Sbrooks	case SERVER_NAME:
651147072Sbrooks		lease->server_name = parse_string(cfile);
652147072Sbrooks		return;
653147072Sbrooks	case RENEW:
654147072Sbrooks		lease->renewal = parse_date(cfile);
655147072Sbrooks		return;
656147072Sbrooks	case REBIND:
657147072Sbrooks		lease->rebind = parse_date(cfile);
658147072Sbrooks		return;
659147072Sbrooks	case EXPIRE:
660147072Sbrooks		lease->expiry = parse_date(cfile);
661147072Sbrooks		return;
662147072Sbrooks	case OPTION:
663147072Sbrooks		parse_option_decl(cfile, lease->options);
664147072Sbrooks		return;
665147072Sbrooks	default:
666147072Sbrooks		parse_warn("expecting lease declaration.");
667147072Sbrooks		skip_to_semi(cfile);
668147072Sbrooks		break;
669147072Sbrooks	}
670147072Sbrooks	token = next_token(&val, cfile);
671147072Sbrooks	if (token != SEMI) {
672147072Sbrooks		parse_warn("expecting semicolon.");
673147072Sbrooks		skip_to_semi(cfile);
674147072Sbrooks	}
675147072Sbrooks}
676147072Sbrooks
677147072Sbrooksstruct option *
678147072Sbrooksparse_option_decl(FILE *cfile, struct option_data *options)
679147072Sbrooks{
680147072Sbrooks	char		*val;
681147072Sbrooks	int		 token;
682147072Sbrooks	u_int8_t	 buf[4];
683147072Sbrooks	u_int8_t	 hunkbuf[1024];
684147072Sbrooks	int		 hunkix = 0;
685147072Sbrooks	char		*vendor;
686147072Sbrooks	char		*fmt;
687147072Sbrooks	struct universe	*universe;
688147072Sbrooks	struct option	*option;
689147072Sbrooks	struct iaddr	 ip_addr;
690147072Sbrooks	u_int8_t	*dp;
691147072Sbrooks	int		 len;
692147072Sbrooks	int		 nul_term = 0;
693147072Sbrooks
694147072Sbrooks	token = next_token(&val, cfile);
695147072Sbrooks	if (!is_identifier(token)) {
696147072Sbrooks		parse_warn("expecting identifier after option keyword.");
697147072Sbrooks		if (token != SEMI)
698147072Sbrooks			skip_to_semi(cfile);
699147072Sbrooks		return (NULL);
700147072Sbrooks	}
701147072Sbrooks	if ((vendor = strdup(val)) == NULL)
702147072Sbrooks		error("no memory for vendor information.");
703147072Sbrooks
704147072Sbrooks	token = peek_token(&val, cfile);
705147072Sbrooks	if (token == DOT) {
706147072Sbrooks		/* Go ahead and take the DOT token... */
707147072Sbrooks		token = next_token(&val, cfile);
708147072Sbrooks
709147072Sbrooks		/* The next token should be an identifier... */
710147072Sbrooks		token = next_token(&val, cfile);
711147072Sbrooks		if (!is_identifier(token)) {
712147072Sbrooks			parse_warn("expecting identifier after '.'");
713147072Sbrooks			if (token != SEMI)
714147072Sbrooks				skip_to_semi(cfile);
715315610Sngie			free(vendor);
716147072Sbrooks			return (NULL);
717147072Sbrooks		}
718147072Sbrooks
719147072Sbrooks		/* Look up the option name hash table for the specified
720147072Sbrooks		   vendor. */
721147072Sbrooks		universe = ((struct universe *)hash_lookup(&universe_hash,
722147072Sbrooks		    (unsigned char *)vendor, 0));
723147072Sbrooks		/* If it's not there, we can't parse the rest of the
724147072Sbrooks		   declaration. */
725147072Sbrooks		if (!universe) {
726147072Sbrooks			parse_warn("no vendor named %s.", vendor);
727147072Sbrooks			skip_to_semi(cfile);
728315610Sngie			free(vendor);
729147072Sbrooks			return (NULL);
730147072Sbrooks		}
731147072Sbrooks	} else {
732147072Sbrooks		/* Use the default hash table, which contains all the
733147072Sbrooks		   standard dhcp option names. */
734147072Sbrooks		val = vendor;
735147072Sbrooks		universe = &dhcp_universe;
736147072Sbrooks	}
737147072Sbrooks
738147072Sbrooks	/* Look up the actual option info... */
739147072Sbrooks	option = (struct option *)hash_lookup(universe->hash,
740147072Sbrooks	    (unsigned char *)val, 0);
741147072Sbrooks
742147072Sbrooks	/* If we didn't get an option structure, it's an undefined option. */
743147072Sbrooks	if (!option) {
744147072Sbrooks		if (val == vendor)
745147072Sbrooks			parse_warn("no option named %s", val);
746147072Sbrooks		else
747147072Sbrooks			parse_warn("no option named %s for vendor %s",
748147072Sbrooks				    val, vendor);
749147072Sbrooks		skip_to_semi(cfile);
750315610Sngie		free(vendor);
751147072Sbrooks		return (NULL);
752147072Sbrooks	}
753147072Sbrooks
754147072Sbrooks	/* Free the initial identifier token. */
755147072Sbrooks	free(vendor);
756147072Sbrooks
757147072Sbrooks	/* Parse the option data... */
758147072Sbrooks	do {
759147072Sbrooks		for (fmt = option->format; *fmt; fmt++) {
760147072Sbrooks			if (*fmt == 'A')
761147072Sbrooks				break;
762147072Sbrooks			switch (*fmt) {
763147072Sbrooks			case 'X':
764147072Sbrooks				len = parse_X(cfile, &hunkbuf[hunkix],
765147072Sbrooks				    sizeof(hunkbuf) - hunkix);
766147072Sbrooks				hunkix += len;
767147072Sbrooks				break;
768147072Sbrooks			case 't': /* Text string... */
769147072Sbrooks				token = next_token(&val, cfile);
770147072Sbrooks				if (token != STRING) {
771147072Sbrooks					parse_warn("expecting string.");
772147072Sbrooks					skip_to_semi(cfile);
773147072Sbrooks					return (NULL);
774147072Sbrooks				}
775147072Sbrooks				len = strlen(val);
776147072Sbrooks				if (hunkix + len + 1 > sizeof(hunkbuf)) {
777147072Sbrooks					parse_warn("option data buffer %s",
778147072Sbrooks					    "overflow");
779147072Sbrooks					skip_to_semi(cfile);
780147072Sbrooks					return (NULL);
781147072Sbrooks				}
782147072Sbrooks				memcpy(&hunkbuf[hunkix], val, len + 1);
783147072Sbrooks				nul_term = 1;
784147072Sbrooks				hunkix += len;
785147072Sbrooks				break;
786147072Sbrooks			case 'I': /* IP address. */
787147072Sbrooks				if (!parse_ip_addr(cfile, &ip_addr))
788147072Sbrooks					return (NULL);
789147072Sbrooks				len = ip_addr.len;
790147072Sbrooks				dp = ip_addr.iabuf;
791147072Sbrooksalloc:
792147072Sbrooks				if (hunkix + len > sizeof(hunkbuf)) {
793147072Sbrooks					parse_warn("option data buffer "
794147072Sbrooks					    "overflow");
795147072Sbrooks					skip_to_semi(cfile);
796147072Sbrooks					return (NULL);
797147072Sbrooks				}
798147072Sbrooks				memcpy(&hunkbuf[hunkix], dp, len);
799147072Sbrooks				hunkix += len;
800147072Sbrooks				break;
801147072Sbrooks			case 'L':	/* Unsigned 32-bit integer... */
802147072Sbrooks			case 'l':	/* Signed 32-bit integer... */
803147072Sbrooks				token = next_token(&val, cfile);
804147072Sbrooks				if (token != NUMBER) {
805147072Sbrooksneed_number:
806147072Sbrooks					parse_warn("expecting number.");
807147072Sbrooks					if (token != SEMI)
808147072Sbrooks						skip_to_semi(cfile);
809147072Sbrooks					return (NULL);
810147072Sbrooks				}
811147072Sbrooks				convert_num(buf, val, 0, 32);
812147072Sbrooks				len = 4;
813147072Sbrooks				dp = buf;
814147072Sbrooks				goto alloc;
815147072Sbrooks			case 's':	/* Signed 16-bit integer. */
816147072Sbrooks			case 'S':	/* Unsigned 16-bit integer. */
817147072Sbrooks				token = next_token(&val, cfile);
818147072Sbrooks				if (token != NUMBER)
819147072Sbrooks					goto need_number;
820147072Sbrooks				convert_num(buf, val, 0, 16);
821147072Sbrooks				len = 2;
822147072Sbrooks				dp = buf;
823147072Sbrooks				goto alloc;
824147072Sbrooks			case 'b':	/* Signed 8-bit integer. */
825147072Sbrooks			case 'B':	/* Unsigned 8-bit integer. */
826147072Sbrooks				token = next_token(&val, cfile);
827147072Sbrooks				if (token != NUMBER)
828147072Sbrooks					goto need_number;
829147072Sbrooks				convert_num(buf, val, 0, 8);
830147072Sbrooks				len = 1;
831147072Sbrooks				dp = buf;
832147072Sbrooks				goto alloc;
833147072Sbrooks			case 'f': /* Boolean flag. */
834147072Sbrooks				token = next_token(&val, cfile);
835147072Sbrooks				if (!is_identifier(token)) {
836147072Sbrooks					parse_warn("expecting identifier.");
837147072Sbrooksbad_flag:
838147072Sbrooks					if (token != SEMI)
839147072Sbrooks						skip_to_semi(cfile);
840147072Sbrooks					return (NULL);
841147072Sbrooks				}
842147072Sbrooks				if (!strcasecmp(val, "true") ||
843147072Sbrooks				    !strcasecmp(val, "on"))
844147072Sbrooks					buf[0] = 1;
845147072Sbrooks				else if (!strcasecmp(val, "false") ||
846147072Sbrooks				    !strcasecmp(val, "off"))
847147072Sbrooks					buf[0] = 0;
848147072Sbrooks				else {
849147072Sbrooks					parse_warn("expecting boolean.");
850147072Sbrooks					goto bad_flag;
851147072Sbrooks				}
852147072Sbrooks				len = 1;
853147072Sbrooks				dp = buf;
854147072Sbrooks				goto alloc;
855147072Sbrooks			default:
856147072Sbrooks				warning("Bad format %c in parse_option_param.",
857147072Sbrooks				    *fmt);
858147072Sbrooks				skip_to_semi(cfile);
859147072Sbrooks				return (NULL);
860147072Sbrooks			}
861147072Sbrooks		}
862147072Sbrooks		token = next_token(&val, cfile);
863147072Sbrooks	} while (*fmt == 'A' && token == COMMA);
864147072Sbrooks
865147072Sbrooks	if (token != SEMI) {
866147072Sbrooks		parse_warn("semicolon expected.");
867147072Sbrooks		skip_to_semi(cfile);
868147072Sbrooks		return (NULL);
869147072Sbrooks	}
870147072Sbrooks
871147072Sbrooks	options[option->code].data = malloc(hunkix + nul_term);
872147072Sbrooks	if (!options[option->code].data)
873147072Sbrooks		error("out of memory allocating option data.");
874147072Sbrooks	memcpy(options[option->code].data, hunkbuf, hunkix + nul_term);
875147072Sbrooks	options[option->code].len = hunkix;
876147072Sbrooks	return (option);
877147072Sbrooks}
878147072Sbrooks
879147072Sbrooksvoid
880147072Sbrooksparse_string_list(FILE *cfile, struct string_list **lp, int multiple)
881147072Sbrooks{
882147072Sbrooks	int			 token;
883147072Sbrooks	char			*val;
884228614Sdim	size_t			 valsize;
885147072Sbrooks	struct string_list	*cur, *tmp;
886147072Sbrooks
887147072Sbrooks	/* Find the last medium in the media list. */
888147072Sbrooks	if (*lp)
889147072Sbrooks		for (cur = *lp; cur->next; cur = cur->next)
890147072Sbrooks			;	/* nothing */
891147072Sbrooks	else
892147072Sbrooks		cur = NULL;
893147072Sbrooks
894147072Sbrooks	do {
895147072Sbrooks		token = next_token(&val, cfile);
896147072Sbrooks		if (token != STRING) {
897147072Sbrooks			parse_warn("Expecting media options.");
898147072Sbrooks			skip_to_semi(cfile);
899147072Sbrooks			return;
900147072Sbrooks		}
901147072Sbrooks
902228614Sdim		valsize = strlen(val) + 1;
903228614Sdim		tmp = new_string_list(valsize);
904147072Sbrooks		if (tmp == NULL)
905147072Sbrooks			error("no memory for string list entry.");
906228615Sdim		memcpy(tmp->string, val, valsize);
907147072Sbrooks		tmp->next = NULL;
908147072Sbrooks
909147072Sbrooks		/* Store this medium at the end of the media list. */
910147072Sbrooks		if (cur)
911147072Sbrooks			cur->next = tmp;
912147072Sbrooks		else
913147072Sbrooks			*lp = tmp;
914147072Sbrooks		cur = tmp;
915147072Sbrooks
916147072Sbrooks		token = next_token(&val, cfile);
917147072Sbrooks	} while (multiple && token == COMMA);
918147072Sbrooks
919147072Sbrooks	if (token != SEMI) {
920147072Sbrooks		parse_warn("expecting semicolon.");
921147072Sbrooks		skip_to_semi(cfile);
922147072Sbrooks	}
923147072Sbrooks}
924147072Sbrooks
925147072Sbrooksvoid
926147072Sbrooksparse_reject_statement(FILE *cfile, struct client_config *config)
927147072Sbrooks{
928147072Sbrooks	int			 token;
929147072Sbrooks	char			*val;
930147072Sbrooks	struct iaddr		 addr;
931147072Sbrooks	struct iaddrlist	*list;
932147072Sbrooks
933147072Sbrooks	do {
934147072Sbrooks		if (!parse_ip_addr(cfile, &addr)) {
935147072Sbrooks			parse_warn("expecting IP address.");
936147072Sbrooks			skip_to_semi(cfile);
937147072Sbrooks			return;
938147072Sbrooks		}
939147072Sbrooks
940147072Sbrooks		list = malloc(sizeof(struct iaddrlist));
941147072Sbrooks		if (!list)
942147072Sbrooks			error("no memory for reject list!");
943147072Sbrooks
944147072Sbrooks		list->addr = addr;
945147072Sbrooks		list->next = config->reject_list;
946147072Sbrooks		config->reject_list = list;
947147072Sbrooks
948147072Sbrooks		token = next_token(&val, cfile);
949147072Sbrooks	} while (token == COMMA);
950147072Sbrooks
951147072Sbrooks	if (token != SEMI) {
952147072Sbrooks		parse_warn("expecting semicolon.");
953147072Sbrooks		skip_to_semi(cfile);
954147072Sbrooks	}
955147072Sbrooks}
956