GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: sbin/dhclient/clparse.c Lines: 0 389 0.0 %
Date: 2017-11-07 Branches: 0 229 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: clparse.c,v 1.129 2017/09/20 19:21:00 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 <sys/queue.h>
44
#include <sys/socket.h>
45
#include <sys/types.h>
46
47
#include <net/if.h>
48
#include <net/if_arp.h>
49
50
#include <netinet/in.h>
51
#include <netinet/if_ether.h>
52
53
#include <signal.h>
54
#include <stdio.h>
55
#include <stdlib.h>
56
#include <string.h>
57
58
#include "dhcp.h"
59
#include "dhcpd.h"
60
#include "dhctoken.h"
61
#include "log.h"
62
63
void parse_client_statement(FILE *, char *);
64
int parse_X(FILE *, uint8_t *, int);
65
int parse_option_list(FILE *, uint8_t *, size_t);
66
void parse_interface_declaration(FILE *, char *);
67
struct client_lease *parse_client_lease_statement(FILE *, char *);
68
void parse_client_lease_declaration(FILE *, struct client_lease *, char *);
69
int parse_option_decl(FILE *, struct option_data *);
70
void parse_reject_statement(FILE *);
71
void add_lease(struct client_lease_tq *, struct client_lease *);
72
73
void
74
add_lease(struct client_lease_tq *tq, struct client_lease *lease)
75
{
76
	struct client_lease	*lp, *nlp;
77
78
	if (lease == NULL)
79
		return;
80
81
	/*
82
	 * The new lease will supersede a lease with the same ssid
83
	 * AND the same Client Identifier AND the same
84
	 * IP address.
85
	 */
86
	TAILQ_FOREACH_SAFE(lp, tq, next, nlp) {
87
		if (lp->ssid_len != lease->ssid_len)
88
			continue;
89
		if (memcmp(lp->ssid, lease->ssid, lp->ssid_len) != 0)
90
			continue;
91
		if ((lease->options[DHO_DHCP_CLIENT_IDENTIFIER].len != 0) &&
92
		    ((lp->options[DHO_DHCP_CLIENT_IDENTIFIER].len !=
93
		    lease->options[DHO_DHCP_CLIENT_IDENTIFIER].len) ||
94
		    memcmp(lp->options[DHO_DHCP_CLIENT_IDENTIFIER].data,
95
		    lease->options[DHO_DHCP_CLIENT_IDENTIFIER].data,
96
		    lp->options[DHO_DHCP_CLIENT_IDENTIFIER].len)))
97
			continue;
98
		if (lp->address.s_addr != lease->address.s_addr)
99
			continue;
100
101
		TAILQ_REMOVE(tq, lp, next);
102
		free_client_lease(lp);
103
	}
104
105
	TAILQ_INSERT_TAIL(tq, lease, next);
106
}
107
108
/*
109
 * client-conf-file :== client-declarations EOF
110
 * client-declarations :== <nil>
111
 *			 | client-declaration
112
 *			 | client-declarations client-declaration
113
 */
114
void
115
read_client_conf(char *name)
116
{
117
	FILE *cfile;
118
	int token;
119
120
	new_parse(path_dhclient_conf);
121
122
	TAILQ_INIT(&config->static_leases);
123
	TAILQ_INIT(&config->reject_list);
124
125
	/* Set some defaults. */
126
	config->link_timeout = 10;	/* secs before going daemon w/o link */
127
	config->timeout = 30;		/* secs to wait for an OFFER */
128
	config->select_interval = 0;	/* secs to wait for other OFFERs */
129
	config->reboot_timeout = 1;	/* secs before giving up on reboot */
130
	config->retry_interval = 1;	/* secs before asking for OFFER */
131
	config->backoff_cutoff = 10;	/* max secs between packet retries */
132
	config->initial_interval = 1;	/* secs before 1st retry */
133
134
	config->requested_options
135
	    [config->requested_option_count++] = DHO_SUBNET_MASK;
136
	config->requested_options
137
	    [config->requested_option_count++] = DHO_BROADCAST_ADDRESS;
138
	config->requested_options
139
	    [config->requested_option_count++] = DHO_TIME_OFFSET;
140
	/* RFC 3442 says CLASSLESS_STATIC_ROUTES must be before ROUTERS! */
141
	config->requested_options
142
	    [config->requested_option_count++] = DHO_CLASSLESS_STATIC_ROUTES;
143
	config->requested_options
144
	    [config->requested_option_count++] = DHO_ROUTERS;
145
	config->requested_options
146
	    [config->requested_option_count++] = DHO_DOMAIN_NAME;
147
	config->requested_options
148
	    [config->requested_option_count++] = DHO_DOMAIN_SEARCH;
149
	config->requested_options
150
	    [config->requested_option_count++] = DHO_DOMAIN_NAME_SERVERS;
151
	config->requested_options
152
	    [config->requested_option_count++] = DHO_HOST_NAME;
153
	config->requested_options
154
	    [config->requested_option_count++] = DHO_BOOTFILE_NAME;
155
	config->requested_options
156
	    [config->requested_option_count++] = DHO_TFTP_SERVER;
157
158
	if ((cfile = fopen(path_dhclient_conf, "r")) != NULL) {
159
		do {
160
			token = peek_token(NULL, cfile);
161
			if (token == EOF)
162
				break;
163
			parse_client_statement(cfile, name);
164
		} while (1);
165
		fclose(cfile);
166
	}
167
}
168
169
/*
170
 * lease-file :== client-lease-statements EOF
171
 * client-lease-statements :== <nil>
172
 *		     | client-lease-statements LEASE client-lease-statement
173
 */
