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

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

/*
6
 * Copyright (c) 1996-2000 Internet Software Consortium.
Ted Lemon's avatar
Ted Lemon committed
7
 * All rights reserved.
8
 *
Ted Lemon's avatar
Ted Lemon committed
9 10 11
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
12
 *
Ted Lemon's avatar
Ted Lemon committed
13 14 15 16 17 18 19 20
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of The Internet Software Consortium nor the names
 *    of its contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
21
 *
Ted Lemon's avatar
Ted Lemon committed
22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
 * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
 * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * This software has been written for the Internet Software Consortium
 * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
 * To learn more about the Internet Software Consortium, see
 * ``http://www.isc.org/''.  To learn more about Vixie Enterprises,
 * see ``http://www.vix.com''.   To learn more about Nominum, Inc., see
 * ``http://www.nominum.com''.
42 43 44 45
 */

#ifndef lint
static char copyright[] =
46
"$Id: clparse.c,v 1.49 2000/08/03 20:59:31 neild Exp $ Copyright (c) 1996-2000 The Internet Software Consortium.  All rights reserved.\n";
47 48 49 50 51 52
#endif /* not lint */

#include "dhcpd.h"

static TIME parsed_time;

53 54
char client_script_name [] = "/etc/dhclient-script";

55 56
struct client_config top_level_config;

57 58 59 60 61 62 63 64 65 66 67
u_int32_t default_requested_options [] = {
	DHO_SUBNET_MASK,
	DHO_BROADCAST_ADDRESS,
	DHO_TIME_OFFSET,
	DHO_ROUTERS,
	DHO_DOMAIN_NAME,
	DHO_DOMAIN_NAME_SERVERS,
	DHO_HOST_NAME,
	0
};

68 69 70 71 72
/* client-conf-file :== client-declarations EOF
   client-declarations :== <nil>
			 | client-declaration
			 | client-declarations client-declaration */

73
isc_result_t read_client_conf ()
74
{
75 76
	int file;
	struct parse *cfile;
77
	const char *val;
78 79 80 81 82
	int token;
	int declaration = 0;
	struct client_config *config;
	struct client_state *state;
	struct interface_info *ip;
83
	isc_result_t status;
84 85

	/* Set up the initial dhcp option universe. */
86
	initialize_common_option_spaces ();
87 88 89 90

	/* Initialize the top level client configuration. */
	memset (&top_level_config, 0, sizeof top_level_config);

91 92 93 94 95
	/* 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
96 97
	top_level_config.backoff_cutoff = 15;
	top_level_config.initial_interval = 3;
Ted Lemon's avatar
Ted Lemon committed
98
	top_level_config.bootp_policy = P_ACCEPT;
99
	top_level_config.script_name = client_script_name;
100
	top_level_config.requested_options = default_requested_options;
101
	top_level_config.omapi_port = -1;
102

103
	group_allocate (&top_level_config.on_receipt, MDL);
Ted Lemon's avatar
Ted Lemon committed
104
	if (!top_level_config.on_receipt)
105
		log_fatal ("no memory for top-level on_receipt group");
Ted Lemon's avatar
Ted Lemon committed
106

107
	group_allocate (&top_level_config.on_transmission, MDL);
Ted Lemon's avatar
Ted Lemon committed
108
	if (!top_level_config.on_transmission)
109
		log_fatal ("no memory for top-level on_transmission group");
Ted Lemon's avatar
Ted Lemon committed
110

111 112 113 114
	if ((file = open (path_dhclient_conf, O_RDONLY)) >= 0) {
		cfile = (struct parse *)0;
		new_parse (&cfile, file, (char *)0, 0, path_dhclient_conf);

115 116 117 118 119 120 121 122 123
		do {
			token = peek_token (&val, cfile);
			if (token == EOF)
				break;
			parse_client_statement (cfile,
						(struct interface_info *)0,
						&top_level_config);
		} while (1);
		token = next_token (&val, cfile); /* Clear the peek buffer */
124 125 126 127 128
		status = (cfile -> warnings_occurred
			  ? ISC_R_BADPARSE
			  : ISC_R_SUCCESS);
		close (file);
		end_parse (&cfile);
129
	}
