parse.c 138 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
 */

#include "dhcpd.h"
36
#include <syslog.h>
37

38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
/* 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,
62
						  unsigned *widthp,
63 64 65 66 67 68 69
						  const char *value)
{
	struct enumeration *e;
	int i;

	e = find_enumeration (name, length);
	if (e) {
70 71
		if (widthp != NULL)
			*widthp = e->width;
72 73 74 75 76 77 78 79
		for (i = 0; e -> values [i].name; i++) {
			if (!strcmp (value, e -> values [i].name))
				return &e -> values [i];
		}
	}
	return (struct enumeration_value *)0;
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

253 254 255 256 257
/* 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
258 259 260 261 262
   than one IP address.

   Note that RFC1123 permits hostnames to consist of all digits,
   making it difficult to quickly disambiguate them from ip addresses.
*/
263

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

277
	token = peek_token (&val, (unsigned *)0, cfile);
278 279 280 281 282 283 284 285 286

	if (token == NUMBER) {
		/*
		 * a hostname may be numeric, but domain names must
		 * start with a letter, so we can disambiguate by
		 * looking ahead a few tokens.  we save the parse
		 * context first, and restore it after we know what
		 * we're dealing with.
		 */
287
		save_parse_state(cfile);
288 289 290 291
		(void) next_token(NULL, NULL, cfile);
		if (next_token(NULL, NULL, cfile) == DOT &&
		    next_token(NULL, NULL, cfile) == NUMBER)
			ipaddr = 1;
292
		restore_parse_state(cfile);
293 294 295 296 297 298 299 300

		if (ipaddr &&
		    parse_numeric_aggregate (cfile, addr, &len, DOT, 10, 8))
			return make_const_data (expr, addr, len, 0, 1, MDL);

	}

	if (is_identifier (token) || token == NUMBER) {
301 302
		name = parse_host_name (cfile);
		if (!name)
303
			return 0;
304 305
		if (!make_host_lookup (expr, name)) {
			dfree(name, MDL);
306
			return 0;
307 308
		}
		dfree(name, MDL);
309 310 311
		if (!uniform) {
			if (!make_limit (&x, *expr, 4))
				return 0;
312
			expression_dereference (expr, MDL);
313 314
			*expr = x;
		}
315 316
	} else {
		if (token != RBRACE && token != LBRACE)
317
			token = next_token (&val, (unsigned *)0, cfile);
318
		parse_warn (cfile, "%s (%d): expecting IP address or hostname",
319 320 321
			    val, token);
		if (token != SEMI)
			skip_to_semi (cfile);
322
		return 0;
323 324
	}

325
	return 1;
326 327 328 329 330 331
}	
	
/*
 * ip-address :== NUMBER DOT NUMBER DOT NUMBER DOT NUMBER
 */

332
int parse_ip_addr (cfile, addr)
333
	struct parse *cfile;
