GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/dhcpd/confpars.c Lines: 0 725 0.0 %
Date: 2017-11-13 Branches: 0 363 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: confpars.c,v 1.33 2017/04/24 14:58:36 krw Exp $ */
2
3
/*
4
 * Copyright (c) 1995, 1996, 1997 The Internet Software Consortium.
5
 * All rights reserved.
6
 *
7
 * Redistribution and use in source and binary forms, with or without
8
 * modification, are permitted provided that the following conditions
9
 * are met:
10
 *
11
 * 1. Redistributions of source code must retain the above copyright
12
 *    notice, this list of conditions and the following disclaimer.
13
 * 2. Redistributions in binary form must reproduce the above copyright
14
 *    notice, this list of conditions and the following disclaimer in the
15
 *    documentation and/or other materials provided with the distribution.
16
 * 3. Neither the name of The Internet Software Consortium nor the names
17
 *    of its contributors may be used to endorse or promote products derived
18
 *    from this software without specific prior written permission.
19
 *
20
 * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
21
 * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
22
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
23
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24
 * DISCLAIMED.  IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
25
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
28
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
29
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32
 * SUCH DAMAGE.
33
 *
34
 * This software has been written for the Internet Software Consortium
35
 * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
36
 * Enterprises.  To learn more about the Internet Software Consortium,
37
 * see ``http://www.vix.com/isc''.  To learn more about Vixie
38
 * Enterprises, see ``http://www.vix.com''.
39
 */
40
41
#include <sys/types.h>
42
#include <sys/socket.h>
43
44
#include <net/if.h>
45
46
#include <netdb.h>
47
#include <stdio.h>
48
#include <stdlib.h>
49
#include <string.h>
50
51
#include "dhcp.h"
52
#include "tree.h"
53
#include "dhcpd.h"
54
#include "dhctoken.h"
55
#include "log.h"
56
57
int	parse_cidr(FILE *, unsigned char *, unsigned char *);
58
59
/*
60
 * conf-file :== parameters declarations EOF
61
 * parameters :== <nil> | parameter | parameters parameter
62
 * declarations :== <nil> | declaration | declarations declaration
63
 */
64
int
65
readconf(void)
66
{
67
	FILE *cfile;
68
	char *val;
69
	int token;
70
	int declaration = 0;
71
72
	new_parse(path_dhcpd_conf);
73
74
	/* Set up the initial dhcp option universe. */
75
	initialize_universes();
76
77
	/* Set up the global defaults. */
78
	root_group.default_lease_time = 43200; /* 12 hours. */
79
	root_group.max_lease_time = 86400; /* 24 hours. */
80
	root_group.bootp_lease_cutoff = MAX_TIME;
81
	root_group.boot_unknown_clients = 1;
82
	root_group.allow_bootp = 1;
83
	root_group.allow_booting = 1;
84
	root_group.authoritative = 1;
85
	root_group.echo_client_id = 1;
86
87
	if ((cfile = fopen(path_dhcpd_conf, "r")) == NULL)
88
		fatal("Can't open %s", path_dhcpd_conf);
89
90
	do {
91
		token = peek_token(&val, cfile);
92
		if (token == EOF)
93
			break;
94
		declaration = parse_statement(cfile, &root_group,
95
						 ROOT_GROUP,
96
						 NULL,
97
						 declaration);
98
	} while (1);
99
	token = next_token(&val, cfile); /* Clear the peek buffer */
100
	fclose(cfile);
101
102
	return !warnings_occurred;
103
}
104
105
/*
106
 * lease-file :== lease-declarations EOF
107
 * lease-statments :== <nil>
108
 *		   | lease-declaration
109
 *		   | lease-declarations lease-declaration
110
 */
111
void
112
read_leases(void)
113
{
114
	FILE *cfile;
115
	char *val;
116
	int token;
117
118
	new_parse(path_dhcpd_db);
119
120
	/*
121
	 * Open the lease file.   If we can't open it, fail.   The reason
122
	 * for this is that although on initial startup, the absence of
123
	 * a lease file is perfectly benign, if dhcpd has been running
124
	 * and this file is absent, it means that dhcpd tried and failed
125
	 * to rewrite the lease database.   If we proceed and the
126
	 * problem which caused the rewrite to fail has been fixed, but no
127
	 * human has corrected the database problem, then we are left
128
	 * thinking that no leases have been assigned to anybody, which
129
	 * could create severe network chaos.
130
	 */
131
	if ((cfile = fopen(path_dhcpd_db, "r")) == NULL) {
132
		log_warn("Can't open lease database (%s)", path_dhcpd_db);
133
		log_warnx("check for failed database rewrite attempt!");
134
		log_warnx("Please read the dhcpd.leases manual page if you");
135
		fatalx("don't know what to do about this.");
136
	}
137
138
	do {
139
		token = next_token(&val, cfile);
140
		if (token == EOF)
141
			break;
142
		if (token != TOK_LEASE) {
143
			log_warnx("Corrupt lease file - possible data loss!");
144
			skip_to_semi(cfile);
145
		} else {
146
			struct lease *lease;
147
			lease = parse_lease_declaration(cfile);
148
			if (lease)
149
				enter_lease(lease);
150
			else
151
				parse_warn("possibly corrupt lease file");
152
		}
153
154
	} while (1);
155
	fclose(cfile);
156
}
157
158
/*
159
 * statement :== parameter | declaration
160
 *
161
 * parameter :== timestamp
162
 *	     | DEFAULT_LEASE_TIME lease_time
163
 *	     | MAX_LEASE_TIME lease_time
164
 *	     | DYNAMIC_BOOTP_LEASE_CUTOFF date
165
 *	     | DYNAMIC_BOOTP_LEASE_LENGTH lease_time
166
 *	     | BOOT_UNKNOWN_CLIENTS boolean
167
 *	     | GET_LEASE_HOSTNAMES boolean
168
 *	     | USE_HOST_DECL_NAME boolean
169
 *	     | NEXT_SERVER ip-addr-or-hostname SEMI
170
 *	     | option_parameter
171
 *	     | SERVER-IDENTIFIER ip-addr-or-hostname SEMI
172
 *	     | FILENAME string-parameter
173
 *	     | SERVER_NAME string-parameter
174
 *	     | hardware-parameter
175
 *	     | fixed-address-parameter
176
 *	     | ALLOW allow-deny-keyword
177
 *	     | DENY allow-deny-keyword
178
 *	     | USE_LEASE_ADDR_FOR_DEFAULT_ROUTE boolean
179
 *
180
 * declaration :== host-declaration
181
 *		 | group-declaration
182
 *		 | shared-network-declaration
183
 *		 | subnet-declaration
184
 *		 | VENDOR_CLASS class-declaration
185
 *		 | USER_CLASS class-declaration
186
 *		 | RANGE address-range-declaration
187
 */
