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

   Common parser code for dhcpd and dhclient. */

/*
Thomas Markwalder's avatar
Thomas Markwalder committed
6
 * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC")
7
 * Copyright (c) 1995-2003 by Internet Software Consortium
8
 *
9 10 11
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
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
 *   Internet Systems Consortium, Inc.
 *   950 Charter Street
 *   Redwood City, CA 94063
 *   <info@isc.org>
25
 *   https://www.isc.org/
Ted Lemon's avatar
Ted Lemon committed
26
 *
27 28 29
 */

#include "dhcpd.h"
30
#include <syslog.h>
31

32 33 34
struct collection default_collection = { NULL, "default", NULL };
struct collection *collections = &default_collection;

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

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

77
/* Skip to the semicolon ending the current statement.   If we encounter
78 79 80 81 82 83 84 85 86 87 88 89
   braces, the matching closing brace terminates the statement.
*/
void skip_to_semi (cfile)
	struct parse *cfile;
{
	skip_to_rbrace(cfile, 0);
}

/* Skips everything from the current point upto (and including) the given
 number of right braces.  If we encounter a semicolon but haven't seen a
 left brace, consume it and return.
 This lets us skip over:
90 91 92 93 94 95 96

   	statement;
	statement foo bar { }
	statement foo bar { statement { } }
	statement}
 
	...et cetera. */
97
void skip_to_rbrace (cfile, brace_count)
98
	struct parse *cfile;
99
	int brace_count;
100
{
101
	enum dhcp_token token;
102
	const char *val;
103

104
#if defined (DEBUG_TOKENS)
105
	log_error("skip_to_rbrace: %d\n", brace_count);
106
#endif
107
	do {
108
		token = peek_token(&val, NULL, cfile);
109
		if (token == RBRACE) {
110 111 112 113 114 115 116
			if (brace_count > 0) {
				--brace_count;
			}

			if (brace_count == 0) {
				/* Eat the brace and return. */
				skip_token(&val, NULL, cfile);
117
				return;
118
			}
119 120
		} else if (token == LBRACE) {
			brace_count++;
121 122 123
		} else if (token == SEMI && (brace_count == 0)) {
			/* Eat the semicolon and return. */
			skip_token(&val, NULL, 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
			skip_token(&val, NULL, cfile);
130
			return;
131
		}
132 133 134

		/* Eat the current token */
		token = next_token(&val, NULL, cfile);
135
	} while (token != END_OF_FILE);
136 137 138
}

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

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

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

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

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

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

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

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

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

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

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

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
261 262 263 264 265
   than one IP address.

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

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

280
	token = peek_token (&val, (unsigned *)0, cfile);
281 282 283 284 285 286 287 288 289

	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.
		 */
290
		save_parse_state(cfile);
291
		skip_token(NULL, NULL, cfile);
292 293 294
		if (next_token(NULL, NULL, cfile) == DOT &&
		    next_token(NULL, NULL, cfile) == NUMBER)
			ipaddr = 1;
295
		restore_parse_state(cfile);
296 297 298 299 300 301 302 303

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

328
	return 1;
329 330 331 332 333 334
}	
	
/*
 * ip-address :== NUMBER DOT NUMBER DOT NUMBER DOT NUMBER
 */

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

379 380 381 382 383 384 385 386 387
	/*
	 * 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
388 389
	v6_len = 0;
	for (;;) {
390
		if ((((token == NAME) || (token == NUMBER_OR_NAME)) &&
David Hankins's avatar
David Hankins committed
391
		     is_hex_string(val)) ||
392 393 394
		    (token == NUMBER) ||
		    (token == TOKEN_ADD) ||
		    (token == DOT) ||
David Hankins's avatar
David Hankins committed
395 396
		    (token == COLON)) {

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

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

Francis Dupont's avatar
Francis Dupont committed
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
/*
 * ip6-prefix :== ip6-address "/" NUMBER
 */
int
parse_ip6_prefix(struct parse *cfile, struct iaddr *addr, u_int8_t *plen) {
	enum dhcp_token token;
	const char *val;
	int n;

	if (!parse_ip6_addr(cfile, addr)) {
		return 0;
	}
	token = next_token(&val, NULL, cfile);
	if (token != SLASH) {
		parse_warn(cfile, "Slash expected.");
		if (token != SEMI)
			skip_to_semi(cfile);
		return 0;
	}
	token = next_token(&val, NULL, cfile);
	if (token != NUMBER) {
		parse_warn(cfile, "Number expected.");
		if (token != SEMI)
			skip_to_semi(cfile);
		return 0;
	}
	n = atoi(val);
	if ((n < 0) || (n > 128)) {
		parse_warn(cfile, "Invalid IPv6 prefix length.");
		skip_to_semi(cfile);
		return 0;
	}
	if (!is_cidr_mask_valid(addr, n)) {
		parse_warn(cfile, "network mask too short.");
		skip_to_semi(cfile);
		return 0;
	}
	*plen = n;
	return 1;
}

