clparse.c 56.5 KB
Newer Older
1 2 3 4 5
/* clparse.c

   Parser for dhclient config and lease files... */

/*
Thomas Markwalder's avatar
Thomas Markwalder committed
6
 * Copyright (c) 2004-2019 by Internet Systems Consortium, Inc. ("ISC")
7
 * Copyright (c) 1996-2003 by Internet Software Consortium
8
 *
9 10 11
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
12
 *
13 14 15 16 17 18 19
 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20
 *
21 22 23 24
 *   Internet Systems Consortium, Inc.
 *   950 Charter Street
 *   Redwood City, CA 94063
 *   <info@isc.org>
25
 *   https://www.isc.org/
Ted Lemon's avatar
Ted Lemon committed
26
 *
27 28 29
 */

#include "dhcpd.h"
30
#include <errno.h>
31

32 33
struct client_config top_level_config;

34
#define NUM_DEFAULT_REQUESTED_OPTS	9
35 36
/* There can be 2 extra requested options for DHCPv4-over-DHCPv6. */
struct option *default_requested_options[NUM_DEFAULT_REQUESTED_OPTS + 2 + 1];
37

David Hankins's avatar
David Hankins committed
38 39
static void parse_client_default_duid(struct parse *cfile);
static void parse_client6_lease_statement(struct parse *cfile);
40
#ifdef DHCPv6
Francis Dupont's avatar
Francis Dupont committed
41 42 43
static struct dhc6_ia *parse_client6_ia_na_statement(struct parse *cfile);
static struct dhc6_ia *parse_client6_ia_ta_statement(struct parse *cfile);
static struct dhc6_ia *parse_client6_ia_pd_statement(struct parse *cfile);
David Hankins's avatar
David Hankins committed
44
static struct dhc6_addr *parse_client6_iaaddr_statement(struct parse *cfile);
Francis Dupont's avatar
Francis Dupont committed
45
static struct dhc6_addr *parse_client6_iaprefix_statement(struct parse *cfile);
46
#endif /* DHCPv6 */
David Hankins's avatar
David Hankins committed
47

48 49
static void parse_lease_id_format (struct parse *cfile);

Thomas Markwalder's avatar
Thomas Markwalder committed
50 51 52
extern void discard_duplicate (struct client_lease** lease_list,
                               struct client_lease* lease);

53
/* client-conf-file :== client-declarations END_OF_FILE
54 55 56 57
   client-declarations :== <nil>
			 | client-declaration
			 | client-declarations client-declaration */

