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

   Common parser code for dhcpd and dhclient. */

/*
6 7
 * Copyright (c) 2004-2005 by Internet Systems Consortium, Inc. ("ISC")
 * Copyright (c) 1995-2003 by Internet Software Consortium
8
 *
9 10 11
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
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 25
 *   Internet Systems Consortium, Inc.
 *   950 Charter Street
 *   Redwood City, CA 94063
 *   <info@isc.org>
 *   http://www.isc.org/
Ted Lemon's avatar
Ted Lemon committed
26
 *
27
 * This software has been written for Internet Systems Consortium
Ted Lemon's avatar
Ted Lemon committed
28
 * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
29
 * To learn more about Internet Systems Consortium, see
Ted Lemon's avatar
Ted Lemon committed
30 31 32
 * ``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''.
33 34 35 36
 */

#ifndef lint
static char copyright[] =
37
"$Id: parse.c,v 1.106 2005/03/17 20:14:59 dhankins Exp $ Copyright (c) 2004-2005 Internet Systems Consortium.  All rights reserved.\n";
38 39 40 41
#endif /* not lint */

#include "dhcpd.h"

42 43 44 45 46 47 48 49 50 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
/* 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;
}

81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
/* 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)
96
	struct parse *cfile;
97 98 99 100 101
{
	skip_to_rbrace (cfile, 0);
}

void skip_to_rbrace (cfile, brace_count)
102
	struct parse *cfile;
103
	int brace_count;
104
{
105
	enum dhcp_token token;
106
	const char *val;
107

108 109 110
#if defined (DEBUG_TOKEN)
	log_error ("skip_to_rbrace: %d\n", brace_count);
#endif
111
	do {
112
		token = peek_token (&val, (unsigned *)0, cfile);
113
		if (token == RBRACE) {
114
			token = next_token (&val, (unsigned *)0, cfile);
115 116 117 118 119 120 121 122
			if (brace_count) {
				if (!--brace_count)
					return;
			} else
				return;
		} else if (token == LBRACE) {
			brace_count++;
		} else if (token == SEMI && !brace_count) {
123
			token = next_token (&val, (unsigned *)0, cfile);
124
			return;
125 126 127 128
		} 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. */
129
			token = next_token (&val, (unsigned *)0, cfile);
130
			return;
131
		}
132
		token = next_token (&val, (unsigned *)0, cfile);
133
	} while (token != END_OF_FILE);
134 135 136
}

int parse_semi (cfile)
137
	struct parse *cfile;
138
{
139
	enum dhcp_token token;
140
	const char *val;
141

142
	token = next_token (&val, (unsigned *)0, cfile);
143
	if (token != SEMI) {
144
		parse_warn (cfile, "semicolon expected.");
145 146 147 148 149 150 151 152
		skip_to_semi (cfile);
		return 0;
	}
	return 1;
}

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

153
int parse_string (cfile, sptr, lptr)
154
	struct parse *cfile;
155 156
	char **sptr;
	unsigned *lptr;
157
{
158
	const char *val;
159
	enum dhcp_token token;
160
	char *s;
161
	unsigned len;
162

163
	token = next_token (&val, &len, cfile);
164
	if (token != STRING) {
165
		parse_warn (cfile, "expecting a string");
166
		skip_to_semi (cfile);
167
		return 0;
168
	}
169
	s = (char *)dmalloc (len + 1, MDL);
170
	if (!s)
171
		log_fatal ("no memory for string %s.", val);
172
	memcpy (s, val, len + 1);
173

174 175 176 177 178 179 180 181 182 183 184
	if (!parse_semi (cfile)) {
		dfree (s, MDL);
		return 0;
	}
	if (sptr)
		*sptr = s;
	else
		dfree (s, MDL);
	if (lptr)
		*lptr = len;
	return 1;
185 186
}

