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