174
void
175
read_client_leases(char *name, struct client_lease_tq *tq)
176
{
177
	FILE	*cfile;
178
	int	 token;
179
180
	TAILQ_INIT(tq);
181
182
	if ((cfile = fopen(path_dhclient_db, "r")) == NULL)
183
		return;
184
185
	new_parse(path_dhclient_db);
186
187
	do {
188
		token = next_token(NULL, cfile);
189
		if (token == EOF)
190
			break;
191
		if (token != TOK_LEASE) {
192
			log_warnx("%s: expecting lease", log_procname);
193
			break;
194
		}
195
		add_lease(tq, parse_client_lease_statement(cfile, name));
196
	} while (1);
197
	fclose(cfile);
198
}
199
200
/*
201
 * client-declaration :==
202
 *	TOK_SEND option-decl |
203
 *	TOK_DEFAULT option-decl |
204
 *	TOK_SUPERSEDE option-decl |
205
 *	TOK_APPEND option-decl |
206
 *	TOK_PREPEND option-decl |
207
 *	TOK_REQUEST option-list |
208
 *	TOK_REQUIRE option-list |
209
 *	TOK_IGNORE option-list |
210
 *	TOK_TIMEOUT number |
211
 *	TOK_RETRY number |
212
 *	TOK_SELECT_TIMEOUT number |
213
 *	TOK_REBOOT number |
214
 *	TOK_BACKOFF_CUTOFF number |
215
 *	TOK_INITIAL_INTERVAL number |
216
 *	interface-declaration |
217
 *	TOK_LEASE client-lease-statement |
218
 *	TOK_REJECT reject-statement
219
 */