58
isc_result_t read_client_conf ()
59 60 61
{
	struct client_config *config;
	struct interface_info *ip;
62
	isc_result_t status;
63 64
	unsigned code;

65 66 67 68 69 70 71 72 73
        /* 
         * TODO: LATER constant is very undescriptive. We should review it and
         * change it to something more descriptive or even better remove it
         * completely as it is currently not used.
         */
#ifdef LATER
        struct parse *parse = NULL;
#endif

74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
	/* Initialize the default request list. */
	memset(default_requested_options, 0, sizeof(default_requested_options));

	/* 1 */
	code = DHO_SUBNET_MASK;
	option_code_hash_lookup(&default_requested_options[0],
				dhcp_universe.code_hash, &code, 0, MDL);

	/* 2 */
	code = DHO_BROADCAST_ADDRESS;
	option_code_hash_lookup(&default_requested_options[1],
				dhcp_universe.code_hash, &code, 0, MDL);

	/* 3 */
	code = DHO_TIME_OFFSET;
	option_code_hash_lookup(&default_requested_options[2],
				dhcp_universe.code_hash, &code, 0, MDL);

	/* 4 */
	code = DHO_ROUTERS;
	option_code_hash_lookup(&default_requested_options[3],
				dhcp_universe.code_hash, &code, 0, MDL);

	/* 5 */
	code = DHO_DOMAIN_NAME;
	option_code_hash_lookup(&default_requested_options[4],
				dhcp_universe.code_hash, &code, 0, MDL);

	/* 6 */
	code = DHO_DOMAIN_NAME_SERVERS;
	option_code_hash_lookup(&default_requested_options[5],
				dhcp_universe.code_hash, &code, 0, MDL);

	/* 7 */
	code = DHO_HOST_NAME;
	option_code_hash_lookup(&default_requested_options[6],
				dhcp_universe.code_hash, &code, 0, MDL);

	/* 8 */
	code = D6O_NAME_SERVERS;
	option_code_hash_lookup(&default_requested_options[7],
				dhcpv6_universe.code_hash, &code, 0, MDL);

	/* 9 */
	code = D6O_DOMAIN_SEARCH;
	option_code_hash_lookup(&default_requested_options[8],
				dhcpv6_universe.code_hash, &code, 0, MDL);

	for (code = 0 ; code < NUM_DEFAULT_REQUESTED_OPTS ; code++) {
		if (default_requested_options[code] == NULL)
			log_fatal("Unable to find option definition for "
				  "index %u during default parameter request "
				  "assembly.", code);
	}
128

129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
#ifdef DHCP4o6
	/* DHCPv4-over-DHCPv6 extra requested options in code order */
	if (dhcpv4_over_dhcpv6 == 1) {
		/* The DHCP4o6 server option should be requested */
		code = D6O_DHCP4_O_DHCP6_SERVER;
		option_code_hash_lookup(&default_requested_options[9],
					dhcpv6_universe.code_hash,
					&code, 0, MDL);
		if (default_requested_options[9] == NULL) {
			log_fatal("Unable to find option definition for "
				  "index %u during default parameter request "
				  "assembly.", code);
		}
	} else if (dhcpv4_over_dhcpv6 > 1) {
		/* Called from run_stateless so the IRT should
		   be requested too */
		code = D6O_INFORMATION_REFRESH_TIME;
		option_code_hash_lookup(&default_requested_options[9],
					dhcpv6_universe.code_hash,
					&code, 0, MDL);
		if (default_requested_options[9] == NULL) {
			log_fatal("Unable to find option definition for "
				  "index %u during default parameter request "
				  "assembly.", code);
		}
		code = D6O_DHCP4_O_DHCP6_SERVER;
		option_code_hash_lookup(&default_requested_options[10],
					dhcpv6_universe.code_hash,
					&code, 0, MDL);
		if (default_requested_options[10] == NULL) {
			log_fatal("Unable to find option definition for "
				  "index %u during default parameter request "
				  "assembly.", code);
		}
	}
#endif
					
166 167 168
	/* Initialize the top level client configuration. */
	memset (&top_level_config, 0, sizeof top_level_config);

169 170 171 172 173
	/* Set some defaults... */
	top_level_config.timeout = 60;
	top_level_config.select_interval = 0;
	top_level_config.reboot_timeout = 10;
	top_level_config.retry_interval = 300;
Ted Lemon's avatar
Ted Lemon committed
174 175
	top_level_config.backoff_cutoff = 15;
	top_level_config.initial_interval = 3;
176
	top_level_config.lease_id_format = TOKEN_OCTAL;
177 178 179 180 181 182 183 184 185 186

	/*
	 * RFC 2131, section 4.4.1 specifies that the client SHOULD wait a
	 * random time between 1 and 10 seconds. However, we choose to not
	 * implement this default. If user is inclined to really have that
	 * delay, he is welcome to do so, using 'initial-delay X;' parameter
	 * in config file.
	 */
	top_level_config.initial_delay = 0;

Ted Lemon's avatar
Ted Lemon committed
187
	top_level_config.bootp_policy = P_ACCEPT;
188
	top_level_config.script_name = path_dhclient_script;
189
	top_level_config.requested_options = default_requested_options;
190
	top_level_config.omapi_port = -1;
191
	top_level_config.do_forward_update = 1;
David Hankins's avatar
David Hankins committed
192 193 194
	/* Requested lease time, used by DHCPv6 (DHCPv4 uses the option cache)
	 */
	top_level_config.requested_lease = 7200;
195

196
	group_allocate (&top_level_config.on_receipt, MDL);
Ted Lemon's avatar
Ted Lemon committed
197
	if (!top_level_config.on_receipt)
198
		log_fatal ("no memory for top-level on_receipt group");
Ted Lemon's avatar
Ted Lemon committed
199

200
	group_allocate (&top_level_config.on_transmission, MDL);
Ted Lemon's avatar
Ted Lemon committed
201
	if (!top_level_config.on_transmission)
202
		log_fatal ("no memory for top-level on_transmission group");
Ted Lemon's avatar
Ted Lemon committed
203

204 205 206
	status = read_client_conf_file (path_dhclient_conf,
					(struct interface_info *)0,
					&top_level_config);
207

208 209
	if (status != ISC_R_SUCCESS) {
		;
210 211
#ifdef LATER
		/* Set up the standard name service updater routine. */
212 213 214
		status = new_parse(&parse, -1, default_client_config,
				   sizeof(default_client_config) - 1,
				   "default client configuration", 0);
215 216
		if (status != ISC_R_SUCCESS)
			log_fatal ("can't begin default client config!");
217
	}
218

219
	if (parse != NULL) {
220
		do {
221
			token = peek_token(&val, NULL, cfile);
222 223
			if (token == END_OF_FILE)
				break;
224
			parse_client_statement(cfile, NULL, &top_level_config);
225
		} while (1);
226
		end_parse(&parse);
227
#endif
228
	}
229 230

	/* Set up state and config structures for clients that don't
Ted Lemon's avatar
Ted Lemon committed
231
	   have per-interface configuration statements. */
232 233 234 235
	config = (struct client_config *)0;
	for (ip = interfaces; ip; ip = ip -> next) {
		if (!ip -> client) {
			ip -> client = (struct client_state *)
Ted Lemon's avatar
Ted Lemon committed
236
				dmalloc (sizeof (struct client_state), MDL);
237
			if (!ip -> client)
238
				log_fatal ("no memory for client state.");
239
			memset (ip -> client, 0, sizeof *(ip -> client));
240
			ip -> client -> interface = ip;
241
		}
Ted Lemon's avatar
Ted Lemon committed
242

243
		if (!ip -> client -> config) {
244 245
			if (!config) {
				config = (struct client_config *)
Ted Lemon's avatar
Ted Lemon committed
246 247
					dmalloc (sizeof (struct client_config),
						 MDL);
248
				if (!config)
249
				    log_fatal ("no memory for client config.");
250 251 252 253 254 255
				memcpy (config, &top_level_config,
					sizeof top_level_config);
			}
			ip -> client -> config = config;
		}
	}
256
	return status;
257 258
}