482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504
/*
 * 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) {
505
			skip_token(&val, NULL, cfile);
506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573
			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 */ 
}

574
/*
575
 * hardware-parameter :== HARDWARE hardware-type colon-separated-hex-list SEMI
576 577 578
 * hardware-type :== ETHERNET | TOKEN_RING | TOKEN_FDDI | INFINIBAND
 * Note that INFINIBAND may not be useful for some items, such as classification
 * as the hardware address won't always be available.
579
 */
580 581

void parse_hardware_param (cfile, hardware)
582
	struct parse *cfile;
583 584
	struct hardware *hardware;
{
585
	const char *val;
586
	enum dhcp_token token;
587
	unsigned hlen;
588 589
	unsigned char *t;

590
	token = next_token(&val, NULL, cfile);
591 592
	switch (token) {
	      case ETHERNET:
593
		hardware->hbuf[0] = HTYPE_ETHER;
594 595
		break;
	      case TOKEN_RING:
596
		hardware->hbuf[0] = HTYPE_IEEE802;
597
		break;
598
	      case TOKEN_FDDI:
599 600 601 602
		hardware->hbuf[0] = HTYPE_FDDI;
		break;
	      case TOKEN_INFINIBAND:
		hardware->hbuf[0] = HTYPE_INFINIBAND;
603
		break;
604
	      default:
605 606
		if (!strncmp(val, "unknown-", 8)) {
			hardware->hbuf[0] = atoi(&val[8]);
607
		} else {
608 609 610
			parse_warn(cfile,
				   "expecting a network hardware type");
			skip_to_semi(cfile);
611 612 613

			return;
		}
614 615 616 617 618 619 620 621 622 623
	}

	/* 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;
624
	token = peek_token(&val, NULL, cfile);
625
	if (token == SEMI) {
626
		hardware->hlen = 1;
627 628
		goto out;
	}
629 630 631
	t = parse_numeric_aggregate(cfile, NULL, &hlen, COLON, 16, 8);
	if (t == NULL) {
		hardware->hlen = 1;
632
		return;
633
	}
634 635 636
	if (hlen + 1 > sizeof(hardware->hbuf)) {
		dfree(t, MDL);
		parse_warn(cfile, "hardware address too long");
637
	} else {
638 639 640 641 642 643
		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);
		dfree(t, MDL);
644 645
	}
	
646
      out:
647
	token = next_token(&val, NULL, cfile);
648
	if (token != SEMI) {
649 650
		parse_warn(cfile, "expecting semicolon.");
		skip_to_semi(cfile);
651 652 653 654 655 656
	}
}

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

void parse_lease_time (cfile, timep)
657
	struct parse *cfile;
658 659
	TIME *timep;
{
660
	const char *val;
661
	enum dhcp_token token;
662
	u_int32_t num;
663

664
	token = next_token (&val, (unsigned *)0, cfile);
665
	if (token != NUMBER) {
666
		parse_warn (cfile, "Expecting numeric lease time");
667 668 669
		skip_to_semi (cfile);
		return;
	}
670
	convert_num(cfile, (unsigned char *)&num, val, 10, 32);
671
	/* Unswap the number - convert_num returns stuff in NBO. */
672
	*timep = ntohl(num);
673 674 675 676 677

	parse_semi (cfile);
}

/* No BNF for numeric aggregates - that's defined by the caller.  What
678 679
   this function does is to parse a sequence of numbers separated by
   the token specified in separator.  If max is zero, any number of
680 681
   numbers will be parsed; otherwise, exactly max numbers are
   expected.  Base and size tell us how to internalize the numbers
Shawn Routhier's avatar
Shawn Routhier committed
682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698
   once they've been tokenized.

   buf - A pointer to space to return the parsed value, if it is null
   then the function will allocate space for the return.

   max - The maximum number of items to store.  If zero there is no
   maximum.  When buf is null and the function needs to allocate space
   it will do an allocation of max size at the beginning if max is non
   zero.  If max is zero then the allocation will be done later, after
   the function has determined the size necessary for the incoming
   string.

   returns NULL on errors or a pointer to the value string on success.
   The pointer will either be buf if it was non-NULL or newly allocated
   space if buf was NULL
 */