188
int
189
parse_statement(FILE *cfile, struct group *group, int type,
190
	struct host_decl *host_decl, int declaration)
191
{
192
	int token;
193
	char *val;
194
	struct shared_network *share;
195
	char *n;
196
	struct tree *tree;
197
	struct tree_cache *cache;
198
	struct hardware hardware;
199
200
	switch (next_token(&val, cfile)) {
201
	case TOK_HOST:
202
		if (type != HOST_DECL)
203
			parse_host_declaration(cfile, group);
204
		else {
205
			parse_warn("host declarations not allowed here.");
206
			skip_to_semi(cfile);
207
		}
208
		return 1;
209
210
	case TOK_GROUP:
211
		if (type != HOST_DECL)
212
			parse_group_declaration(cfile, group);
213
		else {
214
			parse_warn("host declarations not allowed here.");
215
			skip_to_semi(cfile);
216
		}
217
		return 1;
218
219
	case TOK_TIMESTAMP:
220
		break;
221
222
	case TOK_SHARED_NETWORK:
223
		if (type == SHARED_NET_DECL ||
224
		    type == HOST_DECL ||
225
		    type == SUBNET_DECL) {
226
			parse_warn("shared-network parameters not %s.",
227
				    "allowed here");
228
			skip_to_semi(cfile);
229
			break;
230
		}
231
232
		parse_shared_net_declaration(cfile, group);
233
		return 1;
234
235
	case TOK_SUBNET:
236
		if (type == HOST_DECL || type == SUBNET_DECL) {
237
			parse_warn("subnet declarations not allowed here.");
238
			skip_to_semi(cfile);
239
			return 1;
240
		}
241
242
		/* If we're in a subnet declaration, just do the parse. */
243
		if (group->shared_network) {
244
			parse_subnet_declaration(cfile,
245
			    group->shared_network);
246
			break;
247
		}
248
249
		/*
250
		 * Otherwise, cons up a fake shared network structure
251
		 * and populate it with the lone subnet.
252
		 */
253
254
		share = calloc(1, sizeof(struct shared_network));
255
		if (!share)
256
			fatalx("No memory for shared subnet");
257
		share->group = clone_group(group, "parse_statement:subnet");
258
		share->group->shared_network = share;
259
260
		parse_subnet_declaration(cfile, share);
261
262
		/* share->subnets is the subnet we just parsed. */
263
		if (share->subnets) {
264
			share->interface =
265
				share->subnets->interface;
266
267
			/* Make the shared network name from network number. */
268
			n = piaddr(share->subnets->net);
269
			share->name = strdup(n);
270
			if (share->name == NULL)
271
				fatalx("no memory for subnet name");
272
273
			/*
274
			 * Copy the authoritative parameter from the subnet,
275
			 * since there is no opportunity to declare it here.
276
			 */
277
			share->group->authoritative =
278
				share->subnets->group->authoritative;
279
			enter_shared_network(share);
280
		}
281
		return 1;
282
283
	case TOK_VENDOR_CLASS:
284
		parse_class_declaration(cfile, group, 0);
285
		return 1;
286
287
	case TOK_USER_CLASS:
288
		parse_class_declaration(cfile, group, 1);
289
		return 1;
290
291
	case TOK_DEFAULT_LEASE_TIME:
292
		parse_lease_time(cfile, &group->default_lease_time);
293
		break;
294
295
	case TOK_MAX_LEASE_TIME:
296
		parse_lease_time(cfile, &group->max_lease_time);
297
		break;
298
299
	case TOK_DYNAMIC_BOOTP_LEASE_CUTOFF:
300
		group->bootp_lease_cutoff = parse_date(cfile);
301
		break;
302
303
	case TOK_DYNAMIC_BOOTP_LEASE_LENGTH:
304
		parse_lease_time(cfile, &group->bootp_lease_length);
305
		break;
306
307
	case TOK_BOOT_UNKNOWN_CLIENTS:
308
		if (type == HOST_DECL)
309
			parse_warn("boot-unknown-clients not allowed here.");
310
		group->boot_unknown_clients = parse_boolean(cfile);
311
		break;
312
313
	case TOK_GET_LEASE_HOSTNAMES:
314
		if (type == HOST_DECL)
315
			parse_warn("get-lease-hostnames not allowed here.");
316
		group->get_lease_hostnames = parse_boolean(cfile);
317
		break;
318
319
	case TOK_ALWAYS_REPLY_RFC1048:
320
		group->always_reply_rfc1048 = parse_boolean(cfile);
321
		break;
322
323
	case TOK_ECHO_CLIENT_ID:
324
		group->echo_client_id = parse_boolean(cfile);
325
		break;
326
327
	case TOK_USE_HOST_DECL_NAMES:
328
		if (type == HOST_DECL)
329
			parse_warn("use-host-decl-names not allowed here.");
330
		group->use_host_decl_names = parse_boolean(cfile);
331
		break;
332
333
	case TOK_USE_LEASE_ADDR_FOR_DEFAULT_ROUTE:
334
		group->use_lease_addr_for_default_route =
335
			parse_boolean(cfile);
336
		break;
337
338
	case TOK_TOKEN_NOT:
339
		token = next_token(&val, cfile);
340
		switch (token) {
341
		case TOK_AUTHORITATIVE:
342
			if (type == HOST_DECL)
343
			    parse_warn("authority makes no sense here.");
344
			group->authoritative = 0;
345
			parse_semi(cfile);
346
			break;
347
		default:
348
			parse_warn("expecting assertion");
349
			skip_to_semi(cfile);
350
			break;
351
		}
352
		break;
353
354
	case TOK_AUTHORITATIVE:
355
		if (type == HOST_DECL)
356
		    parse_warn("authority makes no sense here.");
357
		group->authoritative = 1;
358
		parse_semi(cfile);
359
		break;
360
361
	case TOK_NEXT_SERVER:
362
		tree = parse_ip_addr_or_hostname(cfile, 0);
363
		if (!tree)
364
			break;
365
		cache = tree_cache(tree);
366
		if (!tree_evaluate (cache))
367
			fatalx("next-server is not known");
368
		group->next_server.len = 4;
369
		memcpy(group->next_server.iabuf,
370
		    cache->value, group->next_server.len);
371
		parse_semi(cfile);
372
		break;
373
374
	case TOK_OPTION:
375
		parse_option_param(cfile, group);
376
		break;
377
378
	case TOK_SERVER_IDENTIFIER:
379
		tree = parse_ip_addr_or_hostname(cfile, 0);
380
		if (!tree)
381
			return declaration;
382
		group->options[DHO_DHCP_SERVER_IDENTIFIER] = tree_cache(tree);
383
		token = next_token(&val, cfile);
384
		break;
385
386
	case TOK_FILENAME:
387
		group->filename = parse_string(cfile);
388
		break;
389
390
	case TOK_SERVER_NAME:
391
		group->server_name = parse_string(cfile);
392
		break;
393
394
	case TOK_HARDWARE:
395
		parse_hardware_param(cfile, &hardware);
396
		if (host_decl)
397
			host_decl->interface = hardware;
398
		else
399
			parse_warn("hardware address parameter %s",
400
				    "not allowed here.");
401
		break;
402
403
	case TOK_FIXED_ADDR:
404
		cache = parse_fixed_addr_param(cfile);
405
		if (host_decl)
406
			host_decl->fixed_addr = cache;
407
		else
408
			parse_warn("fixed-address parameter not %s",
409
				    "allowed here.");
410
		break;
411
412
	case TOK_RANGE:
413
		if (type != SUBNET_DECL || !group->subnet) {
414
			parse_warn("range declaration not allowed here.");
415
			skip_to_semi(cfile);
416
			return declaration;
417
		}
418
		parse_address_range(cfile, group->subnet);
419
		return declaration;
420
421
	case TOK_ALLOW:
422
		parse_allow_deny(cfile, group, 1);
423
		break;
424
425
	case TOK_DENY:
426
		parse_allow_deny(cfile, group, 0);
427
		break;
428
429
	default:
430
		if (declaration)
431
			parse_warn("expecting a declaration.");
432
		else
433
			parse_warn("expecting a parameter or declaration.");
434
		skip_to_semi(cfile);
435
		return declaration;
436
	}
437
438
	if (declaration) {
439
		parse_warn("parameters not allowed after first declaration.");
440
		return 1;
441
	}
442
443
	return 0;
444
}
445
446
/*
447
 * allow-deny-keyword :== BOOTP
448
 *			| BOOTING
449
 *			| DYNAMIC_BOOTP
450
 *			| UNKNOWN_CLIENTS
451
 */
