parse.c 136 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.122 2007/05/11 15:50:18 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;
}	

David Hankins's avatar
David Hankins committed
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
/*
 * Return true if every character in the string is hexidecimal.
 */
static int
is_hex_string(const char *s) {
	while (*s != '\0') {
		if (!isxdigit(*s)) {
			return 0;
		}
		s++;
	}
	return 1;
}

/*
 * ip-address6 :== (complicated set of rules)
 *
 * See section 2.2 of RFC 1884 for details.
 *
 * We are lazy for this. We pull numbers, names, colons, and dots 
 * together and then throw the resulting string at the inet_pton()
 * function.
 */

int
parse_ip6_addr(struct parse *cfile, struct iaddr *addr) {
	enum dhcp_token token;
	const char *val;
	int val_len;

	char v6[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")];
	int v6_len;

	v6_len = 0;
	for (;;) {
		token = peek_token(&val, NULL, cfile);
		if ((((token == NAME) || (token == NUMBER_OR_NAME)) && 
		     is_hex_string(val)) ||
		    (token == NUMBER) || 
		    (token == DOT) || 
		    (token == COLON)) {

			next_token(&val, NULL, cfile);
			val_len = strlen(val);
			if ((v6_len + val_len) >= sizeof(v6)) {
				parse_warn(cfile, "Invalid IPv6 address.");
				skip_to_semi(cfile);
				return 0;
			}
			memcpy(v6+v6_len, val, val_len);
			v6_len += val_len;

		} else {
			break;
		}
	}
	v6[v6_len] = '\0';

	if (inet_pton(AF_INET6, v6, addr->iabuf) <= 0) {
		parse_warn(cfile, "Invalid IPv6 address.");
		skip_to_semi(cfile);
		return 0;
	}
	addr->len = 16;
	return 1;
}

/*
 * Same as parse_ip6_addr() above, but returns the value in the 
 * expression rather than in an address structure.
 */
int
parse_ip6_addr_expr(struct expression **expr, 
		    struct parse *cfile) {
	struct iaddr addr;

	if (!parse_ip6_addr(cfile, &addr)) {
		return 0;
	}
	return make_const_data(expr, addr.iabuf, addr.len, 0, 1, MDL);
}

406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497
/*
 * 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 */ 
}

498
/*
499
 * hardware-parameter :== HARDWARE hardware-type colon-separated-hex-list SEMI
500
 * hardware-type :== ETHERNET | TOKEN_RING | FDDI
501
 */
502 503

void parse_hardware_param (cfile, hardware)
504
	struct parse *cfile;
505 506
	struct hardware *hardware;
{
507
	const char *val;
508
	enum dhcp_token token;
509
	unsigned hlen;
510 511
	unsigned char *t;

512
	token = next_token (&val, (unsigned *)0, cfile);
513 514
	switch (token) {
	      case ETHERNET:
515
		hardware -> hbuf [0] = HTYPE_ETHER;
516 517
		break;
	      case TOKEN_RING:
518
		hardware -> hbuf [0] = HTYPE_IEEE802;
519
		break;
520
	      case FDDI:
521
		hardware -> hbuf [0] = HTYPE_FDDI;
522
		break;
523
	      default:
524
		if (!strncmp (val, "unknown-", 8)) {
525
			hardware -> hbuf [0] = atoi (&val [8]);
526 527 528 529 530 531 532
		} else {
			parse_warn (cfile,
				    "expecting a network hardware type");
			skip_to_semi (cfile);

			return;
		}
533 534 535 536 537 538 539 540 541 542
	}

	/* 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;
543
	token = peek_token (&val, (unsigned *)0, cfile);
544 545 546 547
	if (token == SEMI) {
		hardware -> hlen = 1;
		goto out;
	}
548 549
	t = parse_numeric_aggregate (cfile, (unsigned char *)0, &hlen,
				     COLON, 16, 8);
550 551
	if (!t) {
		hardware -> hlen = 1;
552
		return;
553
	}
554
	if (hlen + 1 > sizeof hardware -> hbuf) {
Ted Lemon's avatar
Ted Lemon committed
555
		dfree (t, MDL);
556
		parse_warn (cfile, "hardware address too long");
557
	} else {
558 559 560 561 562
		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
563
		dfree (t, MDL);
564 565
	}
	
566
      out:
567
	token = next_token (&val, (unsigned *)0, cfile);
568
	if (token != SEMI) {
569
		parse_warn (cfile, "expecting semicolon.");
570 571 572 573 574 575 576
		skip_to_semi (cfile);
	}
}

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

void parse_lease_time (cfile, timep)
577
	struct parse *cfile;
578 579
	TIME *timep;
{
580
	const char *val;
581
	enum dhcp_token token;
582
	u_int32_t num;
583

584
	token = next_token (&val, (unsigned *)0, cfile);
585
	if (token != NUMBER) {
586
		parse_warn (cfile, "Expecting numeric lease time");
587 588 589
		skip_to_semi (cfile);
		return;
	}
590
	convert_num(cfile, (unsigned char *)&num, val, 10, 32);
591
	/* Unswap the number - convert_num returns stuff in NBO. */
592
	*timep = ntohl(num);
593 594 595 596 597