699 700

unsigned char *parse_numeric_aggregate (cfile, buf,
701
					max, separator, base, size)
702
	struct parse *cfile;
703
	unsigned char *buf;
704
	unsigned *max;
705
	int separator;
706
	int base;
707
	unsigned size;
708
{
709
	const char *val;
710
	enum dhcp_token token;
711
	unsigned char *bufp = buf, *s, *t;
712
	unsigned count = 0;
713 714 715
	pair c = (pair)0;

	if (!bufp && *max) {
Ted Lemon's avatar
Ted Lemon committed
716
		bufp = (unsigned char *)dmalloc (*max * size / 8, MDL);
717
		if (!bufp)
718
			log_fatal ("no space for numeric aggregate");
Shawn Routhier's avatar
Shawn Routhier committed
719 720
	}
	s = bufp;
721 722 723

	do {
		if (count) {
724
			token = peek_token (&val, (unsigned *)0, cfile);
725
			if (token != separator) {
726 727 728
				if (!*max)
					break;
				if (token != RBRACE && token != LBRACE)
729 730 731
					token = next_token (&val,
							    (unsigned *)0,
							    cfile);
732
				parse_warn (cfile, "too few numbers.");
733 734
				if (token != SEMI)
					skip_to_semi (cfile);
Shawn Routhier's avatar
Shawn Routhier committed
735 736 737
				/* free bufp if it was allocated */
				if ((bufp != NULL) && (bufp != buf))
					dfree(bufp, MDL);
738 739
				return (unsigned char *)0;
			}
740
			skip_token(&val, (unsigned *)0, cfile);
741
		}
742
		token = next_token (&val, (unsigned *)0, cfile);
743

744
		if (token == END_OF_FILE) {
745
			parse_warn (cfile, "unexpected end of file");
746 747 748 749 750 751
			break;
		}

		/* Allow NUMBER_OR_NAME if base is 16. */
		if (token != NUMBER &&
		    (base != 16 || token != NUMBER_OR_NAME)) {
752
			parse_warn (cfile, "expecting numeric value.");
753
			skip_to_semi (cfile);
Shawn Routhier's avatar
Shawn Routhier committed
754 755 756 757 758 759 760 761 762 763 764
			/* free bufp if it was allocated */
			if ((bufp != NULL) && (bufp != buf))
				dfree(bufp, MDL);
			/* free any linked numbers we may have allocated */
			while (c) {
				pair cdr = c->cdr;
				dfree(c->car, MDL);
				dfree(c, MDL);
				c = cdr;
			}
			return (NULL);
765 766 767 768
		}
		/* If we can, convert the number now; otherwise, build
		   a linked list of all the numbers. */
		if (s) {
769
			convert_num (cfile, s, val, base, size);
770 771
			s += size / 8;
		} else {
Ted Lemon's avatar
Ted Lemon committed
772
			t = (unsigned char *)dmalloc (strlen (val) + 1, MDL);
773
			if (!t)
774
				log_fatal ("no temp space for number.");
775 776
			strcpy ((char *)t, val);
			c = cons ((caddr_t)t, c);
777 778 779 780 781
		}
	} while (++count != *max);

	/* If we had to cons up a list, convert it now. */
	if (c) {
Shawn Routhier's avatar
Shawn Routhier committed
782 783 784 785
		/*
		 * No need to cleanup bufp, to get here we didn't allocate
		 * bufp above
		 */
Ted Lemon's avatar
Ted Lemon committed
786
		bufp = (unsigned char *)dmalloc (count * size / 8, MDL);
787
		if (!bufp)
788
			log_fatal ("no space for numeric aggregate.");
789 790 791 792 793
		s = bufp + count - size / 8;
		*max = count;
	}
	while (c) {
		pair cdr = c -> cdr;
794
		convert_num (cfile, s, (char *)(c -> car), base, size);
795 796
		s -= size / 8;
		/* Free up temp space. */
Ted Lemon's avatar
Ted Lemon committed
797 798
		dfree (c -> car, MDL);
		dfree (c, MDL);
799 800 801 802 803
		c = cdr;
	}
	return bufp;
}

804 805
void convert_num (cfile, buf, str, base, size)
	struct parse *cfile;
806
	unsigned char *buf;
807
	const char *str;
808
	int base;
809
	unsigned size;