452
void
453
parse_allow_deny(FILE *cfile, struct group *group, int flag)
454
{
455
	int token;
456
	char *val;
457
458
	token = next_token(&val, cfile);
459
	switch (token) {
460
	case TOK_BOOTP:
461
		group->allow_bootp = flag;
462
		break;
463
464
	case TOK_BOOTING:
465
		group->allow_booting = flag;
466
		break;
467
468
	case TOK_DYNAMIC_BOOTP:
469
		group->dynamic_bootp = flag;
470
		break;
471
472
	case TOK_UNKNOWN_CLIENTS:
473
		group->boot_unknown_clients = flag;
474
		break;
475
476
	default:
477
		parse_warn("expecting allow/deny key");
478
		skip_to_semi(cfile);
479
		return;
480
	}
481
	parse_semi(cfile);
482
}
483
484
/*
485
 * boolean :== ON SEMI | OFF SEMI | TRUE SEMI | FALSE SEMI
486
 */
487
int
488
parse_boolean(FILE *cfile)
489
{
490
	char *val;
491
	int rv;
492
493
	next_token(&val, cfile);
494
	if (!strcasecmp (val, "true") || !strcasecmp (val, "on"))
495
		rv = 1;
496
	else if (!strcasecmp (val, "false") || !strcasecmp (val, "off"))
497
		rv = 0;
498
	else {
499
		parse_warn("boolean value (true/false/on/off) expected");
500
		skip_to_semi(cfile);
501
		return 0;
502
	}
503
	parse_semi(cfile);
504
	return rv;
505
}
506
507
/*
508
 * Expect a left brace; if there isn't one, skip over the rest of the
509
 * statement and return zero; otherwise, return 1.
510
 */
511
int
512
parse_lbrace(FILE *cfile)
513
{
514
	int token;
515
	char *val;
516
517
	token = next_token(&val, cfile);
518
	if (token != '{') {
519
		parse_warn("expecting left brace.");
520
		skip_to_semi(cfile);
521
		return 0;
522
	}
523
	return 1;
524
}
525
526
527
/*
528
 * host-declaration :== hostname '{' parameters declarations '}'
529
 */