130 131

	/* Set up state and config structures for clients that don't
Ted Lemon's avatar
Ted Lemon committed
132
	   have per-interface configuration statements. */
133 134 135 136
	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
137
				dmalloc (sizeof (struct client_state), MDL);
138
			if (!ip -> client)
139
				log_fatal ("no memory for client state.");
140
			memset (ip -> client, 0, sizeof *(ip -> client));
141
			ip -> client -> interface = ip;
142
		}
Ted Lemon's avatar
Ted Lemon committed
143

144
		if (!ip -> client -> config) {
145 146
			if (!config) {
				config = (struct client_config *)
Ted Lemon's avatar
Ted Lemon committed
147 148
					dmalloc (sizeof (struct client_config),
						 MDL);
149
				if (!config)
150
				    log_fatal ("no memory for client config.");
151 152 153 154 155 156
				memcpy (config, &top_level_config,
					sizeof top_level_config);
			}
			ip -> client -> config = config;
		}
	}
157
	return status;
158 159 160 161
}

/* lease-file :== client-lease-statements EOF
   client-lease-statements :== <nil>
Ted Lemon's avatar
Ted Lemon committed
162
		     | client-lease-statements LEASE client-lease-statement */
163 164 165

void read_client_leases ()
{
166 167
	int file;
	struct parse *cfile;
168
	const char *val;
169 170 171 172
	int token;

	/* Open the lease file.   If we can't open it, just return -
	   we can safely trust the server to remember our state. */
173
	if ((file = open (path_dhclient_db, O_RDONLY)) < 0)
174
		return;
175 176 177
	cfile = (struct parse *)0;
	new_parse (&cfile, file, (char *)0, 0, path_dhclient_db);

178 179 180 181 182
	do {
		token = next_token (&val, cfile);
		if (token == EOF)
			break;
		if (token != LEASE) {
183
			log_error ("Corrupt lease file - possible data loss!");
184 185 186
			skip_to_semi (cfile);
			break;
		} else
187
			parse_client_lease_statement (cfile, 0);
188 189

	} while (1);
190 191 192

	close (file);
	end_parse (&cfile);
193 194 195
}

/* client-declaration :== 
196 197
	SEND option-decl |
	DEFAULT option-decl |
Ted Lemon's avatar
Ted Lemon committed
198
	SUPERSEDE option-decl |
199 200
	PREPEND option-decl |
	APPEND option-decl |
201 202 203 204 205
	hardware-declaration |
	REQUEST option-list |
	REQUIRE option-list |
	TIMEOUT number |
	RETRY number |
206
	REBOOT number |
207 208 209
	SELECT_TIMEOUT number |
	SCRIPT string |
	interface-declaration |
Ted Lemon's avatar
Ted Lemon committed
210
	LEASE client-lease-statement |
211
	ALIAS client-lease-statement |
212
	KEY key-definition */
213 214

void parse_client_statement (cfile, ip, config)
215
	struct parse *cfile;
216 217 218 219
	struct interface_info *ip;
	struct client_config *config;
{
	int token;
220
	const char *val;
221
	struct option *option;
Ted Lemon's avatar
Ted Lemon committed
222
	struct executable_statement *stmt, **p;
223
	enum statement_op op;
224
	int lose;
Ted Lemon's avatar
Ted Lemon committed
225
	char *name;
226
	struct data_string key_id;
227
	enum policy policy;
228
	int known;
229
	int tmp;
230

231
	switch (peek_token (&val, cfile)) {
232
	      case KEY:
233 234 235 236 237 238 239 240 241 242 243 244 245 246
		next_token (&val, cfile);
		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. */
247
			parse_warn (cfile, "key definition not allowed here.");
248 249 250
			skip_to_semi (cfile);
			break;
		}
251
		parse_key (cfile);
252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291
		return;

