parse.c 118 KB
Newer Older
1 2 3 4 5
/* parse.c

   Common parser code for dhcpd and dhclient. */

/*
Ted Lemon's avatar
Ted Lemon committed
6
 * Copyright (c) 1995-2001 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: parse.c,v 1.105 2001/06/27 00:29:56 mellon Exp $ Copyright (c) 1995-2001 The Internet Software Consortium.  All rights reserved.\n";
47 48 49 50
#endif /* not lint */

#include "dhcpd.h"

51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
/* Enumerations can be specified in option formats, and are used for
   parsing, so we define the routines that manage them here. */

struct enumeration *enumerations;

void add_enumeration (struct enumeration *enumeration)
{
	enumeration -> next = enumerations;
	enumerations = enumeration;
}

struct enumeration *find_enumeration (const char *name, int length)
{
	struct enumeration *e;

	for (e = enumerations; e; e = e -> next)
		if (strlen (e -> name) == length &&
		    !memcmp (e -> name, name, (unsigned)length))
			return e;
	return (struct enumeration *)0;
}

struct enumeration_value *find_enumeration_value (const char *name,
						  int length,
						  const char *value)
{
	struct enumeration *e;
	int i;

	e = find_enumeration (name, length);
	if (e) {
		for (i = 0; e -> values [i].name; i++) {
			if (!strcmp (value, e -> values [i].name))
				return &e -> values [i];
		}
	}
	return (struct enumeration_value *)0;
}

90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
/* Skip to the semicolon ending the current statement.   If we encounter
   braces, the matching closing brace terminates the statement.   If we
   encounter a right brace but haven't encountered a left brace, return
   leaving the brace in the token buffer for the caller.   If we see a
   semicolon and haven't seen a left brace, return.   This lets us skip
   over:

   	statement;
	statement foo bar { }
	statement foo bar { statement { } }
	statement}
 
	...et cetera. */

void skip_to_semi (cfile)
105
	struct parse *cfile;
106 107 108 109 110
{
	skip_to_rbrace (cfile, 0);
}

void skip_to_rbrace (cfile, brace_count)
111
	struct parse *cfile;
112
	int brace_count;
113
{
114
	enum dhcp_token token;
115
	const char *val;
116

117 118 119
#if defined (DEBUG_TOKEN)
	log_error ("skip_to_rbrace: %d\n", brace_count);
#endif
120
	do {
121
		token = peek_token (&val, (unsigned *)0, cfile);
122
		if (token == RBRACE) {
123
			token = next_token (&val, (unsigned *)0, cfile);
124 125 126 127 128 129 130 131
			if (brace_count) {
				if (!--brace_count)
					return;
			} else
				return;
		} else if (token == LBRACE) {
			brace_count++;
		} else if (token == SEMI && !brace_count) {
132
			token = next_token (&val, (unsigned *)0, cfile);
133
			return;
134 135 136 137
		} else if (token == EOL) {
			/* EOL only happens when parsing /etc/resolv.conf,
			   and we treat it like a semicolon because the
			   resolv.conf file is line-oriented. */
138
			token = next_token (&val, (unsigned *)0, cfile);
139
			return;
140
		}
141
		token = next_token (&val, (unsigned *)0, cfile);
142
	} while (token != END_OF_FILE);
143 144 145
}

int parse_semi (cfile)
146
	struct parse *cfile;
147
{
148
	enum dhcp_token token;
149
	const char *val;
150

151
	token = next_token (&val, (unsigned *)0, cfile);
152
	if (token != SEMI) {
153
		parse_warn (cfile, "semicolon expected.");
154 155 156 157 158 159 160 161
		skip_to_semi (cfile);
		return 0;
	}
	return 1;
}

/* string-parameter :== STRING SEMI */

162
int parse_string (cfile, sptr, lptr)
163
	struct parse *cfile;
164 165
	char **sptr;
	unsigned *lptr;