530
void
531
parse_host_declaration(FILE *cfile, struct group *group)
532
{
533
	char *val;
534
	int token;
535
	struct host_decl *host;
536
	char *name = parse_host_name(cfile);
537
	int declaration = 0;
538
539
	if (!name)
540
		return;
541
542
	host = calloc(1, sizeof (struct host_decl));
543
	if (!host)
544
		fatalx("can't allocate host decl struct %s.", name);
545
546
	host->name = name;
547
	host->group = clone_group(group, "parse_host_declaration");
548
549
	if (!parse_lbrace(cfile)) {
550
		free(host->name);
551
		free(host->group);
552
		free(host);
553
		return;
554
	}
555
556
	do {
557
		token = peek_token(&val, cfile);
558
		if (token == '}') {
559
			token = next_token(&val, cfile);
560
			break;
561
		}
562
		if (token == EOF) {
563
			token = next_token(&val, cfile);
564
			parse_warn("unexpected end of file");
565
			break;
566
		}
567
		declaration = parse_statement(cfile, host->group,
568
		    HOST_DECL, host, declaration);
569
	} while (1);
570
571
	if (!host->group->options[DHO_HOST_NAME] &&
572
	    host->group->use_host_decl_names) {
573
		host->group->options[DHO_HOST_NAME] =
574
		    new_tree_cache("parse_host_declaration");
575
		if (!host->group->options[DHO_HOST_NAME])
576
			fatalx("can't allocate a tree cache for hostname.");
577
		host->group->options[DHO_HOST_NAME]->len =
578
			strlen(name);
579
		host->group->options[DHO_HOST_NAME]->value =
580
			(unsigned char *)name;
581
		host->group->options[DHO_HOST_NAME]->buf_size =
582
			host->group->options[DHO_HOST_NAME]->len;
583
		host->group->options[DHO_HOST_NAME]->timeout = -1;
584
		host->group->options[DHO_HOST_NAME]->tree =
585
			NULL;
586
	}
587
588
	enter_host(host);
589
}
590
591
/*
592
 * class-declaration :== STRING '{' parameters declarations '}'
593
 */
594
void
595
parse_class_declaration(FILE *cfile, struct group *group, int type)
596
{
597
	char *val;
598
	int token;
599
	struct class *class;
600
	int declaration = 0;
601
602
	token = next_token(&val, cfile);
603
	if (token != TOK_STRING) {
604
		parse_warn("Expecting class name");
605
		skip_to_semi(cfile);
606
		return;
607
	}
608
609
	class = add_class (type, val);
610
	if (!class)
611
		fatalx("No memory for class %s.", val);
612
	class->group = clone_group(group, "parse_class_declaration");
613
614
	if (!parse_lbrace(cfile)) {
615
		free(class->name);
616
		free(class->group);
617
		free(class);
618
		return;
619
	}
620
621
	do {
622
		token = peek_token(&val, cfile);
623
		if (token == '}') {
624
			token = next_token(&val, cfile);
625
			break;
626
		} else if (token == EOF) {
627
			token = next_token(&val, cfile);
628
			parse_warn("unexpected end of file");
629
			break;
630
		} else {
631
			declaration = parse_statement(cfile, class->group,
632
			    CLASS_DECL, NULL, declaration);
633
		}
634
	} while (1);
635
}
636
637
/*
638
 * shared-network-declaration :==
639
 *			hostname LBRACE declarations parameters RBRACE
640
 */
641
void
642
parse_shared_net_declaration(FILE *cfile, struct group *group)
643
{
644
	char *val;
645
	int token;
646
	struct shared_network *share;
647
	char *name;
648
	int declaration = 0;
649
650
	share = calloc(1, sizeof(struct shared_network));
651
	if (!share)
652
		fatalx("No memory for shared subnet");
653
	share->leases = NULL;
654
	share->last_lease = NULL;
655
	share->insertion_point = NULL;
656
	share->next = NULL;
657
	share->interface = NULL;
658
	share->group = clone_group(group, "parse_shared_net_declaration");
659
	share->group->shared_network = share;
660
661
	/* Get the name of the shared network. */
662
	token = peek_token(&val, cfile);
663
	if (token == TOK_STRING) {
664
		token = next_token(&val, cfile);
665
666
		if (val[0] == 0) {
667
			parse_warn("zero-length shared network name");
668
			val = "<no-name-given>";
669
		}
670
		name = strdup(val);
671
		if (name == NULL)
672
			fatalx("no memory for shared network name");
673
	} else {
674
		name = parse_host_name(cfile);
675
		if (!name) {
676
			free(share->group);
677
			free(share);
678
			return;
679
		}
680
	}
681
	share->name = name;
682
683
	if (!parse_lbrace(cfile)) {
684
		free(share->group);
685
		free(share->name);
686
		free(share);
687
		return;
688
	}
689
690
	do {
691
		token = peek_token(&val, cfile);
692
		if (token == '}') {
693
			token = next_token(&val, cfile);
694
			if (!share->subnets) {
695
				free(share->group);
696
				free(share->name);
697
				free(share);
698
				parse_warn("empty shared-network decl");
699
				return;
700
			}
701
			enter_shared_network(share);
702
			return;
703
		} else if (token == EOF) {
704
			token = next_token(&val, cfile);
705
			parse_warn("unexpected end of file");
706
			break;
707
		}
708
709
		declaration = parse_statement(cfile, share->group,
710
		    SHARED_NET_DECL, NULL, declaration);
711
	} while (1);
712
}
713
714
/*
715
 * subnet-declaration :==
716
 *	net NETMASK netmask RBRACE parameters declarations LBRACE
717
 */