		/* REQUIRE can either start a policy statement or a
		   comma-seperated list of names of required options. */
	      case REQUIRE:
		next_token (&val, cfile);
		token = peek_token (&val, cfile);
		if (token == AUTHENTICATION) {
			policy = P_REQUIRE;
			goto do_policy;
		}
		parse_option_list (cfile, &config -> required_options);
		return;

	      case IGNORE:
		next_token (&val, cfile);
		policy = P_IGNORE;
		goto do_policy;

	      case ACCEPT:
		next_token (&val, cfile);
		policy = P_ACCEPT;
		goto do_policy;

	      case PREFER:
		next_token (&val, cfile);
		policy = P_PREFER;
		goto do_policy;

	      case DONT:
		next_token (&val, cfile);
		policy = P_DONT;
		goto do_policy;

	      do_policy:
		token = next_token (&val, cfile);
		if (token == AUTHENTICATION) {
			if (policy != P_PREFER &&
			    policy != P_REQUIRE &&
			    policy != P_DONT) {
292 293
				parse_warn (cfile,
					    "invalid authentication policy.");
294 295 296 297
				skip_to_semi (cfile);
				return;
			}
			config -> auth_policy = policy;
298
		} else if (token != TOKEN_BOOTP) {
299 300 301
			if (policy != P_PREFER &&
			    policy != P_IGNORE &&
			    policy != P_ACCEPT) {
302
				parse_warn (cfile, "invalid bootp policy.");
303 304 305 306 307
				skip_to_semi (cfile);
				return;
			}
			config -> bootp_policy = policy;
		} else {
308
			parse_warn (cfile, "expecting a policy type.");
309 310 311
			skip_to_semi (cfile);
			return;
		} 
312
		break;
313

314
	      case SEND:
Ted Lemon's avatar
Ted Lemon committed
315
		p = &config -> on_transmission -> statements;
316
		op = supersede_option_statement;
Ted Lemon's avatar
Ted Lemon committed
317 318
	      do_option:
		token = next_token (&val, cfile);
319 320
		known = 0;
		option = parse_option_name (cfile, 0, &known);
321 322
		if (!option)
			return;
Ted Lemon's avatar
Ted Lemon committed
323 324 325
		stmt = (struct executable_statement *)0;
		if (!parse_option_statement (&stmt, cfile, 1, option, op))
			return;
Ted Lemon's avatar
Ted Lemon committed
326 327
		for (; *p; p = &((*p) -> next))
			;
328
		executable_statement_reference (p, stmt, MDL);
Ted Lemon's avatar
Ted Lemon committed
329
		stmt -> next = (struct executable_statement *)0;
330 331
		return;

Ted Lemon's avatar
Ted Lemon committed
332 333
	      case OPTION:
		token = next_token (&val, cfile);
334 335 336 337

		token = peek_token (&val, cfile);
		if (token == SPACE) {
			if (ip) {
338 339
				parse_warn (cfile,
					    "option space definitions %s",
340 341 342 343 344 345 346 347
					    " may not be scoped.");
				skip_to_semi (cfile);
				break;
			}
			parse_option_space_decl (cfile);
			return;
		}

348
		option = parse_option_name (cfile, 1, &known);
Ted Lemon's avatar
Ted Lemon committed
349 350 351 352 353
		if (!option)
			return;

		token = next_token (&val, cfile);
		if (token != CODE) {
354
			parse_warn (cfile, "expecting \"code\" keyword.");
Ted Lemon's avatar
Ted Lemon committed
355
			skip_to_semi (cfile);
Ted Lemon's avatar
Ted Lemon committed
356
			free_option (option, MDL);
Ted Lemon's avatar
Ted Lemon committed
357 358 359
			return;
		}
		if (ip) {
360 361
			parse_warn (cfile,
				    "option definitions may only appear in %s",
Ted Lemon's avatar
Ted Lemon committed
362 363
				    "the outermost scope.");
			skip_to_semi (cfile);
Ted Lemon's avatar
Ted Lemon committed
364
			free_option (option, MDL);
Ted Lemon's avatar
Ted Lemon committed
365 366 367
			return;
		}
		if (!parse_option_code_definition (cfile, option))
Ted Lemon's avatar
Ted Lemon committed
368
			free_option (option, MDL);
Ted Lemon's avatar
Ted Lemon committed
369 370
		return;

371
	      case DEFAULT:
Ted Lemon's avatar
Ted Lemon committed
372
		p = &config -> on_receipt -> statements;
Ted Lemon's avatar
Ted Lemon committed
373 374
		op = default_option_statement;
		goto do_option;
375 376