220
void
221
parse_client_statement(FILE *cfile, char *name)
222
{
223
	uint8_t		 optlist[DHO_COUNT];
224
	char		*string;
225
	int		 code, count, token;
226
227
	token = next_token(NULL, cfile);
228
229
	switch (token) {
230
	case TOK_SEND:
231
		parse_option_decl(cfile, &config->send_options[0]);
232
		break;
233
	case TOK_DEFAULT:
234
		code = parse_option_decl(cfile, &config->defaults[0]);
235
		if (code != -1)
236
			config->default_actions[code] = ACTION_DEFAULT;
237
		break;
238
	case TOK_SUPERSEDE:
239
		code = parse_option_decl(cfile, &config->defaults[0]);
240
		if (code != -1)
241
			config->default_actions[code] = ACTION_SUPERSEDE;
242
		break;
243
	case TOK_APPEND:
244
		code = parse_option_decl(cfile, &config->defaults[0]);
245
		if (code != -1)
246
			config->default_actions[code] = ACTION_APPEND;
247
		break;
248
	case TOK_PREPEND:
249
		code = parse_option_decl(cfile, &config->defaults[0]);
250
		if (code != -1)
251
			config->default_actions[code] = ACTION_PREPEND;
252
		break;
253
	case TOK_REQUEST:
254
		count = parse_option_list(cfile, optlist, sizeof(optlist));
255
		if (count != -1) {
256
			config->requested_option_count = count;
257
			memcpy(config->requested_options, optlist,
258
			    sizeof(config->requested_options));
259
		}
260
		break;
261
	case TOK_REQUIRE:
262
		count = parse_option_list(cfile, optlist, sizeof(optlist));
263
		if (count != -1) {
264
			config->required_option_count = count;
265
			memcpy(config->required_options, optlist,
266
			    sizeof(config->required_options));
267
		}
268
		break;
269
	case TOK_IGNORE:
270
		count = parse_option_list(cfile, optlist, sizeof(optlist));
271
		if (count != -1) {
272
			config->ignored_option_count = count;
273
			memcpy(config->ignored_options, optlist,
274
			    sizeof(config->ignored_options));
275
		}
276
		break;
277
	case TOK_LINK_TIMEOUT:
278
		parse_lease_time(cfile, &config->link_timeout);
279
		break;
280
	case TOK_TIMEOUT:
281
		parse_lease_time(cfile, &config->timeout);
282
		break;
283
	case TOK_RETRY:
284
		parse_lease_time(cfile, &config->retry_interval);
285
		break;
286
	case TOK_SELECT_TIMEOUT:
287
		parse_lease_time(cfile, &config->select_interval);
288
		break;
289
	case TOK_REBOOT:
290
		parse_lease_time(cfile, &config->reboot_timeout);
291
		break;
292
	case TOK_BACKOFF_CUTOFF:
293
		parse_lease_time(cfile, &config->backoff_cutoff);
294
		break;
295
	case TOK_INITIAL_INTERVAL:
296
		parse_lease_time(cfile, &config->initial_interval);
297
		break;
298
	case TOK_INTERFACE:
299
		parse_interface_declaration(cfile, name);
300
		break;
301
	case TOK_LEASE:
302
		add_lease(&config->static_leases,
303
		    parse_client_lease_statement(cfile, name));
304
		break;
305
	case TOK_REJECT:
306
		parse_reject_statement(cfile);
307
		break;
308
	case TOK_FILENAME:
309
		string = parse_string(cfile, NULL);
310
		free(config->filename);
311
		config->filename = string;
312
		parse_semi(cfile);
313
		break;
314
	case TOK_SERVER_NAME:
315
		string = parse_string(cfile, NULL);
316
		free(config->server_name);
317
		config->server_name = string;
318
		parse_semi(cfile);
319
		break;
320
	case TOK_FIXED_ADDR:
321
		if (parse_ip_addr(cfile, &config->address) != 0)
322
			parse_semi(cfile);
323
		break;
324
	case TOK_NEXT_SERVER:
325
		if (parse_ip_addr(cfile, &config->next_server) != 0)
326
			parse_semi(cfile);
327
		break;
328
	default:
329
		parse_warn("expecting statement.");
330
		if (token != ';')
331
			skip_to_semi(cfile);
332
		break;
333
	}
334
}
335
336
int
337
parse_X(FILE *cfile, uint8_t *buf, int max)
338
{
339
	int	 token;
340
	char	*val;
341
	int	 len;
342
343
	token = peek_token(&val, cfile);
344
	if (token == TOK_NUMBER_OR_NAME) {
345
		len = 0;
346
		for (token = ':'; token == ':';
347
		     token = next_token(NULL, cfile)) {
348
			if (parse_hex(cfile, &buf[len]) == 0)
349
				break;
350
			if (++len == max)
351
				break;
352
			if (peek_token(NULL, cfile) == ';')
353
				return len;
354
		}
355
		if (token != ':') {
356
			parse_warn("expecting ':'.");
357
			skip_to_semi(cfile);
358
			return -1;
359
		} else {
360
			parse_warn("expecting hex value.");
361
			skip_to_semi(cfile);
362
			return -1;
363
		}
364
	} else if (token == TOK_STRING) {
365
		token = next_token(&val, cfile);
366
		len = strlen(val);
367
		if (len + 1 > max) {
368
			parse_warn("string constant too long.");
369
			skip_to_semi(cfile);
370
			return -1;
371
		}
372
		memcpy(buf, val, len + 1);
373
	} else {
374
		token = next_token(NULL, cfile);
375
		parse_warn("expecting string or hex data.");
376
		if (token != ';')
377
			skip_to_semi(cfile);
378
		return -1;
379
	}
380
	return len;
381
}
382
383
/*
384
 * option-list :== option_name |
385
 *		   option_list COMMA option_name
386
 */
