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