187 188 189 190 191
/*
 * hostname :== IDENTIFIER
 *		| IDENTIFIER DOT
 *		| hostname DOT IDENTIFIER
 */
192 193

char *parse_host_name (cfile)
194
	struct parse *cfile;
195
{
196
	const char *val;
197
	enum dhcp_token token;
198
	unsigned len = 0;
199 200 201
	char *s;
	char *t;
	pair c = (pair)0;
202
	int ltid = 0;
203 204 205 206
	
	/* Read a dotted hostname... */
	do {
		/* Read a token, which should be an identifier. */
207
		token = peek_token (&val, (unsigned *)0, cfile);
Ted Lemon's avatar
Ted Lemon committed
208 209
		if (!is_identifier (token) && token != NUMBER)
			break;
210
		token = next_token (&val, (unsigned *)0, cfile);
Ted Lemon's avatar
Ted Lemon committed
211

212
		/* Store this identifier... */
Ted Lemon's avatar
Ted Lemon committed
213
		if (!(s = (char *)dmalloc (strlen (val) + 1, MDL)))
214
			log_fatal ("can't allocate temp space for hostname.");
215 216 217 218 219
		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. */
220
		token = peek_token (&val, (unsigned *)0, cfile);
221
		if (token == DOT) {
222
			token = next_token (&val, (unsigned *)0, cfile);
223 224 225
			ltid = 1;
		} else
			ltid = 0;
226 227
	} while (token == DOT);

Ted Lemon's avatar
Ted Lemon committed
228 229 230 231
	/* Should be at least one token. */
	if (!len)
		return (char *)0;

232
	/* Assemble the hostname together into a string. */
233
	if (!(s = (char *)dmalloc (len + ltid, MDL)))
234
		log_fatal ("can't allocate space for hostname.");
235
	t = s + len + ltid;
236
	*--t = 0;
237 238
	if (ltid)
		*--t = '.';
239 240
	while (c) {
		pair cdr = c -> cdr;
241
		unsigned l = strlen ((char *)(c -> car));
242 243 244
		t -= l;
		memcpy (t, (char *)(c -> car), l);
		/* Free up temp space. */
Ted Lemon's avatar
Ted Lemon committed
245 246
		dfree (c -> car, MDL);
		dfree (c, MDL);
247 248 249 250 251 252 253
		c = cdr;
		if (t != s)
			*--t = '.';
	}
	return s;
}

254 255 256 257 258 259 260
/* 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. */

261 262
int parse_ip_addr_or_hostname (expr, cfile, uniform)
	struct expression **expr;
263
	struct parse *cfile;
264 265
	int uniform;
{
266
	const char *val;
267
	enum dhcp_token token;
268
	unsigned char addr [4];
269
	unsigned len = sizeof addr;
270
	char *name;
271
	struct expression *x = (struct expression *)0;
272

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

303
	return 1;
304 305 306 307 308 309
}	
	
/*
 * ip-address :== NUMBER DOT NUMBER DOT NUMBER DOT NUMBER
 */

310
int parse_ip_addr (cfile, addr)
311
	struct parse *cfile;
312 313
	struct iaddr *addr;
{
314
	const char *val;
315
	enum dhcp_token token;
316 317 318 319 320 321 322 323

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

324 325
/*
 * hardware-parameter :== HARDWARE hardware-type colon-seperated-hex-list SEMI
326
 * hardware-type :== ETHERNET | TOKEN_RING | FDDI
327
 */
328 329

void parse_hardware_param (cfile, hardware)
330
	struct parse *cfile;
331 332
	struct hardware *hardware;
{
333
	const char *val;
334
	enum dhcp_token token;
335
	unsigned hlen;
336 337
	unsigned char *t;

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