387
int
388
parse_option_list(FILE *cfile, uint8_t *list, size_t sz)
389
{
390
	unsigned int	 ix, j;
391
	int		 i;
392
	int		 token;
393
	char		*val;
394
395
	memset(list, DHO_PAD, sz);
396
	ix = 0;
397
	do {
398
		token = next_token(&val, cfile);
399
		if (token == ';' && ix == 0) {
400
			/* Empty list. */
401
			return 0;
402
		}
403
		if (is_identifier(token) == 0) {
404
			parse_warn("expecting option name.");
405
			goto syntaxerror;
406
		}
407
		/*
408
		 * 0 (DHO_PAD) and 255 (DHO_END) are not valid in option
409
		 * lists.  They are not really options and it makes no sense
410
		 * to request, require or ignore them.
411
		 */
412
413
		i = name_to_code(val);
414
		if (i == DHO_END) {
415
			parse_warn("expecting option name.");
416
			goto syntaxerror;
417
		}
418
		if (ix == sz) {
419
			parse_warn("too many options.");
420
			goto syntaxerror;
421
		}
422
		/* Avoid storing duplicate options in the list. */
423
		for (j = 0; j < ix && list[j] != i; j++)
424
			;
425
		if (j == ix)
426
			list[ix++] = i;
427
		token = peek_token(NULL, cfile);
428
		if (token == ',')
429
			token = next_token(NULL, cfile);
430
	} while (token == ',');
431
432
	if (parse_semi(cfile) != 0)
433
		return ix;
434
435
syntaxerror:
436
	if (token != ';')
437
		skip_to_semi(cfile);
438
	return -1;
439
}
440
441
/*
442
 * interface-declaration :==
443
 *	INTERFACE string LBRACE client-declarations RBRACE
444
 */
445
void
446
parse_interface_declaration(FILE *cfile, char *name)
447
{
448
	char	*val;
449
	int	 token;
450
451
	token = next_token(&val, cfile);
452
	if (token != TOK_STRING) {
453
		parse_warn("expecting string.");
454
		if (token != ';')
455
			skip_to_semi(cfile);
456
		return;
457
	}
458
459
	if (strcmp(name, val) != 0) {
460
		skip_to_semi(cfile);
461
		return;
462
	}
463
464
	token = next_token(&val, cfile);
465
	if (token != '{') {
466
		parse_warn("expecting '{'.");
467
		if (token != ';')
468
			skip_to_semi(cfile);
469
		return;
470
	}
471
472
	do {
473
		token = peek_token(&val, cfile);
474
		if (token == EOF) {
475
			parse_warn("unterminated interface declaration.");
476
			return;
477
		}
478
		if (token == '}')
479
			break;
480
		parse_client_statement(cfile, name);
481
	} while (1);
482
	token = next_token(&val, cfile);
483
}
484
485
/*
486
 * client-lease-statement :==
487
 *	RBRACE client-lease-declarations LBRACE
488
 *
489
 *	client-lease-declarations :==
490
 *		<nil> |
491
 *		client-lease-declaration |
492
 *		client-lease-declarations client-lease-declaration
493
 */
494
struct client_lease *
495
parse_client_lease_statement(FILE *cfile, char *name)
496
{
497
	struct client_lease	*lease;
498
	int			 token;
499
500
	token = next_token(NULL, cfile);
501
	if (token != '{') {
502
		parse_warn("expecting '{'.");
503
		if (token != ';')
504
			skip_to_semi(cfile);
505
		return NULL;
506
	}
507
508
	lease = calloc(1, sizeof(*lease));
509
	if (lease == NULL)
510
		fatal("lease");
511
512
	do {
513
		token = peek_token(NULL, cfile);
514
		if (token == EOF) {
515
			parse_warn("unterminated lease declaration.");
516
			free_client_lease(lease);
517
			return NULL;
518
		}
519
		if (token == '}')
520
			break;
521
		parse_client_lease_declaration(cfile, lease, name);
522
	} while (1);
523
	token = next_token(NULL, cfile);
524
525
	return lease;
526
}
527
528
/*
529
 * client-lease-declaration :==
530
 *	BOOTP |
531
 *	INTERFACE string |
532
 *	FIXED_ADDR ip_address |
533
 *	FILENAME string |
534
 *	SERVER_NAME string |
535
 *	OPTION option-decl |
536
 *	RENEW time-decl |
537
 *	REBIND time-decl |
538
 *	EXPIRE time-decl
539
 */