	parse_semi (cfile);
}

/* No BNF for numeric aggregates - that's defined by the caller.  What
598 599
   this function does is to parse a sequence of numbers separated by
   the token specified in separator.  If max is zero, any number of
600 601 602 603 604
   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,
605
					max, separator, base, size)
606
	struct parse *cfile;
607
	unsigned char *buf;
608
	unsigned *max;
609
	int separator;
610
	int base;
611
	unsigned size;
612
{
613
	const char *val;
614
	enum dhcp_token token;
615
	unsigned char *bufp = buf, *s, *t;
616
	unsigned count = 0;
617 618 619
	pair c = (pair)0;

	if (!bufp && *max) {
Ted Lemon's avatar
Ted Lemon committed
620
		bufp = (unsigned char *)dmalloc (*max * size / 8, MDL);
621
		if (!bufp)
622 623
			log_fatal ("no space for numeric aggregate");
		s = 0;
624 625 626 627 628
	} else
		s = bufp;

	do {
		if (count) {
629
			token = peek_token (&val, (unsigned *)0, cfile);
630
			if (token != separator) {
631 632 633
				if (!*max)
					break;
				if (token != RBRACE && token != LBRACE)
634 635 636
					token = next_token (&val,
							    (unsigned *)0,
							    cfile);
637
				parse_warn (cfile, "too few numbers.");
638 639 640 641
				if (token != SEMI)
					skip_to_semi (cfile);
				return (unsigned char *)0;
			}
642
			token = next_token (&val, (unsigned *)0, cfile);
643
		}
644
		token = next_token (&val, (unsigned *)0, cfile);
645

646
		if (token == END_OF_FILE) {
647
			parse_warn (cfile, "unexpected end of file");
648 649 650 651 652 653
			break;
		}

		/* Allow NUMBER_OR_NAME if base is 16. */
		if (token != NUMBER &&
		    (base != 16 || token != NUMBER_OR_NAME)) {
654
			parse_warn (cfile, "expecting numeric value.");
655 656 657 658 659 660
			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) {
661
			convert_num (cfile, s, val, base, size);
662 663
			s += size / 8;
		} else {
Ted Lemon's avatar
Ted Lemon committed
664
			t = (unsigned char *)dmalloc (strlen (val) + 1, MDL);
665
			if (!t)
666
				log_fatal ("no temp space for number.");
667 668
			strcpy ((char *)t, val);
			c = cons ((caddr_t)t, c);
669 670 671 672 673
		}
	} while (++count != *max);

	/* If we had to cons up a list, convert it now. */
	if (c) {
Ted Lemon's avatar
Ted Lemon committed
674
		bufp = (unsigned char *)dmalloc (count * size / 8, MDL);
675
		if (!bufp)
676
			log_fatal ("no space for numeric aggregate.");
677 678 679 680 681
		s = bufp + count - size / 8;
		*max = count;
	}
	while (c) {
		pair cdr = c -> cdr;
682
		convert_num (cfile, s, (char *)(c -> car), base, size);
683 684
		s -= size / 8;
		/* Free up temp space. */
Ted Lemon's avatar
Ted Lemon committed
685 686
		dfree (c -> car, MDL);
		dfree (c, MDL);
687 688 689 690 691
		c = cdr;
	}
	return bufp;
}

692 693
void convert_num (cfile, buf, str, base, size)
	struct parse *cfile;
694
	unsigned char *buf;
695
	const char *str;
696
	int base;
697
	unsigned size;
698
{
699
	const char *ptr = str;
700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736
	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 {
737
			parse_warn (cfile, "Bogus number: %s.", str);
738 739 740
			break;
		}
		if (tval >= base) {
741 742
			parse_warn (cfile,
				    "Bogus number %s: digit %d not in base %d",
743
				    str, tval, base);
744 745 746 747 748 749 750 751 752 753 754 755
			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:
756
			parse_warn (cfile,
757
				    "%s%lo exceeds max (%d) for precision.",
Ted Lemon's avatar
Ted Lemon committed
758 759
				    negative ? "-" : "",
				    (unsigned long)val, max);
760 761
			break;
		      case 16:
762
			parse_warn (cfile,
763
				    "%s%lx exceeds max (%d) for precision.",
Ted Lemon's avatar
Ted Lemon committed
764 765
				    negative ? "-" : "",
				    (unsigned long)val, max);
766 767
			break;
		      default:
768
			parse_warn (cfile,
769
				    "%s%lu exceeds max (%d) for precision.",
Ted Lemon's avatar
Ted Lemon committed
770 771
				    negative ? "-" : "",
				    (unsigned long)val, max);
772 773 774 775 776 777 778 779 780 781
			break;
		}
	}

	if (negative) {
		switch (size) {
		      case 8:
			*buf = -(unsigned long)val;
			break;
		      case 16:
782
			putShort (buf, -(long)val);
783 784
			break;
		      case 32:
785
			putLong (buf, -(long)val);
786 787
			break;
		      default:
788 789
			parse_warn (cfile,
				    "Unexpected integer size: %d\n", size);
790 791 792 793 794 795 796 797 798 799 800 801 802 803
			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:
804 805
			parse_warn (cfile,
				    "Unexpected integer size: %d\n", size);
806 807 808 809 810
			break;
		}
	}
}

