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