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