			return;
		}
359 360 361 362 363 364 365 366 367 368
	}

	/* 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;
369
	token = peek_token (&val, (unsigned *)0, cfile);
370 371 372 373
	if (token == SEMI) {
		hardware -> hlen = 1;
		goto out;
	}
374 375
	t = parse_numeric_aggregate (cfile, (unsigned char *)0, &hlen,
				     COLON, 16, 8);
376 377
	if (!t) {
		hardware -> hlen = 1;
378
		return;
379
	}
380
	if (hlen + 1 > sizeof hardware -> hbuf) {
Ted Lemon's avatar
Ted Lemon committed
381
		dfree (t, MDL);
382
		parse_warn (cfile, "hardware address too long");
383
	} else {
384 385 386 387 388
		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
389
		dfree (t, MDL);
390 391
	}
	
392
      out:
393
	token = next_token (&val, (unsigned *)0, cfile);
394
	if (token != SEMI) {
395
		parse_warn (cfile, "expecting semicolon.");
396 397 398 399 400 401 402
		skip_to_semi (cfile);
	}
}

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

void parse_lease_time (cfile, timep)
403
	struct parse *cfile;
404 405
	TIME *timep;
{
406
	const char *val;
407
	enum dhcp_token token;
408

409
	token = next_token (&val, (unsigned *)0, cfile);
410
	if (token != NUMBER) {
411
		parse_warn (cfile, "Expecting numeric lease time");
412 413 414
		skip_to_semi (cfile);
		return;
	}
415
	convert_num (cfile, (unsigned char *)timep, val, 10, 32);
416 417 418 419 420 421 422 423 424 425 426 427 428 429 430
	/* 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)
431
	struct parse *cfile;
432
	unsigned char *buf;
433
	unsigned *max;
434 435
	int seperator;
	int base;
436
	unsigned size;
437
{
438
	const char *val;
439
	enum dhcp_token token;
440
	unsigned char *bufp = buf, *s, *t;
441
	unsigned count = 0;
442 443 444
	pair c = (pair)0;

	if (!bufp && *max) {
Ted Lemon's avatar
Ted Lemon committed
445
		bufp = (unsigned char *)dmalloc (*max * size / 8, MDL);
446
		if (!bufp)
447 448
			log_fatal ("no space for numeric aggregate");
		s = 0;
449 450 451 452 453
	} else
		s = bufp;

	do {
		if (count) {
454
			token = peek_token (&val, (unsigned *)0, cfile);
455 456 457 458
			if (token != seperator) {
				if (!*max)
					break;
				if (token != RBRACE && token != LBRACE)
459 460 461
					token = next_token (&val,
							    (unsigned *)0,
							    cfile);
462
				parse_warn (cfile, "too few numbers.");
463 464 465 466
				if (token != SEMI)
					skip_to_semi (cfile);
				return (unsigned char *)0;
			}
467
			token = next_token (&val, (unsigned *)0, cfile);
468
		}
469
		token = next_token (&val, (unsigned *)0, cfile);
470

471
		if (token == END_OF_FILE) {
472
			parse_warn (cfile, "unexpected end of file");
473 474 475 476 477 478
			break;
		}

		/* Allow NUMBER_OR_NAME if base is 16. */
		if (token != NUMBER &&
		    (base != 16 || token != NUMBER_OR_NAME)) {
479
			parse_warn (cfile, "expecting numeric value.");
480 481 482 483 484 485
			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) {
486
			convert_num (cfile, s, val, base, size);
487 488
			s += size / 8;
		} else {
Ted Lemon's avatar
Ted Lemon committed
489
			t = (unsigned char *)dmalloc (strlen (val) + 1, MDL);
490
			if (!t)
491
				log_fatal ("no temp space for number.");
492 493
			strcpy ((char *)t, val);
			c = cons ((caddr_t)t, c);
494 495 496 497 498
		}
	} while (++count != *max);

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

517 518
void convert_num (cfile, buf, str, base, size)
	struct parse *cfile;
519
	unsigned char *buf;
520
	const char *str;
521
	int base;
522
	unsigned size;