718
void
719
parse_subnet_declaration(FILE *cfile, struct shared_network *share)
720
{
721
	char *val;
722
	int token;
723
	struct subnet *subnet, *t, *u;
724
	struct iaddr iaddr;
725
	unsigned char addr[4];
726
	int len = sizeof addr;
727
	int declaration = 0;
728
729
	subnet = calloc(1, sizeof(struct subnet));
730
	if (!subnet)
731
		fatalx("No memory for new subnet");
732
	subnet->shared_network = share;
733
	subnet->group = clone_group(share->group, "parse_subnet_declaration");
734
	subnet->group->subnet = subnet;
735
736
	/* Get the network number. */
737
	if (!parse_numeric_aggregate(cfile, addr, &len, '.', 10, 8)) {
738
		free(subnet->group);
739
		free(subnet);
740
		return;
741
	}
742
	memcpy(iaddr.iabuf, addr, len);
743
	iaddr.len = len;
744
	subnet->net = iaddr;
745
746
	token = next_token(&val, cfile);
747
	if (token != TOK_NETMASK) {
748
		free(subnet->group);
749
		free(subnet);
750
		parse_warn("Expecting netmask");
751
		skip_to_semi(cfile);
752
		return;
753
	}
754
755
	/* Get the netmask. */
756
	if (!parse_numeric_aggregate(cfile, addr, &len, '.', 10, 8)) {
757
		free(subnet->group);
758
		free(subnet);
759
		return;
760
	}
761
	memcpy(iaddr.iabuf, addr, len);
762
	iaddr.len = len;
763
	subnet->netmask = iaddr;
764
765
	/* Save only the subnet number. */
766
	subnet->net = subnet_number(subnet->net, subnet->netmask);
767
768
	enter_subnet(subnet);
769
770
	if (!parse_lbrace(cfile))
771
		return;
772
773
	do {
774
		token = peek_token(&val, cfile);
775
		if (token == '}') {
776
			token = next_token(&val, cfile);
777
			break;
778
		} else if (token == EOF) {
779
			token = next_token(&val, cfile);
780
			parse_warn("unexpected end of file");
781
			break;
782
		}
783
		declaration = parse_statement(cfile, subnet->group,
784
		    SUBNET_DECL, NULL, declaration);
785
	} while (1);
786
787
	/*
788
	 * If this subnet supports dynamic bootp, flag it so in the
789
	 * shared_network containing it.
790
	 */
791
	if (subnet->group->dynamic_bootp)
792
		share->group->dynamic_bootp = 1;
793
794
	/* Add the subnet to the list of subnets in this shared net. */
795
	if (!share->subnets)
796
		share->subnets = subnet;
797
	else {
798
		u = NULL;
799
		for (t = share->subnets; t; t = t->next_sibling) {
800
			if (subnet_inner_than(subnet, t, 0)) {
801
				if (u)
802
					u->next_sibling = subnet;
803
				else
804
					share->subnets = subnet;
805
				subnet->next_sibling = t;
806
				return;
807
			}
808
			u = t;
809
		}
810
		u->next_sibling = subnet;
811
	}
812
}
813
814
/*
815
 * group-declaration :== RBRACE parameters declarations LBRACE
816
 */
817
void
818
parse_group_declaration(FILE *cfile, struct group *group)
819
{
820
	char *val;
821
	int token;
822
	struct group *g;
823
	int declaration = 0;
824
825
	g = clone_group(group, "parse_group_declaration");
826
827
	if (!parse_lbrace(cfile)) {
828
		free(g);
829
		return;
830
	}
831
832
	do {
833
		token = peek_token(&val, cfile);
834
		if (token == '}') {
835
			token = next_token(&val, cfile);
836
			break;
837
		} else if (token == EOF) {
838
			token = next_token(&val, cfile);
839
			parse_warn("unexpected end of file");
840
			break;
841
		}
842
		declaration = parse_statement(cfile, g, GROUP_DECL, NULL,
843
		    declaration);
844
	} while (1);
845
}
846
847
/*
848
 * cidr :== ip-address "/" bit-count
849
 * ip-address :== NUMBER [ DOT NUMBER [ DOT NUMBER [ DOT NUMBER ] ] ]
850
 * bit-count :== 0..32
851
 */
852
int
853
parse_cidr(FILE *cfile, unsigned char *addr, unsigned char *prefix)
854
{
855
	const char *errstr;
856
	char *val;
857
	int token;
858
	int len = 4;
859
860
	token = peek_token(&val, cfile);
861
862
	if (!parse_numeric_aggregate(cfile, addr, &len, '.', 10, 8)) {
863
		parse_warn("Expecting CIDR subnet");
864
		goto nocidr;
865
	}
866
867
	token = next_token(&val, cfile);
868
	if (token != '/') {
869
		parse_warn("Expecting '/'");
870
		goto nocidr;
871
	}
872
873
	token = next_token(&val, cfile);
874
875
	if (token == TOK_NUMBER_OR_NAME)
876
		*prefix = strtonum(val, 0, 32, &errstr);
877
878
	if (token != TOK_NUMBER_OR_NAME || errstr) {
879
		*prefix = 0;
880
		parse_warn("Expecting CIDR prefix length, got '%s'", val);
881
		goto nocidr;
882
	}
883
884
	return 1;
885
886
nocidr:
887
	if (token != ';')
888
		skip_to_semi(cfile);
889
	return 0;
890
}
891
892
/*
893
 * ip-addr-or-hostname :== ip-address | hostname
894
 * ip-address :== NUMBER DOT NUMBER DOT NUMBER DOT NUMBER
895
 *
896
 * Parse an ip address or a hostname.   If uniform is zero, put in
897
 * a TREE_LIMIT node to catch hostnames that evaluate to more than
898
 * one IP address.
899
 */
900
struct tree *
901
parse_ip_addr_or_hostname(FILE *cfile, int uniform)
902
{
903
	char *val;
904
	int token;
905
	unsigned char addr[4];
906
	int len = sizeof addr;
907
	char *name;
908
	struct tree *rv;
909
	struct hostent *h;
910
911
	name = NULL;
912
	h = NULL;
913
914
	token = peek_token(&val, cfile);
915
	if (is_identifier(token)) {
916
		name = parse_host_name(cfile);
917
		if (name)
918
			h = gethostbyname(name);
919
		if (name && h) {
920
			rv = tree_const(h->h_addr_list[0], h->h_length);
921
			if (!uniform)
922
				rv = tree_limit(rv, 4);
923
			return rv;
924
		}
925
	}
926
927
	if (token == TOK_NUMBER_OR_NAME) {
928
		if (!parse_numeric_aggregate(cfile, addr, &len, '.', 10, 8)) {
929
			parse_warn("%s (%d): expecting IP address or hostname",
930
				    val, token);
931
			return NULL;
932
		}
933
		rv = tree_const(addr, len);
934
	} else {
935
		if (token != '{' && token != '}')
936
			token = next_token(&val, cfile);
937
		parse_warn("%s (%d): expecting IP address or hostname",
938
			    val, token);
939
		if (token != ';')
940
			skip_to_semi(cfile);
941
		return NULL;
942
	}
943
944
	return rv;
945
}
946
947
948
/*
949
 * fixed-addr-parameter :== ip-addrs-or-hostnames SEMI
950
 * ip-addrs-or-hostnames :== ip-addr-or-hostname
951
 *			   | ip-addrs-or-hostnames ip-addr-or-hostname
952
 */