166
{
167
	const char *val;
168
	enum dhcp_token token;
169
	char *s;
170
	unsigned len;
171

172
	token = next_token (&val, &len, cfile);
173
	if (token != STRING) {
174
		parse_warn (cfile, "expecting a string");
175
		skip_to_semi (cfile);
176
		return 0;
177
	}
178
	s = (char *)dmalloc (len + 1, MDL);
179
	if (!s)
180
		log_fatal ("no memory for string %s.", val);
181
	memcpy (s, val, len + 1);
182

183 184 185 186 187 188 189 190 191 192 193
	if (!parse_semi (cfile)) {
		dfree (s, MDL);
		return 0;
	}
	if (sptr)
		*sptr = s;
	else
		dfree (s, MDL);
	if (lptr)
		*lptr = len;
	return 1;
194 195
}

196 197 198 199 200
/*
 * hostname :== IDENTIFIER
 *		| IDENTIFIER DOT
 *		| hostname DOT IDENTIFIER
 */
201 202

char *parse_host_name (cfile)
203
	struct parse *cfile;
204
{
205
	const char *val;
206
	enum dhcp_token token;
207
	unsigned len = 0;
208 209 210
	char *s;
	char *t;
	pair c = (pair)0;
211
	int ltid = 0;
212 213 214 215
	
	/* Read a dotted hostname... */
	do {
		/* Read a token, which should be an identifier. */
216
		token = peek_token (&val, (unsigned *)0, cfile);
Ted Lemon's avatar
Ted Lemon committed
217 218
		if (!is_identifier (token) && token != NUMBER)
			break;
219
		token = next_token (&val, (unsigned *)0, cfile);
Ted Lemon's avatar
Ted Lemon committed
220

221
		/* Store this identifier... */
Ted Lemon's avatar
Ted Lemon committed
222
		if (!(s = (char *)dmalloc (strlen (val) + 1, MDL)))
223
			log_fatal ("can't allocate temp space for hostname.");
224 225 226 227 228
		strcpy (s, val);
		c = cons ((caddr_t)s, c);
		len += strlen (s) + 1;
		/* Look for a dot; if it's there, keep going, otherwise
		   we're done. */
229
		token = peek_token (&val, (unsigned *)0, cfile);
230
		if (token == DOT) {
231
			token = next_token (&val, (unsigned *)0, cfile);
232 233 234
			ltid = 1;
		} else
			ltid = 0;
235 236
	} while (token == DOT);

Ted Lemon's avatar
Ted Lemon committed
237 238 239 240
	/* Should be at least one token. */
	if (!len)
		return (char *)0;

241
	/* Assemble the hostname together into a string. */
242
	if (!(s = (char *)dmalloc (len + ltid, MDL)))
243
		log_fatal ("can't allocate space for hostname.");
244
	t = s + len + ltid;
245
	*--t = 0;
246 247
	if (ltid)
		*--t = '.';
248 249
	while (c) {
		pair cdr = c -> cdr;
250
		unsigned l = strlen ((char *)(c -> car));
251 252 253
		t -= l;
		memcpy (t, (char *)(c -> car), l);
		/* Free up temp space. */
Ted Lemon's avatar
Ted Lemon committed
254 255
		dfree (c -> car, MDL);
		dfree (c, MDL);
256 257 258 259 260 261 262
		c = cdr;
		if (t != s)
			*--t = '.';
	}
	return s;
}

263 264 265 266 267 268 269
/* ip-addr-or-hostname :== ip-address | hostname
   ip-address :== NUMBER DOT NUMBER DOT NUMBER DOT NUMBER
   
   Parse an ip address or a hostname.   If uniform is zero, put in
   an expr_substring node to limit hostnames that evaluate to more
   than one IP address. */

270 271
int parse_ip_addr_or_hostname (expr, cfile, uniform)
	struct expression **expr;
272
	struct parse *cfile;