259 260 261 262 263 264 265 266
int read_client_conf_file (const char *name, struct interface_info *ip,
			   struct client_config *client)
{
	int file;
	struct parse *cfile;
	const char *val;
	int token;
	isc_result_t status;
267

268 269 270
	if ((file = open (name, O_RDONLY)) < 0)
		return uerr2isc (errno);

271 272 273 274
	cfile = NULL;
	status = new_parse(&cfile, file, NULL, 0, path_dhclient_conf, 0);
	if (status != ISC_R_SUCCESS || cfile == NULL)
		return status;
275 276 277 278 279 280 281

	do {
		token = peek_token (&val, (unsigned *)0, cfile);
		if (token == END_OF_FILE)
			break;
		parse_client_statement (cfile, ip, client);
	} while (1);
282
	skip_token(&val, (unsigned *)0, cfile);
283
	status = (cfile -> warnings_occurred
284
		  ? DHCP_R_BADPARSE
285 286 287 288 289 290
		  : ISC_R_SUCCESS);
	end_parse (&cfile);
	return status;
}


291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329
/* lease-file :== client-lease-statements END_OF_FILE
   client-lease-statements :== <nil>
		     | client-lease-statements LEASE client-lease-statement
 * This routine looks through a lease file and only tries to parse
 * the duid statements.
 */

void read_client_duid ()
{
	int file;
	isc_result_t status;
	struct parse *cfile;
	const char *val;
	int token;

	/* Open the lease file.   If we can't open it, just return -
	   we can safely trust the server to remember our state. */
	if ((file = open (path_dhclient_duid, O_RDONLY)) < 0)
		return;

	cfile = NULL;
	status = new_parse(&cfile, file, NULL, 0, path_dhclient_duid, 0);
	if (status != ISC_R_SUCCESS || cfile == NULL)
		return;

	while ((token = next_token(&val, NULL, cfile)) != END_OF_FILE) {
		/*
		 * All we care about is DUIDs - if we get anything else
		 * just toss it and continue looking for DUIDs until we
		 * run out of file.  
		 */
		if (token == DEFAULT_DUID) {
			parse_client_default_duid(cfile);
		}
	}

	end_parse(&cfile);
}

330
/* lease-file :== client-lease-statements END_OF_FILE
331
   client-lease-statements :== <nil>
Ted Lemon's avatar
Ted Lemon committed
332
		     | client-lease-statements LEASE client-lease-statement */
333 334 335

void read_client_leases ()
{
336
	int file;
337
	isc_result_t status;
338
	struct parse *cfile;
339
	const char *val;
340 341 342 343
	int token;

	/* Open the lease file.   If we can't open it, just return -
	   we can safely trust the server to remember our state. */
344
	if ((file = open (path_dhclient_db, O_RDONLY)) < 0)
345
		return;
346 347 348 349

	cfile = NULL;
	status = new_parse(&cfile, file, NULL, 0, path_dhclient_db, 0);
	if (status != ISC_R_SUCCESS || cfile == NULL)
350
		return;
351

352
	do {
353
		token = next_token (&val, (unsigned *)0, cfile);
354
		if (token == END_OF_FILE)
355
			break;
David Hankins's avatar
David Hankins committed
356 357 358 359 360 361 362 363 364 365 366 367 368 369 370

		switch (token) {
		      case DEFAULT_DUID:
			parse_client_default_duid(cfile);
			break;

		      case LEASE:
			parse_client_lease_statement(cfile, 0);
			break;

		      case LEASE6:
			parse_client6_lease_statement(cfile);
			break;

		      default:
371
			log_error ("Corrupt lease file - possible data loss!");
372 373
			skip_to_semi (cfile);
			break;
David Hankins's avatar
David Hankins committed
374
		}
375
	} while (1);
376 377

	end_parse (&cfile);
378 379 380
}

