parse.c 131 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.119 2007/01/29 10:25:54 shane 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 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415
/*
 * ip-address-with-subnet :== ip-address |
 *                          ip-address "/" NUMBER
 */

int
parse_ip_addr_with_subnet(cfile, match)
	struct parse *cfile;
	struct iaddrmatch *match;
{
	const char *val, *orig;
	enum dhcp_token token;
	int prefixlen;
	int fflen;
	unsigned char newval, warnmask=0;

	if (parse_ip_addr(cfile, &match->addr)) {
		/* default to host mask */
		prefixlen = match->addr.len * 8;

		token = peek_token(&val, NULL, cfile);

		if (token == SLASH) {
			next_token(&val, NULL, cfile);
			token = next_token(&val, NULL, cfile);

			if (token != NUMBER) {
				parse_warn(cfile, "Invalid CIDR prefix length:"
						  " expecting a number.");
				return 0;
			}

			prefixlen = atoi(val);

			if (prefixlen < 0 ||
			    prefixlen > (match->addr.len * 8)) {
				parse_warn(cfile, "subnet prefix is out of "
						  "range [0..%d].",
						  match->addr.len * 8);
				return 0;
			}
		}

		/* construct a suitable mask field */

		/* copy length */
		match->mask.len = match->addr.len;

		/* count of 0xff bytes in mask */
		fflen = prefixlen / 8;

		/* set leading mask */
		memset(match->mask.iabuf, 0xff, fflen);

		/* set zeroes */
		if (fflen < match->mask.len) {
			match->mask.iabuf[fflen] =
			    "\x00\x80\xc0\xe0\xf0\xf8\xfc\xfe"[prefixlen % 8];

			memset(match->mask.iabuf+fflen+1, 0x00, 
			       match->mask.len - fflen - 1);

			/* AND-out insignificant bits from supplied netmask. */
			orig = piaddr(match->addr);
			do {
				newval = match->addr.iabuf[fflen] &
					 match->mask.iabuf[fflen];

				if (newval != match->addr.iabuf[fflen]) {
					warnmask = 1;
					match->addr.iabuf[fflen] = newval;
				}
			} while (++fflen < match->mask.len);

			if (warnmask) {
				log_error("Warning: Extraneous bits removed "
					  "in address component of %s/%d.",
					  orig, prefixlen);
				log_error("New value: %s/%d.",
					  piaddr(match->addr), prefixlen);
			}
		}

		return 1;
	}

	parse_warn(cfile,
		   "expecting ip-address or ip-address/prefixlen");

	return 0;  /* let caller pick up pieces */ 
}

416
/*
417
 * hardware-parameter :== HARDWARE hardware-type colon-separated-hex-list SEMI
418
 * hardware-type :== ETHERNET | TOKEN_RING | FDDI
419
 */
420 421

void parse_hardware_param (cfile, hardware)
422
	struct parse *cfile;
423 424
	struct hardware *hardware;
{
425
	const char *val;
426
	enum dhcp_token token;
427
	unsigned hlen;
428 429
	unsigned char *t;

430
	token = next_token (&val, (unsigned *)0, cfile);
431 432
	switch (token) {
	      case ETHERNET:
433
		hardware -> hbuf [0] = HTYPE_ETHER;
434 435
		break;
	      case TOKEN_RING:
436
		hardware -> hbuf [0] = HTYPE_IEEE802;
437
		break;
438
	      case FDDI:
439
		hardware -> hbuf [0] = HTYPE_FDDI;
440
		break;
441
	      default:
442
		if (!strncmp (val, "unknown-", 8)) {
443
			hardware -> hbuf [0] = atoi (&val [8]);
444 445 446 447 448 449 450
		} else {
			parse_warn (cfile,
				    "expecting a network hardware type");
			skip_to_semi (cfile);

			return;
		}
451 452 453 454 455 456 457 458 459 460
	}