540
void
541
parse_client_lease_declaration(FILE *cfile, struct client_lease *lease,
542
    char *name)
543
{
544
	char		*val;
545
	unsigned int	 len;
546
	int		 token;
547
548
	token = next_token(&val, cfile);
549
550
	switch (token) {
551
	case TOK_BOOTP:
552
		/* 'bootp' is just a comment. See BOOTP_LEASE(). */
553
		break;
554
	case TOK_INTERFACE:
555
		token = next_token(&val, cfile);
556
		if (token != TOK_STRING) {
557
			parse_warn("expecting string.");
558
			if (token != ';')
559
				skip_to_semi(cfile);
560
			return;
561
		}
562
		if (strcmp(name, val) != 0) {
563
			if (lease->is_static == 0)
564
				parse_warn("wrong interface name.");
565
			skip_to_semi(cfile);
566
			return;
567
		}
568
		break;
569
	case TOK_FIXED_ADDR:
570
		if (parse_ip_addr(cfile, &lease->address) == 0)
571
			return;
572
		break;
573
	case TOK_NEXT_SERVER:
574
		if (parse_ip_addr(cfile, &lease->next_server) == 0)
575
			return;
576
		break;
577
	case TOK_FILENAME:
578
		lease->filename = parse_string(cfile, NULL);
579
		break;
580
	case TOK_SERVER_NAME:
581
		lease->server_name = parse_string(cfile, NULL);
582
		break;
583
	case TOK_SSID:
584
		val = parse_string(cfile, &len);
585
		if (val && len <= sizeof(lease->ssid)) {
586
			memset(lease->ssid, 0, sizeof(lease->ssid));
587
			memcpy(lease->ssid, val, len);
588
			lease->ssid_len = len;
589
		}
590
		free(val);
591
		break;
592
	case TOK_RENEW:
593
		lease->renewal = parse_date(cfile);
594
		return;
595
	case TOK_REBIND:
596
		lease->rebind = parse_date(cfile);
597
		return;
598
	case TOK_EXPIRE:
599
		lease->expiry = parse_date(cfile);
600
		return;
601
	case TOK_OPTION:
602
		parse_option_decl(cfile, lease->options);
603
		return;
604
	default:
605
		parse_warn("expecting lease declaration.");
606
		if (token != ';')
607
			skip_to_semi(cfile);
608
		return;
609
	}
610
611
	parse_semi(cfile);
612
}
613
614
int
615
parse_option_decl(FILE *cfile, struct option_data *options)
616
{
617
	char		*val;
618
	int		 token;
619
	uint8_t		 buf[4];
620
	uint8_t		 cidr[5];
621
	uint8_t		 hunkbuf[1024];
622
	unsigned int	 hunkix = 0;
623
	char		*fmt;
624
	struct in_addr	 ip_addr;
625
	uint8_t		*dp;
626
	int		 len, code;
627
	int		 nul_term = 0;
628
629
	token = next_token(&val, cfile);
630
	if (is_identifier(token) == 0) {
631
		parse_warn("expecting identifier.");
632
		if (token != ';')
633
			skip_to_semi(cfile);
634
		return -1;
635
	}
636
637
	/* Look up the actual option info. */
638
	code = name_to_code(val);
639
	if (code == DHO_END) {
640
		parse_warn("unknown option name.");
641
		skip_to_semi(cfile);
642
		return -1;
643
	}
644
645
	/* Parse the option data. */
646
	do {
647
		for (fmt = code_to_format(code); *fmt; fmt++) {
648
			if (*fmt == 'A')
649
				break;
650
			switch (*fmt) {
651
			case 'X':
652
				len = parse_X(cfile, &hunkbuf[hunkix],
653
				    sizeof(hunkbuf) - hunkix);
654
				if (len == -1)
655
					return -1;
656
				hunkix += len;
657
				dp = NULL;
658
				break;
659
			case 't': /* Text string. */
660
				val = parse_string(cfile, &len);
661
				if (val == NULL)
662
					return -1;
663
				if (hunkix + len + 1 > sizeof(hunkbuf)) {
664
					parse_warn("option data buffer "
665
					    "overflow");
666
					skip_to_semi(cfile);
667
					return -1;
668
				}
669
				memcpy(&hunkbuf[hunkix], val, len + 1);
670
				nul_term = 1;
671
				hunkix += len;
672
				free(val);
673
				dp = NULL;
674
				break;
675
			case 'I': /* IP address. */
676
				if (parse_ip_addr(cfile, &ip_addr) == 0)
677
					return -1;
678
				len = sizeof(ip_addr);
679
				dp = (uint8_t *)&ip_addr;
680
				break;
681
			case 'l':	/* Signed 32-bit integer. */
682
				if (parse_decimal(cfile, buf, 'l') == 0) {
683
					parse_warn("expecting signed 32-bit "
684
					    "integer.");
685
					skip_to_semi(cfile);
686
					return -1;
687
				}
688
				len = 4;
689
				dp = buf;
690
				break;
691
			case 'L':	/* Unsigned 32-bit integer. */
692
				if (parse_decimal(cfile, buf, 'L') == 0) {
693
					parse_warn("expecting unsigned 32-bit "
694
					    "integer.");
695
					skip_to_semi(cfile);
696
					return -1;
697
				}
698
				len = 4;
699
				dp = buf;
700
				break;
701
			case 'S':	/* Unsigned 16-bit integer. */
702
				if (parse_decimal(cfile, buf, 'S') == 0) {
703
					parse_warn("expecting unsigned 16-bit "
704
					    "integer.");
705
					skip_to_semi(cfile);
706
					return -1;
707
				}
708
				len = 2;
709
				dp = buf;
710
				break;
711
			case 'B':	/* Unsigned 8-bit integer. */
712
				if (parse_decimal(cfile, buf, 'B') == 0) {
713
					parse_warn("expecting unsigned 8-bit "
714
					    "integer.");
715
					skip_to_semi(cfile);
716
					return -1;
717
				}
718
				len = 1;
719
				dp = buf;
720
				break;
721
			case 'f': /* Boolean flag. */
722
				if (parse_boolean(cfile, buf) == 0) {
723
					parse_warn("expecting boolean.");
724
					skip_to_semi(cfile);
725
					return -1;
726
				}
727
				len = 1;
728
				dp = buf;
729
				break;
730
			case 'C':
731
				if (parse_cidr(cfile, cidr) == 0)
732
					return -1;
733
				len = 1 + (cidr[0] + 7) / 8;
734
				dp = cidr;
735
				break;
736
			default:
737
				log_warnx("%s: bad format %c in "
738
				    "parse_option_param", log_procname, *fmt);
739
				skip_to_semi(cfile);
740
				return -1;
741
			}
742
			if (dp != NULL && len > 0) {
743
				if (hunkix + len > sizeof(hunkbuf)) {
744
					parse_warn("option data buffer "
745
					    "overflow");
746
					skip_to_semi(cfile);
747
					return -1;
748
				}
749
				memcpy(&hunkbuf[hunkix], dp, len);
750
				hunkix += len;
751
			}
752
		}
753
		token = peek_token(NULL, cfile);
754
		if (*fmt == 'A' && token == ',')
755
			token = next_token(NULL, cfile);
756
	} while (*fmt == 'A' && token == ',');
757
758
	if (parse_semi(cfile) == 0)
759
		return -1;
760
761
	options[code].data = malloc(hunkix + nul_term);
762
	if (options[code].data == NULL)
763
		fatal("option data");
764
	memcpy(options[code].data, hunkbuf, hunkix + nul_term);
765
	options[code].len = hunkix;
766
	return code;
767
}
768
769
void
770
parse_reject_statement(FILE *cfile)
771
{
772
	struct in_addr		 addr;
773
	struct reject_elem	*elem;
774
	int			 token;
775
776
	do {
777
		if (parse_ip_addr(cfile, &addr) == 0)
778
			return;
779
780
		elem = malloc(sizeof(*elem));
781
		if (elem == NULL)
782
			fatal("reject address");
783
784
		elem->addr = addr;
785
		TAILQ_INSERT_TAIL(&config->reject_list, elem, next);
786
787
		token = peek_token(NULL, cfile);
788
		if (token == ',')
789
			token = next_token(NULL, cfile);
790
	} while (token == ',');
791
792
	parse_semi(cfile);
793
}