/* client-declaration :== 
381 382
	SEND option-decl |
	DEFAULT option-decl |
Ted Lemon's avatar
Ted Lemon committed
383
	SUPERSEDE option-decl |
384 385
	PREPEND option-decl |
	APPEND option-decl |
386
	hardware-declaration |
387 388
	ALSO REQUEST option-list |
	ALSO REQUIRE option-list |
389 390 391 392
	REQUEST option-list |
	REQUIRE option-list |
	TIMEOUT number |
	RETRY number |
393
	REBOOT number |
394 395
	SELECT_TIMEOUT number |
	SCRIPT string |
396
	VENDOR_SPACE string |
397
	interface-declaration |
Ted Lemon's avatar
Ted Lemon committed
398
	LEASE client-lease-statement |
399
	ALIAS client-lease-statement |
400
	KEY key-definition */
401 402

void parse_client_statement (cfile, ip, config)
403
	struct parse *cfile;
404 405 406 407
	struct interface_info *ip;
	struct client_config *config;
{
	int token;
408
	const char *val;
409
	struct option *option = NULL;
410
	struct executable_statement *stmt;
411
	int lose;
Ted Lemon's avatar
Ted Lemon committed
412
	char *name;
413
	enum policy policy;
414
	int known;
415
	int tmp, i;
416
	isc_result_t status;
417
	struct option ***append_list, **new_list, **cat_list;
418

419
	switch (peek_token (&val, (unsigned *)0, cfile)) {
420
	      case INCLUDE:
421
		skip_token(&val, (unsigned *)0, cfile);
422 423 424 425 426 427 428 429 430 431 432 433
		token = next_token (&val, (unsigned *)0, cfile);
		if (token != STRING) {
			parse_warn (cfile, "filename string expected.");
			skip_to_semi (cfile);
		} else {
			status = read_client_conf_file (val, ip, config);
			if (status != ISC_R_SUCCESS)
				parse_warn (cfile, "%s: bad parse.", val);
			parse_semi (cfile);
		}
		return;
		
434
	      case KEY:
435
		skip_token(&val, (unsigned *)0, cfile);
436 437 438 439 440 441 442 443 444 445 446 447 448
		if (ip) {
			/* This may seem arbitrary, but there's a reason for
			   doing it: the authentication key database is not
			   scoped.  If we allow the user to declare a key other
			   than in the outer scope, the user is very likely to
			   believe that the key will only be used in that
			   scope.  If the user only wants the key to be used on
			   one interface, because it's known that the other
			   interface may be connected to an insecure net and
			   the secret key is considered sensitive, we don't
			   want to lull them into believing they've gotten
			   their way.   This is a bit contrived, but people
			   tend not to be entirely rational about security. */
449
			parse_warn (cfile, "key definition not allowed here.");
450 451 452
			skip_to_semi (cfile);
			break;
		}
453
		parse_key (cfile);
454 455
		return;

456 457
	      case TOKEN_ALSO:
		/* consume ALSO */
458
		skip_token(&val, NULL, cfile);
459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522

		/* consume type of ALSO list. */
		token = next_token(&val, NULL, cfile);

		if (token == REQUEST) {
			append_list = &config->requested_options;
		} else if (token == REQUIRE) {
			append_list = &config->required_options;
		} else {
			parse_warn(cfile, "expected REQUEST or REQUIRE list");
			skip_to_semi(cfile);
			return;
		}

		/* If there is no list, cut the concat short. */
		if (*append_list == NULL) {
			parse_option_list(cfile, append_list);
			return;
		}

		/* Count the length of the existing list. */
		for (i = 0 ; (*append_list)[i] != NULL ; i++)
			; /* This space intentionally left blank. */

		/* If there's no codes on the list, cut the concat short. */
		if (i == 0) {
			parse_option_list(cfile, append_list);
			return;
		}

		tmp = parse_option_list(cfile, &new_list);

		if (tmp == 0 || new_list == NULL)
			return;

		/* Allocate 'i + tmp' buckets plus a terminator. */
		cat_list = dmalloc(sizeof(struct option *) * (i + tmp + 1),
				   MDL);

		if (cat_list == NULL) {
			log_error("Unable to allocate memory for new "
				  "request list.");
			skip_to_semi(cfile);
			return;
		}

		for (i = 0 ; (*append_list)[i] != NULL ; i++)
			option_reference(&cat_list[i], (*append_list)[i], MDL);

		tmp = i;

		for (i = 0 ; new_list[i] != 0 ; i++)
			option_reference(&cat_list[tmp++], new_list[i], MDL);

		cat_list[tmp] = 0;

		/* XXX: We cannot free the old list, because it may have been
		 * XXX: assigned from an outer configuration scope (or may be
		 * XXX: the static default setting).
		 */
		*append_list = cat_list;

		return;

523
		/* REQUIRE can either start a policy statement or a
524
		   comma-separated list of names of required options. */
525
	      case REQUIRE:
526
		skip_token(&val, (unsigned *)0, cfile);
527
		token = peek_token (&val, (unsigned *)0, cfile);
528 529 530 531 532 533 534 535
		if (token == AUTHENTICATION) {
			policy = P_REQUIRE;
			goto do_policy;
		}
		parse_option_list (cfile, &config -> required_options);
		return;

	      case IGNORE:
536
		skip_token(&val, (unsigned *)0, cfile);
537 538 539 540
		policy = P_IGNORE;
		goto do_policy;

	      case ACCEPT:
541
		skip_token(&val, (unsigned *)0, cfile);
542 543 544 545
		policy = P_ACCEPT;
		goto do_policy;

	      case PREFER:
546
		skip_token(&val, (unsigned *)0, cfile);
547 548 549 550
		policy = P_PREFER;
		goto do_policy;

	      case DONT:
551
		skip_token(&val, (unsigned *)0, cfile);
552 553 554 555
		policy = P_DONT;
		goto do_policy;

	      do_policy:
556
		token = next_token (&val, (unsigned *)0, cfile);
557 558 559 560
		if (token == AUTHENTICATION) {
			if (policy != P_PREFER &&
			    policy != P_REQUIRE &&
			    policy != P_DONT) {
561 562
				parse_warn (cfile,
					    "invalid authentication policy.");
563 564 565 566
				skip_to_semi (cfile);
				return;
			}
			config -> auth_policy = policy;
567
		} else if (token != TOKEN_BOOTP) {
568 569 570
			if (policy != P_PREFER &&
			    policy != P_IGNORE &&
			    policy != P_ACCEPT) {
571
				parse_warn (cfile, "invalid bootp policy.");
572 573 574 575 576
				skip_to_semi (cfile);
				return;
			}
			config -> bootp_policy = policy;
		} else {
577
			parse_warn (cfile, "expecting a policy type.");
578 579 580
			skip_to_semi (cfile);
			return;
		} 
581
		break;
582

Ted Lemon's avatar
Ted Lemon committed
583
	      case OPTION:
584
		skip_token(&val, (unsigned *)0, cfile);
585
		token = peek_token (&val, (unsigned *)0, cfile);
586 587
		if (token == SPACE) {
			if (ip) {
588 589
				parse_warn (cfile,
					    "option space definitions %s",
590 591 592 593 594 595 596 597
					    " may not be scoped.");
				skip_to_semi (cfile);
				break;
			}
			parse_option_space_decl (cfile);
			return;
		}

598
		known = 0;
599 600
		status = parse_option_name(cfile, 1, &known, &option);
		if (status != ISC_R_SUCCESS || option == NULL)
Ted Lemon's avatar
Ted Lemon committed
601 602
			return;

603
		token = next_token (&val, (unsigned *)0, cfile);
Ted Lemon's avatar
Ted Lemon committed
604
		if (token != CODE) {
605
			parse_warn (cfile, "expecting \"code\" keyword.");
Ted Lemon's avatar
Ted Lemon committed
606
			skip_to_semi (cfile);
607
			option_dereference(&option, MDL);
Ted Lemon's avatar
Ted Lemon committed
608 609 610
			return;
		}
		if (ip) {
611 612
			parse_warn (cfile,
				    "option definitions may only appear in %s",
Ted Lemon's avatar
Ted Lemon committed
613 614
				    "the outermost scope.");
			skip_to_semi (cfile);
615
			option_dereference(&option, MDL);
Ted Lemon's avatar
Ted Lemon committed
616 617
			return;
		}
618 619 620 621 622 623 624 625 626 627 628 629

		/*
		 * If the option was known, remove it from the code and name
		 * hash tables before redefining it.
		 */
		if (known) {
			option_name_hash_delete(option->universe->name_hash,
						option->name, 0, MDL);
			option_code_hash_delete(option->universe->code_hash,
						&option->code, 0, MDL);
		}

630 631
		parse_option_code_definition(cfile, option);
		option_dereference(&option, MDL);
Ted Lemon's avatar
Ted Lemon committed
632 633
		return;

634
	      case MEDIA:
635
		skip_token(&val, (unsigned *)0, cfile);
636 637
		parse_string_list (cfile, &config -> media, 1);
		return;
638 639

	      case HARDWARE:
640
		skip_token(&val, (unsigned *)0, cfile);
641 642 643
		if (ip) {
			parse_hardware_param (cfile, &ip -> hw_address);
		} else {
644
			parse_warn (cfile, "hardware address parameter %s",
645 646 647 648 649
				    "not allowed here.");
			skip_to_semi (cfile);
		}
		return;

650
	      case ANYCAST_MAC:
651
		skip_token(&val, NULL, cfile);
652 653 654 655 656 657 658 659 660
		if (ip != NULL) {
			parse_hardware_param(cfile, &ip->anycast_mac_addr);
		} else {
			parse_warn(cfile, "anycast mac address parameter "
				   "not allowed here.");
			skip_to_semi (cfile);
		}
		return;

661
	      case REQUEST:
662
		skip_token(&val, (unsigned *)0, cfile);
Ted Lemon's avatar
Ted Lemon committed
663
		if (config -> requested_options == default_requested_options)
664
			config -> requested_options = NULL;
665
		parse_option_list (cfile, &config -> requested_options);
666 667 668
		return;

	      case TIMEOUT:
669
		skip_token(&val, (unsigned *)0, cfile);
670 671 672 673
		parse_lease_time (cfile, &config -> timeout);
		return;

	      case RETRY:
674
		skip_token(&val, (unsigned *)0, cfile);
675 676 677 678
		parse_lease_time (cfile, &config -> retry_interval);
		return;

	      case SELECT_TIMEOUT:
679
		skip_token(&val, (unsigned *)0, cfile);
680 681 682
		parse_lease_time (cfile, &config -> select_interval);
		return;

683
	      case OMAPI:
684
		skip_token(&val, (unsigned *)0, cfile);
685
		token = next_token (&val, (unsigned *)0, cfile);
686 687 688 689 690 691
		if (token != PORT) {
			parse_warn (cfile,
				    "unexpected omapi subtype: %s", val);
			skip_to_semi (cfile);
			return;
		}
692
		token = next_token (&val, (unsigned *)0, cfile);
693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708
		if (token != NUMBER) {
			parse_warn (cfile, "invalid port number: `%s'", val);
			skip_to_semi (cfile);
			return;
		}
		tmp = atoi (val);
		if (tmp < 0 || tmp > 65535)
			parse_warn (cfile, "invalid omapi port %d.", tmp);
		else if (config != &top_level_config)
			parse_warn (cfile,
				    "omapi port only works at top level.");
		else
			config -> omapi_port = tmp;
		parse_semi (cfile);
		return;
		
709
	      case DO_FORWARD_UPDATE:
710
		skip_token(&val, (unsigned *)0, cfile);
711 712 713 714 715 716 717 718 719 720 721 722 723 724 725
		token = next_token (&val, (unsigned *)0, cfile);
		if (!strcasecmp (val, "on") ||
		    !strcasecmp (val, "true"))
			config -> do_forward_update = 1;
		else if (!strcasecmp (val, "off") ||
			 !strcasecmp (val, "false"))
			config -> do_forward_update = 0;
		else {
			parse_warn (cfile, "expecting boolean value.");
			skip_to_semi (cfile);
			return;
		}
		parse_semi (cfile);
		return;

726
	      case REBOOT:
727
		skip_token(&val, (unsigned *)0, cfile);
728 729 730
		parse_lease_time (cfile, &config -> reboot_timeout);
		return;

731
	      case BACKOFF_CUTOFF:
732
		skip_token(&val, (unsigned *)0, cfile);
733 734 735 736
		parse_lease_time (cfile, &config -> backoff_cutoff);
		return;

	      case INITIAL_INTERVAL:
737
		skip_token(&val, (unsigned *)0, cfile);
738 739 740
		parse_lease_time (cfile, &config -> initial_interval);
		return;

741
	      case INITIAL_DELAY:
742
		skip_token(&val, (unsigned *)0, cfile);
743 744 745
		parse_lease_time (cfile, &config -> initial_delay);
		return;

746
	      case SCRIPT:
747
		skip_token(&val, (unsigned *)0, cfile);
748
		parse_string (cfile, &config -> script_name, (unsigned *)0);
749 750
		return;

751
	      case VENDOR:
752
		skip_token(&val, (unsigned *)0, cfile);
753
		token = next_token (&val, (unsigned *)0, cfile);
754 755 756 757 758
		if (token != OPTION) {
			parse_warn (cfile, "expecting 'vendor option space'");
			skip_to_semi (cfile);
			return;
		}
759
		token = next_token (&val, (unsigned *)0, cfile);
760 761 762 763 764
		if (token != SPACE) {
			parse_warn (cfile, "expecting 'vendor option space'");
			skip_to_semi (cfile);
			return;
		}
765
		token = next_token (&val, (unsigned *)0, cfile);
766 767 768 769 770 771 772 773 774
		if (!is_identifier (token)) {
			parse_warn (cfile, "expecting an identifier.");
			skip_to_semi (cfile);
			return;
		}
		config -> vendor_space_name = dmalloc (strlen (val) + 1, MDL);
		if (!config -> vendor_space_name)
			log_fatal ("no memory for vendor option space name.");
		strcpy (config -> vendor_space_name, val);
775 776 777 778 779 780 781 782
		for (i = 0; i < universe_count; i++)
			if (!strcmp (universes [i] -> name,
				     config -> vendor_space_name))
				break;
		if (i == universe_count) {
			log_error ("vendor option space %s not found.",
				   config -> vendor_space_name);
		}
783
		parse_semi (cfile);
784 785
		return;

786
	      case INTERFACE:
787
		skip_token(&val, (unsigned *)0, cfile);
788
		if (ip)
789
			parse_warn (cfile, "nested interface declaration.");
Ted Lemon's avatar
Ted Lemon committed
790
		parse_interface_declaration (cfile, config, (char *)0);
791 792
		return;

Ted Lemon's avatar
Ted Lemon committed
793
	      case PSEUDO:
794
		skip_token(&val, (unsigned *)0, cfile);
795
		token = next_token (&val, (unsigned *)0, cfile);
Ted Lemon's avatar
Ted Lemon committed
796
		name = dmalloc (strlen (val) + 1, MDL);
Ted Lemon's avatar
Ted Lemon committed
797
		if (!name)
798
			log_fatal ("no memory for pseudo interface name");
Ted Lemon's avatar
Ted Lemon committed
799 800 801 802
		strcpy (name, val);
		parse_interface_declaration (cfile, config, name);
		return;
		
803
	      case LEASE:
804
		skip_token(&val, (unsigned *)0, cfile);
805
		parse_client_lease_statement (cfile, 1);
806 807
		return;

Ted Lemon's avatar
Ted Lemon committed
808
	      case ALIAS:
809
		skip_token(&val, (unsigned *)0, cfile);
Ted Lemon's avatar
Ted Lemon committed
810 811 812
		parse_client_lease_statement (cfile, 2);
		return;

Ted Lemon's avatar
Ted Lemon committed
813
	      case REJECT:
814
		skip_token(&val, (unsigned *)0, cfile);
Ted Lemon's avatar
Ted Lemon committed
815 816 817
		parse_reject_statement (cfile, config);
		return;

818 819 820 821 822 823
	      case LEASE_ID_FORMAT:
		skip_token(&val, (unsigned *)0, cfile);
		parse_lease_id_format(cfile);
		break;


824
	      default:
825
		lose = 0;
Ted Lemon's avatar
Ted Lemon committed
826
		stmt = (struct executable_statement *)0;
827 828
		if (!parse_executable_statement (&stmt,
						 cfile, &lose, context_any)) {
829
			if (!lose) {
830
				parse_warn (cfile, "expecting a statement.");
831 832 833
				skip_to_semi (cfile);
			}
		} else {
Ted Lemon's avatar
Ted Lemon committed
834
			struct executable_statement **eptr, *sptr;
835 836 837 838
			if (stmt &&
			    (stmt -> op == send_option_statement ||
			     (stmt -> op == on_statement &&
			      (stmt -> data.on.evtypes & ON_TRANSMISSION)))) {
839
			    eptr = &config -> on_transmission -> statements;
Ted Lemon's avatar
Ted Lemon committed
840 841 842 843 844 845 846 847 848 849 850 851 852 853
			    if (stmt -> op == on_statement) {
				    sptr = (struct executable_statement *)0;
				    executable_statement_reference
					    (&sptr,
					     stmt -> data.on.statements, MDL);
				    executable_statement_dereference (&stmt,
								      MDL);
				    executable_statement_reference (&stmt,
								    sptr,
								    MDL);
				    executable_statement_dereference (&sptr,
								      MDL);
			    }
			} else
854 855
			    eptr = &config -> on_receipt -> statements;

856 857 858 859 860 861
			if (stmt) {
				for (; *eptr; eptr = &(*eptr) -> next)
					;
				executable_statement_reference (eptr,
								stmt, MDL);
			}
862 863
			return;
		}
864 865
		break;
	}
866
	parse_semi (cfile);
867 868 869 870 871
}