	/* 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;
461
	token = peek_token (&val, (unsigned *)0, cfile);
462 463 464 465
	if (token == SEMI) {
		hardware -> hlen = 1;
		goto out;
	}
466 467
	t = parse_numeric_aggregate (cfile, (unsigned char *)0, &hlen,
				     COLON, 16, 8);
468 469
	if (!t) {
		hardware -> hlen = 1;
470
		return;
471
	}
472
	if (hlen + 1 > sizeof hardware -> hbuf) {
Ted Lemon's avatar
Ted Lemon committed
473
		dfree (t, MDL);
474
		parse_warn (cfile, "hardware address too long");
475
	} else {
476 477 478 479 480
		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
481
		dfree (t, MDL);
482 483
	}
	
484
      out:
485
	token = next_token (&val, (unsigned *)0, cfile);
486
	if (token != SEMI) {
487
		parse_warn (cfile, "expecting semicolon.");
488 489 490 491 492 493 494
		skip_to_semi (cfile);
	}
}

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

void parse_lease_time (cfile, timep)
495
	struct parse *cfile;
496 497
	TIME *timep;
{
498
	const char *val;
499
	enum dhcp_token token;
500
	u_int32_t num;
501

502
	token = next_token (&val, (unsigned *)0, cfile);
503
	if (token != NUMBER) {
504
		parse_warn (cfile, "Expecting numeric lease time");
505 506 507
		skip_to_semi (cfile);
		return;
	}
508
	convert_num(cfile, (unsigned char *)&num, val, 10, 32);
509
	/* Unswap the number - convert_num returns stuff in NBO. */
510
	*timep = ntohl(num);
511 512 513 514 515

	parse_semi (cfile);
}

/* No BNF for numeric aggregates - that's defined by the caller.  What
516 517
   this function does is to parse a sequence of numbers separated by
   the token specified in separator.  If max is zero, any number of
518 519 520 521 522
   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,
523
					max, separator, base, size)
524
	struct parse *cfile;
525
	unsigned char *buf;
526
	unsigned *max;
527
	int separator;
528
	int base;
529
	unsigned size;
530
{
531
	const char *val;
532
	enum dhcp_token token;
533
	unsigned char *bufp = buf, *s, *t;
534
	unsigned count = 0;
535 536 537
	pair c = (pair)0;

	if (!bufp && *max) {
Ted Lemon's avatar
Ted Lemon committed
538
		bufp = (unsigned char *)dmalloc (*max * size / 8, MDL);
539
		if (!bufp)
540 541
			log_fatal ("no space for numeric aggregate");
		s = 0;
542 543 544 545 546
	} else
		s = bufp;

	do {
		if (count) {
547
			token = peek_token (&val, (unsigned *)0, cfile);
548
			if (token != separator) {
549 550 551
				if (!*max)
					break;
				if (token != RBRACE && token != LBRACE)
552 553 554
					token = next_token (&val,
							    (unsigned *)0,
							    cfile);
555
				parse_warn (cfile, "too few numbers.");
556 557 558 559
				if (token != SEMI)
					skip_to_semi (cfile);
				return (unsigned char *)0;
			}
560
			token = next_token (&val, (unsigned *)0, cfile);
561
		}
562
		token = next_token (&val, (unsigned *)0, cfile);
563

564
		if (token == END_OF_FILE) {
565
			parse_warn (cfile, "unexpected end of file");
566 567 568 569 570 571
			break;
		}

		/* Allow NUMBER_OR_NAME if base is 16. */
		if (token != NUMBER &&
		    (base != 16 || token != NUMBER_OR_NAME)) {
572
			parse_warn (cfile, "expecting numeric value.");
573 574 575 576 577 578
			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) {
579
			convert_num (cfile, s, val, base, size);
580 581
			s += size / 8;
		} else {
Ted Lemon's avatar
Ted Lemon committed
582
			t = (unsigned char *)dmalloc (strlen (val) + 1, MDL);
583
			if (!t)
584
				log_fatal ("no temp space for number.");
585 586
			strcpy ((char *)t, val);
			c = cons ((caddr_t)t, c);
587 588 589 590 591
		}
	} while (++count != *max);

	/* If we had to cons up a list, convert it now. */
	if (c) {
Ted Lemon's avatar
Ted Lemon committed
592
		bufp = (unsigned char *)dmalloc (count * size / 8, MDL);
593
		if (!bufp)
594
			log_fatal ("no space for numeric aggregate.");
595 596 597 598 599
		s = bufp + count - size / 8;
		*max = count;
	}
	while (c) {
		pair cdr = c -> cdr;
600
		convert_num (cfile, s, (char *)(c -> car), base, size);
601 602
		s -= size / 8;
		/* Free up temp space. */
Ted Lemon's avatar
Ted Lemon committed
603 604
		dfree (c -> car, MDL);
		dfree (c, MDL);
605 606 607 608 609
		c = cdr;
	}
	return bufp;
}

