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

   Common parser code for dhcpd and dhclient. */

/*
6
 * Copyright (c) 2004-2006 by Internet Systems Consortium, Inc. ("ISC")
7
 * 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.108 2006/05/11 16:31:29 dhankins Exp $ Copyright (c) 2004-2006 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
	u_int32_t num;
409

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

	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)
432
	struct parse *cfile;
433
	unsigned char *buf;
434
	unsigned *max;
435 436
	int seperator;
	int base;
437
	unsigned size;
438
{
439
	const char *val;
440
	enum dhcp_token token;
441
	unsigned char *bufp = buf, *s, *t;
442
	unsigned count = 0;
443 444 445
	pair c = (pair)0;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

	/* If we didn't get an option structure, it's an undefined option. */
882
	if (option) {
883 884
		if (known)
			*known = 1;
885
	} else {
886 887 888 889
		/* 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) {
890
			option = new_option (MDL);
891 892 893
			if (val == uname)
				option -> name = val;
			else {
894
				char *s;
Ted Lemon's avatar