334 335 336 337 338 339 340 341 342
	struct iaddr *addr;
{
	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
343
/*
Francis Dupont's avatar
Francis Dupont committed
344
 * Return true if every character in the string is hexadecimal.
David Hankins's avatar
David Hankins committed
345 346 347 348
 */
static int
is_hex_string(const char *s) {
	while (*s != '\0') {
349
		if (!isxdigit((int)*s)) {
David Hankins's avatar
David Hankins committed
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
			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;

376 377 378 379 380 381 382 383 384
	/*
	 * First token is non-raw. This way we eat any whitespace before 
	 * our IPv6 address begins, like one would expect.
	 */
	token = peek_token(&val, NULL, cfile);

	/*
	 * Gather symbols.
	 */
David Hankins's avatar
David Hankins committed
385 386 387 388 389 390 391 392
	v6_len = 0;
	for (;;) {
		if ((((token == NAME) || (token == NUMBER_OR_NAME)) && 
		     is_hex_string(val)) ||
		    (token == NUMBER) || 
		    (token == DOT) || 
		    (token == COLON)) {

393
			next_raw_token(&val, NULL, cfile);
David Hankins's avatar
David Hankins committed
394 395 396 397 398 399 400 401 402 403 404 405
			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;
		}
406
		token = peek_raw_token(&val, NULL, cfile);
David Hankins's avatar
David Hankins committed
407 408 409
	}
	v6[v6_len] = '\0';

410 411 412
	/*
	 * Use inet_pton() for actual work.
	 */
David Hankins's avatar
David Hankins committed
413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436
	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);
}

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 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528
/*
 * 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 */ 
}

529
/*
530
 * hardware-parameter :== HARDWARE hardware-type colon-separated-hex-list SEMI
531
 * hardware-type :== ETHERNET | TOKEN_RING | FDDI
532
 */
533 534

void parse_hardware_param (cfile, hardware)
535
	struct parse *cfile;
536 537
	struct hardware *hardware;
{
538
	const char *val;
539
	enum dhcp_token token;
540
	unsigned hlen;
541 542
	unsigned char *t;

543
	token = next_token (&val, (unsigned *)0, cfile);
544 545
	switch (token) {
	      case ETHERNET:
546
		hardware -> hbuf [0] = HTYPE_ETHER;
547 548
		break;
	      case TOKEN_RING:
549
		hardware -> hbuf [0] = HTYPE_IEEE802;
550
		break;
551
	      case FDDI:
552
		hardware -> hbuf [0] = HTYPE_FDDI;
553
		break;
554
	      default:
555
		if (!strncmp (val, "unknown-", 8)) {
556
			hardware -> hbuf [0] = atoi (&val [8]);
557 558 559 560 561 562 563
		} else {
			parse_warn (cfile,
				    "expecting a network hardware type");
			skip_to_semi (cfile);

			return;
		}
564 565 566 567 568 569 570 571 572 573
	}

	/* 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;
574
	token = peek_token (&val, (unsigned *)0, cfile);
575 576 577 578
	if (token == SEMI) {
		hardware -> hlen = 1;
		goto out;
	}
579 580
	t = parse_numeric_aggregate (cfile, (unsigned char *)0, &hlen,
				     COLON, 16, 8);
581 582
	if (!t) {
		hardware -> hlen = 1;
583
		return;
584
	}
585
	if (hlen + 1 > sizeof hardware -> hbuf) {
Ted Lemon's avatar
Ted Lemon committed
586
		dfree (t, MDL);
587
		parse_warn (cfile, "hardware address too long");
588
	} else {
589 590 591 592 593
		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
594
		dfree (t, MDL);
595 596
	}
	
597
      out:
598
	token = next_token (&val, (unsigned *)0, cfile);
599
	if (token != SEMI) {
600
		parse_warn (cfile, "expecting semicolon.");
601 602 603 604 605 606 607
		skip_to_semi (cfile);
	}
}

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

void parse_lease_time (cfile, timep)
608
	struct parse *cfile;
609 610
	TIME *timep;
{
611
	const char *val;
612
	enum dhcp_token token;
613
	u_int32_t num;
614

615
	token = next_token (&val, (unsigned *)0, cfile);
616
	if (token != NUMBER) {
617
		parse_warn (cfile, "Expecting numeric lease time");
618 619 620
		skip_to_semi (cfile);
		return;
	}
621
	convert_num(cfile, (unsigned char *)&num, val, 10, 32);
622
	/* Unswap the number - convert_num returns stuff in NBO. */
623
	*timep = ntohl(num);
624 625 626 627 628