273 274
	int uniform;
{
275
	const char *val;
276
	enum dhcp_token token;
277
	unsigned char addr [4];
278
	unsigned len = sizeof addr;
279
	char *name;
280
	struct expression *x = (struct expression *)0;
281

282
	token = peek_token (&val, (unsigned *)0, cfile);
283 284 285
	if (is_identifier (token)) {
		name = parse_host_name (cfile);
		if (!name)
286 287 288 289 290 291
			return 0;
		if (!make_host_lookup (expr, name))
			return 0;
		if (!uniform) {
			if (!make_limit (&x, *expr, 4))
				return 0;
292
			expression_dereference (expr, MDL);
293 294
			*expr = x;
		}
295 296
	} else if (token == NUMBER) {
		if (!parse_numeric_aggregate (cfile, addr, &len, DOT, 10, 8))
297
			return 0;
298
		return make_const_data (expr, addr, len, 0, 1, MDL);
299 300
	} else {
		if (token != RBRACE && token != LBRACE)
301
			token = next_token (&val, (unsigned *)0, cfile);
302
		parse_warn (cfile, "%s (%d): expecting IP address or hostname",
303 304 305
			    val, token);
		if (token != SEMI)
			skip_to_semi (cfile);
306
		return 0;
307 308
	}

309
	return 1;
310 311 312 313 314 315
}	
	
/*
 * ip-address :== NUMBER DOT NUMBER DOT NUMBER DOT NUMBER
 */

316
int parse_ip_addr (cfile, addr)
317
	struct parse *cfile;
318 319
	struct iaddr *addr;
{
320
	const char *val;
321
	enum dhcp_token token;
322 323 324 325 326 327 328 329

	addr -> len = 4;
	if (parse_numeric_aggregate (cfile, addr -> iabuf,
				     &addr -> len, DOT, 10, 8))
		return 1;
	return 0;
}	

330 331 332 333
/*
 * hardware-parameter :== HARDWARE hardware-type colon-seperated-hex-list SEMI
 * hardware-type :== ETHERNET | TOKEN_RING
 */
334 335

void parse_hardware_param (cfile, hardware)
336
	struct parse *cfile;
337 338
	struct hardware *hardware;
{
339
	const char *val;
340
	enum dhcp_token token;
341
	unsigned hlen;
342 343
	unsigned char *t;

344
	token = next_token (&val, (unsigned *)0, cfile);
345 346
	switch (token) {
	      case ETHERNET:
347
		hardware -> hbuf [0] = HTYPE_ETHER;
348 349
		break;
	      case TOKEN_RING:
350
		hardware -> hbuf [0] = HTYPE_IEEE802;
351
		break;
352
	      case FDDI:
353
		hardware -> hbuf [0] = HTYPE_FDDI;
354
		break;
355
	      default:
356
		if (!strncmp (val, "unknown-", 8)) {
357
			hardware -> hbuf [0] = atoi (&val [8]);
358 359 360 361 362 363 364
		} else {
			parse_warn (cfile,
				    "expecting a network hardware type");
			skip_to_semi (cfile);

			return;
		}
365 366 367 368 369 370 371 372 373 374
	}

	/* Parse the hardware address information.   Technically,
	   it would make a lot of sense to restrict the length of the
	   data we'll accept here to the length of a particular hardware
	   address type.   Unfortunately, there are some broken clients
	   out there that put bogus data in the chaddr buffer, and we accept
	   that data in the lease file rather than simply failing on such
	   clients.   Yuck. */
	hlen = 0;
375
	token = peek_token (&val, (unsigned *)0, cfile);
376 377 378 379
	if (token == SEMI) {
		hardware -> hlen = 1;
		goto out;
	}
380 381
	t = parse_numeric_aggregate (cfile, (unsigned char *)0, &hlen,
				     COLON, 16, 8);
382 383
	if (!t) {
		hardware -> hlen = 1;
384
		return;
385
	}
386
	if (hlen + 1 > sizeof hardware -> hbuf) {
Ted Lemon's avatar
Ted Lemon committed
387
		dfree (t, MDL);
388
		parse_warn (cfile, "hardware address too long");
389
	} else {
390 391 392 393 394
		hardware -> hlen = hlen + 1;
		memcpy ((unsigned char *)&hardware -> hbuf [1], t, hlen);
		if (hlen + 1 < sizeof hardware -> hbuf)
			memset (&hardware -> hbuf [hlen + 1], 0,
				(sizeof hardware -> hbuf) - hlen - 1);
Ted Lemon's avatar
Ted Lemon committed
395
		dfree (t, MDL);
396 397
	}
	
398
      out:
399
	token = next_token (&val, (unsigned *)0, cfile);
400
	if (token != SEMI) {
401
		parse_warn (cfile, "expecting semicolon.");
402 403 404 405 406 407 408
		skip_to_semi (cfile);
	}
}

