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

   Common parser code for dhcpd and dhclient. */

/*
David Hankins's avatar
David Hankins committed
6
 * Copyright (c) 2004-2007 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[] =
David Hankins's avatar
David Hankins committed
37
"$Id: parse.c,v 1.124 2007/05/19 19:16:24 dhankins Exp $ Copyright (c) 2004-2007 Internet Systems Consortium.  All rights reserved.\n";
38 39 40
#endif /* not lint */

#include "dhcpd.h"
41
#include <syslog.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 81
/* 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;
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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 498
/*
 * 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 */ 
}

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

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

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

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

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

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

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

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

	parse_semi (cfile);
}

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

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

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

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

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

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

693 694
void convert_num (cfile, buf, str, base, size)
	struct parse *cfile;
695
	unsigned char *buf;
696
	const char *str;
697
	int base;
698
	unsigned size;
699
{
700
	const char *ptr = str;
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 737
	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 {
738
			parse_warn (cfile, "Bogus number: %s.", str);
739 740 741
			break;
		}
		if (tval >= base) {
742 743
			parse_warn (cfile,
				    "Bogus number %s: digit %d not in base %d",
744
				    str, tval, base);
745 746 747 748 749 750 751 752 753 754 755 756
			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:
757
			parse_warn (cfile,
758
				    "%s%lo exceeds max (%d) for precision.",
Ted Lemon's avatar
Ted Lemon committed
759 760
				    negative ? "-" : "",
				    (unsigned long)val, max);
761 762
			break;
		      case 16:
763
			parse_warn (cfile,
764
				    "%s%lx exceeds max (%d) for precision.",
Ted Lemon's avatar
Ted Lemon committed
765 766
				    negative ? "-" : "",
				    (unsigned long)val, max);
767 768
			break;
		      default:
769
			parse_warn (cfile,
770
				    "%s%lu exceeds max (%d) for precision.",
Ted Lemon's avatar
Ted Lemon committed
771 772
				    negative ? "-" : "",
				    (unsigned long)val, max);
773 774 775 776 777 778 779 780 781 782
			break;
		}
	}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

	/* Guess the time value... */