	parse_semi (cfile);
}

/* No BNF for numeric aggregates - that's defined by the caller.  What
629 630
   this function does is to parse a sequence of numbers separated by
   the token specified in separator.  If max is zero, any number of
631 632 633 634 635
   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,
636
					max, separator, base, size)
637
	struct parse *cfile;
638
	unsigned char *buf;
639
	unsigned *max;
640
	int separator;
641
	int base;
642
	unsigned size;
643
{
644
	const char *val;
645
	enum dhcp_token token;
646
	unsigned char *bufp = buf, *s, *t;
647
	unsigned count = 0;
648 649 650
	pair c = (pair)0;

	if (!bufp && *max) {
Ted Lemon's avatar
Ted Lemon committed
651
		bufp = (unsigned char *)dmalloc (*max * size / 8, MDL);
652
		if (!bufp)
653 654
			log_fatal ("no space for numeric aggregate");
		s = 0;
655 656 657 658 659
	} else
		s = bufp;

	do {
		if (count) {
660
			token = peek_token (&val, (unsigned *)0, cfile);
661
			if (token != separator) {
662 663 664
				if (!*max)
					break;
				if (token != RBRACE && token != LBRACE)
665 666 667
					token = next_token (&val,
							    (unsigned *)0,
							    cfile);
668
				parse_warn (cfile, "too few numbers.");
669 670 671 672
				if (token != SEMI)
					skip_to_semi (cfile);
				return (unsigned char *)0;
			}
673
			token = next_token (&val, (unsigned *)0, cfile);
674
		}
675
		token = next_token (&val, (unsigned *)0, cfile);
676

677
		if (token == END_OF_FILE) {
678
			parse_warn (cfile, "unexpected end of file");
679 680 681 682 683 684
			break;
		}

		/* Allow NUMBER_OR_NAME if base is 16. */
		if (token != NUMBER &&
		    (base != 16 || token != NUMBER_OR_NAME)) {
685
			parse_warn (cfile, "expecting numeric value.");
686 687 688 689 690 691
			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) {
692
			convert_num (cfile, s, val, base, size);
693 694
			s += size / 8;
		} else {
Ted Lemon's avatar
Ted Lemon committed
695
			t = (unsigned char *)dmalloc (strlen (val) + 1, MDL);
696
			if (!t)
697
				log_fatal ("no temp space for number.");
698 699
			strcpy ((char *)t, val);
			c = cons ((caddr_t)t, c);
700 701 702 703 704
		}
	} while (++count != *max);

	/* If we had to cons up a list, convert it now. */
	if (c) {
Ted Lemon's avatar
Ted Lemon committed
705
		bufp = (unsigned char *)dmalloc (count * size / 8, MDL);
706
		if (!bufp)
707
			log_fatal ("no space for numeric aggregate.");
708 709 710 711 712
		s = bufp + count - size / 8;
		*max = count;
	}
	while (c) {
		pair cdr = c -> cdr;
713
		convert_num (cfile, s, (char *)(c -> car), base, size);
714 715
		s -= size / 8;
		/* Free up temp space. */
Ted Lemon's avatar
Ted Lemon committed
716 717
		dfree (c -> car, MDL);
		dfree (c, MDL);
718 719 720 721 722
		c = cdr;
	}
	return bufp;
}

723 724
void convert_num (cfile, buf, str, base, size)
	struct parse *cfile;
725
	unsigned char *buf;
726
	const char *str;
727
	int base;
728
	unsigned size;
729
{
730
	const unsigned char *ptr = (const unsigned char *)str;
731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767
	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 {
768
			parse_warn (cfile, "Bogus number: %s.", str);
769 770 771
			break;
		}
		if (tval >= base) {
772 773
			parse_warn (cfile,
				    "Bogus number %s: digit %d not in base %d",
774
				    str, tval, base);
775 776 777 778 779 780 781 782 783 784 785 786
			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:
787
			parse_warn (cfile,
788
				    "%s%lo exceeds max (%d) for precision.",
Ted Lemon's avatar
Ted Lemon committed
789 790
				    negative ? "-" : "",
				    (unsigned long)val, max);
791 792
			break;
		      case 16:
793
			parse_warn (cfile,
794
				    "%s%lx exceeds max (%d) for precision.",
Ted Lemon's avatar
Ted Lemon committed
795 796
				    negative ? "-" : "",
				    (unsigned long)val, max);
797 798
			break;
		      default:
799
			parse_warn (cfile,
800
				    "%s%lu exceeds max (%d) for precision.",
Ted Lemon's avatar
Ted Lemon committed
801 802
				    negative ? "-" : "",
				    (unsigned long)val, max);
803 804 805 806 807 808 809 810 811 812
			break;
		}
	}

	if (negative) {
		switch (size) {
		      case 8:
			*buf = -(unsigned long)val;
			break;
		      case 16:
813
			putShort (buf, -(long)val);
814 815
			break;
		      case 32:
816
			putLong (buf, -(long)val);
817 818
			break;
		      default:
819 820
			parse_warn (cfile,
				    "Unexpected integer size: %d\n", size);
821 822 823 824 825 826 827 828 829 830 831 832 833 834
			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:
835 836
			parse_warn (cfile,
				    "Unexpected integer size: %d\n", size);
837 838 839 840 841
			break;
		}
	}
}