953
struct tree_cache *
954
parse_fixed_addr_param(FILE *cfile)
955
{
956
	char *val;
957
	int token;
958
	struct tree *tree = NULL;
959
	struct tree *tmp;
960
961
	do {
962
		tmp = parse_ip_addr_or_hostname(cfile, 0);
963
		if (tree)
964
			tree = tree_concat(tree, tmp);
965
		else
966
			tree = tmp;
967
		token = peek_token(&val, cfile);
968
		if (token == ',')
969
			token = next_token(&val, cfile);
970
	} while (token == ',');
971
972
	if (!parse_semi(cfile))
973
		return NULL;
974
	return tree_cache(tree);
975
}
976
977
/*
978
 * option_parameter :== identifier DOT identifier <syntax> SEMI
979
 *		      | identifier <syntax> SEMI
980
 *
981
 * Option syntax is handled specially through format strings, so it
982
 * would be painful to come up with BNF for it. However, it always
983
 * starts as above and ends in a SEMI.
984
 */
985
void
986
parse_option_param(FILE *cfile, struct group *group)
987
{
988
	char *val;
989
	int token;
990
	unsigned char buf[4];
991
	unsigned char cprefix;
992
	char *vendor;
993
	char *fmt;
994
	struct universe *universe;
995
	struct option *option;
996
	struct tree *tree = NULL;
997
	struct tree *t;
998
999
	token = next_token(&val, cfile);
1000
	if (!is_identifier(token)) {
1001
		parse_warn("expecting identifier after option keyword.");
1002
		if (token != ';')
1003
			skip_to_semi(cfile);
1004
		return;
1005
	}
1006
	vendor = strdup(val);
1007
	if (vendor == NULL)
1008
		fatalx("no memory for vendor token.");
1009
	token = peek_token(&val, cfile);
1010
	if (token == '.') {
1011
		/* Go ahead and take the DOT token. */
1012
		token = next_token(&val, cfile);
1013
1014
		/* The next token should be an identifier. */
1015
		token = next_token(&val, cfile);
1016
		if (!is_identifier(token)) {
1017
			parse_warn("expecting identifier after '.'");
1018
			if (token != ';')
1019
				skip_to_semi(cfile);
1020
			free(vendor);
1021
			return;
1022
		}
1023
1024
		/*
1025
		 * Look up the option name hash table for the specified
1026
		 * vendor.
1027
		 */
1028
		universe = ((struct universe *)hash_lookup(&universe_hash,
1029
		    (unsigned char *)vendor, 0));
1030
		/*
1031
		 * If it's not there, we can't parse the rest of the
1032
		 * declaration.
1033
		 */
1034
		if (!universe) {
1035
			parse_warn("no vendor named %s.", vendor);
1036
			skip_to_semi(cfile);
1037
			free(vendor);
1038
			return;
1039
		}
1040
	} else {
1041
		/*
1042
		 * Use the default hash table, which contains all the
1043
		 * standard dhcp option names.
1044
		 */
1045
		val = vendor;
1046
		universe = &dhcp_universe;
1047
	}
1048
1049
	/* Look up the actual option info. */
1050
	option = (struct option *)hash_lookup(universe->hash,
1051
	    (unsigned char *)val, 0);
1052
1053
	/* If we didn't get an option structure, it's an undefined option. */
1054
	if (!option) {
1055
		if (val == vendor)
1056
			parse_warn("no option named %s", val);
1057
		else
1058
			parse_warn("no option named %s for vendor %s",
1059
				    val, vendor);
1060
		skip_to_semi(cfile);
1061
		free(vendor);
1062
		return;
1063
	}
1064
1065
	/* Free the initial identifier token. */
1066
	free(vendor);
1067
1068
	/* Parse the option data. */
1069
	do {
1070
		/*
1071
		 * Set a flag if this is an array of a simple type (i.e.,
1072
		 * not an array of pairs of IP addresses, or something
1073
		 * like that.
1074
		 */
1075
		int uniform = option->format[1] == 'A';
1076
1077
		for (fmt = option->format; *fmt; fmt++) {
1078
			if (*fmt == 'A')
1079
				break;
1080
			switch (*fmt) {
1081
			case 'X':
1082
				token = peek_token(&val, cfile);
1083
				if (token == TOK_NUMBER_OR_NAME) {
1084
					do {
1085
						token = next_token
1086
							(&val, cfile);
1087
						if (token !=
1088
						    TOK_NUMBER_OR_NAME) {
1089
							parse_warn("expecting "
1090
							    "hex number.");
1091
							if (token != ';')
1092
								skip_to_semi(
1093
								    cfile);
1094
							return;
1095
						}
1096
						convert_num(buf, val, 16, 8);
1097
						tree = tree_concat(tree,
1098
						    tree_const(buf, 1));
1099
						token = peek_token(&val,
1100
						    cfile);
1101
						if (token == ':')
1102
							token =
1103
							    next_token(&val,
1104
							    cfile);
1105
					} while (token == ':');
1106
				} else if (token == TOK_STRING) {
1107
					token = next_token(&val, cfile);
1108
					tree = tree_concat(tree,
1109
					    tree_const((unsigned char *)val,
1110
					    strlen(val)));
1111
				} else {
1112
					parse_warn("expecting string %s.",
1113
					    "or hexadecimal data");
1114
					skip_to_semi(cfile);
1115
					return;
1116
				}
1117
				break;
1118
1119
			case 't': /* Text string. */
1120
				token = next_token(&val, cfile);
1121
				if (token != TOK_STRING
1122
				    && !is_identifier(token)) {
1123
					parse_warn("expecting string.");
1124
					if (token != ';')
1125
						skip_to_semi(cfile);
1126
					return;
1127
				}
1128
				tree = tree_concat(tree,
1129
				    tree_const((unsigned char *)val,
1130
				    strlen(val)));
1131
				break;
1132
1133
			case 'I': /* IP address or hostname. */
1134
				t = parse_ip_addr_or_hostname(cfile, uniform);
1135
				if (!t)
1136
					return;
1137
				tree = tree_concat(tree, t);
1138
				break;
1139
1140
			case 'L': /* Unsigned 32-bit integer. */
1141
			case 'l': /* Signed 32-bit integer. */
1142
				token = next_token(&val, cfile);
1143
				if (token != TOK_NUMBER && token !=
1144
				    TOK_NUMBER_OR_NAME) {
1145
					parse_warn("expecting number.");
1146
					if (token != ';')
1147
						skip_to_semi(cfile);
1148
					return;
1149
				}
1150
				convert_num(buf, val, 0, 32);
1151
				tree = tree_concat(tree, tree_const(buf, 4));
1152
				break;
1153
			case 's': /* Signed 16-bit integer. */
1154
			case 'S': /* Unsigned 16-bit integer. */
1155
				token = next_token(&val, cfile);
1156
				if (token != TOK_NUMBER && token !=
1157
				    TOK_NUMBER_OR_NAME) {
1158
					parse_warn("expecting number.");
1159
					if (token != ';')
1160
						skip_to_semi(cfile);
1161
					return;
1162
				}
1163
				convert_num(buf, val, 0, 16);
1164
				tree = tree_concat(tree, tree_const(buf, 2));
1165
				break;
1166
			case 'b': /* Signed 8-bit integer. */
1167
			case 'B': /* Unsigned 8-bit integer. */
1168
				token = next_token(&val, cfile);
1169
				if (token != TOK_NUMBER && token !=
1170
				    TOK_NUMBER_OR_NAME) {
1171
					parse_warn("expecting number.");
1172
					if (token != ';')
1173
						skip_to_semi(cfile);
1174
					return;
1175
				}
1176
				convert_num(buf, val, 0, 8);
1177
				tree = tree_concat(tree, tree_const(buf, 1));
1178
				break;
1179
			case 'f': /* Boolean flag. */
1180
				token = next_token(&val, cfile);
1181
				if (!is_identifier(token)) {
1182
					parse_warn("expecting identifier.");
1183
					if (token != ';')
1184
						skip_to_semi(cfile);
1185
					return;
1186
				}
1187
				if (!strcasecmp(val, "true")
1188
				    || !strcasecmp(val, "on"))
1189
					buf[0] = 1;
1190
				else if (!strcasecmp(val, "false")
1191
					 || !strcasecmp(val, "off"))
1192
					buf[0] = 0;
1193
				else {
1194
					parse_warn("expecting boolean.");
1195
					if (token != ';')
1196
						skip_to_semi(cfile);
1197
					return;
1198
				}
1199
				tree = tree_concat(tree, tree_const(buf, 1));
1200
				break;
1201
			case 'C':
1202
				if (!parse_cidr(cfile, buf, &cprefix))
1203
					return;
1204
				tree = tree_concat(tree, tree_const(&cprefix,
1205
				    sizeof(cprefix)));
1206
				if (cprefix > 0)
1207
					tree = tree_concat(tree, tree_const(
1208
					    buf, (cprefix + 7) / 8));
1209
				break;
1210
			default:
1211
				log_warnx("Bad format %c in "
1212
				    "parse_option_param.", *fmt);
1213
				skip_to_semi(cfile);
1214
				return;
1215
			}
1216
		}
1217
		if (*fmt == 'A') {
1218
			token = peek_token(&val, cfile);
1219
			if (token == ',') {
1220
				token = next_token(&val, cfile);
1221
				continue;
1222
			}
1223
			break;
1224
		}
1225
	} while (*fmt == 'A');
1226
1227
	token = next_token(&val, cfile);
1228
	if (token != ';') {
1229
		parse_warn("semicolon expected.");
1230
		skip_to_semi(cfile);
1231
		return;
1232
	}
1233
	group->options[option->code] = tree_cache(tree);
1234
}
1235
1236
/*
1237
 * lease_declaration :== LEASE ip_address LBRACE lease_parameters RBRACE
1238
 *
1239
 * lease_parameters :== <nil>
1240
 *		      | lease_parameter
1241
 *		      | lease_parameters lease_parameter
1242
 *
1243
 * lease_parameter :== STARTS date
1244
 *		     | ENDS date
1245
 *		     | TIMESTAMP date
1246
 *		     | HARDWARE hardware-parameter
1247
 *		     | UID hex_numbers SEMI
1248
 *		     | HOSTNAME hostname SEMI
1249
 *		     | CLIENT_HOSTNAME hostname SEMI
1250
 *		     | CLASS identifier SEMI
1251
 *		     | DYNAMIC_BOOTP SEMI
1252
 */