523
{
524
	const char *ptr = str;
525 526 527 528 529 530 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
	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 {
562
			parse_warn (cfile, "Bogus number: %s.", str);
563 564 565
			break;
		}
		if (tval >= base) {
566 567
			parse_warn (cfile,
				    "Bogus number %s: digit %d not in base %d",
568
				    str, tval, base);
569 570 571 572 573 574 575 576 577 578 579 580
			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:
581
			parse_warn (cfile,
582
				    "%s%lo exceeds max (%d) for precision.",
Ted Lemon's avatar
Ted Lemon committed
583 584
				    negative ? "-" : "",
				    (unsigned long)val, max);
585 586
			break;
		      case 16:
587
			parse_warn (cfile,
588
				    "%s%lx exceeds max (%d) for precision.",
Ted Lemon's avatar
Ted Lemon committed
589 590
				    negative ? "-" : "",
				    (unsigned long)val, max);
591 592
			break;
		      default:
593
			parse_warn (cfile,
594
				    "%s%lu exceeds max (%d) for precision.",
Ted Lemon's avatar
Ted Lemon committed
595 596
				    negative ? "-" : "",
				    (unsigned long)val, max);
597 598 599 600 601 602 603 604 605 606
			break;
		}
	}

	if (negative) {
		switch (size) {
		      case 8:
			*buf = -(unsigned long)val;
			break;
		      case 16:
607
			putShort (buf, -(long)val);
608 609
			break;
		      case 32:
610
			putLong (buf, -(long)val);
611 612
			break;
		      default:
613 614
			parse_warn (cfile,
				    "Unexpected integer size: %d\n", size);
615 616 617 618 619 620 621 622 623 624 625 626 627 628
			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:
629 630
			parse_warn (cfile,
				    "Unexpected integer size: %d\n", size);
631 632 633 634 635
			break;
		}
	}
}

636 637
/*
 * date :== NUMBER NUMBER SLASH NUMBER SLASH NUMBER 
638 639 640
 *		NUMBER COLON NUMBER COLON NUMBER SEMI |
 *          NUMBER NUMBER SLASH NUMBER SLASH NUMBER 
 *		NUMBER COLON NUMBER COLON NUMBER NUMBER SEMI |
641
 *	    NEVER
642
 *
643 644 645 646
 * 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.
647
 */
648 649

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

660
	/* Day of week, or "never"... */
661
	token = next_token (&val, (unsigned *)0, cfile);
662 663 664 665 666 667
	if (token == NEVER) {
		if (!parse_semi (cfile))
			return 0;
		return MAX_TIME;
	}

668
	if (token != NUMBER) {
669
		parse_warn (cfile, "numeric day of week expected.");
670 671 672 673
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}
674
	wday = atoi (val);
675 676

	/* Year... */
677
	token = next_token (&val, (unsigned *)0, cfile);
678
	if (token != NUMBER) {
679
		parse_warn (cfile, "numeric year expected.");
680 681 682 683
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}
684 685 686 687 688

	/* 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. */
689 690 691
	year = atoi (val);
	if (year > 1900)
		year -= 1900;
692 693

	/* Slash seperating year from month... */
694
	token = next_token (&val, (unsigned *)0, cfile);
695
	if (token != SLASH) {
696 697
		parse_warn (cfile,
			    "expected slash seperating year from month.");
698 699 700 701 702 703
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}

	/* Month... */
704
	token = next_token (&val, (unsigned *)0, cfile);
705
	if (token != NUMBER) {
706
		parse_warn (cfile, "numeric month expected.");
707 708 709 710
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}
711
	mon = atoi (val) - 1;
712 713

	/* Slash seperating month from day... */
714
	token = next_token (&val, (unsigned *)0, cfile);
715
	if (token != SLASH) {
716 717
		parse_warn (cfile,
			    "expected slash seperating month from day.");
718 719 720 721 722
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}

723
	/* Day of month... */
