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