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