/* lease-time :== NUMBER SEMI */

void parse_lease_time (cfile, timep)
409
	struct parse *cfile;
410 411
	TIME *timep;
{
412
	const char *val;
413
	enum dhcp_token token;
414

415
	token = next_token (&val, (unsigned *)0, cfile);
416
	if (token != NUMBER) {
417
		parse_warn (cfile, "Expecting numeric lease time");
418 419 420
		skip_to_semi (cfile);
		return;
	}
421
	convert_num (cfile, (unsigned char *)timep, val, 10, 32);
422 423 424 425 426 427 428 429 430 431 432 433 434 435 436
	/* Unswap the number - convert_num returns stuff in NBO. */
	*timep = ntohl (*timep); /* XXX */

	parse_semi (cfile);
}

/* No BNF for numeric aggregates - that's defined by the caller.  What
   this function does is to parse a sequence of numbers seperated by
   the token specified in seperator.  If max is zero, any number of
   numbers will be parsed; otherwise, exactly max numbers are
   expected.  Base and size tell us how to internalize the numbers
   once they've been tokenized. */

unsigned char *parse_numeric_aggregate (cfile, buf,
					max, seperator, base, size)
437
	struct parse *cfile;
438
	unsigned char *buf;
439
	unsigned *max;
440 441
	int seperator;
	int base;
442
	unsigned size;
443
{
444
	const char *val;
445
	enum dhcp_token token;
446
	unsigned char *bufp = buf, *s, *t;
447
	unsigned count = 0;
448 449 450
	pair c = (pair)0;

	if (!bufp && *max) {
Ted Lemon's avatar
Ted Lemon committed
451
		bufp = (unsigned char *)dmalloc (*max * size / 8, MDL);
452
		if (!bufp)
453 454
			log_fatal ("no space for numeric aggregate");
		s = 0;
455 456 457 458 459
	} else
		s = bufp;

	do {
		if (count) {
460
			token = peek_token (&val, (unsigned *)0, cfile);
461 462 463 464
			if (token != seperator) {
				if (!*max)
					break;
				if (token != RBRACE && token != LBRACE)
465 466 467
					token = next_token (&val,
							    (unsigned *)0,
							    cfile);
468
				parse_warn (cfile, "too few numbers.");
469 470 471 472
				if (token != SEMI)
					skip_to_semi (cfile);
				return (unsigned char *)0;
			}
473
			token = next_token (&val, (unsigned *)0, cfile);
474
		}
475
		token = next_token (&val, (unsigned *)0, cfile);
476

477
		if (token == END_OF_FILE) {
478
			parse_warn (cfile, "unexpected end of file");
479 480 481 482 483 484
			break;
		}

		/* Allow NUMBER_OR_NAME if base is 16. */
		if (token != NUMBER &&
		    (base != 16 || token != NUMBER_OR_NAME)) {
485
			parse_warn (cfile, "expecting numeric value.");
486 487 488 489 490 491
			skip_to_semi (cfile);
			return (unsigned char *)0;
		}
		/* If we can, convert the number now; otherwise, build
		   a linked list of all the numbers. */
		if (s) {
492
			convert_num (cfile, s, val, base, size);
493 494
			s += size / 8;
		} else {
Ted Lemon's avatar
Ted Lemon committed
495
			t = (unsigned char *)dmalloc (strlen (val) + 1, MDL);
496
			if (!t)
497
				log_fatal ("no temp space for number.");
498 499
			strcpy ((char *)t, val);
			c = cons ((caddr_t)t, c);
500 501 502 503 504
		}
	} while (++count != *max);

	/* If we had to cons up a list, convert it now. */
	if (c) {
Ted Lemon's avatar
Ted Lemon committed
505
		bufp = (unsigned char *)dmalloc (count * size / 8, MDL);
506
		if (!bufp)
507
			log_fatal ("no space for numeric aggregate.");
508 509 510 511 512
		s = bufp + count - size / 8;
		*max = count;
	}
	while (c) {
		pair cdr = c -> cdr;
513
		convert_num (cfile, s, (char *)(c -> car), base, size);
514 515
		s -= size / 8;
		/* Free up temp space. */
Ted Lemon's avatar
Ted Lemon committed
516 517
		dfree (c -> car, MDL);
		dfree (c, MDL);
518 519 520 521 522
		c = cdr;
	}
	return bufp;
}

