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 <isc/util.h>
31
#include <syslog.h>
32

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

575
/*
576
 * hardware-parameter :== HARDWARE hardware-type colon-separated-hex-list SEMI
577 578 579
 * 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.
580
 */
581 582

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

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

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

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

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

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

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

	parse_semi (cfile);
}

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

700 701

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1005
	/* Slash separating year from month... */
1006
	token = peek_token(&val, NULL, cfile);
1007