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