811 812
/*
 * date :== NUMBER NUMBER SLASH NUMBER SLASH NUMBER 
813 814 815
 *		NUMBER COLON NUMBER COLON NUMBER SEMI |
 *          NUMBER NUMBER SLASH NUMBER SLASH NUMBER 
 *		NUMBER COLON NUMBER COLON NUMBER NUMBER SEMI |
816
 *	    NEVER
817
 *
818 819 820 821
 * 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.
822
 */
823 824

TIME parse_date (cfile)
825
	struct parse *cfile;
826 827 828
{
	struct tm tm;
	int guess;
829
	int tzoff, wday, year, mon, mday, hour, min, sec;
830
	const char *val;
831
	enum dhcp_token token;
832 833 834
	static int months [11] = { 31, 59, 90, 120, 151, 181,
					  212, 243, 273, 304, 334 };

835
	/* Day of week, or "never"... */
836
	token = next_token (&val, (unsigned *)0, cfile);
837 838 839 840 841 842
	if (token == NEVER) {
		if (!parse_semi (cfile))
			return 0;
		return MAX_TIME;
	}

843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861
	/* 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;
	}

862
	if (token != NUMBER) {
863
		parse_warn (cfile, "numeric day of week expected.");
864 865 866 867
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}
868
	wday = atoi (val);
869 870

	/* Year... */
871
	token = next_token (&val, (unsigned *)0, cfile);
872
	if (token != NUMBER) {
873
		parse_warn (cfile, "numeric year expected.");
874 875 876 877
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}
878 879 880 881 882

	/* 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. */
883 884 885
	year = atoi (val);
	if (year > 1900)
		year -= 1900;
886

887
	/* Slash separating year from month... */
888
	token = next_token (&val, (unsigned *)0, cfile);
889
	if (token != SLASH) {
890
		parse_warn (cfile,
891
			    "expected slash separating year from month.");
892 893 894 895 896 897
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}

	/* Month... */
898
	token = next_token (&val, (unsigned *)0, cfile);
899
	if (token != NUMBER) {
900
		parse_warn (cfile, "numeric month expected.");
901 902 903 904
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}
905
	mon = atoi (val) - 1;
906

907
	/* Slash separating month from day... */
908
	token = next_token (&val, (unsigned *)0, cfile);
909
	if (token != SLASH) {
910
		parse_warn (cfile,
911
			    "expected slash separating month from day.");
912 913 914 915 916
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}

917
	/* Day of month... */
918
	token = next_token (&val, (unsigned *)0, cfile);
919
	if (token != NUMBER) {
920
		parse_warn (cfile, "numeric day of month expected.");
921 922 923 924
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}
925
	mday = atoi (val);
926 927

	/* Hour... */
928
	token = next_token (&val, (unsigned *)0, cfile);
929
	if (token != NUMBER) {
930
		parse_warn (cfile, "numeric hour expected.");
931 932 933 934
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}
935
	hour = atoi (val);
936

937
	/* Colon separating hour from minute... */
938
	token = next_token (&val, (unsigned *)0, cfile);
939
	if (token != COLON) {
940
		parse_warn (cfile,
941
			    "expected colon separating hour from minute.");
942 943 944 945 946 947
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}

	/* Minute... */
948
	token = next_token (&val, (unsigned *)0, cfile);
949
	if (token != NUMBER) {
950
		parse_warn (cfile, "numeric minute expected.");
951 952 953 954
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}
955
	min = atoi (val);
956

957
	/* Colon separating minute from second... */
958
	token = next_token (&val, (unsigned *)0, cfile);
959
	if (token != COLON) {
960
		parse_warn (cfile,
961
			    "expected colon separating minute from second.");
962 963 964 965 966
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}

967
	/* Second... */
968
	token = next_token (&val, (unsigned *)0, cfile);
969
	if (token != NUMBER) {
970
		parse_warn (cfile, "numeric second expected.");
971 972 973 974
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}
975
	sec = atoi (val);
976

977
	token = peek_token (&val, (unsigned *)0, cfile);
978
	if (token == NUMBER) {
979
		token = next_token (&val, (unsigned *)0, cfile);
980 981 982
		tzoff = atoi (val);
	} else
		tzoff = 0;
983 984

	/* Make sure the date ends in a semicolon... */
Ted Lemon's avatar
Ted Lemon committed
985
	if (!parse_semi (cfile))
986 987 988
		return 0;

	/* Guess the time value... */
989 990 991 992
	guess = ((((((365 * (year - 70) +	/* Days in years since '70 */
		      (year - 69) / 4 +		/* Leap days since '70 */
		      (mon