523 524
void convert_num (cfile, buf, str, base, size)
	struct parse *cfile;
525
	unsigned char *buf;
526
	const char *str;
527
	int base;
528
	unsigned size;
529
{
530
	const char *ptr = str;
531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567
	int negative = 0;
	u_int32_t val = 0;
	int tval;
	int max;

	if (*ptr == '-') {
		negative = 1;
		++ptr;
	}

	/* If base wasn't specified, figure it out from the data. */
	if (!base) {
		if (ptr [0] == '0') {
			if (ptr [1] == 'x') {
				base = 16;
				ptr += 2;
			} else if (isascii (ptr [1]) && isdigit (ptr [1])) {
				base = 8;
				ptr += 1;
			} else {
				base = 10;
			}
		} else {
			base = 10;
		}
	}

	do {
		tval = *ptr++;
		/* XXX assumes ASCII... */
		if (tval >= 'a')
			tval = tval - 'a' + 10;
		else if (tval >= 'A')
			tval = tval - 'A' + 10;
		else if (tval >= '0')
			tval -= '0';
		else {
568
			parse_warn (cfile, "Bogus number: %s.", str);
569 570 571
			break;
		}
		if (tval >= base) {
572 573
			parse_warn (cfile,
				    "Bogus number %s: digit %d not in base %d",
574
				    str, tval, base);
575 576 577 578 579 580 581 582 583 584 585 586
			break;
		}
		val = val * base + tval;
	} while (*ptr);

	if (negative)
		max = (1 << (size - 1));
	else
		max = (1 << (size - 1)) + ((1 << (size - 1)) - 1);
	if (val > max) {
		switch (base) {
		      case 8:
587
			parse_warn (cfile,
588
				    "%s%lo exceeds max (%d) for precision.",
Ted Lemon's avatar
Ted Lemon committed
589 590
				    negative ? "-" : "",
				    (unsigned long)val, max);
591 592
			break;
		      case 16:
593
			parse_warn (cfile,
594
				    "%s%lx exceeds max (%d) for precision.",
Ted Lemon's avatar
Ted Lemon committed
595 596
				    negative ? "-" : "",
				    (unsigned long)val, max);
597 598
			break;
		      default:
599
			parse_warn (cfile,
600
				    "%s%lu exceeds max (%d) for precision.",
Ted Lemon's avatar
Ted Lemon committed
601 602
				    negative ? "-" : "",
				    (unsigned long)val, max);
603 604 605 606 607 608 609 610 611 612
			break;
		}
	}

	if (negative) {
		switch (size) {
		      case 8:
			*buf = -(unsigned long)val;
			break;
		      case 16:
613
			putShort (buf, -(long)val);
614 615
			break;
		      case 32:
616
			putLong (buf, -(long)val);
617 618
			break;
		      default:
619 620
			parse_warn (cfile,
				    "Unexpected integer size: %d\n", size);
621 622 623 624 625 626 627 628 629 630 631 632 633 634
			break;
		}
	} else {
		switch (size) {
		      case 8:
			*buf = (u_int8_t)val;
			break;
		      case 16:
			putUShort (buf, (u_int16_t)val);
			break;
		      case 32:
			putULong (buf, val);
			break;
		      default:
635 636
			parse_warn (cfile,
				    "Unexpected integer size: %d\n", size);
637 638 639 640 641
			break;
		}
	}
}

642 643
/*
 * date :== NUMBER NUMBER SLASH NUMBER SLASH NUMBER 
644 645 646
 *		NUMBER COLON NUMBER COLON NUMBER SEMI |
 *          NUMBER NUMBER SLASH NUMBER SLASH NUMBER 
 *		NUMBER COLON NUMBER COLON NUMBER NUMBER SEMI |
647
 *	    NEVER
648
 *
649 650 651 652
 * Dates are stored in GMT or with a timezone offset; first number is day
 * of week; next is year/month/day; next is hours:minutes:seconds on a
 * 24-hour clock, followed by the timezone offset in seconds, which is
 * optional.
653
 */
654 655