/* option-list :== option_name |
   		   option_list COMMA option_name */

872 873
int
parse_option_list(struct parse *cfile, struct option ***list)
874
{
875
	int ix;
876
	int token;
877
	const char *val;
878
	pair p = (pair)0, q = (pair)0, r;
879 880
	struct option *option = NULL;
	isc_result_t status;
881 882 883

	ix = 0;
	do {
884 885 886
		token = peek_token (&val, (unsigned *)0, cfile);
		if (token == SEMI) {
			token = next_token (&val, (unsigned *)0, cfile);
887
			break;
888
		}
889
		if (!is_identifier (token)) {
Ted Lemon's avatar
Ted Lemon committed
890
			parse_warn (cfile, "%s: expected option name.", val);
891
			skip_token(&val, (unsigned *)0, cfile);
892
			skip_to_semi (cfile);
893
			return 0;
894
		}
895 896
		status = parse_option_name(cfile, 0, NULL, &option);
		if (status != ISC_R_SUCCESS || option == NULL) {
897
			parse_warn (cfile, "%s: expected option name.", val);
898
			return 0;
899
		}
Ted Lemon's avatar
Ted Lemon committed
900
		r = new_pair (MDL);
901
		if (!r)
902
			log_fatal ("can't allocate pair for option code.");
903 904
		/* XXX: we should probably carry a reference across this */
		r->car = (caddr_t)option;
905
		option_dereference(&option, MDL);
906 907 908 909 910 911 912
		r -> cdr = (pair)0;
		if (p)
			q -> cdr = r;
		else
			p = r;
		q = r;
		++ix;
913
		token = next_token (&val, (unsigned *)0, cfile);
914 915
	} while (token == COMMA);
	if (token != SEMI) {
916
		parse_warn (cfile, "expecting semicolon.");
917
		skip_to_semi (cfile);
918
		return 0;
919
	}
920 921
	/* XXX we can't free the list here, because we may have copied
	   XXX it from an outer config state. */
922
	*list = NULL;
923
	if (ix) {
924
		*list = dmalloc ((ix + 1) * sizeof(struct option *), MDL);
925 926 927 928 929
		if (!*list)
			log_error ("no memory for option list.");
		else {
			ix = 0;
			for (q = p; q; q = q -> cdr)
930 931 932
				option_reference(&(*list)[ix++],
						 (struct option *)q->car, MDL);
			(*list)[ix] = NULL;
933 934 935
		}
		while (p) {
			q = p -> cdr;
Ted Lemon's avatar
Ted Lemon committed
936
			free_pair (p, MDL);
937 938
			p = q;
		}
939
	}
940 941

	return ix;
942 943 944 945 946
}