	      case SUPERSEDE:
Ted Lemon's avatar
Ted Lemon committed
377
		p = &config -> on_receipt -> statements;
Ted Lemon's avatar
Ted Lemon committed
378 379
		op = supersede_option_statement;
		goto do_option;
380 381

	      case APPEND:
Ted Lemon's avatar
Ted Lemon committed
382
		p = &config -> on_receipt -> statements;
Ted Lemon's avatar
Ted Lemon committed
383 384
		op = append_option_statement;
		goto do_option;
385 386

	      case PREPEND:
Ted Lemon's avatar
Ted Lemon committed
387
		p = &config -> on_receipt -> statements;
Ted Lemon's avatar
Ted Lemon committed
388 389
		op = prepend_option_statement;
		goto do_option;
390 391

	      case MEDIA:
392
		token = next_token (&val, cfile);
393 394
		parse_string_list (cfile, &config -> media, 1);
		return;
395 396

	      case HARDWARE:
397
		token = next_token (&val, cfile);
398 399 400
		if (ip) {
			parse_hardware_param (cfile, &ip -> hw_address);
		} else {
401
			parse_warn (cfile, "hardware address parameter %s",
402 403 404 405 406 407
				    "not allowed here.");
			skip_to_semi (cfile);
		}
		return;

	      case REQUEST:
408
		token = next_token (&val, cfile);
Ted Lemon's avatar
Ted Lemon committed
409
		if (config -> requested_options == default_requested_options)
410
			config -> requested_options = (u_int32_t *)0;
411
		parse_option_list (cfile, &config -> requested_options);
412 413 414
		return;

	      case TIMEOUT:
415
		token = next_token (&val, cfile);
416 417 418 419
		parse_lease_time (cfile, &config -> timeout);
		return;

	      case RETRY:
420
		token = next_token (&val, cfile);
421 422 423 424
		parse_lease_time (cfile, &config -> retry_interval);
		return;

	      case SELECT_TIMEOUT:
425
		token = next_token (&val, cfile);
426 427 428
		parse_lease_time (cfile, &config -> select_interval);
		return;

429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453
	      case OMAPI:
		token = next_token (&val, cfile);
		if (token != PORT) {
			parse_warn (cfile,
				    "unexpected omapi subtype: %s", val);
			skip_to_semi (cfile);
			return;
		}
		token = next_token (&val, cfile);
		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;
		
454
	      case REBOOT:
455
		token = next_token (&val, cfile);
456 457 458
		parse_lease_time (cfile, &config -> reboot_timeout);
		return;

459
	      case BACKOFF_CUTOFF:
460
		token = next_token (&val, cfile);
461 462 463 464
		parse_lease_time (cfile, &config -> backoff_cutoff);
		return;

	      case INITIAL_INTERVAL:
465
		token = next_token (&val, cfile);
466 467 468
		parse_lease_time (cfile, &config -> initial_interval);
		return;

469
	      case SCRIPT:
470
		token = next_token (&val, cfile);
471 472 473 474
		config -> script_name = parse_string (cfile);
		return;