TIME parse_date (cfile)
656
	struct parse *cfile;
657 658 659
{
	struct tm tm;
	int guess;
660
	int tzoff, wday, year, mon, mday, hour, min, sec;
661
	const char *val;
662
	enum dhcp_token token;
663 664 665
	static int months [11] = { 31, 59, 90, 120, 151, 181,
					  212, 243, 273, 304, 334 };

666
	/* Day of week, or "never"... */
667
	token = next_token (&val, (unsigned *)0, cfile);
668 669 670 671 672 673
	if (token == NEVER) {
		if (!parse_semi (cfile))
			return 0;
		return MAX_TIME;
	}

674
	if (token != NUMBER) {
675
		parse_warn (cfile, "numeric day of week expected.");
676 677 678 679
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}
680
	wday = atoi (val);
681 682

	/* Year... */
683
	token = next_token (&val, (unsigned *)0, cfile);
684
	if (token != NUMBER) {
685
		parse_warn (cfile, "numeric year expected.");
686 687 688 689
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}
690 691 692 693 694

	/* Note: the following is not a Y2K bug - it's a Y1.9K bug.   Until
	   somebody invents a time machine, I think we can safely disregard
	   it.   This actually works around a stupid Y2K bug that was present
	   in a very early beta release of dhcpd. */
695 696 697
	year = atoi (val);
	if (year > 1900)
		year -= 1900;
698 699

	/* Slash seperating year from month... */
700
	token = next_token (&val, (unsigned *)0, cfile);
701
	if (token != SLASH) {
702 703
		parse_warn (cfile,
			    "expected slash seperating year from month.");
704 705 706 707 708 709
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}

	/* Month... */
710
	token = next_token (&val, (unsigned *)0, cfile);
711
	if (token != NUMBER) {
712
		parse_warn (cfile, "numeric month expected.");
713 714 715 716
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}
717
	mon = atoi (val) - 1;
718 719

	/* Slash seperating month from day... */
720
	token = next_token (&val, (unsigned *)0, cfile);
721
	if (token != SLASH) {
722 723
		parse_warn (cfile,
			    "expected slash seperating month from day.");
724 725 726 727 728 729
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}

	/* Month... */
730
	token = next_token (&val, (unsigned *)0, cfile);
731
	if (token != NUMBER) {
732
		parse_warn (cfile, "numeric day of month expected.");
733 734 735 736
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}
737
	mday = atoi (val);
738 739

	/* Hour... */
740
	token = next_token (&val, (unsigned *)0, cfile);
741
	if (token != NUMBER) {
742
		parse_warn (cfile, "numeric hour expected.");
743 744 745 746
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}
747
	hour = atoi (val);
748 749

	/* Colon seperating hour from minute... */
750
	token = next_token (&val, (unsigned *)0, cfile);
751
	if (token != COLON) {
752 753
		parse_warn (cfile,
			    "expected colon seperating hour from minute.");
754 755 756 757 758 759
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}

	/* Minute... */
760
	token = next_token (&val, (unsigned *)0, cfile);
761
	if (token != NUMBER) {
762
		parse_warn (cfile, "numeric minute expected.");
763 764 765 766
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}
767
	min = atoi (val);
768 769

	/* Colon seperating minute from second... */
770
	token = next_token (&val, (unsigned *)0, cfile);
771
	if (token != COLON) {
772 773
		parse_warn (cfile,
			    "expected colon seperating hour from minute.");
774 775 776 777 778 779
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}

	/* Minute... */
780
	token = next_token (&val, (unsigned *)0, cfile);
781
	if (token != NUMBER) {
782
		parse_warn (cfile, "numeric minute expected.");
783 784 785 786
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}
787
	sec = atoi (val);
788

789
	token = peek_token (&val, (unsigned *)0, cfile);
790
	if (token == NUMBER) {
791
		token = next_token (&val, (unsigned *)0, cfile);
792 793 794
		tzoff = atoi (val);
	} else
		tzoff = 0;
795 796

	/* Make sure the date ends in a semicolon... */
Ted Lemon's avatar
Ted Lemon committed
797
	if (!parse_semi (cfile))
798 799 800
		return 0;

	/* Guess the time value... */
801 802 803 804
	guess = ((((((365 * (year - 70) +	/* Days in years since '70 */
		      (year - 69) / 4 +		/* Leap days since '70 */
		      (mon			/* Days in months this year */
		       ? months [mon - 1]
805
		       : 0) +
806 807 808 809 810
		      (mon > 1 &&		/* Leap day this year */
		       !((year - 72) & 3)) +
		      mday - 1) * 24) +		/* Day of month */
		    hour) * 60) +
		  min) * 60) + sec + tzoff;
811 812 813 814 815 816 817 818 819 820 821 822

	/* This guess could be wrong because of leap seconds or other
	   weirdness we don't know about that the system does.   For
	   now, we're just going to accept the guess, but at some point
	   it might be nice to do a successive approximation here to
	   get an exact value.   Even if the error is small, if the
	   server is restarted frequently (and thus the lease database
	   is reread), the error could accumulate into something
	   significant. */

	return guess;
}
823

