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