	      case INTERFACE:
475
		token = next_token (&val, cfile);
476
		if (ip)
477
			parse_warn (cfile, "nested interface declaration.");
Ted Lemon's avatar
Ted Lemon committed
478
		parse_interface_declaration (cfile, config, (char *)0);
479 480
		return;

Ted Lemon's avatar
Ted Lemon committed
481 482 483
	      case PSEUDO:
		token = next_token (&val, cfile);
		token = next_token (&val, cfile);
Ted Lemon's avatar
Ted Lemon committed
484
		name = dmalloc (strlen (val) + 1, MDL);
Ted Lemon's avatar
Ted Lemon committed
485
		if (!name)
486
			log_fatal ("no memory for pseudo interface name");
Ted Lemon's avatar
Ted Lemon committed
487 488 489 490
		strcpy (name, val);
		parse_interface_declaration (cfile, config, name);
		return;
		
491
	      case LEASE:
492
		token = next_token (&val, cfile);
493
		parse_client_lease_statement (cfile, 1);
494 495
		return;

Ted Lemon's avatar
Ted Lemon committed
496
	      case ALIAS:
497
		token = next_token (&val, cfile);
Ted Lemon's avatar
Ted Lemon committed
498 499 500
		parse_client_lease_statement (cfile, 2);
		return;

Ted Lemon's avatar
Ted Lemon committed
501
	      case REJECT:
502
		token = next_token (&val, cfile);
Ted Lemon's avatar
Ted Lemon committed
503 504 505
		parse_reject_statement (cfile, config);
		return;

506
	      default:
507
		lose = 0;
Ted Lemon's avatar
Ted Lemon committed
508
		stmt = (struct executable_statement *)0;
509 510
		if (!parse_executable_statement (&stmt,
						 cfile, &lose, context_any)) {
511
			if (!lose) {
512
				parse_warn (cfile, "expecting a statement.");
513 514 515
				skip_to_semi (cfile);
			}
		} else {
Ted Lemon's avatar
Ted Lemon committed
516
			if (!config -> on_receipt -> statements) {
Ted Lemon's avatar
Ted Lemon committed
517 518
				executable_statement_reference
					(&config -> on_receipt -> statements,
Ted Lemon's avatar
Ted Lemon committed
519
					 stmt, MDL);
520 521
			} else {
				struct executable_statement *s;
Ted Lemon's avatar
Ted Lemon committed
522
				for (s = config -> on_receipt -> statements;
523 524
				     s -> next; s = s -> next)
					;
Ted Lemon's avatar
Ted Lemon committed
525 526
				executable_statement_reference (&s -> next,
								stmt, MDL);
527 528 529
			}
			return;
		}
530 531
		break;
	}
532
	parse_semi (cfile);
533 534
}

535
int parse_X (cfile, buf, max)
536
	struct parse *cfile;
537
	u_int8_t *buf;
538
	unsigned max;
539 540
{
	int token;
541 542
	const char *val;
	unsigned len;
543
	u_int8_t *s;
544 545 546

	token = peek_token (&val, cfile);
	if (token == NUMBER_OR_NAME || token == NUMBER) {
547
		len = 0;
548 549 550
		do {
			token = next_token (&val, cfile);
			if (token != NUMBER && token != NUMBER_OR_NAME) {
551 552
				parse_warn (cfile,
					    "expecting hexadecimal constant.");
553 554 555
				skip_to_semi (cfile);
				return 0;
			}
556
			convert_num (cfile, &buf [len], val, 16, 8);
557
			if (len++ > max) {
558 559
				parse_warn (cfile,
					    "hexadecimal constant too long.");
560 561 562 563 564 565 566
				skip_to_semi (cfile);
				return 0;
			}
			token = peek_token (&val, cfile);
			if (token == COLON)
				token = next_token (&val, cfile);
		} while (token == COLON);
Ted Lemon's avatar
Ted Lemon committed
567
		val = (char *)buf;
568 569
	} else if (token == STRING) {
		token = next_token (&val, cfile);
570 571
		len = strlen (val);
		if (len + 1 > max) {
572
			parse_warn (cfile, "string constant too long.");
573 574 575 576
			skip_to_semi (cfile);
			return 0;
		}
		memcpy (buf, val, len + 1);
577
	} else {
578
		parse_warn (cfile, "expecting string or hexadecimal data");
579 580 581
		skip_to_semi (cfile);
		return 0;
	}
582
	return len;
583 584 585 586 587
}

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

