1 /*	$OpenBSD: clparse.c,v 1.34 2007/02/14 23:19:26 deraadt Exp $	*/
2 
3 /* Parser for dhclient config and lease files... */
4 
5 /*
6  * Copyright (c) 1997 The Internet Software Consortium.
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  *
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. Neither the name of The Internet Software Consortium nor the names
19  *    of its contributors may be used to endorse or promote products derived
20  *    from this software without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
23  * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
24  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
25  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
26  * DISCLAIMED.  IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
27  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
29  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
30  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
31  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
32  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
33  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  *
36  * This software has been written for the Internet Software Consortium
37  * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
38  * Enterprises.  To learn more about the Internet Software Consortium,
39  * see ``http://www.vix.com/isc''.  To learn more about Vixie
40  * Enterprises, see ``http://www.vix.com''.
41  */
42 
43 #include "dhcpd.h"
44 #include "dhctoken.h"
45 
46 /*
47  * client-conf-file :== client-declarations EOF
48  * client-declarations :== <nil>
49  *			 | client-declaration
50  *			 | client-declarations client-declaration
51  */
52 int
read_client_conf(void)53 read_client_conf(void)
54 {
55 	FILE *cfile;
56 	char *val;
57 	int token;
58 
59 	new_parse(path_dhclient_conf);
60 
61 	/* Set some defaults... */
62 	config->link_timeout = 10;
63 	config->timeout = 60;
64 	config->select_interval = 0;
65 	config->reboot_timeout = 10;
66 	config->retry_interval = 300;
67 	config->backoff_cutoff = 15;
68 	config->initial_interval = 3;
69 	config->bootp_policy = ACCEPT;
70 	config->script_name = _PATH_DHCLIENT_SCRIPT;
71 	config->requested_options
72 	    [config->requested_option_count++] = DHO_SUBNET_MASK;
73 	config->requested_options
74 	    [config->requested_option_count++] = DHO_BROADCAST_ADDRESS;
75 	config->requested_options
76 	    [config->requested_option_count++] = DHO_TIME_OFFSET;
77 	config->requested_options
78 	    [config->requested_option_count++] = DHO_ROUTERS;
79 	config->requested_options
80 	    [config->requested_option_count++] = DHO_DOMAIN_NAME;
81 	config->requested_options
82 	    [config->requested_option_count++] = DHO_DOMAIN_NAME_SERVERS;
83 	config->requested_options
84 	    [config->requested_option_count++] = DHO_HOST_NAME;
85 
86 	if ((cfile = fopen(path_dhclient_conf, "r")) != NULL) {
87 		do {
88 			token = peek_token(&val, cfile);
89 			if (token == EOF)
90 				break;
91 			parse_client_statement(cfile);
92 		} while (1);
93 		token = next_token(&val, cfile); /* Clear the peek buffer */
94 		fclose(cfile);
95 	}
96 
97 	return (!warnings_occurred);
98 }
99 
100 /*
101  * lease-file :== client-lease-statements EOF
102  * client-lease-statements :== <nil>
103  *		     | client-lease-statements LEASE client-lease-statement
104  */
105 void
read_client_leases(void)106 read_client_leases(void)
107 {
108 	FILE	*cfile;
109 	char	*val;
110 	int	 token;
111 
112 	new_parse(path_dhclient_db);
113 
114 	/* Open the lease file.   If we can't open it, just return -
115 	   we can safely trust the server to remember our state. */
116 	if ((cfile = fopen(path_dhclient_db, "r")) == NULL)
117 		return;
118 	do {
119 		token = next_token(&val, cfile);
120 		if (token == EOF)
121 			break;
122 		if (token != TOK_LEASE) {
123 			warning("Corrupt lease file - possible data loss!");
124 			skip_to_semi(cfile);
125 			break;
126 		} else
127 			parse_client_lease_statement(cfile, 0);
128 
129 	} while (1);
130 	fclose(cfile);
131 }
132 
133 /*
134  * client-declaration :==
135  *	TOK_SEND option-decl |
136  *	TOK_DEFAULT option-decl |
137  *	TOK_SUPERSEDE option-decl |
138  *	TOK_APPEND option-decl |
139  *	TOK_PREPEND option-decl |
140  *	TOK_MEDIA string-list |
141  *	hardware-declaration |
142  *	TOK_REQUEST option-list |
143  *	TOK_REQUIRE option-list |
144  *	TOK_TIMEOUT number |
145  *	TOK_RETRY number |
146  *	TOK_SELECT_TIMEOUT number |
147  *	TOK_REBOOT number |
148  *	TOK_BACKOFF_CUTOFF number |
149  *	TOK_INITIAL_INTERVAL number |
150  *	TOK_SCRIPT string |
151  *	interface-declaration |
152  *	TOK_LEASE client-lease-statement |
153  *	TOK_ALIAS client-lease-statement |
154  *	TOK_REJECT reject-statement
155  */
156 void
parse_client_statement(FILE * cfile)157 parse_client_statement(FILE *cfile)
158 {
159 	char *val;
160 	int token, code;
161 
162 	switch (next_token(&val, cfile)) {
163 	case TOK_SEND:
164 		parse_option_decl(cfile, &config->send_options[0]);
165 		return;
166 	case TOK_DEFAULT:
167 		code = parse_option_decl(cfile, &config->defaults[0]);
168 		if (code != -1)
169 			config->default_actions[code] = ACTION_DEFAULT;
170 		return;
171 	case TOK_SUPERSEDE:
172 		code = parse_option_decl(cfile, &config->defaults[0]);
173 		if (code != -1)
174 			config->default_actions[code] = ACTION_SUPERSEDE;
175 		return;
176 	case TOK_APPEND:
177 		code = parse_option_decl(cfile, &config->defaults[0]);
178 		if (code != -1)
179 			config->default_actions[code] = ACTION_APPEND;
180 		return;
181 	case TOK_PREPEND:
182 		code = parse_option_decl(cfile, &config->defaults[0]);
183 		if (code != -1)
184 			config->default_actions[code] = ACTION_PREPEND;
185 		return;
186 	case TOK_MEDIA:
187 		parse_string_list(cfile, &config->media, 1);
188 		return;
189 	case TOK_HARDWARE:
190 		parse_hardware_param(cfile, &ifi->hw_address);
191 		return;
192 	case TOK_REQUEST:
193 		config->requested_option_count =
194 			parse_option_list(cfile, config->requested_options);
195 		return;
196 	case TOK_REQUIRE:
197 		memset(config->required_options, 0,
198 		    sizeof(config->required_options));
199 		parse_option_list(cfile, config->required_options);
200 		return;
201 	case TOK_LINK_TIMEOUT:
202 		parse_lease_time(cfile, &config->link_timeout);
203 		return;
204 	case TOK_TIMEOUT:
205 		parse_lease_time(cfile, &config->timeout);
206 		return;
207 	case TOK_RETRY:
208 		parse_lease_time(cfile, &config->retry_interval);
209 		return;
210 	case TOK_SELECT_TIMEOUT:
211 		parse_lease_time(cfile, &config->select_interval);
212 		return;
213 	case TOK_REBOOT:
214 		parse_lease_time(cfile, &config->reboot_timeout);
215 		return;
216 	case TOK_BACKOFF_CUTOFF:
217 		parse_lease_time(cfile, &config->backoff_cutoff);
218 		return;
219 	case TOK_INITIAL_INTERVAL:
220 		parse_lease_time(cfile, &config->initial_interval);
221 		return;
222 	case TOK_SCRIPT:
223 		config->script_name = parse_string(cfile);
224 		return;
225 	case TOK_INTERFACE:
226 		parse_interface_declaration(cfile);
227 		return;
228 	case TOK_LEASE:
229 		parse_client_lease_statement(cfile, 1);
230 		return;
231 	case TOK_ALIAS:
232 		parse_client_lease_statement(cfile, 2);
233 		return;
234 	case TOK_REJECT:
235 		parse_reject_statement(cfile);
236 		return;
237 	default:
238 		parse_warn("expecting a statement.");
239 		skip_to_semi(cfile);
240 		break;
241 	}
242 	token = next_token(&val, cfile);
243 	if (token != ';') {
244 		parse_warn("semicolon expected.");
245 		skip_to_semi(cfile);
246 	}
247 }
248 
249 int
parse_X(FILE * cfile,u_int8_t * buf,int max)250 parse_X(FILE *cfile, u_int8_t *buf, int max)
251 {
252 	int	 token;
253 	char	*val;
254 	int	 len;
255 
256 	token = peek_token(&val, cfile);
257 	if (token == TOK_NUMBER_OR_NAME || token == TOK_NUMBER) {
258 		len = 0;
259 		do {
260 			token = next_token(&val, cfile);
261 			if (token != TOK_NUMBER && token != TOK_NUMBER_OR_NAME) {
262 				parse_warn("expecting hexadecimal constant.");
263 				skip_to_semi(cfile);
264 				return (0);
265 			}
266 			convert_num(&buf[len], val, 16, 8);
267 			if (len++ > max) {
268 				parse_warn("hexadecimal constant too long.");
269 				skip_to_semi(cfile);
270 				return (0);
271 			}
272 			token = peek_token(&val, cfile);
273 			if (token == ':')
274 				token = next_token(&val, cfile);
275 		} while (token == ':');
276 		val = (char *)buf;
277 	} else if (token == TOK_STRING) {
278 		token = next_token(&val, cfile);
279 		len = strlen(val);
280 		if (len + 1 > max) {
281 			parse_warn("string constant too long.");
282 			skip_to_semi(cfile);
283 			return (0);
284 		}
285 		memcpy(buf, val, len + 1);
286 	} else {
287 		parse_warn("expecting string or hexadecimal data");
288 		skip_to_semi(cfile);
289 		return (0);
290 	}
291 	return (len);
292 }
293 
294 /*
295  * option-list :== option_name |
296  *		   option_list COMMA option_name
297  */
298 int
parse_option_list(FILE * cfile,u_int8_t * list)299 parse_option_list(FILE *cfile, u_int8_t *list)
300 {
301 	int	 ix, i;
302 	int	 token;
303 	char	*val;
304 
305 	ix = 0;
306 	do {
307 		token = next_token(&val, cfile);
308 		if (!is_identifier(token)) {
309 			parse_warn("expected option name.");
310 			skip_to_semi(cfile);
311 			return (0);
312 		}
313 		for (i = 0; i < 256; i++)
314 			if (!strcasecmp(dhcp_options[i].name, val))
315 				break;
316 
317 		if (i == 256) {
318 			parse_warn("%s: unexpected option name.", val);
319 			skip_to_semi(cfile);
320 			return (0);
321 		}
322 		list[ix++] = i;
323 		if (ix == 256) {
324 			parse_warn("%s: too many options.", val);
325 			skip_to_semi(cfile);
326 			return (0);
327 		}
328 		token = next_token(&val, cfile);
329 	} while (token == ',');
330 	if (token != ';') {
331 		parse_warn("expecting semicolon.");
332 		skip_to_semi(cfile);
333 		return (0);
334 	}
335 	return (ix);
336 }
337 
338 /*
339  * interface-declaration :==
340  *	INTERFACE string LBRACE client-declarations RBRACE
341  */
342 void
parse_interface_declaration(FILE * cfile)343 parse_interface_declaration(FILE *cfile)
344 {
345 	char *val;
346 	int token;
347 
348 	token = next_token(&val, cfile);
349 	if (token != TOK_STRING) {
350 		parse_warn("expecting interface name (in quotes).");
351 		skip_to_semi(cfile);
352 		return;
353 	}
354 
355 	if (strcmp(ifi->name, val) != 0) {
356 		skip_to_semi(cfile);
357 		return;
358 	}
359 
360 	token = next_token(&val, cfile);
361 	if (token != '{') {
362 		parse_warn("expecting left brace.");
363 		skip_to_semi(cfile);
364 		return;
365 	}
366 
367 	do {
368 		token = peek_token(&val, cfile);
369 		if (token == EOF) {
370 			parse_warn("unterminated interface declaration.");
371 			return;
372 		}
373 		if (token == '}')
374 			break;
375 		parse_client_statement(cfile);
376 	} while (1);
377 	token = next_token(&val, cfile);
378 }
379 
380 /*
381  * client-lease-statement :==
382  *	RBRACE client-lease-declarations LBRACE
383  *
384  *	client-lease-declarations :==
385  *		<nil> |
386  *		client-lease-declaration |
387  *		client-lease-declarations client-lease-declaration
388  */
389 void
parse_client_lease_statement(FILE * cfile,int is_static)390 parse_client_lease_statement(FILE *cfile, int is_static)
391 {
392 	struct client_lease	*lease, *lp, *pl;
393 	int			 token;
394 	char			*val;
395 
396 	token = next_token(&val, cfile);
397 	if (token != '{') {
398 		parse_warn("expecting left brace.");
399 		skip_to_semi(cfile);
400 		return;
401 	}
402 
403 	lease = malloc(sizeof(struct client_lease));
404 	if (!lease)
405 		error("no memory for lease.");
406 	memset(lease, 0, sizeof(*lease));
407 	lease->is_static = is_static;
408 
409 	do {
410 		token = peek_token(&val, cfile);
411 		if (token == EOF) {
412 			parse_warn("unterminated lease declaration.");
413 			return;
414 		}
415 		if (token == '}')
416 			break;
417 		parse_client_lease_declaration(cfile, lease);
418 	} while (1);
419 	token = next_token(&val, cfile);
420 
421 	/* If the lease declaration didn't include an interface
422 	 * declaration that we recognized, it's of no use to us.
423 	 */
424 	if (!ifi) {
425 		free_client_lease(lease);
426 		return;
427 	}
428 
429 	/* If this is an alias lease, it doesn't need to be sorted in. */
430 	if (is_static == 2) {
431 		client->alias = lease;
432 		return;
433 	}
434 
435 	/*
436 	 * The new lease may supersede a lease that's not the active
437 	 * lease but is still on the lease list, so scan the lease list
438 	 * looking for a lease with the same address, and if we find it,
439 	 * toss it.
440 	 */
441 	pl = NULL;
442 	for (lp = client->leases; lp; lp = lp->next) {
443 		if (lp->address.len == lease->address.len &&
444 		    !memcmp(lp->address.iabuf, lease->address.iabuf,
445 		    lease->address.len)) {
446 			if (pl)
447 				pl->next = lp->next;
448 			else
449 				client->leases = lp->next;
450 			free_client_lease(lp);
451 			break;
452 		}
453 	}
454 
455 	/*
456 	 * If this is a preloaded lease, just put it on the list of
457 	 * recorded leases - don't make it the active lease.
458 	 */
459 	if (is_static) {
460 		lease->next = client->leases;
461 		client->leases = lease;
462 		return;
463 	}
464 
465 	/*
466 	 * The last lease in the lease file on a particular interface is
467 	 * the active lease for that interface.    Of course, we don't
468 	 * know what the last lease in the file is until we've parsed
469 	 * the whole file, so at this point, we assume that the lease we
470 	 * just parsed is the active lease for its interface.   If
471 	 * there's already an active lease for the interface, and this
472 	 * lease is for the same ip address, then we just toss the old
473 	 * active lease and replace it with this one.   If this lease is
474 	 * for a different address, then if the old active lease has
475 	 * expired, we dump it; if not, we put it on the list of leases
476 	 * for this interface which are still valid but no longer
477 	 * active.
478 	 */
479 	if (client->active) {
480 		if (client->active->expiry < cur_time)
481 			free_client_lease(client->active);
482 		else if (client->active->address.len ==
483 		    lease->address.len &&
484 		    !memcmp(client->active->address.iabuf,
485 		    lease->address.iabuf, lease->address.len))
486 			free_client_lease(client->active);
487 		else {
488 			client->active->next = client->leases;
489 			client->leases = client->active;
490 		}
491 	}
492 	client->active = lease;
493 
494 	/* Phew. */
495 }
496 
497 /*
498  * client-lease-declaration :==
499  *	BOOTP |
500  *	INTERFACE string |
501  *	FIXED_ADDR ip_address |
502  *	FILENAME string |
503  *	SERVER_NAME string |
504  *	OPTION option-decl |
505  *	RENEW time-decl |
506  *	REBIND time-decl |
507  *	EXPIRE time-decl
508  */
509 void
parse_client_lease_declaration(FILE * cfile,struct client_lease * lease)510 parse_client_lease_declaration(FILE *cfile, struct client_lease *lease)
511 {
512 	char *val;
513 	int token;
514 
515 	switch (next_token(&val, cfile)) {
516 	case TOK_BOOTP:
517 		lease->is_bootp = 1;
518 		break;
519 	case TOK_INTERFACE:
520 		token = next_token(&val, cfile);
521 		if (token != TOK_STRING) {
522 			parse_warn("expecting interface name (in quotes).");
523 			skip_to_semi(cfile);
524 			break;
525 		}
526 		if (strcmp(ifi->name, val) != 0) {
527 			parse_warn("wrong interface name. Expecting '%s'.",
528 			   ifi->name);
529 			skip_to_semi(cfile);
530 			break;
531 		}
532 		break;
533 	case TOK_FIXED_ADDR:
534 		if (!parse_ip_addr(cfile, &lease->address))
535 			return;
536 		break;
537 	case TOK_MEDIUM:
538 		parse_string_list(cfile, &lease->medium, 0);
539 		return;
540 	case TOK_FILENAME:
541 		lease->filename = parse_string(cfile);
542 		return;
543 	case TOK_SERVER_NAME:
544 		lease->server_name = parse_string(cfile);
545 		return;
546 	case TOK_RENEW:
547 		lease->renewal = parse_date(cfile);
548 		return;
549 	case TOK_REBIND:
550 		lease->rebind = parse_date(cfile);
551 		return;
552 	case TOK_EXPIRE:
553 		lease->expiry = parse_date(cfile);
554 		return;
555 	case TOK_OPTION:
556 		parse_option_decl(cfile, lease->options);
557 		return;
558 	default:
559 		parse_warn("expecting lease declaration.");
560 		skip_to_semi(cfile);
561 		break;
562 	}
563 	token = next_token(&val, cfile);
564 	if (token != ';') {
565 		parse_warn("expecting semicolon.");
566 		skip_to_semi(cfile);
567 	}
568 }
569 
570 int
parse_option_decl(FILE * cfile,struct option_data * options)571 parse_option_decl(FILE *cfile, struct option_data *options)
572 {
573 	char		*val;
574 	int		 token;
575 	u_int8_t	 buf[4];
576 	u_int8_t	 hunkbuf[1024];
577 	int		 hunkix = 0;
578 	char		*fmt;
579 	struct iaddr	 ip_addr;
580 	u_int8_t	*dp;
581 	int		 len, code;
582 	int		 nul_term = 0;
583 
584 	token = next_token(&val, cfile);
585 	if (!is_identifier(token)) {
586 		parse_warn("expecting identifier after option keyword.");
587 		if (token != ';')
588 			skip_to_semi(cfile);
589 		return (-1);
590 	}
591 
592 	/* Look up the actual option info. */
593 	fmt = NULL;
594 	for (code = 0; code < 256; code++)
595 		if (strcmp(dhcp_options[code].name, val) == 0)
596 			break;
597 
598 	if (code > 255) {
599 		parse_warn("no option named %s", val);
600 		skip_to_semi(cfile);
601 		return (-1);
602 	}
603 
604 	/* Parse the option data... */
605 	do {
606 		for (fmt = dhcp_options[code].format; *fmt; fmt++) {
607 			if (*fmt == 'A')
608 				break;
609 			switch (*fmt) {
610 			case 'X':
611 				len = parse_X(cfile, &hunkbuf[hunkix],
612 				    sizeof(hunkbuf) - hunkix);
613 				hunkix += len;
614 				break;
615 			case 't': /* Text string... */
616 				token = next_token(&val, cfile);
617 				if (token != TOK_STRING) {
618 					parse_warn("expecting string.");
619 					skip_to_semi(cfile);
620 					return (-1);
621 				}
622 				len = strlen(val);
623 				if (hunkix + len + 1 > sizeof(hunkbuf)) {
624 					parse_warn("option data buffer %s",
625 					    "overflow");
626 					skip_to_semi(cfile);
627 					return (-1);
628 				}
629 				memcpy(&hunkbuf[hunkix], val, len + 1);
630 				nul_term = 1;
631 				hunkix += len;
632 				break;
633 			case 'I': /* IP address. */
634 				if (!parse_ip_addr(cfile, &ip_addr))
635 					return (-1);
636 				len = ip_addr.len;
637 				dp = ip_addr.iabuf;
638 alloc:
639 				if (hunkix + len > sizeof(hunkbuf)) {
640 					parse_warn("option data buffer "
641 					    "overflow");
642 					skip_to_semi(cfile);
643 					return (-1);
644 				}
645 				memcpy(&hunkbuf[hunkix], dp, len);
646 				hunkix += len;
647 				break;
648 			case 'L':	/* Unsigned 32-bit integer... */
649 			case 'l':	/* Signed 32-bit integer... */
650 				token = next_token(&val, cfile);
651 				if (token != TOK_NUMBER) {
652 need_number:
653 					parse_warn("expecting number.");
654 					if (token != ';')
655 						skip_to_semi(cfile);
656 					return (-1);
657 				}
658 				convert_num(buf, val, 0, 32);
659 				len = 4;
660 				dp = buf;
661 				goto alloc;
662 			case 's':	/* Signed 16-bit integer. */
663 			case 'S':	/* Unsigned 16-bit integer. */
664 				token = next_token(&val, cfile);
665 				if (token != TOK_NUMBER)
666 					goto need_number;
667 				convert_num(buf, val, 0, 16);
668 				len = 2;
669 				dp = buf;
670 				goto alloc;
671 			case 'b':	/* Signed 8-bit integer. */
672 			case 'B':	/* Unsigned 8-bit integer. */
673 				token = next_token(&val, cfile);
674 				if (token != TOK_NUMBER)
675 					goto need_number;
676 				convert_num(buf, val, 0, 8);
677 				len = 1;
678 				dp = buf;
679 				goto alloc;
680 			case 'f': /* Boolean flag. */
681 				token = next_token(&val, cfile);
682 				if (!is_identifier(token)) {
683 					parse_warn("expecting identifier.");
684 bad_flag:
685 					if (token != ';')
686 						skip_to_semi(cfile);
687 					return (-1);
688 				}
689 				if (!strcasecmp(val, "true") ||
690 				    !strcasecmp(val, "on"))
691 					buf[0] = 1;
692 				else if (!strcasecmp(val, "false") ||
693 				    !strcasecmp(val, "off"))
694 					buf[0] = 0;
695 				else {
696 					parse_warn("expecting boolean.");
697 					goto bad_flag;
698 				}
699 				len = 1;
700 				dp = buf;
701 				goto alloc;
702 			default:
703 				warning("Bad format %c in parse_option_param.",
704 				    *fmt);
705 				skip_to_semi(cfile);
706 				return (-1);
707 			}
708 		}
709 		token = next_token(&val, cfile);
710 	} while (*fmt == 'A' && token == ',');
711 
712 	if (token != ';') {
713 		parse_warn("semicolon expected.");
714 		skip_to_semi(cfile);
715 		return (-1);
716 	}
717 
718 	options[code].data = malloc(hunkix + nul_term);
719 	if (!options[code].data)
720 		error("out of memory allocating option data.");
721 	memcpy(options[code].data, hunkbuf, hunkix + nul_term);
722 	options[code].len = hunkix;
723 	return (code);
724 }
725 
726 void
parse_string_list(FILE * cfile,struct string_list ** lp,int multiple)727 parse_string_list(FILE *cfile, struct string_list **lp, int multiple)
728 {
729 	int			 token;
730 	char			*val;
731 	struct string_list	*cur, *tmp;
732 
733 	/* Find the last medium in the media list. */
734 	if (*lp)
735 		for (cur = *lp; cur->next; cur = cur->next)
736 			;	/* nothing */
737 	else
738 		cur = NULL;
739 
740 	do {
741 		token = next_token(&val, cfile);
742 		if (token != TOK_STRING) {
743 			parse_warn("Expecting media options.");
744 			skip_to_semi(cfile);
745 			return;
746 		}
747 
748 		tmp = malloc(sizeof(struct string_list) + strlen(val));
749 		if (tmp == NULL)
750 			error("no memory for string list entry.");
751 		strlcpy(tmp->string, val, strlen(val) + 1);
752 		tmp->next = NULL;
753 
754 		/* Store this medium at the end of the media list. */
755 		if (cur)
756 			cur->next = tmp;
757 		else
758 			*lp = tmp;
759 		cur = tmp;
760 
761 		token = next_token(&val, cfile);
762 	} while (multiple && token == ',');
763 
764 	if (token != ';') {
765 		parse_warn("expecting semicolon.");
766 		skip_to_semi(cfile);
767 	}
768 }
769 
770 void
parse_reject_statement(FILE * cfile)771 parse_reject_statement(FILE *cfile)
772 {
773 	struct iaddrlist *list;
774 	struct iaddr addr;
775 	char *val;
776 	int token;
777 
778 	do {
779 		if (!parse_ip_addr(cfile, &addr)) {
780 			parse_warn("expecting IP address.");
781 			skip_to_semi(cfile);
782 			return;
783 		}
784 
785 		list = malloc(sizeof(struct iaddrlist));
786 		if (!list)
787 			error("no memory for reject list!");
788 
789 		list->addr = addr;
790 		list->next = config->reject_list;
791 		config->reject_list = list;
792 
793 		token = next_token(&val, cfile);
794 	} while (token == ',');
795 
796 	if (token != ';') {
797 		parse_warn("expecting semicolon.");
798 		skip_to_semi(cfile);
799 	}
800 }
801