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

   Common parser code for dhcpd and dhclient. */

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

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

74
/* Skip to the semicolon ending the current statement.   If we encounter
75 76 77 78 79 80 81 82 83 84 85 86
   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:
87 88 89 90 91 92 93

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

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

			if (brace_count == 0) {
				/* Eat the brace and return. */
				skip_token(&val, NULL, cfile);
114
				return;
115
			}
116 117
		} else if (token == LBRACE) {
			brace_count++;
118 119 120
		} else if (token == SEMI && (brace_count == 0)) {
			/* Eat the semicolon and return. */
			skip_token(&val, NULL, cfile);
121
			return;
122 123 124 125
		} 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. */
126
			skip_token(&val, NULL, cfile);
127
			return;
128
		}
129 130 131

		/* Eat the current token */
		token = next_token(&val, NULL, 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
		skip_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
		skip_token(NULL, NULL, cfile);
289 290 291
		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
	v6_len = 0;
	for (;;) {
387
		if ((((token == NAME) || (token == NUMBER_OR_NAME)) &&
David Hankins's avatar
David Hankins committed
388
		     is_hex_string(val)) ||
389 390 391
		    (token == NUMBER) ||
		    (token == TOKEN_ADD) ||
		    (token == DOT) ||
David Hankins's avatar
David Hankins committed
392 393
		    (token == COLON)) {

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

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

479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501
/*
 * 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) {
502
			skip_token(&val, NULL, cfile);
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 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
			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 */ 
}

571
/*
572
 * hardware-parameter :== HARDWARE hardware-type colon-separated-hex-list SEMI
573 574 575
 * 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.
576
 */
577 578

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

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

			return;
		}
611 612 613 614 615 616 617 618 619 620
	}

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

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

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

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

	parse_semi (cfile);
}

/* No BNF for numeric aggregates - that's defined by the caller.  What
675 676
   this function does is to parse a sequence of numbers separated by
   the token specified in separator.  If max is zero, any number of
677 678
   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
679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695
   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
 */

696 697

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

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

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

741
		if (token == END_OF_FILE) {
742
			parse_warn (cfile, "unexpected end of file");
743 744 745 746 747 748
			break;
		}

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

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

801 802
void convert_num (cfile, buf, str, base, size)
	struct parse *cfile;
803
	unsigned char *buf;
804
	const char *str;
805
	int base;
806
	unsigned size;
807
{
808
	const unsigned char *ptr = (const unsigned char *)str;
809 810 811 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
	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 {
846
			parse_warn (cfile, "Bogus number: %s.", str);
847 848 849
			break;
		}
		if (tval >= base) {
850 851
			parse_warn (cfile,
				    "Bogus number %s: digit %d not in base %d",
852
				    str, tval, base);
853 854 855 856 857 858 859 860 861 862 863 864
			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:
865
			parse_warn (cfile,
866
				    "%s%lo exceeds max (%d) for precision.",
Ted Lemon's avatar
Ted Lemon committed
867 868
				    negative ? "-" : "",
				    (unsigned long)val, max);
869 870
			break;
		      case 16:
871
			parse_warn (cfile,
872
				    "%s%lx exceeds max (%d) for precision.",
Ted Lemon's avatar
Ted Lemon committed
873 874
				    negative ? "-" : "",
				    (unsigned long)val, max);
875 876
			break;
		      default:
877
			parse_warn (cfile,
878
				    "%s%lu exceeds max (%d) for precision.",
Ted Lemon's avatar
Ted Lemon committed
879 880
				    negative ? "-" : "",
				    (unsigned long)val, max);
881 882 883 884 885 886 887 888 889 890
			break;
		}
	}

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

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

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

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

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

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

968
		skip_token(&val, NULL, cfile); /* consume number */
969 970
		guess = atoi(val);

971
		return((TIME)guess);
972 973
	}

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

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

	/* 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. */
997
	year = atoi(val);
998 999
	if (year > 1900)
		year -= 1900;
1000

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