588
void parse_option_list (cfile, list)
589
	struct parse *cfile;
590
	u_int32_t **list;
591 592 593
{
	int ix, i;
	int token;
594
	const char *val;
595
	pair p = (pair)0, q, r;
596 597 598 599

	ix = 0;
	do {
		token = next_token (&val, cfile);
600 601
		if (token == SEMI)
			break;
602
		if (!is_identifier (token)) {
Ted Lemon's avatar
Ted Lemon committed
603
			parse_warn (cfile, "%s: expected option name.", val);
604
			skip_to_semi (cfile);
605
			return;
606 607 608 609 610 611
		}
		for (i = 0; i < 256; i++) {
			if (!strcasecmp (dhcp_options [i].name, val))
				break;
		}
		if (i == 256) {
612
			parse_warn (cfile, "%s: expected option name.", val);
613
			skip_to_semi (cfile);
614
			return;
615
		}
Ted Lemon's avatar
Ted Lemon committed
616
		r = new_pair (MDL);
617
		if (!r)
618
			log_fatal ("can't allocate pair for option code.");
619 620 621 622 623 624 625 626
		r -> car = (caddr_t)i;
		r -> cdr = (pair)0;
		if (p)
			q -> cdr = r;
		else
			p = r;
		q = r;
		++ix;
627 628 629
		token = next_token (&val, cfile);
	} while (token == COMMA);
	if (token != SEMI) {
630
		parse_warn (cfile, "expecting semicolon.");
631
		skip_to_semi (cfile);
632 633 634
		return;
	}
	if (*list)
Ted Lemon's avatar
Ted Lemon committed
635
		dfree (*list, MDL);
636
	if (ix) {
Ted Lemon's avatar
Ted Lemon committed
637
		*list = dmalloc ((ix + 1) * sizeof **list, MDL);
638 639 640 641 642 643 644 645 646 647
		if (!*list)
			log_error ("no memory for option list.");
		else {
			ix = 0;
			for (q = p; q; q = q -> cdr)
				(*list) [ix++] = (u_int32_t)q -> car;
			(*list) [ix] = 0;
		}
		while (p) {
			q = p -> cdr;
Ted Lemon's avatar
Ted Lemon committed
648
			free_pair (p, MDL);
649 650
			p = q;
		}
651 652 653 654 655 656
	}
}

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

Ted Lemon's avatar
Ted Lemon committed
657
void parse_interface_declaration (cfile, outer_config, name)
658
	struct parse *cfile;
659
	struct client_config *outer_config;
Ted Lemon's avatar
Ted Lemon committed
660
	char *name;