724
	token = next_token (&val, (unsigned *)0, cfile);
725
	if (token != NUMBER) {
726
		parse_warn (cfile, "numeric day of month expected.");
727 728 729 730
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}
731
	mday = atoi (val);
732 733

	/* Hour... */
734
	token = next_token (&val, (unsigned *)0, cfile);
735
	if (token != NUMBER) {
736
		parse_warn (cfile, "numeric hour expected.");
737 738 739 740
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}
741
	hour = atoi (val);
742 743

	/* Colon seperating hour from minute... */
744
	token = next_token (&val, (unsigned *)0, cfile);
745
	if (token != COLON) {
746 747
		parse_warn (cfile,
			    "expected colon seperating hour from minute.");
748 749 750 751 752 753
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}

	/* Minute... */
754
	token = next_token (&val, (unsigned *)0, cfile);
755
	if (token != NUMBER) {
756
		parse_warn (cfile, "numeric minute expected.");
757 758 759 760
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}
761
	min = atoi (val);
762 763

	/* Colon seperating minute from second... */
764
	token = next_token (&val, (unsigned *)0, cfile);
765
	if (token != COLON) {
766 767
		parse_warn (cfile,
			    "expected colon seperating hour from minute.");
768 769 770 771 772 773
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}

	/* Minute... */
774
	token = next_token (&val, (unsigned *)0, cfile);
775
	if (token != NUMBER) {
776
		parse_warn (cfile, "numeric minute expected.");
777 778 779 780
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}
781
	sec = atoi (val);
782

783
	token = peek_token (&val, (unsigned *)0, cfile);
784
	if (token == NUMBER) {
785
		token = next_token (&val, (unsigned *)0, cfile);
786 787 788
		tzoff = atoi (val);
	} else
		tzoff = 0;
789 790

	/* Make sure the date ends in a semicolon... */
Ted Lemon's avatar
Ted Lemon committed
791
	if (!parse_semi (cfile))
792 793 794
		return 0;

	/* Guess the time value... */
795 796 797 798
	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]
799
		       : 0) +
800 801 802 803 804
		      (mon > 1 &&		/* Leap day this year */
		       !((year - 72) & 3)) +
		      mday - 1) * 24) +		/* Day of month */
		    hour) * 60) +
		  min) * 60) + sec + tzoff;
805 806 807 808 809 810 811 812 813 814 815 816

	/* 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;
}
817

818 819 820 821 822
/*
 * option-name :== IDENTIFIER |
 		   IDENTIFIER . IDENTIFIER
 */

823
struct option *parse_option_name (cfile, allocate, known)
824
	struct parse *cfile;
825
	int allocate;
826
	int *known;
827
{
828
	const char *val;
829
	enum dhcp_token token;
830
	char *uname;
831 832 833
	struct universe *universe;
	struct option *option;

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

		/* The next token should be an identifier... */
852
		token = next_token (&val, (unsigned *)0, cfile);
853
		if (!is_identifier (token)) {
854
			parse_warn (cfile, "expecting identifier after '.'");
855 856 857 858 859 860
			if (token != SEMI)
				skip_to_semi (cfile);
			return (struct option *)0;
		}

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

	/* Look up the actual option info... */
877 878
	option = (struct option *)0;
	option_hash_lookup (&option, universe -> hash, val, 0, MDL);
879 880

	/* If we didn't get an option structure, it's an undefined option. */
881
	if (option) {
882 883
		if (known)
			*known = 1;
884
	} else {
885 886 887 888
		/* 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) {
889
			option = new_option (MDL);
890 891 892
			if (val == uname)
				option -> name = val;
			else {
893
				char *s;
Ted Lemon's avatar
Ted Lemon committed
894 895
				dfree (uname, MDL);
				s = dmalloc (strlen (val) + 1, MDL);
896
				if (!s)
897 898
				    log_fatal ("no memory for option %s.%s",
					       universe -> name, val);