610 611
void convert_num (cfile, buf, str, base, size)
	struct parse *cfile;
612
	unsigned char *buf;
613
	const char *str;
614
	int base;
615
	unsigned size;
616
{
617
	const char *ptr = str;
618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654
	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 {
655
			parse_warn (cfile, "Bogus number: %s.", str);
656 657 658
			break;
		}
		if (tval >= base) {
659 660
			parse_warn (cfile,
				    "Bogus number %s: digit %d not in base %d",
661
				    str, tval, base);
662 663 664 665 666 667 668 669 670 671 672 673
			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:
674
			parse_warn (cfile,
675
				    "%s%lo exceeds max (%d) for precision.",
Ted Lemon's avatar
Ted Lemon committed
676 677
				    negative ? "-" : "",
				    (unsigned long)val, max);
678 679
			break;
		      case 16:
680
			parse_warn (cfile,
681
				    "%s%lx exceeds max (%d) for precision.",
Ted Lemon's avatar
Ted Lemon committed
682 683
				    negative ? "-" : "",
				    (unsigned long)val, max);
684 685
			break;
		      default:
686
			parse_warn (cfile,
687
				    "%s%lu exceeds max (%d) for precision.",
Ted Lemon's avatar
Ted Lemon committed
688 689
				    negative ? "-" : "",
				    (unsigned long)val, max);
690 691 692 693 694 695 696 697 698 699
			break;
		}
	}

	if (negative) {
		switch (size) {
		      case 8:
			*buf = -(unsigned long)val;
			break;
		      case 16:
700
			putShort (buf, -(long)val);
701 702
			break;
		      case 32:
703
			putLong (buf, -(long)val);
704 705
			break;
		      default:
706 707
			parse_warn (cfile,
				    "Unexpected integer size: %d\n", size);
708 709 710 711 712 713 714 715 716 717 718 719 720 721
			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:
722 723
			parse_warn (cfile,
				    "Unexpected integer size: %d\n", size);
724 725 726 727 728
			break;
		}
	}
}

729 730
/*
 * date :== NUMBER NUMBER SLASH NUMBER SLASH NUMBER 
731 732 733
 *		NUMBER COLON NUMBER COLON NUMBER SEMI |
 *          NUMBER NUMBER SLASH NUMBER SLASH NUMBER 
 *		NUMBER COLON NUMBER COLON NUMBER NUMBER SEMI |
734
 *	    NEVER
735
 *
736 737 738 739
 * 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.
740
 */
741 742

TIME parse_date (cfile)
743
	struct parse *cfile;