842 843
/*
 * date :== NUMBER NUMBER SLASH NUMBER SLASH NUMBER 
844 845 846
 *		NUMBER COLON NUMBER COLON NUMBER SEMI |
 *          NUMBER NUMBER SLASH NUMBER SLASH NUMBER 
 *		NUMBER COLON NUMBER COLON NUMBER NUMBER SEMI |
847
 *	    NEVER
848
 *
849
 * Dates are stored in UTC or with a timezone offset; first number is day
850 851 852
 * 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.
853
 */
854

855 856 857
/* just parse the date */
TIME 
parse_date_core(cfile)
858
	struct parse *cfile;
859 860
{
	int guess;
861
	int tzoff, wday, year, mon, mday, hour, min, sec;
862
	const char *val;
863
	enum dhcp_token token;
864 865 866
	static int months [11] = { 31, 59, 90, 120, 151, 181,
					  212, 243, 273, 304, 334 };

867
	/* Day of week, or "never"... */
868
	token = next_token (&val, (unsigned *)0, cfile);
869 870 871 872 873 874
	if (token == NEVER) {
		if (!parse_semi (cfile))
			return 0;
		return MAX_TIME;
	}

875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893
	/* 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;
	}

894
	if (token != NUMBER) {
895
		parse_warn (cfile, "numeric day of week expected.");
896 897 898 899
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}
900
	wday = atoi (val);
901 902

	/* Year... */
903
	token = next_token (&val, (unsigned *)0, cfile);
904
	if (token != NUMBER) {
905
		parse_warn (cfile, "numeric year expected.");
906 907 908 909
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}
910 911 912 913 914

	/* 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. */
915 916 917
	year = atoi (val);
	if (year > 1900)
		year -= 1900;
918

919
	/* Slash separating year from month... */
920
	token = next_token (&val, (unsigned *)0, cfile);
921
	if (token != SLASH) {
922
		parse_warn (cfile,
923
			    "expected slash separating year from month.");
924 925 926 927 928 929
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}

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

939
	/* Slash separating month from day... */
940
	token = next_token (&val, (unsigned *)0, cfile);
941
	if (token != SLASH) {
942
		parse_warn (cfile,
943
			    "expected slash separating month from day.");
944 945 946 947 948
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}

949
	/* Day of month... */
950
	token = next_token (&val, (unsigned *)0, cfile);
951
	if (token != NUMBER) {
952
		parse_warn (cfile, "numeric day of month expected.");
953 954 955 956
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}
957
	mday = atoi (val);
958 959

	/* Hour... */
960
	token = next_token (&val, (unsigned *)0, cfile);
961
	if (token != NUMBER) {
962
		parse_warn (cfile, "numeric hour expected.");
963 964 965 966
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}
967
	hour = atoi (val);
968

969
	/* Colon separating hour from minute... */
970
	token = next_token (&val, (unsigned *)0, cfile);
971
	if (token != COLON) {
972
		parse_warn (cfile,
973
			    "expected colon separating hour from minute.");
974 975 976 977 978 979
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}

	/* Minute... */
980
	token = next_token (&val, (unsigned *)0, cfile);
981
	if (token != NUMBER) {
982
		parse_warn (cfile, "numeric minute expected.");
983 984 985 986
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}
987
	min = atoi (val);
988

989
	/* Colon separating minute from second... */
990
	token = next_token (&val, (unsigned *)0, cfile);