xref: /dragonfly/sbin/dhclient/clparse.c (revision 0085a56d)
1 /*	$OpenBSD: src/sbin/dhclient/clparse.c,v 1.41 2012/10/27 23:08:53 krw 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
53 read_client_conf(void)
54 {
55 	FILE *cfile;
56 	int token;
57 
58 	new_parse(path_dhclient_conf);
59 
60 	/* Set some defaults... */
61 	config->link_timeout = 30;
62 	config->timeout = 60;
63 	config->select_interval = 0;
64 	config->reboot_timeout = 10;
65 	config->retry_interval = 300;
66 	config->backoff_cutoff = 15;
67 	config->initial_interval = 3;
68 	config->bootp_policy = ACCEPT;
69 	config->script_name = _PATH_DHCLIENT_SCRIPT;
70 	config->requested_options
71 	    [config->requested_option_count++] = DHO_SUBNET_MASK;
72 	config->requested_options
73 	    [config->requested_option_count++] = DHO_BROADCAST_ADDRESS;
74 	config->requested_options
75 	    [config->requested_option_count++] = DHO_TIME_OFFSET;
76 	config->requested_options
77 	    [config->requested_option_count++] = DHO_CLASSLESS_ROUTES;
78 	config->requested_options
79 	    [config->requested_option_count++] = DHO_ROUTERS;
80 	config->requested_options
81 	    [config->requested_option_count++] = DHO_DOMAIN_NAME;
82 	config->requested_options
83 	    [config->requested_option_count++] = DHO_DOMAIN_NAME_SERVERS;
84 	config->requested_options
85 	    [config->requested_option_count++] = DHO_HOST_NAME;
86 
87 	if ((cfile = fopen(path_dhclient_conf, "r")) != NULL) {
88 		do {
89 			token = peek_token(NULL, cfile);
90 			if (token == EOF)
91 				break;
92 			parse_client_statement(cfile);
93 		} while (1);
94 		token = next_token(NULL, cfile); /* Clear the peek buffer */
95 		fclose(cfile);
96 	}
97 
98 	return (!warnings_occurred);
99 }
100 
101 /*
102  * lease-file :== client-lease-statements EOF
103  * client-lease-statements :== <nil>
104  *		     | client-lease-statements LEASE client-lease-statement
105  */
106 void
107 read_client_leases(void)
108 {
109 	FILE	*cfile;
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(NULL, 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
157 parse_client_statement(FILE *cfile)
158 {
159 	u_int8_t ignorelist[256];
160 	int token, code, count, i;
161 
162 	switch (next_token(NULL, 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_IGNORE:
177 		count = parse_option_list(cfile, ignorelist);
178 		for (i = 0; i < count; i++)
179 			config->default_actions[ignorelist[i]] = ACTION_IGNORE;
180 		return;
181 	case TOK_APPEND:
182 		code = parse_option_decl(cfile, &config->defaults[0]);
183 		if (code != -1)
184 			config->default_actions[code] = ACTION_APPEND;
185 		return;
186 	case TOK_PREPEND:
187 		code = parse_option_decl(cfile, &config->defaults[0]);
188 		if (code != -1)
189 			config->default_actions[code] = ACTION_PREPEND;
190 		return;
191 	case TOK_MEDIA:
192 		skip_to_semi(cfile);
193 		return;
194 	case TOK_HARDWARE:
195 		parse_hardware_param(cfile, &ifi->hw_address);
196 		return;
197 	case TOK_REQUEST:
198 		config->requested_option_count =
199 			parse_option_list(cfile, config->requested_options);
200 		return;
201 	case TOK_REQUIRE:
202 		memset(config->required_options, 0,
203 		    sizeof(config->required_options));
204 		parse_option_list(cfile, config->required_options);
205 		return;
206 	case TOK_LINK_TIMEOUT:
207 		parse_lease_time(cfile, &config->link_timeout);
208 		return;
209 	case TOK_TIMEOUT:
210 		parse_lease_time(cfile, &config->timeout);
211 		return;
212 	case TOK_RETRY:
213 		parse_lease_time(cfile, &config->retry_interval);
214 		return;
215 	case TOK_SELECT_TIMEOUT:
216 		parse_lease_time(cfile, &config->select_interval);
217 		return;
218 	case TOK_REBOOT:
219 		parse_lease_time(cfile, &config->reboot_timeout);
220 		return;
221 	case TOK_BACKOFF_CUTOFF:
222 		parse_lease_time(cfile, &config->backoff_cutoff);
223 		return;
224 	case TOK_INITIAL_INTERVAL:
225 		parse_lease_time(cfile, &config->initial_interval);
226 		return;
227 	case TOK_SCRIPT:
228 		config->script_name = parse_string(cfile);
229 		return;
230 	case TOK_INTERFACE:
231 		parse_interface_declaration(cfile);
232 		return;
233 	case TOK_LEASE:
234 		parse_client_lease_statement(cfile, 1);
235 		return;
236 	case TOK_ALIAS:
237 		skip_to_semi(cfile);
238 		return;
239 	case TOK_REJECT:
240 		parse_reject_statement(cfile);
241 		return;
242 	default:
243 		parse_warn("expecting a statement.");
244 		skip_to_semi(cfile);
245 		break;
246 	}
247 	token = next_token(NULL, cfile);
248 	if (token != ';') {
249 		parse_warn("semicolon expected.");
250 		skip_to_semi(cfile);
251 	}
252 }
253 
254 int
255 parse_X(FILE *cfile, u_int8_t *buf, int max)
256 {
257 	int	 token;
258 	char	*val;
259 	int	 len;
260 
261 	token = peek_token(&val, cfile);
262 	if (token == TOK_NUMBER_OR_NAME || token == TOK_NUMBER) {
263 		len = 0;
264 		do {
265 			token = next_token(&val, cfile);
266 			if (token != TOK_NUMBER && token != TOK_NUMBER_OR_NAME) {
267 				parse_warn("expecting hexadecimal constant.");
268 				skip_to_semi(cfile);
269 				return (0);
270 			}
271 			convert_num(&buf[len], val, 16, 8);
272 			if (len++ > max) {
273 				parse_warn("hexadecimal constant too long.");
274 				skip_to_semi(cfile);
275 				return (0);
276 			}
277 			token = peek_token(&val, cfile);
278 			if (token == ':')
279 				token = next_token(&val, cfile);
280 		} while (token == ':');
281 		val = (char *)buf;
282 	} else if (token == TOK_STRING) {
283 		token = next_token(&val, cfile);
284 		len = strlen(val);
285 		if (len + 1 > max) {
286 			parse_warn("string constant too long.");
287 			skip_to_semi(cfile);
288 			return (0);
289 		}
290 		memcpy(buf, val, len + 1);
291 	} else {
292 		parse_warn("expecting string or hexadecimal data");
293 		skip_to_semi(cfile);
294 		return (0);
295 	}
296 	return (len);
297 }
298 
299 /*
300  * option-list :== option_name |
301  *		   option_list COMMA option_name
302  */
303 int
304 parse_option_list(FILE *cfile, u_int8_t *list)
305 {
306 	int	 ix, i;
307 	int	 token;
308 	char	*val;
309 
310 	ix = 0;
311 	do {
312 		token = next_token(&val, cfile);
313 		if (!is_identifier(token)) {
314 			parse_warn("expected option name.");
315 			skip_to_semi(cfile);
316 			return (0);
317 		}
318 		for (i = 0; i < 256; i++)
319 			if (!strcasecmp(dhcp_options[i].name, val))
320 				break;
321 
322 		if (i == 256) {
323 			parse_warn("%s: unexpected option name.", val);
324 			skip_to_semi(cfile);
325 			return (0);
326 		}
327 		list[ix++] = i;
328 		if (ix == 256) {
329 			parse_warn("%s: too many options.", val);
330 			skip_to_semi(cfile);
331 			return (0);
332 		}
333 		token = next_token(&val, cfile);
334 	} while (token == ',');
335 	if (token != ';') {
336 		parse_warn("expecting semicolon.");
337 		skip_to_semi(cfile);
338 		return (0);
339 	}
340 	return (ix);
341 }
342 
343 /*
344  * interface-declaration :==
345  *	INTERFACE string LBRACE client-declarations RBRACE
346  */
347 void
348 parse_interface_declaration(FILE *cfile)
349 {
350 	char *val;
351 	int token;
352 
353 	token = next_token(&val, cfile);
354 	if (token != TOK_STRING) {
355 		parse_warn("expecting interface name (in quotes).");
356 		skip_to_semi(cfile);
357 		return;
358 	}
359 
360 	if (strcmp(ifi->name, val) != 0) {
361 		skip_to_semi(cfile);
362 		return;
363 	}
364 
365 	token = next_token(&val, cfile);
366 	if (token != '{') {
367 		parse_warn("expecting left brace.");
368 		skip_to_semi(cfile);
369 		return;
370 	}
371 
372 	do {
373 		token = peek_token(&val, cfile);
374 		if (token == EOF) {
375 			parse_warn("unterminated interface declaration.");
376 			return;
377 		}
378 		if (token == '}')
379 			break;
380 		parse_client_statement(cfile);
381 	} while (1);
382 	token = next_token(&val, cfile);
383 }
384 
385 /*
386  * client-lease-statement :==
387  *	RBRACE client-lease-declarations LBRACE
388  *
389  *	client-lease-declarations :==
390  *		<nil> |
391  *		client-lease-declaration |
392  *		client-lease-declarations client-lease-declaration
393  */
394 void
395 parse_client_lease_statement(FILE *cfile, int is_static)
396 {
397 	struct client_lease	*lease, *lp, *pl;
398 	int			 token;
399 
400 	token = next_token(NULL, cfile);
401 	if (token != '{') {
402 		parse_warn("expecting left brace.");
403 		skip_to_semi(cfile);
404 		return;
405 	}
406 
407 	lease = malloc(sizeof(struct client_lease));
408 	if (!lease)
409 		error("no memory for lease.");
410 	memset(lease, 0, sizeof(*lease));
411 	lease->is_static = is_static;
412 
413 	do {
414 		token = peek_token(NULL, cfile);
415 		if (token == EOF) {
416 			parse_warn("unterminated lease declaration.");
417 			return;
418 		}
419 		if (token == '}')
420 			break;
421 		parse_client_lease_declaration(cfile, lease);
422 	} while (1);
423 	token = next_token(NULL, cfile);
424 
425 	/* If the lease declaration didn't include an interface
426 	 * declaration that we recognized, it's of no use to us.
427 	 */
428 	if (!ifi) {
429 		free_client_lease(lease);
430 		return;
431 	}
432 
433 	/*
434 	 * The new lease may supersede a lease that's not the active
435 	 * lease but is still on the lease list, so scan the lease list
436 	 * looking for a lease with the same address, and if we find it,
437 	 * toss it.
438 	 */
439 	pl = NULL;
440 	for (lp = client->leases; lp; lp = lp->next) {
441 		if (addr_eq(lp->address, lease->address)) {
442 			if (pl)
443 				pl->next = lp->next;
444 			else
445 				client->leases = lp->next;
446 			free_client_lease(lp);
447 			break;
448 		} else
449 			pl = lp;
450 	}
451 
452 	/*
453 	 * If this is a preloaded lease, just put it on the list of
454 	 * recorded leases - don't make it the active lease.
455 	 */
456 	if (is_static) {
457 		lease->next = client->leases;
458 		client->leases = lease;
459 		return;
460 	}
461 
462 	/*
463 	 * The last lease in the lease file on a particular interface is
464 	 * the active lease for that interface.    Of course, we don't
465 	 * know what the last lease in the file is until we've parsed
466 	 * the whole file, so at this point, we assume that the lease we
467 	 * just parsed is the active lease for its interface.   If
468 	 * there's already an active lease for the interface, and this
469 	 * lease is for the same ip address, then we just toss the old
470 	 * active lease and replace it with this one.   If this lease is
471 	 * for a different address, then if the old active lease has
472 	 * expired, we dump it; if not, we put it on the list of leases
473 	 * for this interface which are still valid but no longer
474 	 * active.
475 	 */
476 	if (client->active) {
477 		if (client->active->expiry < time(NULL))
478 			free_client_lease(client->active);
479 		else if (addr_eq(client->active->address, lease->address))
480 			free_client_lease(client->active);
481 		else {
482 			client->active->next = client->leases;
483 			client->leases = client->active;
484 		}
485 	}
486 	client->active = lease;
487 
488 	/* Phew. */
489 }
490 
491 /*
492  * client-lease-declaration :==
493  *	BOOTP |
494  *	INTERFACE string |
495  *	FIXED_ADDR ip_address |
496  *	FILENAME string |
497  *	SERVER_NAME string |
498  *	OPTION option-decl |
499  *	RENEW time-decl |
500  *	REBIND time-decl |
501  *	EXPIRE time-decl
502  */
503 void
504 parse_client_lease_declaration(FILE *cfile, struct client_lease *lease)
505 {
506 	char *val;
507 	int token;
508 
509 	switch (next_token(&val, cfile)) {
510 	case TOK_BOOTP:
511 		lease->is_bootp = 1;
512 		break;
513 	case TOK_INTERFACE:
514 		token = next_token(&val, cfile);
515 		if (token != TOK_STRING) {
516 			parse_warn("expecting interface name (in quotes).");
517 			skip_to_semi(cfile);
518 			break;
519 		}
520 		if (strcmp(ifi->name, val) != 0) {
521 			parse_warn("wrong interface name. Expecting '%s'.",
522 			   ifi->name);
523 			skip_to_semi(cfile);
524 			break;
525 		}
526 		break;
527 	case TOK_FIXED_ADDR:
528 		if (!parse_ip_addr(cfile, &lease->address))
529 			return;
530 		break;
531 	case TOK_MEDIUM:
532 		skip_to_semi(cfile);
533 		return;
534 	case TOK_FILENAME:
535 		lease->filename = parse_string(cfile);
536 		return;
537 	case TOK_SERVER_NAME:
538 		lease->server_name = parse_string(cfile);
539 		return;
540 	case TOK_RENEW:
541 		lease->renewal = parse_date(cfile);
542 		return;
543 	case TOK_REBIND:
544 		lease->rebind = parse_date(cfile);
545 		return;
546 	case TOK_EXPIRE:
547 		lease->expiry = parse_date(cfile);
548 		return;
549 	case TOK_OPTION:
550 		parse_option_decl(cfile, lease->options);
551 		return;
552 	default:
553 		parse_warn("expecting lease declaration.");
554 		skip_to_semi(cfile);
555 		break;
556 	}
557 	token = next_token(&val, cfile);
558 	if (token != ';') {
559 		parse_warn("expecting semicolon.");
560 		skip_to_semi(cfile);
561 	}
562 }
563 
564 int
565 parse_option_decl(FILE *cfile, struct option_data *options)
566 {
567 	char		*val;
568 	int		 token;
569 	u_int8_t	 buf[4];
570 	u_int8_t	 hunkbuf[1024];
571 	int		 hunkix = 0;
572 	char		*fmt;
573 	struct iaddr	 ip_addr;
574 	u_int8_t	*dp;
575 	int		 len, code;
576 	int		 nul_term = 0;
577 
578 	token = next_token(&val, cfile);
579 	if (!is_identifier(token)) {
580 		parse_warn("expecting identifier after option keyword.");
581 		if (token != ';')
582 			skip_to_semi(cfile);
583 		return (-1);
584 	}
585 
586 	/* Look up the actual option info. */
587 	fmt = NULL;
588 	for (code = 0; code < 256; code++)
589 		if (strcmp(dhcp_options[code].name, val) == 0)
590 			break;
591 
592 	if (code > 255) {
593 		parse_warn("no option named %s", val);
594 		skip_to_semi(cfile);
595 		return (-1);
596 	}
597 
598 	/* Parse the option data... */
599 	do {
600 		for (fmt = dhcp_options[code].format; *fmt; fmt++) {
601 			if (*fmt == 'A')
602 				break;
603 			switch (*fmt) {
604 			case 'X':
605 				len = parse_X(cfile, &hunkbuf[hunkix],
606 				    sizeof(hunkbuf) - hunkix);
607 				hunkix += len;
608 				break;
609 			case 't': /* Text string... */
610 				token = next_token(&val, cfile);
611 				if (token != TOK_STRING) {
612 					parse_warn("expecting string.");
613 					skip_to_semi(cfile);
614 					return (-1);
615 				}
616 				len = strlen(val);
617 				if (hunkix + len + 1 > sizeof(hunkbuf)) {
618 					parse_warn("option data buffer %s",
619 					    "overflow");
620 					skip_to_semi(cfile);
621 					return (-1);
622 				}
623 				memcpy(&hunkbuf[hunkix], val, len + 1);
624 				nul_term = 1;
625 				hunkix += len;
626 				break;
627 			case 'I': /* IP address. */
628 				if (!parse_ip_addr(cfile, &ip_addr))
629 					return (-1);
630 				len = ip_addr.len;
631 				dp = ip_addr.iabuf;
632 alloc:
633 				if (hunkix + len > sizeof(hunkbuf)) {
634 					parse_warn("option data buffer "
635 					    "overflow");
636 					skip_to_semi(cfile);
637 					return (-1);
638 				}
639 				memcpy(&hunkbuf[hunkix], dp, len);
640 				hunkix += len;
641 				break;
642 			case 'L':	/* Unsigned 32-bit integer... */
643 			case 'l':	/* Signed 32-bit integer... */
644 				token = next_token(&val, cfile);
645 				if (token != TOK_NUMBER) {
646 need_number:
647 					parse_warn("expecting number.");
648 					if (token != ';')
649 						skip_to_semi(cfile);
650 					return (-1);
651 				}
652 				convert_num(buf, val, 0, 32);
653 				len = 4;
654 				dp = buf;
655 				goto alloc;
656 			case 's':	/* Signed 16-bit integer. */
657 			case 'S':	/* Unsigned 16-bit integer. */
658 				token = next_token(&val, cfile);
659 				if (token != TOK_NUMBER)
660 					goto need_number;
661 				convert_num(buf, val, 0, 16);
662 				len = 2;
663 				dp = buf;
664 				goto alloc;
665 			case 'b':	/* Signed 8-bit integer. */
666 			case 'B':	/* Unsigned 8-bit integer. */
667 				token = next_token(&val, cfile);
668 				if (token != TOK_NUMBER)
669 					goto need_number;
670 				convert_num(buf, val, 0, 8);
671 				len = 1;
672 				dp = buf;
673 				goto alloc;
674 			case 'f': /* Boolean flag. */
675 				token = next_token(&val, cfile);
676 				if (!is_identifier(token)) {
677 					parse_warn("expecting identifier.");
678 bad_flag:
679 					if (token != ';')
680 						skip_to_semi(cfile);
681 					return (-1);
682 				}
683 				if (!strcasecmp(val, "true") ||
684 				    !strcasecmp(val, "on"))
685 					buf[0] = 1;
686 				else if (!strcasecmp(val, "false") ||
687 				    !strcasecmp(val, "off"))
688 					buf[0] = 0;
689 				else {
690 					parse_warn("expecting boolean.");
691 					goto bad_flag;
692 				}
693 				len = 1;
694 				dp = buf;
695 				goto alloc;
696 			default:
697 				warning("Bad format %c in parse_option_param.",
698 				    *fmt);
699 				skip_to_semi(cfile);
700 				return (-1);
701 			}
702 		}
703 		token = next_token(&val, cfile);
704 	} while (*fmt == 'A' && token == ',');
705 
706 	if (token != ';') {
707 		parse_warn("semicolon expected.");
708 		skip_to_semi(cfile);
709 		return (-1);
710 	}
711 
712 	options[code].data = malloc(hunkix + nul_term);
713 	if (!options[code].data)
714 		error("out of memory allocating option data.");
715 	memcpy(options[code].data, hunkbuf, hunkix + nul_term);
716 	options[code].len = hunkix;
717 	return (code);
718 }
719 
720 void
721 parse_reject_statement(FILE *cfile)
722 {
723 	struct iaddrlist *list;
724 	struct iaddr addr;
725 	int token;
726 
727 	do {
728 		if (!parse_ip_addr(cfile, &addr)) {
729 			parse_warn("expecting IP address.");
730 			skip_to_semi(cfile);
731 			return;
732 		}
733 
734 		list = malloc(sizeof(struct iaddrlist));
735 		if (!list)
736 			error("no memory for reject list!");
737 
738 		list->addr = addr;
739 		list->next = config->reject_list;
740 		config->reject_list = list;
741 
742 		token = next_token(NULL, cfile);
743 	} while (token == ',');
744 
745 	if (token != ';') {
746 		parse_warn("expecting semicolon.");
747 		skip_to_semi(cfile);
748 	}
749 }
750