744 745 746
{
	struct tm tm;
	int guess;
747
	int tzoff, wday, year, mon, mday, hour, min, sec;
748
	const char *val;
749
	enum dhcp_token token;
750 751 752
	static int months [11] = { 31, 59, 90, 120, 151, 181,
					  212, 243, 273, 304, 334 };

753
	/* Day of week, or "never"... */
754
	token = next_token (&val, (unsigned *)0, cfile);
755 756 757 758 759 760
	if (token == NEVER) {
		if (!parse_semi (cfile))
			return 0;
		return MAX_TIME;
	}

761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779
	/* This indicates 'local' time format. */
	if (token == EPOCH) {
		token = next_token(&val, NULL, cfile);

		if (token != NUMBER) {
			parse_warn(cfile, "Seconds since epoch expected.");
			if (token != SEMI)
				skip_to_semi(cfile);
			return (TIME)0;
		}

		guess = atoi(val);

		if (!parse_semi(cfile))
			return (TIME)0;

		return guess;
	}

780
	if (token != NUMBER) {
781
		parse_warn (cfile, "numeric day of week expected.");
782 783 784 785
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}
786
	wday = atoi (val);
787 788

	/* Year... */
789
	token = next_token (&val, (unsigned *)0, cfile);
790
	if (token != NUMBER) {
791
		parse_warn (cfile, "numeric year expected.");
792 793 794 795
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}
796 797 798 799 800

	/* 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. */
801 802 803
	year = atoi (val);
	if (year > 1900)
		year -= 1900;
804

805
	/* Slash separating year from month... */
806
	token = next_token (&val, (unsigned *)0, cfile);
807
	if (token != SLASH) {
808
		parse_warn (cfile,
809
			    "expected slash separating year from month.");
810 811 812 813 814 815
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}

	/* Month... */
816
	token = next_token (&val, (unsigned *)0, cfile);
817
	if (token != NUMBER) {
818
		parse_warn (cfile, "numeric month expected.");
819 820 821 822
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}
823
	mon = atoi (val) - 1;
824

825
	/* Slash separating month from day... */
826
	token = next_token (&val, (unsigned *)0, cfile);
827
	if (token != SLASH) {
828
		parse_warn (cfile,
829
			    "expected slash separating month from day.");
830 831 832 833 834
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}

835
	/* Day of month... */
836
	token = next_token (&val, (unsigned *)0, cfile);
837
	if (token != NUMBER) {
838
		parse_warn (cfile, "numeric day of month expected.");
839 840 841 842
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}
843
	mday = atoi (val);
844 845

	/* Hour... */
846
	token = next_token (&val, (unsigned *)0, cfile);
847
	if (token != NUMBER) {
848
		parse_warn (cfile, "numeric hour expected.");
849 850 851 852
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}
853
	hour = atoi (val);
854

855
	/* Colon separating hour from minute... */
856
	token = next_token (&val, (unsigned *)0, cfile);
857
	if (token != COLON) {
858
		parse_warn (cfile,
859
			    "expected colon separating hour from minute.");
860 861 862 863 864 865
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}

	/* Minute... */
866
	token = next_token (&val, (unsigned *)0, cfile);
867
	if (token != NUMBER) {
868
		parse_warn (cfile, "numeric minute expected.");
869 870 871 872
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}
873
	min = atoi (val);
874

875
	/* Colon separating minute from second... */
876
	token = next_token (&val, (unsigned *)0, cfile);
877
	if (token != COLON) {
878
		parse_warn (cfile,
879
			    "expected colon separating hour from minute.");
880 881 882 883 884 885
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}

	/* Minute... */
886
	token = next_token (&val, (unsigned *)0, cfile);
887
	if (token != NUMBER) {
888
		parse_warn (cfile, "numeric minute expected.");
889 890 891 892
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}
893
	sec = atoi (val);
894

895
	token = peek_token (&val, (unsigned *)0, cfile);
896
	if (token == NUMBER) {
897
		token = next_token (&val, (unsigned *)0, cfile);
898 899 900
		tzoff = atoi (val);
	} else
		tzoff = 0;
901 902

	/* Make sure the date ends in a semicolon... */
Ted Lemon's avatar
Ted Lemon committed
903
	if (!parse_semi (cfile))
904 905 906
		return 0;

	/* Guess the time value... */
907 908 909 910
	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]
911
		       : 0) +
912 913 914 915 916
		      (mon > 1 &&		/* Leap day this year */
		       !((year - 72) & 3)) +
		      mday - 1) * 24) +		/* Day of month */
		    hour) * 60) +
		  min) * 60) + sec + tzoff;
917 918 919 920 921 922 923 924 925 926 927 928

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

930 931 932 933 934
/*
 * option-name :== IDENTIFIER |
 		   IDENTIFIER . IDENTIFIER
 */

935 936
isc_result_t
parse_option_name (cfile, allocate, known, opt)
937
	struct parse *cfile;
938
	int allocate;
939
	int *known;
940
	struct option **opt;
941
{
942
	const char *val;
943
	enum dhcp_token token;
944
	char *uname;