xref: /freebsd/sbin/dhclient/clparse.c (revision 1d386b48a555f61cb7325543adbbb5c3f3407a66)
1 /*	$OpenBSD: clparse.c,v 1.18 2004/09/15 18:15:18 henning Exp $	*/
2 
3 /* Parser for dhclient config and lease files... */
4 
5 /*-
6  * SPDX-License-Identifier: BSD-3-Clause
7  *
8  * Copyright (c) 1997 The Internet Software Consortium.
9  * All rights reserved.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  *
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  * 3. Neither the name of The Internet Software Consortium nor the names
21  *    of its contributors may be used to endorse or promote products derived
22  *    from this software without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
25  * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
26  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
27  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
28  * DISCLAIMED.  IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
29  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
31  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
32  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
33  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
34  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
35  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36  * SUCH DAMAGE.
37  *
38  * This software has been written for the Internet Software Consortium
39  * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
40  * Enterprises.  To learn more about the Internet Software Consortium,
41  * see ``http://www.vix.com/isc''.  To learn more about Vixie
42  * Enterprises, see ``http://www.vix.com''.
43  */
44 
45 #include <sys/cdefs.h>
46 #include "dhcpd.h"
47 #include "dhctoken.h"
48 
49 struct client_config top_level_config;
50 static struct interface_info *dummy_interfaces;
51 
52 static char client_script_name[] = "/sbin/dhclient-script";
53 
54 /*
55  * client-conf-file :== client-declarations EOF
56  * client-declarations :== <nil>
57  *			 | client-declaration
58  *			 | client-declarations client-declaration
59  */
60 int
read_client_conf(void)61 read_client_conf(void)
62 {
63 	FILE			*cfile;
64 	char			*val;
65 	int			 token;
66 	struct client_config	*config;
67 
68 	new_parse(path_dhclient_conf);
69 
70 	/* Set up the initial dhcp option universe. */
71 	initialize_universes();
72 
73 	/* Initialize the top level client configuration. */
74 	memset(&top_level_config, 0, sizeof(top_level_config));
75 
76 	/* Set some defaults... */
77 	top_level_config.vlan_pcp = 0;
78 	top_level_config.timeout = 60;
79 	top_level_config.select_interval = 0;
80 	top_level_config.reboot_timeout = 10;
81 	top_level_config.retry_interval = 300;
82 	top_level_config.backoff_cutoff = 15;
83 	top_level_config.initial_interval = 3;
84 	top_level_config.bootp_policy = ACCEPT;
85 	top_level_config.script_name = client_script_name;
86 	top_level_config.requested_options
87 	    [top_level_config.requested_option_count++] = DHO_SUBNET_MASK;
88 	top_level_config.requested_options
89 	    [top_level_config.requested_option_count++] = DHO_BROADCAST_ADDRESS;
90 	top_level_config.requested_options
91 	    [top_level_config.requested_option_count++] = DHO_TIME_OFFSET;
92 	top_level_config.requested_options
93 	    [top_level_config.requested_option_count++] = DHO_CLASSLESS_ROUTES;
94 	top_level_config.requested_options
95 	    [top_level_config.requested_option_count++] = DHO_ROUTERS;
96 	top_level_config.requested_options
97 	    [top_level_config.requested_option_count++] = DHO_DOMAIN_NAME;
98 	top_level_config.requested_options
99 	    [top_level_config.requested_option_count++] =
100 	    DHO_DOMAIN_NAME_SERVERS;
101 	top_level_config.requested_options
102 	    [top_level_config.requested_option_count++] = DHO_HOST_NAME;
103 	top_level_config.requested_options
104 	    [top_level_config.requested_option_count++] = DHO_DOMAIN_SEARCH;
105 	top_level_config.requested_options
106 	    [top_level_config.requested_option_count++] = DHO_INTERFACE_MTU;
107 
108 	if ((cfile = fopen(path_dhclient_conf, "r")) != NULL) {
109 		do {
110 			token = peek_token(&val, cfile);
111 			if (token == EOF)
112 				break;
113 			parse_client_statement(cfile, NULL, &top_level_config);
114 		} while (1);
115 		token = next_token(&val, cfile); /* Clear the peek buffer */
116 		fclose(cfile);
117 	}
118 
119 	/*
120 	 * Set up state and config structures for clients that don't
121 	 * have per-interface configuration declarations.
122 	 */
123 	config = NULL;
124 	if (!ifi->client) {
125 		ifi->client = malloc(sizeof(struct client_state));
126 		if (!ifi->client)
127 			error("no memory for client state.");
128 		memset(ifi->client, 0, sizeof(*(ifi->client)));
129 	}
130 	if (!ifi->client->config) {
131 		if (!config) {
132 			config = malloc(sizeof(struct client_config));
133 			if (!config)
134 				error("no memory for client config.");
135 			memcpy(config, &top_level_config,
136 				sizeof(top_level_config));
137 		}
138 		ifi->client->config = config;
139 	}
140 
141 	return (!warnings_occurred);
142 }
143 
144 /*
145  * lease-file :== client-lease-statements EOF
146  * client-lease-statements :== <nil>
147  *		     | client-lease-statements LEASE client-lease-statement
148  */
149 void
read_client_leases(void)150 read_client_leases(void)
151 {
152 	FILE	*cfile;
153 	char	*val;
154 	int	 token;
155 
156 	new_parse(path_dhclient_db);
157 
158 	/* Open the lease file.   If we can't open it, just return -
159 	   we can safely trust the server to remember our state. */
160 	if ((cfile = fopen(path_dhclient_db, "r")) == NULL)
161 		return;
162 	do {
163 		token = next_token(&val, cfile);
164 		if (token == EOF)
165 			break;
166 		if (token != LEASE) {
167 			warning("Corrupt lease file - possible data loss!");
168 			skip_to_semi(cfile);
169 			break;
170 		} else
171 			parse_client_lease_statement(cfile, 0);
172 
173 	} while (1);
174 	fclose(cfile);
175 }
176 
177 /*
178  * client-declaration :==
179  *	SEND option-decl |
180  *	DEFAULT option-decl |
181  *	SUPERSEDE option-decl |
182  *	PREPEND option-decl |
183  *	APPEND option-decl |
184  *	hardware-declaration |
185  *	REQUEST option-list |
186  *	REQUIRE option-list |
187  *	IGNORE option-list |
188  *	TIMEOUT number |
189  *	RETRY number |
190  *	REBOOT number |
191  *	SELECT_TIMEOUT number |
192  *	SCRIPT string |
193  *	interface-declaration |
194  *	LEASE client-lease-statement |
195  *	ALIAS client-lease-statement
196  */
197 void
parse_client_statement(FILE * cfile,struct interface_info * ip,struct client_config * config)198 parse_client_statement(FILE *cfile, struct interface_info *ip,
199     struct client_config *config)
200 {
201 	char		*val;
202 	struct option	*option;
203 	time_t		 tmp;
204 
205 	switch (next_token(&val, cfile)) {
206 	case SEND:
207 		parse_option_decl(cfile, &config->send_options[0]);
208 		return;
209 	case DEFAULT:
210 		option = parse_option_decl(cfile, &config->defaults[0]);
211 		if (option)
212 			config->default_actions[option->code] = ACTION_DEFAULT;
213 		return;
214 	case SUPERSEDE:
215 		option = parse_option_decl(cfile, &config->defaults[0]);
216 		if (option)
217 			config->default_actions[option->code] =
218 			    ACTION_SUPERSEDE;
219 		return;
220 	case APPEND:
221 		option = parse_option_decl(cfile, &config->defaults[0]);
222 		if (option)
223 			config->default_actions[option->code] = ACTION_APPEND;
224 		return;
225 	case PREPEND:
226 		option = parse_option_decl(cfile, &config->defaults[0]);
227 		if (option)
228 			config->default_actions[option->code] = ACTION_PREPEND;
229 		return;
230 	case MEDIA:
231 		parse_string_list(cfile, &config->media, 1);
232 		return;
233 	case HARDWARE:
234 		if (ip)
235 			parse_hardware_param(cfile, &ip->hw_address);
236 		else {
237 			parse_warn("hardware address parameter %s",
238 				    "not allowed here.");
239 			skip_to_semi(cfile);
240 		}
241 		return;
242 	case REQUEST:
243 		config->requested_option_count =
244 			parse_option_list(cfile, config->requested_options);
245 		return;
246 	case REQUIRE:
247 		memset(config->required_options, 0,
248 		    sizeof(config->required_options));
249 		parse_option_list(cfile, config->required_options);
250 		return;
251 	case IGNORE:
252 		parse_option_list(cfile, config->ignored_options);
253 		return;
254 	case TIMEOUT:
255 		parse_lease_time(cfile, &config->timeout);
256 		return;
257 	case RETRY:
258 		parse_lease_time(cfile, &config->retry_interval);
259 		return;
260 	case SELECT_TIMEOUT:
261 		parse_lease_time(cfile, &config->select_interval);
262 		return;
263 	case REBOOT:
264 		parse_lease_time(cfile, &config->reboot_timeout);
265 		return;
266 	case VLAN_PCP:
267 		parse_lease_time(cfile, &tmp);
268 		config->vlan_pcp = (u_int)tmp;
269 		return;
270 	case BACKOFF_CUTOFF:
271 		parse_lease_time(cfile, &config->backoff_cutoff);
272 		return;
273 	case INITIAL_INTERVAL:
274 		parse_lease_time(cfile, &config->initial_interval);
275 		return;
276 	case SCRIPT:
277 		config->script_name = parse_string(cfile);
278 		return;
279 	case INTERFACE:
280 		if (ip)
281 			parse_warn("nested interface declaration.");
282 		parse_interface_declaration(cfile, config);
283 		return;
284 	case LEASE:
285 		parse_client_lease_statement(cfile, 1);
286 		return;
287 	case ALIAS:
288 		parse_client_lease_statement(cfile, 2);
289 		return;
290 	case REJECT:
291 		parse_reject_statement(cfile, config);
292 		return;
293 	default:
294 		break;
295 	}
296 
297 	parse_warn("expecting a statement.");
298 	skip_to_semi(cfile);
299 }
300 
301 unsigned
parse_X(FILE * cfile,u_int8_t * buf,unsigned max)302 parse_X(FILE *cfile, u_int8_t *buf, unsigned max)
303 {
304 	int	 token;
305 	char	*val;
306 	unsigned len;
307 
308 	token = peek_token(&val, cfile);
309 	if (token == NUMBER_OR_NAME || token == NUMBER) {
310 		len = 0;
311 		do {
312 			token = next_token(&val, cfile);
313 			if (token != NUMBER && token != NUMBER_OR_NAME) {
314 				parse_warn("expecting hexadecimal constant.");
315 				skip_to_semi(cfile);
316 				return (0);
317 			}
318 			convert_num(&buf[len], val, 16, 8);
319 			if (len++ > max) {
320 				parse_warn("hexadecimal constant too long.");
321 				skip_to_semi(cfile);
322 				return (0);
323 			}
324 			token = peek_token(&val, cfile);
325 			if (token == COLON)
326 				token = next_token(&val, cfile);
327 		} while (token == COLON);
328 		val = (char *)buf;
329 	} else if (token == STRING) {
330 		token = next_token(&val, cfile);
331 		len = strlen(val);
332 		if (len + 1 > max) {
333 			parse_warn("string constant too long.");
334 			skip_to_semi(cfile);
335 			return (0);
336 		}
337 		memcpy(buf, val, len + 1);
338 	} else {
339 		parse_warn("expecting string or hexadecimal data");
340 		skip_to_semi(cfile);
341 		return (0);
342 	}
343 	return (len);
344 }
345 
346 /*
347  * option-list :== option_name |
348  *		   option_list COMMA option_name
349  */
350 int
parse_option_list(FILE * cfile,u_int8_t * list)351 parse_option_list(FILE *cfile, u_int8_t *list)
352 {
353 	int	 ix, i;
354 	int	 token;
355 	char	*val;
356 
357 	ix = 0;
358 	do {
359 		token = next_token(&val, cfile);
360 		if (!is_identifier(token)) {
361 			parse_warn("expected option name.");
362 			skip_to_semi(cfile);
363 			return (0);
364 		}
365 		for (i = 0; i < 256; i++)
366 			if (!strcasecmp(dhcp_options[i].name, val))
367 				break;
368 
369 		if (i == 256) {
370 			parse_warn("%s: unexpected option name.", val);
371 			skip_to_semi(cfile);
372 			return (0);
373 		}
374 		list[ix++] = i;
375 		if (ix == 256) {
376 			parse_warn("%s: too many options.", val);
377 			skip_to_semi(cfile);
378 			return (0);
379 		}
380 		token = next_token(&val, cfile);
381 	} while (token == COMMA);
382 	if (token != SEMI) {
383 		parse_warn("expecting semicolon.");
384 		skip_to_semi(cfile);
385 		return (0);
386 	}
387 	return (ix);
388 }
389 
390 /*
391  * interface-declaration :==
392  *	INTERFACE string LBRACE client-declarations RBRACE
393  */
394 void
parse_interface_declaration(FILE * cfile,struct client_config * outer_config)395 parse_interface_declaration(FILE *cfile, struct client_config *outer_config)
396 {
397 	int			 token;
398 	char			*val;
399 	struct interface_info	*ip;
400 
401 	token = next_token(&val, cfile);
402 	if (token != STRING) {
403 		parse_warn("expecting interface name (in quotes).");
404 		skip_to_semi(cfile);
405 		return;
406 	}
407 
408 	ip = interface_or_dummy(val);
409 
410 	if (!ip->client)
411 		make_client_state(ip);
412 
413 	if (!ip->client->config)
414 		make_client_config(ip, outer_config);
415 
416 	token = next_token(&val, cfile);
417 	if (token != LBRACE) {
418 		parse_warn("expecting left brace.");
419 		skip_to_semi(cfile);
420 		return;
421 	}
422 
423 	do {
424 		token = peek_token(&val, cfile);
425 		if (token == EOF) {
426 			parse_warn("unterminated interface declaration.");
427 			return;
428 		}
429 		if (token == RBRACE)
430 			break;
431 		parse_client_statement(cfile, ip, ip->client->config);
432 	} while (1);
433 	token = next_token(&val, cfile);
434 }
435 
436 struct interface_info *
interface_or_dummy(char * name)437 interface_or_dummy(char *name)
438 {
439 	struct interface_info	*ip;
440 
441 	/* Find the interface (if any) that matches the name. */
442 	if (!strcmp(ifi->name, name))
443 		return (ifi);
444 
445 	/* If it's not a real interface, see if it's on the dummy list. */
446 	for (ip = dummy_interfaces; ip; ip = ip->next)
447 		if (!strcmp(ip->name, name))
448 			return (ip);
449 
450 	/*
451 	 * If we didn't find an interface, make a dummy interface as a
452 	 * placeholder.
453 	 */
454 	ip = malloc(sizeof(*ip));
455 	if (!ip)
456 		error("Insufficient memory to record interface %s", name);
457 	memset(ip, 0, sizeof(*ip));
458 	strlcpy(ip->name, name, IFNAMSIZ);
459 	ip->next = dummy_interfaces;
460 	dummy_interfaces = ip;
461 	return (ip);
462 }
463 
464 void
make_client_state(struct interface_info * ip)465 make_client_state(struct interface_info *ip)
466 {
467 	ip->client = malloc(sizeof(*(ip->client)));
468 	if (!ip->client)
469 		error("no memory for state on %s", ip->name);
470 	memset(ip->client, 0, sizeof(*(ip->client)));
471 }
472 
473 void
make_client_config(struct interface_info * ip,struct client_config * config)474 make_client_config(struct interface_info *ip, struct client_config *config)
475 {
476 	ip->client->config = malloc(sizeof(struct client_config));
477 	if (!ip->client->config)
478 		error("no memory for config for %s", ip->name);
479 	memset(ip->client->config, 0, sizeof(*(ip->client->config)));
480 	memcpy(ip->client->config, config, sizeof(*config));
481 }
482 
483 /*
484  * client-lease-statement :==
485  *	RBRACE client-lease-declarations LBRACE
486  *
487  *	client-lease-declarations :==
488  *		<nil> |
489  *		client-lease-declaration |
490  *		client-lease-declarations client-lease-declaration
491  */
492 void
parse_client_lease_statement(FILE * cfile,int is_static)493 parse_client_lease_statement(FILE *cfile, int is_static)
494 {
495 	struct client_lease	*lease, *lp, *pl;
496 	struct interface_info	*ip;
497 	int			 token;
498 	char			*val;
499 
500 	token = next_token(&val, cfile);
501 	if (token != LBRACE) {
502 		parse_warn("expecting left brace.");
503 		skip_to_semi(cfile);
504 		return;
505 	}
506 
507 	lease = malloc(sizeof(struct client_lease));
508 	if (!lease)
509 		error("no memory for lease.");
510 	memset(lease, 0, sizeof(*lease));
511 	lease->is_static = is_static;
512 
513 	ip = NULL;
514 
515 	do {
516 		token = peek_token(&val, cfile);
517 		if (token == EOF) {
518 			parse_warn("unterminated lease declaration.");
519 			free_client_lease(lease);
520 			return;
521 		}
522 		if (token == RBRACE)
523 			break;
524 		parse_client_lease_declaration(cfile, lease, &ip);
525 	} while (1);
526 	token = next_token(&val, cfile);
527 
528 	/* If the lease declaration didn't include an interface
529 	 * declaration that we recognized, it's of no use to us.
530 	 */
531 	if (!ip) {
532 		free_client_lease(lease);
533 		return;
534 	}
535 
536 	/* Make sure there's a client state structure... */
537 	if (!ip->client)
538 		make_client_state(ip);
539 
540 	/* If this is an alias lease, it doesn't need to be sorted in. */
541 	if (is_static == 2) {
542 		ip->client->alias = lease;
543 		return;
544 	}
545 
546 	/*
547 	 * The new lease may supersede a lease that's not the active
548 	 * lease but is still on the lease list, so scan the lease list
549 	 * looking for a lease with the same address, and if we find it,
550 	 * toss it.
551 	 */
552 	pl = NULL;
553 	for (lp = ip->client->leases; lp; lp = lp->next) {
554 		if (lp->address.len == lease->address.len &&
555 		    !memcmp(lp->address.iabuf, lease->address.iabuf,
556 		    lease->address.len)) {
557 			if (pl)
558 				pl->next = lp->next;
559 			else
560 				ip->client->leases = lp->next;
561 			free_client_lease(lp);
562 			break;
563 		}
564 	}
565 
566 	/*
567 	 * If this is a preloaded lease, just put it on the list of
568 	 * recorded leases - don't make it the active lease.
569 	 */
570 	if (is_static) {
571 		lease->next = ip->client->leases;
572 		ip->client->leases = lease;
573 		return;
574 	}
575 
576 	/*
577 	 * The last lease in the lease file on a particular interface is
578 	 * the active lease for that interface.    Of course, we don't
579 	 * know what the last lease in the file is until we've parsed
580 	 * the whole file, so at this point, we assume that the lease we
581 	 * just parsed is the active lease for its interface.   If
582 	 * there's already an active lease for the interface, and this
583 	 * lease is for the same ip address, then we just toss the old
584 	 * active lease and replace it with this one.   If this lease is
585 	 * for a different address, then if the old active lease has
586 	 * expired, we dump it; if not, we put it on the list of leases
587 	 * for this interface which are still valid but no longer
588 	 * active.
589 	 */
590 	if (ip->client->active) {
591 		if (ip->client->active->expiry < cur_time)
592 			free_client_lease(ip->client->active);
593 		else if (ip->client->active->address.len ==
594 		    lease->address.len &&
595 		    !memcmp(ip->client->active->address.iabuf,
596 		    lease->address.iabuf, lease->address.len))
597 			free_client_lease(ip->client->active);
598 		else {
599 			ip->client->active->next = ip->client->leases;
600 			ip->client->leases = ip->client->active;
601 		}
602 	}
603 	ip->client->active = lease;
604 
605 	/* Phew. */
606 }
607 
608 /*
609  * client-lease-declaration :==
610  *	BOOTP |
611  *	INTERFACE string |
612  *	FIXED_ADDR ip_address |
613  *	FILENAME string |
614  *	SERVER_NAME string |
615  *	OPTION option-decl |
616  *	RENEW time-decl |
617  *	REBIND time-decl |
618  *	EXPIRE time-decl
619  */
620 void
parse_client_lease_declaration(FILE * cfile,struct client_lease * lease,struct interface_info ** ipp)621 parse_client_lease_declaration(FILE *cfile, struct client_lease *lease,
622     struct interface_info **ipp)
623 {
624 	int			 token;
625 	char			*val;
626 	struct interface_info	*ip;
627 
628 	switch (next_token(&val, cfile)) {
629 	case BOOTP:
630 		lease->is_bootp = 1;
631 		break;
632 	case INTERFACE:
633 		token = next_token(&val, cfile);
634 		if (token != STRING) {
635 			parse_warn("expecting interface name (in quotes).");
636 			skip_to_semi(cfile);
637 			return;
638 		}
639 		ip = interface_or_dummy(val);
640 		*ipp = ip;
641 		break;
642 	case FIXED_ADDR:
643 		if (!parse_ip_addr(cfile, &lease->address))
644 			return;
645 		break;
646 	case MEDIUM:
647 		parse_string_list(cfile, &lease->medium, 0);
648 		return;
649 	case FILENAME:
650 		lease->filename = parse_string(cfile);
651 		return;
652 	case NEXT_SERVER:
653 		if (!parse_ip_addr(cfile, &lease->nextserver))
654 			return;
655 		break;
656 	case SERVER_NAME:
657 		lease->server_name = parse_string(cfile);
658 		return;
659 	case RENEW:
660 		lease->renewal = parse_date(cfile);
661 		return;
662 	case REBIND:
663 		lease->rebind = parse_date(cfile);
664 		return;
665 	case EXPIRE:
666 		lease->expiry = parse_date(cfile);
667 		return;
668 	case OPTION:
669 		parse_option_decl(cfile, lease->options);
670 		return;
671 	default:
672 		parse_warn("expecting lease declaration.");
673 		skip_to_semi(cfile);
674 		return;
675 	}
676 	token = next_token(&val, cfile);
677 	if (token != SEMI) {
678 		parse_warn("expecting semicolon.");
679 		skip_to_semi(cfile);
680 	}
681 }
682 
683 struct option *
parse_option_decl(FILE * cfile,struct option_data * options)684 parse_option_decl(FILE *cfile, struct option_data *options)
685 {
686 	char		*val;
687 	int		 token;
688 	u_int8_t	 buf[4];
689 	u_int8_t	 hunkbuf[1024];
690 	unsigned	 hunkix = 0;
691 	char		*vendor;
692 	const char	*fmt;
693 	struct universe	*universe;
694 	struct option	*option;
695 	struct iaddr	 ip_addr;
696 	u_int8_t	*dp;
697 	unsigned	 len;
698 	int		 nul_term = 0;
699 
700 	token = next_token(&val, cfile);
701 	if (!is_identifier(token)) {
702 		parse_warn("expecting identifier after option keyword.");
703 		if (token != SEMI)
704 			skip_to_semi(cfile);
705 		return (NULL);
706 	}
707 	if ((vendor = strdup(val)) == NULL)
708 		error("no memory for vendor information.");
709 
710 	token = peek_token(&val, cfile);
711 	if (token == DOT) {
712 		/* Go ahead and take the DOT token... */
713 		token = next_token(&val, cfile);
714 
715 		/* The next token should be an identifier... */
716 		token = next_token(&val, cfile);
717 		if (!is_identifier(token)) {
718 			parse_warn("expecting identifier after '.'");
719 			if (token != SEMI)
720 				skip_to_semi(cfile);
721 			free(vendor);
722 			return (NULL);
723 		}
724 
725 		/* Look up the option name hash table for the specified
726 		   vendor. */
727 		universe = ((struct universe *)hash_lookup(&universe_hash,
728 		    (unsigned char *)vendor, 0));
729 		/* If it's not there, we can't parse the rest of the
730 		   declaration. */
731 		if (!universe) {
732 			parse_warn("no vendor named %s.", vendor);
733 			skip_to_semi(cfile);
734 			free(vendor);
735 			return (NULL);
736 		}
737 	} else {
738 		/* Use the default hash table, which contains all the
739 		   standard dhcp option names. */
740 		val = vendor;
741 		universe = &dhcp_universe;
742 	}
743 
744 	/* Look up the actual option info... */
745 	option = (struct option *)hash_lookup(universe->hash,
746 	    (unsigned char *)val, 0);
747 
748 	/* If we didn't get an option structure, it's an undefined option. */
749 	if (!option) {
750 		if (val == vendor)
751 			parse_warn("no option named %s", val);
752 		else
753 			parse_warn("no option named %s for vendor %s",
754 				    val, vendor);
755 		skip_to_semi(cfile);
756 		free(vendor);
757 		return (NULL);
758 	}
759 
760 	/* Free the initial identifier token. */
761 	free(vendor);
762 
763 	/* Parse the option data... */
764 	do {
765 		for (fmt = option->format; *fmt; fmt++) {
766 			if (*fmt == 'A')
767 				break;
768 			switch (*fmt) {
769 			case 'X':
770 				len = parse_X(cfile, &hunkbuf[hunkix],
771 				    sizeof(hunkbuf) - hunkix);
772 				hunkix += len;
773 				break;
774 			case 't': /* Text string... */
775 				token = next_token(&val, cfile);
776 				if (token != STRING) {
777 					parse_warn("expecting string.");
778 					skip_to_semi(cfile);
779 					return (NULL);
780 				}
781 				len = strlen(val);
782 				if (hunkix + len + 1 > sizeof(hunkbuf)) {
783 					parse_warn("option data buffer %s",
784 					    "overflow");
785 					skip_to_semi(cfile);
786 					return (NULL);
787 				}
788 				memcpy(&hunkbuf[hunkix], val, len + 1);
789 				nul_term = 1;
790 				hunkix += len;
791 				break;
792 			case 'I': /* IP address. */
793 				if (!parse_ip_addr(cfile, &ip_addr))
794 					return (NULL);
795 				len = ip_addr.len;
796 				dp = ip_addr.iabuf;
797 alloc:
798 				if (hunkix + len > sizeof(hunkbuf)) {
799 					parse_warn("option data buffer "
800 					    "overflow");
801 					skip_to_semi(cfile);
802 					return (NULL);
803 				}
804 				memcpy(&hunkbuf[hunkix], dp, len);
805 				hunkix += len;
806 				break;
807 			case 'L':	/* Unsigned 32-bit integer... */
808 			case 'l':	/* Signed 32-bit integer... */
809 				token = next_token(&val, cfile);
810 				if (token != NUMBER) {
811 need_number:
812 					parse_warn("expecting number.");
813 					if (token != SEMI)
814 						skip_to_semi(cfile);
815 					return (NULL);
816 				}
817 				convert_num(buf, val, 0, 32);
818 				len = 4;
819 				dp = buf;
820 				goto alloc;
821 			case 's':	/* Signed 16-bit integer. */
822 			case 'S':	/* Unsigned 16-bit integer. */
823 				token = next_token(&val, cfile);
824 				if (token != NUMBER)
825 					goto need_number;
826 				convert_num(buf, val, 0, 16);
827 				len = 2;
828 				dp = buf;
829 				goto alloc;
830 			case 'b':	/* Signed 8-bit integer. */
831 			case 'B':	/* Unsigned 8-bit integer. */
832 				token = next_token(&val, cfile);
833 				if (token != NUMBER)
834 					goto need_number;
835 				convert_num(buf, val, 0, 8);
836 				len = 1;
837 				dp = buf;
838 				goto alloc;
839 			case 'f': /* Boolean flag. */
840 				token = next_token(&val, cfile);
841 				if (!is_identifier(token)) {
842 					parse_warn("expecting identifier.");
843 bad_flag:
844 					if (token != SEMI)
845 						skip_to_semi(cfile);
846 					return (NULL);
847 				}
848 				if (!strcasecmp(val, "true") ||
849 				    !strcasecmp(val, "on"))
850 					buf[0] = 1;
851 				else if (!strcasecmp(val, "false") ||
852 				    !strcasecmp(val, "off"))
853 					buf[0] = 0;
854 				else {
855 					parse_warn("expecting boolean.");
856 					goto bad_flag;
857 				}
858 				len = 1;
859 				dp = buf;
860 				goto alloc;
861 			default:
862 				warning("Bad format %c in parse_option_param.",
863 				    *fmt);
864 				skip_to_semi(cfile);
865 				return (NULL);
866 			}
867 		}
868 		token = next_token(&val, cfile);
869 	} while (*fmt == 'A' && token == COMMA);
870 
871 	if (token != SEMI) {
872 		parse_warn("semicolon expected.");
873 		skip_to_semi(cfile);
874 		return (NULL);
875 	}
876 
877 	options[option->code].data = malloc(hunkix + nul_term);
878 	if (!options[option->code].data)
879 		error("out of memory allocating option data.");
880 	memcpy(options[option->code].data, hunkbuf, hunkix + nul_term);
881 	options[option->code].len = hunkix;
882 	return (option);
883 }
884 
885 void
parse_string_list(FILE * cfile,struct string_list ** lp,int multiple)886 parse_string_list(FILE *cfile, struct string_list **lp, int multiple)
887 {
888 	int			 token;
889 	char			*val;
890 	size_t			 valsize;
891 	struct string_list	*cur, *tmp;
892 
893 	/* Find the last medium in the media list. */
894 	if (*lp)
895 		for (cur = *lp; cur->next; cur = cur->next)
896 			;	/* nothing */
897 	else
898 		cur = NULL;
899 
900 	do {
901 		token = next_token(&val, cfile);
902 		if (token != STRING) {
903 			parse_warn("Expecting media options.");
904 			skip_to_semi(cfile);
905 			return;
906 		}
907 
908 		valsize = strlen(val) + 1;
909 		tmp = new_string_list(valsize);
910 		if (tmp == NULL)
911 			error("no memory for string list entry.");
912 		memcpy(tmp->string, val, valsize);
913 		tmp->next = NULL;
914 
915 		/* Store this medium at the end of the media list. */
916 		if (cur)
917 			cur->next = tmp;
918 		else
919 			*lp = tmp;
920 		cur = tmp;
921 
922 		token = next_token(&val, cfile);
923 	} while (multiple && token == COMMA);
924 
925 	if (token != SEMI) {
926 		parse_warn("expecting semicolon.");
927 		skip_to_semi(cfile);
928 	}
929 }
930 
931 void
parse_reject_statement(FILE * cfile,struct client_config * config)932 parse_reject_statement(FILE *cfile, struct client_config *config)
933 {
934 	int			 token;
935 	char			*val;
936 	struct iaddr		 addr;
937 	struct iaddrlist	*list;
938 
939 	do {
940 		if (!parse_ip_addr(cfile, &addr)) {
941 			parse_warn("expecting IP address.");
942 			skip_to_semi(cfile);
943 			return;
944 		}
945 
946 		list = malloc(sizeof(struct iaddrlist));
947 		if (!list)
948 			error("no memory for reject list!");
949 
950 		list->addr = addr;
951 		list->next = config->reject_list;
952 		config->reject_list = list;
953 
954 		token = next_token(&val, cfile);
955 	} while (token == COMMA);
956 
957 	if (token != SEMI) {
958 		parse_warn("expecting semicolon.");
959 		skip_to_semi(cfile);
960 	}
961 }
962