1253
struct lease *
1254
parse_lease_declaration(FILE *cfile)
1255
{
1256
	char *val;
1257
	int token;
1258
	unsigned char addr[4];
1259
	int len = sizeof addr;
1260
	int seenmask = 0;
1261
	int seenbit;
1262
	char tbuf[32];
1263
	static struct lease lease;
1264
1265
	/* Zap the lease structure. */
1266
	memset(&lease, 0, sizeof lease);
1267
1268
	/* Get the address for which the lease has been issued. */
1269
	if (!parse_numeric_aggregate(cfile, addr, &len, '.', 10, 8))
1270
		return NULL;
1271
	memcpy(lease.ip_addr.iabuf, addr, len);
1272
	lease.ip_addr.len = len;
1273
1274
	if (!parse_lbrace(cfile))
1275
		return NULL;
1276
1277
	do {
1278
		token = next_token(&val, cfile);
1279
		if (token == '}')
1280
			break;
1281
		else if (token == EOF) {
1282
			parse_warn("unexpected end of file");
1283
			break;
1284
		}
1285
		strlcpy(tbuf, val, sizeof tbuf);
1286
1287
		/* Parse any of the times associated with the lease. */
1288
		if (token == TOK_STARTS || token == TOK_ENDS || token ==
1289
		    TOK_TIMESTAMP) {
1290
			time_t t;
1291
			t = parse_date(cfile);
1292
			switch (token) {
1293
			case TOK_STARTS:
1294
				seenbit = 1;
1295
				lease.starts = t;
1296
				break;
1297
1298
			case TOK_ENDS:
1299
				seenbit = 2;
1300
				lease.ends = t;
1301
				break;
1302
1303
			case TOK_TIMESTAMP:
1304
				seenbit = 4;
1305
				lease.timestamp = t;
1306
				break;
1307
1308
			default:
1309
				/*NOTREACHED*/
1310
				seenbit = 0;
1311
				break;
1312
			}
1313
		} else {
1314
			switch (token) {
1315
				/* Colon-separated hexadecimal octets. */
1316
			case TOK_UID:
1317
				seenbit = 8;
1318
				token = peek_token(&val, cfile);
1319
				if (token == TOK_STRING) {
1320
					token = next_token(&val, cfile);
1321
					lease.uid_len = strlen(val);
1322
					lease.uid = malloc(lease.uid_len);
1323
					if (!lease.uid) {
1324
						log_warnx("no space for uid");
1325
						return NULL;
1326
					}
1327
					memcpy(lease.uid, val, lease.uid_len);
1328
					parse_semi(cfile);
1329
				} else {
1330
					lease.uid_len = 0;
1331
					lease.uid =
1332
					    parse_numeric_aggregate(cfile,
1333
					    NULL, &lease.uid_len, ':', 16, 8);
1334
					if (!lease.uid) {
1335
						log_warnx("no space for uid");
1336
						return NULL;
1337
					}
1338
					if (lease.uid_len == 0) {
1339
						lease.uid = NULL;
1340
						parse_warn("zero-length uid");
1341
						seenbit = 0;
1342
						break;
1343
					}
1344
				}
1345
				if (!lease.uid)
1346
					fatalx("No memory for lease uid");
1347
				break;
1348
1349
			case TOK_CLASS:
1350
				seenbit = 32;
1351
				token = next_token(&val, cfile);
1352
				if (!is_identifier(token)) {
1353
					if (token != ';')
1354
						skip_to_semi(cfile);
1355
					return NULL;
1356
				}
1357
				/* for now, we aren't using this. */
1358
				break;
1359
1360
			case TOK_HARDWARE:
1361
				seenbit = 64;
1362
				parse_hardware_param(cfile,
1363
				    &lease.hardware_addr);
1364
				break;
1365
1366
			case TOK_DYNAMIC_BOOTP:
1367
				seenbit = 128;
1368
				lease.flags |= BOOTP_LEASE;
1369
				break;
1370
1371
			case TOK_ABANDONED:
1372
				seenbit = 256;
1373
				lease.flags |= ABANDONED_LEASE;
1374
				break;
1375
1376
			case TOK_HOSTNAME:
1377
				seenbit = 512;
1378
				token = peek_token(&val, cfile);
1379
				if (token == TOK_STRING)
1380
					lease.hostname = parse_string(cfile);
1381
				else
1382
					lease.hostname =
1383
					    parse_host_name(cfile);
1384
				if (!lease.hostname) {
1385
					seenbit = 0;
1386
					return NULL;
1387
				}
1388
				break;
1389
1390
			case TOK_CLIENT_HOSTNAME:
1391
				seenbit = 1024;
1392
				token = peek_token(&val, cfile);
1393
				if (token == TOK_STRING)
1394
					lease.client_hostname =
1395
					    parse_string(cfile);
1396
				else
1397
					lease.client_hostname =
1398
					    parse_host_name(cfile);
1399
				break;
1400
1401
			default:
1402
				skip_to_semi(cfile);
1403
				seenbit = 0;
1404
				return NULL;
1405
			}
1406
1407
			if (token != TOK_HARDWARE && token != TOK_STRING) {
1408
				token = next_token(&val, cfile);
1409
				if (token != ';') {
1410
					parse_warn("semicolon expected.");
1411
					skip_to_semi(cfile);
1412
					return NULL;
1413
				}
1414
			}
1415
		}
1416
		if (seenmask & seenbit) {
1417
			parse_warn("Too many %s parameters in lease %s\n",
1418
			    tbuf, piaddr(lease.ip_addr));
1419
		} else
1420
			seenmask |= seenbit;
1421
1422
	} while (1);
1423
	return &lease;
1424
}
1425
1426
/*
1427
 * address-range-declaration :== ip-address ip-address SEMI
1428
 *			       | DYNAMIC_BOOTP ip-address ip-address SEMI
1429
 */
