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$");
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.");
513147072Sbrooks			return;
514147072Sbrooks		}
515147072Sbrooks		if (token == RBRACE)
516147072Sbrooks			break;
517147072Sbrooks		parse_client_lease_declaration(cfile, lease, &ip);
518147072Sbrooks	} while (1);
519147072Sbrooks	token = next_token(&val, cfile);
520147072Sbrooks
521147072Sbrooks	/* If the lease declaration didn't include an interface
522147072Sbrooks	 * declaration that we recognized, it's of no use to us.
523147072Sbrooks	 */
524147072Sbrooks	if (!ip) {
525147072Sbrooks		free_client_lease(lease);
526147072Sbrooks		return;
527147072Sbrooks	}
528147072Sbrooks
529147072Sbrooks	/* Make sure there's a client state structure... */
530147072Sbrooks	if (!ip->client)
531147072Sbrooks		make_client_state(ip);
532147072Sbrooks
533147072Sbrooks	/* If this is an alias lease, it doesn't need to be sorted in. */
534147072Sbrooks	if (is_static == 2) {
535147072Sbrooks		ip->client->alias = lease;
536147072Sbrooks		return;
537147072Sbrooks	}
538147072Sbrooks
539147072Sbrooks	/*
540147072Sbrooks	 * The new lease may supersede a lease that's not the active
541147072Sbrooks	 * lease but is still on the lease list, so scan the lease list
542147072Sbrooks	 * looking for a lease with the same address, and if we find it,
543147072Sbrooks	 * toss it.
544147072Sbrooks	 */
545147072Sbrooks	pl = NULL;
546147072Sbrooks	for (lp = ip->client->leases; lp; lp = lp->next) {
547147072Sbrooks		if (lp->address.len == lease->address.len &&
548147072Sbrooks		    !memcmp(lp->address.iabuf, lease->address.iabuf,
549147072Sbrooks		    lease->address.len)) {
550147072Sbrooks			if (pl)
551147072Sbrooks				pl->next = lp->next;
552147072Sbrooks			else
553147072Sbrooks				ip->client->leases = lp->next;
554147072Sbrooks			free_client_lease(lp);
555147072Sbrooks			break;
556147072Sbrooks		}
557147072Sbrooks	}
558147072Sbrooks
559147072Sbrooks	/*
560147072Sbrooks	 * If this is a preloaded lease, just put it on the list of
561147072Sbrooks	 * recorded leases - don't make it the active lease.
562147072Sbrooks	 */
563147072Sbrooks	if (is_static) {
564147072Sbrooks		lease->next = ip->client->leases;
565147072Sbrooks		ip->client->leases = lease;
566147072Sbrooks		return;
567147072Sbrooks	}
568147072Sbrooks
569147072Sbrooks	/*
570147072Sbrooks	 * The last lease in the lease file on a particular interface is
571147072Sbrooks	 * the active lease for that interface.    Of course, we don't
572147072Sbrooks	 * know what the last lease in the file is until we've parsed
573147072Sbrooks	 * the whole file, so at this point, we assume that the lease we
574147072Sbrooks	 * just parsed is the active lease for its interface.   If
575147072Sbrooks	 * there's already an active lease for the interface, and this
576147072Sbrooks	 * lease is for the same ip address, then we just toss the old
577147072Sbrooks	 * active lease and replace it with this one.   If this lease is
578147072Sbrooks	 * for a different address, then if the old active lease has
579147072Sbrooks	 * expired, we dump it; if not, we put it on the list of leases
580147072Sbrooks	 * for this interface which are still valid but no longer
581147072Sbrooks	 * active.
582147072Sbrooks	 */
583147072Sbrooks	if (ip->client->active) {
584147072Sbrooks		if (ip->client->active->expiry < cur_time)
585147072Sbrooks			free_client_lease(ip->client->active);
586147072Sbrooks		else if (ip->client->active->address.len ==
587147072Sbrooks		    lease->address.len &&
588147072Sbrooks		    !memcmp(ip->client->active->address.iabuf,
589147072Sbrooks		    lease->address.iabuf, lease->address.len))
590147072Sbrooks			free_client_lease(ip->client->active);
591147072Sbrooks		else {
592147072Sbrooks			ip->client->active->next = ip->client->leases;
593147072Sbrooks			ip->client->leases = ip->client->active;
594147072Sbrooks		}
595147072Sbrooks	}
596147072Sbrooks	ip->client->active = lease;
597147072Sbrooks
598147072Sbrooks	/* Phew. */
599147072Sbrooks}
600147072Sbrooks
601147072Sbrooks/*
602147072Sbrooks * client-lease-declaration :==
603147072Sbrooks *	BOOTP |
604147072Sbrooks *	INTERFACE string |
605147072Sbrooks *	FIXED_ADDR ip_address |
606147072Sbrooks *	FILENAME string |
607147072Sbrooks *	SERVER_NAME string |
608147072Sbrooks *	OPTION option-decl |
609147072Sbrooks *	RENEW time-decl |
610147072Sbrooks *	REBIND time-decl |
611147072Sbrooks *	EXPIRE time-decl
612147072Sbrooks */
613147072Sbrooksvoid
614147072Sbrooksparse_client_lease_declaration(FILE *cfile, struct client_lease *lease,
615147072Sbrooks    struct interface_info **ipp)
616147072Sbrooks{
617147072Sbrooks	int			 token;
618147072Sbrooks	char			*val;
619147072Sbrooks	struct interface_info	*ip;
620147072Sbrooks
621147072Sbrooks	switch (next_token(&val, cfile)) {
622147072Sbrooks	case BOOTP:
623147072Sbrooks		lease->is_bootp = 1;
624147072Sbrooks		break;
625147072Sbrooks	case INTERFACE:
626147072Sbrooks		token = next_token(&val, cfile);
627147072Sbrooks		if (token != STRING) {
628147072Sbrooks			parse_warn("expecting interface name (in quotes).");
629147072Sbrooks			skip_to_semi(cfile);
630147072Sbrooks			break;
631147072Sbrooks		}
632147072Sbrooks		ip = interface_or_dummy(val);
633147072Sbrooks		*ipp = ip;
634147072Sbrooks		break;
635147072Sbrooks	case FIXED_ADDR:
636147072Sbrooks		if (!parse_ip_addr(cfile, &lease->address))
637147072Sbrooks			return;
638147072Sbrooks		break;
639147072Sbrooks	case MEDIUM:
640147072Sbrooks		parse_string_list(cfile, &lease->medium, 0);
641147072Sbrooks		return;
642147072Sbrooks	case FILENAME:
643147072Sbrooks		lease->filename = parse_string(cfile);
644147072Sbrooks		return;
645252506Sbms	case NEXT_SERVER:
646252506Sbms		if (!parse_ip_addr(cfile, &lease->nextserver))
647252506Sbms			return;
648252506Sbms		break;
649147072Sbrooks	case SERVER_NAME:
650147072Sbrooks		lease->server_name = parse_string(cfile);
651147072Sbrooks		return;
652147072Sbrooks	case RENEW:
653147072Sbrooks		lease->renewal = parse_date(cfile);
654147072Sbrooks		return;
655147072Sbrooks	case REBIND:
656147072Sbrooks		lease->rebind = parse_date(cfile);
657147072Sbrooks		return;
658147072Sbrooks	case EXPIRE:
659147072Sbrooks		lease->expiry = parse_date(cfile);
660147072Sbrooks		return;
661147072Sbrooks	case OPTION:
662147072Sbrooks		parse_option_decl(cfile, lease->options);
663147072Sbrooks		return;
664147072Sbrooks	default:
665147072Sbrooks		parse_warn("expecting lease declaration.");
666147072Sbrooks		skip_to_semi(cfile);
667147072Sbrooks		break;
668147072Sbrooks	}
669147072Sbrooks	token = next_token(&val, cfile);
670147072Sbrooks	if (token != SEMI) {
671147072Sbrooks		parse_warn("expecting semicolon.");
672147072Sbrooks		skip_to_semi(cfile);
673147072Sbrooks	}
674147072Sbrooks}
675147072Sbrooks
676147072Sbrooksstruct option *
677147072Sbrooksparse_option_decl(FILE *cfile, struct option_data *options)
678147072Sbrooks{
679147072Sbrooks	char		*val;
680147072Sbrooks	int		 token;
681147072Sbrooks	u_int8_t	 buf[4];
682147072Sbrooks	u_int8_t	 hunkbuf[1024];
683147072Sbrooks	int		 hunkix = 0;
684147072Sbrooks	char		*vendor;
685147072Sbrooks	char		*fmt;
686147072Sbrooks	struct universe	*universe;
687147072Sbrooks	struct option	*option;
688147072Sbrooks	struct iaddr	 ip_addr;
689147072Sbrooks	u_int8_t	*dp;
690147072Sbrooks	int		 len;
691147072Sbrooks	int		 nul_term = 0;
692147072Sbrooks
693147072Sbrooks	token = next_token(&val, cfile);
694147072Sbrooks	if (!is_identifier(token)) {
695147072Sbrooks		parse_warn("expecting identifier after option keyword.");
696147072Sbrooks		if (token != SEMI)
697147072Sbrooks			skip_to_semi(cfile);
698147072Sbrooks		return (NULL);
699147072Sbrooks	}
700147072Sbrooks	if ((vendor = strdup(val)) == NULL)
701147072Sbrooks		error("no memory for vendor information.");
702147072Sbrooks
703147072Sbrooks	token = peek_token(&val, cfile);
704147072Sbrooks	if (token == DOT) {
705147072Sbrooks		/* Go ahead and take the DOT token... */
706147072Sbrooks		token = next_token(&val, cfile);
707147072Sbrooks
708147072Sbrooks		/* The next token should be an identifier... */
709147072Sbrooks		token = next_token(&val, cfile);
710147072Sbrooks		if (!is_identifier(token)) {
711147072Sbrooks			parse_warn("expecting identifier after '.'");
712147072Sbrooks			if (token != SEMI)
713147072Sbrooks				skip_to_semi(cfile);
714147072Sbrooks			return (NULL);
715147072Sbrooks		}
716147072Sbrooks
717147072Sbrooks		/* Look up the option name hash table for the specified
718147072Sbrooks		   vendor. */
719147072Sbrooks		universe = ((struct universe *)hash_lookup(&universe_hash,
720147072Sbrooks		    (unsigned char *)vendor, 0));
721147072Sbrooks		/* If it's not there, we can't parse the rest of the
722147072Sbrooks		   declaration. */
723147072Sbrooks		if (!universe) {
724147072Sbrooks			parse_warn("no vendor named %s.", vendor);
725147072Sbrooks			skip_to_semi(cfile);
726147072Sbrooks			return (NULL);
727147072Sbrooks		}
728147072Sbrooks	} else {
729147072Sbrooks		/* Use the default hash table, which contains all the
730147072Sbrooks		   standard dhcp option names. */
731147072Sbrooks		val = vendor;
732147072Sbrooks		universe = &dhcp_universe;
733147072Sbrooks	}
734147072Sbrooks
735147072Sbrooks	/* Look up the actual option info... */
736147072Sbrooks	option = (struct option *)hash_lookup(universe->hash,
737147072Sbrooks	    (unsigned char *)val, 0);
738147072Sbrooks
739147072Sbrooks	/* If we didn't get an option structure, it's an undefined option. */
740147072Sbrooks	if (!option) {
741147072Sbrooks		if (val == vendor)
742147072Sbrooks			parse_warn("no option named %s", val);
743147072Sbrooks		else
744147072Sbrooks			parse_warn("no option named %s for vendor %s",
745147072Sbrooks				    val, vendor);
746147072Sbrooks		skip_to_semi(cfile);
747147072Sbrooks		return (NULL);
748147072Sbrooks	}
749147072Sbrooks
750147072Sbrooks	/* Free the initial identifier token. */
751147072Sbrooks	free(vendor);
752147072Sbrooks
753147072Sbrooks	/* Parse the option data... */
754147072Sbrooks	do {
755147072Sbrooks		for (fmt = option->format; *fmt; fmt++) {
756147072Sbrooks			if (*fmt == 'A')
757147072Sbrooks				break;
758147072Sbrooks			switch (*fmt) {
759147072Sbrooks			case 'X':
760147072Sbrooks				len = parse_X(cfile, &hunkbuf[hunkix],
761147072Sbrooks				    sizeof(hunkbuf) - hunkix);
762147072Sbrooks				hunkix += len;
763147072Sbrooks				break;
764147072Sbrooks			case 't': /* Text string... */
765147072Sbrooks				token = next_token(&val, cfile);
766147072Sbrooks				if (token != STRING) {
767147072Sbrooks					parse_warn("expecting string.");
768147072Sbrooks					skip_to_semi(cfile);
769147072Sbrooks					return (NULL);
770147072Sbrooks				}
771147072Sbrooks				len = strlen(val);
772147072Sbrooks				if (hunkix + len + 1 > sizeof(hunkbuf)) {
773147072Sbrooks					parse_warn("option data buffer %s",
774147072Sbrooks					    "overflow");
775147072Sbrooks					skip_to_semi(cfile);
776147072Sbrooks					return (NULL);
777147072Sbrooks				}
778147072Sbrooks				memcpy(&hunkbuf[hunkix], val, len + 1);
779147072Sbrooks				nul_term = 1;
780147072Sbrooks				hunkix += len;
781147072Sbrooks				break;
782147072Sbrooks			case 'I': /* IP address. */
783147072Sbrooks				if (!parse_ip_addr(cfile, &ip_addr))
784147072Sbrooks					return (NULL);
785147072Sbrooks				len = ip_addr.len;
786147072Sbrooks				dp = ip_addr.iabuf;
787147072Sbrooksalloc:
788147072Sbrooks				if (hunkix + len > sizeof(hunkbuf)) {
789147072Sbrooks					parse_warn("option data buffer "
790147072Sbrooks					    "overflow");
791147072Sbrooks					skip_to_semi(cfile);
792147072Sbrooks					return (NULL);
793147072Sbrooks				}
794147072Sbrooks				memcpy(&hunkbuf[hunkix], dp, len);
795147072Sbrooks				hunkix += len;
796147072Sbrooks				break;
797147072Sbrooks			case 'L':	/* Unsigned 32-bit integer... */
798147072Sbrooks			case 'l':	/* Signed 32-bit integer... */
799147072Sbrooks				token = next_token(&val, cfile);
800147072Sbrooks				if (token != NUMBER) {
801147072Sbrooksneed_number:
802147072Sbrooks					parse_warn("expecting number.");
803147072Sbrooks					if (token != SEMI)
804147072Sbrooks						skip_to_semi(cfile);
805147072Sbrooks					return (NULL);
806147072Sbrooks				}
807147072Sbrooks				convert_num(buf, val, 0, 32);
808147072Sbrooks				len = 4;
809147072Sbrooks				dp = buf;
810147072Sbrooks				goto alloc;
811147072Sbrooks			case 's':	/* Signed 16-bit integer. */
812147072Sbrooks			case 'S':	/* Unsigned 16-bit integer. */
813147072Sbrooks				token = next_token(&val, cfile);
814147072Sbrooks				if (token != NUMBER)
815147072Sbrooks					goto need_number;
816147072Sbrooks				convert_num(buf, val, 0, 16);
817147072Sbrooks				len = 2;
818147072Sbrooks				dp = buf;
819147072Sbrooks				goto alloc;
820147072Sbrooks			case 'b':	/* Signed 8-bit integer. */
821147072Sbrooks			case 'B':	/* Unsigned 8-bit integer. */
822147072Sbrooks				token = next_token(&val, cfile);
823147072Sbrooks				if (token != NUMBER)
824147072Sbrooks					goto need_number;
825147072Sbrooks				convert_num(buf, val, 0, 8);
826147072Sbrooks				len = 1;
827147072Sbrooks				dp = buf;
828147072Sbrooks				goto alloc;
829147072Sbrooks			case 'f': /* Boolean flag. */
830147072Sbrooks				token = next_token(&val, cfile);
831147072Sbrooks				if (!is_identifier(token)) {
832147072Sbrooks					parse_warn("expecting identifier.");
833147072Sbrooksbad_flag:
834147072Sbrooks					if (token != SEMI)
835147072Sbrooks						skip_to_semi(cfile);
836147072Sbrooks					return (NULL);
837147072Sbrooks				}
838147072Sbrooks				if (!strcasecmp(val, "true") ||
839147072Sbrooks				    !strcasecmp(val, "on"))
840147072Sbrooks					buf[0] = 1;
841147072Sbrooks				else if (!strcasecmp(val, "false") ||
842147072Sbrooks				    !strcasecmp(val, "off"))
843147072Sbrooks					buf[0] = 0;
844147072Sbrooks				else {
845147072Sbrooks					parse_warn("expecting boolean.");
846147072Sbrooks					goto bad_flag;
847147072Sbrooks				}
848147072Sbrooks				len = 1;
849147072Sbrooks				dp = buf;
850147072Sbrooks				goto alloc;
851147072Sbrooks			default:
852147072Sbrooks				warning("Bad format %c in parse_option_param.",
853147072Sbrooks				    *fmt);
854147072Sbrooks				skip_to_semi(cfile);
855147072Sbrooks				return (NULL);
856147072Sbrooks			}
857147072Sbrooks		}
858147072Sbrooks		token = next_token(&val, cfile);
859147072Sbrooks	} while (*fmt == 'A' && token == COMMA);
860147072Sbrooks
861147072Sbrooks	if (token != SEMI) {
862147072Sbrooks		parse_warn("semicolon expected.");
863147072Sbrooks		skip_to_semi(cfile);
864147072Sbrooks		return (NULL);
865147072Sbrooks	}
866147072Sbrooks
867147072Sbrooks	options[option->code].data = malloc(hunkix + nul_term);
868147072Sbrooks	if (!options[option->code].data)
869147072Sbrooks		error("out of memory allocating option data.");
870147072Sbrooks	memcpy(options[option->code].data, hunkbuf, hunkix + nul_term);
871147072Sbrooks	options[option->code].len = hunkix;
872147072Sbrooks	return (option);
873147072Sbrooks}
874147072Sbrooks
875147072Sbrooksvoid
876147072Sbrooksparse_string_list(FILE *cfile, struct string_list **lp, int multiple)
877147072Sbrooks{
878147072Sbrooks	int			 token;
879147072Sbrooks	char			*val;
880228614Sdim	size_t			 valsize;
881147072Sbrooks	struct string_list	*cur, *tmp;
882147072Sbrooks
883147072Sbrooks	/* Find the last medium in the media list. */
884147072Sbrooks	if (*lp)
885147072Sbrooks		for (cur = *lp; cur->next; cur = cur->next)
886147072Sbrooks			;	/* nothing */
887147072Sbrooks	else
888147072Sbrooks		cur = NULL;
889147072Sbrooks
890147072Sbrooks	do {
891147072Sbrooks		token = next_token(&val, cfile);
892147072Sbrooks		if (token != STRING) {
893147072Sbrooks			parse_warn("Expecting media options.");
894147072Sbrooks			skip_to_semi(cfile);
895147072Sbrooks			return;
896147072Sbrooks		}
897147072Sbrooks
898228614Sdim		valsize = strlen(val) + 1;
899228614Sdim		tmp = new_string_list(valsize);
900147072Sbrooks		if (tmp == NULL)
901147072Sbrooks			error("no memory for string list entry.");
902228615Sdim		memcpy(tmp->string, val, valsize);
903147072Sbrooks		tmp->next = NULL;
904147072Sbrooks
905147072Sbrooks		/* Store this medium at the end of the media list. */
906147072Sbrooks		if (cur)
907147072Sbrooks			cur->next = tmp;
908147072Sbrooks		else
909147072Sbrooks			*lp = tmp;
910147072Sbrooks		cur = tmp;
911147072Sbrooks
912147072Sbrooks		token = next_token(&val, cfile);
913147072Sbrooks	} while (multiple && token == COMMA);
914147072Sbrooks
915147072Sbrooks	if (token != SEMI) {
916147072Sbrooks		parse_warn("expecting semicolon.");
917147072Sbrooks		skip_to_semi(cfile);
918147072Sbrooks	}
919147072Sbrooks}
920147072Sbrooks
921147072Sbrooksvoid
922147072Sbrooksparse_reject_statement(FILE *cfile, struct client_config *config)
923147072Sbrooks{
924147072Sbrooks	int			 token;
925147072Sbrooks	char			*val;
926147072Sbrooks	struct iaddr		 addr;
927147072Sbrooks	struct iaddrlist	*list;
928147072Sbrooks
929147072Sbrooks	do {
930147072Sbrooks		if (!parse_ip_addr(cfile, &addr)) {
931147072Sbrooks			parse_warn("expecting IP address.");
932147072Sbrooks			skip_to_semi(cfile);
933147072Sbrooks			return;
934147072Sbrooks		}
935147072Sbrooks
936147072Sbrooks		list = malloc(sizeof(struct iaddrlist));
937147072Sbrooks		if (!list)
938147072Sbrooks			error("no memory for reject list!");
939147072Sbrooks
940147072Sbrooks		list->addr = addr;
941147072Sbrooks		list->next = config->reject_list;
942147072Sbrooks		config->reject_list = list;
943147072Sbrooks
944147072Sbrooks		token = next_token(&val, cfile);
945147072Sbrooks	} while (token == COMMA);
946147072Sbrooks
947147072Sbrooks	if (token != SEMI) {
948147072Sbrooks		parse_warn("expecting semicolon.");
949147072Sbrooks		skip_to_semi(cfile);
950147072Sbrooks	}
951147072Sbrooks}
952