661 662
{
	int token;
663
	const char *val;
Ted Lemon's avatar
Ted Lemon committed
664
	struct client_state *client, **cp;
665
	struct interface_info *ip = (struct interface_info *)0;
666 667 668

	token = next_token (&val, cfile);
	if (token != STRING) {
669
		parse_warn (cfile, "expecting interface name (in quotes).");
670 671 672 673
		skip_to_semi (cfile);
		return;
	}

674 675
	if (!interface_or_dummy (&ip, val))
		log_fatal ("Can't allocate interface %s.", val);
676

Ted Lemon's avatar
Ted Lemon committed
677 678 679 680 681 682 683 684 685 686 687 688 689 690 691
	/* 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;
	}
692

Ted Lemon's avatar
Ted Lemon committed
693 694
	if (!client -> config)
		make_client_config (client, outer_config);
695

696 697 698
	ip -> flags &= ~INTERFACE_AUTOMATIC;
	interfaces_requested = 1;

699 700
	token = next_token (&val, cfile);
	if (token != LBRACE) {
701
		parse_warn (cfile, "expecting left brace.");
702 703 704 705 706 707 708
		skip_to_semi (cfile);
		return;
	}

	do {
		token = peek_token (&val, cfile);
		if (token == EOF) {
709 710
			parse_warn (cfile,
				    "unterminated interface declaration.");
711 712 713 714
			return;
		}
		if (token == RBRACE)
			break;
Ted Lemon's avatar
Ted Lemon committed
715
		parse_client_statement (cfile, ip, client -> config);
716 717 718 719
	} while (1);
	token = next_token (&val, cfile);
}

720
int interface_or_dummy (struct interface_info **pi, const char *name)
721
{
722 723
	struct interface_info *i;
	struct interface_info *ip = (struct interface_info *)0;
724
	isc_result_t status;
725 726

	/* Find the interface (if any) that matches the name. */
727 728 729
	for (i = interfaces; i; i = i -> next) {
		if (!strcmp (i -> name, name)) {
			interface_reference (&ip, i, MDL);
730
			break;
731
		}
732 733 734 735 736
	}

	/* If it's not a real interface, see if it's on the dummy list. */
	if (!ip) {
		for (ip = dummy_interfaces; ip; ip = ip -> next) {
737 738
			if (!strcmp (ip -> name, name)) {
				interface_reference (&ip, i, MDL);
739
				break;
740
			}
741 742 743 744 745 746
		}
	}

	/* If we didn't find an interface, make a dummy interface as
	   a placeholder. */
	if (!ip) {
747 748 749 750 751
		isc_result_t status;
		status = interface_allocate (&ip, MDL);
		if (status != ISC_R_SUCCESS)
			log_fatal ("Can't record interface %s: %s",
				   name, isc_result_totext (status));
752
		strcpy (ip -> name, name);
753 754 755 756 757 758
		if (dummy_interfaces) {
			interface_reference (&ip -> next,
					     dummy_interfaces, MDL);
			interface_dereference (&dummy_interfaces, MDL);
		}
		interface_reference (&dummy_interfaces, ip, MDL);
759
	}
760
	if (pi)
761
		status = interface_reference (pi, ip, MDL);
762
	interface_dereference (&ip, MDL);
763 764
	if (status != ISC_R_SUCCESS)
		return 0;
765
	return 1;
766 767
}

Ted Lemon's avatar
Ted Lemon committed
768 769
void make_client_state (state)
	struct client_state **state;
770
{
Ted Lemon's avatar
Ted Lemon committed
771
	*state = ((struct client_state *)dmalloc (sizeof **state, MDL));
Ted Lemon's avatar
Ted Lemon committed
772
	if (!*state)
773
		log_fatal ("no memory for client state\n");
Ted Lemon's avatar
Ted Lemon committed
774
	memset (*state, 0, sizeof **state);
775 776
}

Ted Lemon's avatar
Ted Lemon committed
777 778
void make_client_config (client, config)
	struct client_state *client;
779 780
	struct client_config *config;
{
Ted Lemon's avatar
Ted Lemon committed
781
	client -> config = (((struct client_config *)
Ted Lemon's avatar
Ted Lemon committed
782
			     dmalloc (sizeof (struct client_config), MDL)));
Ted Lemon's avatar
Ted Lemon committed
783
	if (!client -> config)
784
		log_fatal ("no memory for client config\n");
Ted Lemon's avatar
Ted Lemon committed
785
	memcpy (client -> config, config, sizeof *config);
786 787 788 789 790
	if (!clone_group (&client -> config -> on_receipt,
			  config -> on_receipt, MDL) ||
	    !clone_group (&client -> config -> on_transmission,
			  config -> on_transmission, MDL))
		log_fatal ("no memory for client state groups.");
791 792
}

793
/* client-lease-statement :==
Ted Lemon's avatar
Ted Lemon committed
794
	RBRACE client-lease-declarations LBRACE
795 796 797 798 799 800 801

	client-lease-declarations :==
		<nil> |
		client-lease-declaration |
		client-lease-declarations client-lease-declaration */


802
void parse_client_lease_statement (cfile, is_static)
803
	struct parse *cfile;
804
	int is_static;