824 825 826 827 828
/*
 * option-name :== IDENTIFIER |
 		   IDENTIFIER . IDENTIFIER
 */

829
struct option *parse_option_name (cfile, allocate, known)
830
	struct parse *cfile;
831
	int allocate;
832
	int *known;
833
{
834
	const char *val;
835
	enum dhcp_token token;
836
	char *uname;
837 838 839
	struct universe *universe;
	struct option *option;

840
	token = next_token (&val, (unsigned *)0, cfile);
841
	if (!is_identifier (token)) {
842 843
		parse_warn (cfile,
			    "expecting identifier after option keyword.");
844 845 846 847
		if (token != SEMI)
			skip_to_semi (cfile);
		return (struct option *)0;
	}
Ted Lemon's avatar
Ted Lemon committed
848
	uname = dmalloc (strlen (val) + 1, MDL);
849 850 851
	if (!uname)
		log_fatal ("no memory for uname information.");
	strcpy (uname, val);
852
	token = peek_token (&val, (unsigned *)0, cfile);
853 854
	if (token == DOT) {
		/* Go ahead and take the DOT token... */
855
		token = next_token (&val, (unsigned *)0, cfile);
856 857

		/* The next token should be an identifier... */
858
		token = next_token (&val, (unsigned *)0, cfile);
859
		if (!is_identifier (token)) {
860
			parse_warn (cfile, "expecting identifier after '.'");
861 862 863 864 865 866
			if (token != SEMI)
				skip_to_semi (cfile);
			return (struct option *)0;
		}

		/* Look up the option name hash table for the specified
867
		   uname. */
868 869 870
		universe = (struct universe *)0;
		if (!universe_hash_lookup (&universe, universe_hash,
					   uname, 0, MDL)) {
871
			parse_warn (cfile, "no option space named %s.", uname);
872 873 874 875 876 877
			skip_to_semi (cfile);
			return (struct option *)0;
		}
	} else {
		/* Use the default hash table, which contains all the
		   standard dhcp option names. */
878
		val = uname;
879 880 881 882
		universe = &dhcp_universe;
	}

	/* Look up the actual option info... */
883 884
	option = (struct option *)0;
	option_hash_lookup (&option, universe -> hash, val, 0, MDL);
885 886

	/* If we didn't get an option structure, it's an undefined option. */
887
	if (option) {
888 889
		if (known)
			*known = 1;
890
	} else {
891 892 893 894
		/* If we've been told to allocate, that means that this
		   (might) be an option code definition, so we'll create
		   an option structure just in case. */
		if (allocate) {
895
			option = new_option (MDL);
896 897 898
			if (val == uname)
				option -> name = val;
			else {
899
				char *s;
Ted Lemon's avatar
Ted Lemon committed
900 901
				dfree (uname, MDL);
				s = dmalloc (strlen (val) + 1, MDL);
902
				if (!s)
903 904
				    log_fatal ("no memory for option %s.%s",
					       universe -> name, val);
905 906
				strcpy (s, val);
				option -> name = s;
907 908
			}
			option -> universe = universe;
909
			option -> code = 0;
910 911 912
			return option;
		}
		if (val == uname)
913
			parse_warn (cfile, "no option named %s", val);
914
		else
915
			parse_warn (cfile, "no option named %s in space %s",