GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: sbin/dhclient/clparse.c Lines: 0 385 0.0 %
Date: 2016-12-06 Branches: 0 249 0.0 %

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