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