810
{
811
	const unsigned char *ptr = (const unsigned char *)str;
812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848
	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 {
849
			parse_warn (cfile, "Bogus number: %s.", str);
850 851 852
			break;
		}
		if (tval >= base) {
853 854
			parse_warn (cfile,
				    "Bogus number %s: digit %d not in base %d",
855
				    str, tval, base);
856 857 858 859 860 861 862 863 864 865 866 867
			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:
868
			parse_warn (cfile,
869
				    "%s%lo exceeds max (%d) for precision.",
Ted Lemon's avatar
Ted Lemon committed
870 871
				    negative ? "-" : "",
				    (unsigned long)val, max);
872 873
			break;
		      case 16:
874
			parse_warn (cfile,
875
				    "%s%lx exceeds max (%d) for precision.",
Ted Lemon's avatar
Ted Lemon committed
876 877
				    negative ? "-" : "",
				    (unsigned long)val, max);
878 879
			break;
		      default:
880
			parse_warn (cfile,
881
				    "%s%lu exceeds max (%d) for precision.",
Ted Lemon's avatar
Ted Lemon committed
882 883
				    negative ? "-" : "",
				    (unsigned long)val, max);
884 885 886 887 888 889 890 891 892 893
			break;
		}
	}

	if (negative) {
		switch (size) {
		      case 8:
			*buf = -(unsigned long)val;
			break;
		      case 16:
894
			putShort (buf, -(long)val);
895 896
			break;
		      case 32:
897
			putLong (buf, -(long)val);
898 899
			break;
		      default:
900 901
			parse_warn (cfile,
				    "Unexpected integer size: %d\n", size);
902 903 904 905 906 907 908 909 910 911 912 913 914 915
			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:
916 917
			parse_warn (cfile,
				    "Unexpected integer size: %d\n", size);
918 919 920 921 922
			break;
		}
	}
}

923 924
/*
 * date :== NUMBER NUMBER SLASH NUMBER SLASH NUMBER 
925
 *		NUMBER COLON NUMBER COLON NUMBER |
926
 *          NUMBER NUMBER SLASH NUMBER SLASH NUMBER 
927 928
 *		NUMBER COLON NUMBER COLON NUMBER NUMBER |
 *          EPOCH NUMBER |
929
 *	    NEVER
930
 *
931
 * Dates are stored in UTC or with a timezone offset; first number is day
932 933 934
 * 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.
935
 */
936

937 938 939 940
/*
 * just parse the date
 * any trailing semi must be consumed by the caller of this routine
 */
941 942
TIME 
parse_date_core(cfile)
943
	struct parse *cfile;
944 945
{
	int guess;
946
	int tzoff, year, mon, mday, hour, min, sec;
947
	const char *val;
948
	enum dhcp_token token;
949 950
	static int months[11] = { 31, 59, 90, 120, 151, 181,
				  212, 243, 273, 304, 334 };
951

952 953
	/* "never", "epoch" or day of week */
	token = peek_token(&val, NULL, cfile);
954
	if (token == NEVER) {
955
		skip_token(&val, NULL, cfile); /* consume NEVER */
956
		return(MAX_TIME);
957 958
	}

959 960
	/* This indicates 'local' time format. */
	if (token == EPOCH) {
961
		skip_token(&val, NULL, cfile); /* consume EPOCH */
962
		token = peek_token(&val, NULL, cfile);
963 964 965

		if (token != NUMBER) {
			if (token != SEMI)
966
				skip_token(&val, NULL, cfile);
967 968
			parse_warn(cfile, "Seconds since epoch expected.");
			return((TIME)0);
969 970
		}

971
		skip_token(&val, NULL, cfile); /* consume number */
972 973
		guess = atoi(val);

974
		return((TIME)guess);
975 976
	}

977 978
	if (token != NUMBER) {
		if (token != SEMI)
979
			skip_token(&val, NULL, cfile);
980 981
		parse_warn(cfile, "numeric day of week expected.");
		return((TIME)0);
982
	}
983
	skip_token(&val, NULL, cfile); /* consume day of week */
984
        /* we are not using this for anything */
985 986

	/* Year... */
987
	token = peek_token(&val, NULL, cfile);
988 989
	if (token != NUMBER) {
		if (token != SEMI)
990
			skip_token(&val, NULL, cfile);
991 992
		parse_warn(cfile, "numeric year expected.");
		return((TIME)0);
993
	}
994
	skip_token(&val, NULL, cfile); /* consume year */
995 996 997 998 999

	/* 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. */
1000
	year = atoi(val);
1001 1002
	if (year > 1900)
		year -= 1900;
1003

1004
	/* Slash separating year from month... */
1005
	token = peek_token(&val, NULL, cfile);
1006 1007
	if (token != SLASH) {
		if (token != SEMI)