1430
void
1431
parse_address_range(FILE *cfile, struct subnet *subnet)
1432
{
1433
	struct iaddr low, high;
1434
	unsigned char addr[4];
1435
	int len = sizeof addr, token, dynamic = 0;
1436
	char *val;
1437
1438
	if ((token = peek_token(&val, cfile)) == TOK_DYNAMIC_BOOTP) {
1439
		token = next_token(&val, cfile);
1440
		subnet->group->dynamic_bootp = dynamic = 1;
1441
	}
1442
1443
	/* Get the bottom address in the range. */
1444
	if (!parse_numeric_aggregate(cfile, addr, &len, '.', 10, 8))
1445
		return;
1446
	memcpy(low.iabuf, addr, len);
1447
	low.len = len;
1448
1449
	/* Only one address? */
1450
	token = peek_token(&val, cfile);
1451
	if (token == ';')
1452
		high = low;
1453
	else {
1454
		/* Get the top address in the range. */
1455
		if (!parse_numeric_aggregate(cfile, addr, &len, '.', 10, 8))
1456
			return;
1457
		memcpy(high.iabuf, addr, len);
1458
		high.len = len;
1459
	}
1460
1461
	token = next_token(&val, cfile);
1462
	if (token != ';') {
1463
		parse_warn("semicolon expected.");
1464
		skip_to_semi(cfile);
1465
		return;
1466
	}
1467
1468
	/* Create the new address range. */
1469
	new_address_range(low, high, subnet, dynamic);
1470
}