805 806
{
	struct client_lease *lease, *lp, *pl;
Ted Lemon's avatar
Ted Lemon committed
807
	struct interface_info *ip = (struct interface_info *)0;
808
	int token;
809
	const char *val;
Ted Lemon's avatar
Ted Lemon committed
810
	struct client_state *client = (struct client_state *)0;
811 812 813

	token = next_token (&val, cfile);
	if (token != LBRACE) {
814
		parse_warn (cfile, "expecting left brace.");
815 816 817 818
		skip_to_semi (cfile);
		return;
	}

Ted Lemon's avatar
Ted Lemon committed
819 820
	lease = ((struct client_lease *)
		 dmalloc (sizeof (struct client_lease), MDL));
821
	if (!lease)
822
		log_fatal ("no memory for lease.\n");
823
	memset (lease, 0, sizeof *lease);
824
	lease -> is_static = is_static;
Ted Lemon's avatar
Ted Lemon committed
825
	if (!option_state_allocate (&lease -> options, MDL))
826
		log_fatal ("no memory for lease options.\n");
827 828 829 830

	do {
		token = peek_token (&val, cfile);
		if (token == EOF) {
831
			parse_warn (cfile, "unterminated lease declaration.");
832 833 834 835
			return;
		}
		if (token == RBRACE)
			break;
Ted Lemon's avatar
Ted Lemon committed
836
		parse_client_lease_declaration (cfile, lease, &ip, &client);
837 838 839 840 841 842
	} while (1);
	token = next_token (&val, cfile);

	/* If the lease declaration didn't include an interface
	   declaration that we recognized, it's of no use to us. */
	if (!ip) {
843
		destroy_client_lease (lease);
844 845 846
		return;
	}

847
	/* Make sure there's a client state structure... */
Ted Lemon's avatar
Ted Lemon committed
848 849 850 851 852 853
	if (!ip -> client) {
		make_client_state (&ip -> client);
		ip -> client -> interface = ip;
	}
	if (!client)
		client = ip -> client;
854

Ted Lemon's avatar
Ted Lemon committed
855 856 857 858 859 860
	/* If this is an alias lease, it doesn't need to be sorted in. */
	if (is_static == 2) {
		ip -> client -> alias = lease;
		return;
	}

861 862 863 864 865
	/* The new lease may supersede a lease that's not the
	   active lease but is still on the lease list, so scan the
	   lease list looking for a lease with the same address, and
	   if we find it, toss it. */
	pl = (struct client_lease *)0;
Ted Lemon's avatar
Ted Lemon committed
866
	for (lp = client -> leases; lp; lp = lp -> next) {
867 868 869 870 871 872
		if (lp -> address.len == lease -> address.len &&
		    !memcmp (lp -> address.iabuf, lease -> address.iabuf,
			     lease -> address.len)) {
			if (pl)
				pl -> next = lp -> next;
			else
Ted Lemon's avatar
Ted Lemon committed
873
				client -> leases = lp -> next;
874
			destroy_client_lease (lp);
875 876 877 878 879 880 881
			break;
		}
	}

	/* If this is a preloaded lease, just put it on the list of recorded
	   leases - don't make it the active lease. */
	if (is_static) {
Ted Lemon's avatar
Ted Lemon committed
882 883
		lease -> next = client -> leases;
		client -> leases = lease;
884 885 886
		return;
	}
		
887 888 889 890 891 892 893 894 895 896 897
	/* The last lease in the lease file on a particular interface is
	   the active lease for that interface.    Of course, we don't know
	   what the last lease in the file is until we've parsed the whole
	   file, so at this point, we assume that the lease we just parsed
	   is the active lease for its interface.   If there's already
	   an active lease for the interface, and this lease is for the same
	   ip address, then we just toss the old active lease and replace
	   it with this one.   If this lease is for a different address,
	   then if the old active lease has expired, we dump it; if not,
	   we put it on the list of leases for this interface which are
	   still valid but no longer active. */
Ted Lemon's avatar
Ted Lemon committed
898