/* interface-declaration :==
   	INTERFACE string LBRACE client-declarations RBRACE */

Ted Lemon's avatar
Ted Lemon committed
947
void parse_interface_declaration (cfile, outer_config, name)
948
	struct parse *cfile;
949
	struct client_config *outer_config;
Ted Lemon's avatar
Ted Lemon committed
950
	char *name;
951 952
{
	int token;
953
	const char *val;
Ted Lemon's avatar
Ted Lemon committed
954
	struct client_state *client, **cp;
955
	struct interface_info *ip = (struct interface_info *)0;
956

957
	token = next_token (&val, (unsigned *)0, cfile);
958
	if (token != STRING) {
959
		parse_warn (cfile, "expecting interface name (in quotes).");
960 961 962 963
		skip_to_semi (cfile);
		return;
	}

964 965
	if (!interface_or_dummy (&ip, val))
		log_fatal ("Can't allocate interface %s.", val);
966

Ted Lemon's avatar
Ted Lemon committed
967 968 969 970 971 972 973 974 975 976 977 978 979 980 981
	/* If we were given a name, this is a pseudo-interface. */
	if (name) {
		make_client_state (&client);
		client -> name = name;
		client -> interface = ip;
		for (cp = &ip -> client; *cp; cp = &((*cp) -> next))
			;
		*cp = client;
	} else {
		if (!ip -> client) {
			make_client_state (&ip -> client);
			ip -> client -> interface = ip;
		}
		client = ip -> client;
	}
982

Ted Lemon's avatar
Ted Lemon committed
983 984
	if (!client -> config)
		make_client_config (client, outer_config);
985

986 987 988
	ip -> flags &= ~INTERFACE_AUTOMATIC;
	interfaces_requested = 1;

989
	token = next_token (&val, (unsigned *)0, cfile);
990
	if (token != LBRACE) {
991
		parse_warn (cfile, "expecting left brace.");
992 993 994 995 996
		skip_to_semi (cfile);
		return;
	}

	do {
997
		token = peek_token (&val, (unsigned *)0, cfile);
998
		if (token == END_OF_FILE) {
999 1000
			parse_warn (cfile,
				    "unterminated interface declaration.");
1001 1002 1003 1004
			return;
		}
		if (token == RBRACE)
			break;
Ted Lemon's avatar
Ted Lemon committed
1005
		parse_client_statement (cfile, ip, client -> config);
1006
	} while (1);
1007
	skip_token(&val, (unsigned *)0, cfile);
1008 1009
}

1010
int interface_or_dummy (struct interface_info **pi, const char *name)
1011
{
1012 1013
	struct interface_info *i;
	struct interface_info *ip = (struct interface_info *)0;
1014
	isc_result_t status;
1015 1016

	/* Find the interface (if any) that matches the name. */
1017 1018 1019
	for (i = interfaces; i; i = i -> next) {
		if (!strcmp (i -> name, name)) {
			interface_reference (&ip, i, MDL);
1020
			break;