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