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