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