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

   Common parser code for dhcpd and dhclient. */

/*
6
 * Copyright (c) 2004-2014 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 75 76 77 78 79 80 81 82 83 84 85 86 87 88
/* 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)
89
	struct parse *cfile;
90 91 92 93 94
{
	skip_to_rbrace (cfile, 0);
}

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

int parse_semi (cfile)
130
	struct parse *cfile;
131
{
132
	enum dhcp_token token;
133
	const char *val;
134

135
	token = next_token (&val, (unsigned *)0, cfile);
136
	if (token != SEMI) {
137
		parse_warn (cfile, "semicolon expected.");
138 139 140 141 142 143 144 145
		skip_to_semi (cfile);
		return 0;
	}
	return 1;
}

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

146
int parse_string (cfile, sptr, lptr)
147
	struct parse *cfile;
148 149
	char **sptr;
	unsigned *lptr;
150
{
151
	const char *val;
152
	enum dhcp_token token;
153
	char *s;
154
	unsigned len;
155

156
	token = next_token (&val, &len, cfile);
157
	if (token != STRING) {
158
		parse_warn (cfile, "expecting a string");
159
		skip_to_semi (cfile);
160
		return 0;
161
	}
162
	s = (char *)dmalloc (len + 1, MDL);
163
	if (!s)
164
		log_fatal ("no memory for string %s.", val);
165
	memcpy (s, val, len + 1);
166

167 168 169 170 171 172 173 174 175 176 177
	if (!parse_semi (cfile)) {
		dfree (s, MDL);
		return 0;
	}
	if (sptr)
		*sptr = s;
	else
		dfree (s, MDL);
	if (lptr)
		*lptr = len;
	return 1;
178 179
}

180 181 182 183 184
/*
 * hostname :== IDENTIFIER
 *		| IDENTIFIER DOT
 *		| hostname DOT IDENTIFIER
 */
185 186

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

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

Ted Lemon's avatar
Ted Lemon committed
221 222 223 224
	/* Should be at least one token. */
	if (!len)
		return (char *)0;

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

247 248 249 250 251
/* 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
252 253 254 255 256
   than one IP address.

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

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

271
	token = peek_token (&val, (unsigned *)0, cfile);
272 273 274 275 276 277 278 279 280

	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.
		 */
281
		save_parse_state(cfile);
282
		skip_token(NULL, NULL, cfile);
283 284 285
		if (next_token(NULL, NULL, cfile) == DOT &&
		    next_token(NULL, NULL, cfile) == NUMBER)
			ipaddr = 1;
286
		restore_parse_state(cfile);
287 288 289 290 291 292 293 294

		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) {
295 296
		name = parse_host_name (cfile);
		if (!name)
297
			return 0;
298 299
		if (!make_host_lookup (expr, name)) {
			dfree(name, MDL);
300
			return 0;
301 302
		}
		dfree(name, MDL);
303 304 305
		if (!uniform) {
			if (!make_limit (&x, *expr, 4))
				return 0;
306
			expression_dereference (expr, MDL);
307 308
			*expr = x;
		}
309 310
	} else {
		if (token != RBRACE && token != LBRACE)
311
			token = next_token (&val, (unsigned *)0, cfile);
312
		parse_warn (cfile, "%s (%d): expecting IP address or hostname",
313 314 315
			    val, token);
		if (token != SEMI)
			skip_to_semi (cfile);
316
		return 0;
317 318
	}

319
	return 1;
320 321 322 323 324 325
}	
	
/*
 * ip-address :== NUMBER DOT NUMBER DOT NUMBER DOT NUMBER
 */

326
int parse_ip_addr (cfile, addr)
327
	struct parse *cfile;
328 329 330 331 332 333 334 335 336
	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
337
/*
Francis Dupont's avatar
Francis Dupont committed
338
 * Return true if every character in the string is hexadecimal.
David Hankins's avatar
David Hankins committed
339 340 341 342
 */
static int
is_hex_string(const char *s) {
	while (*s != '\0') {
343
		if (!isxdigit((int)*s)) {
David Hankins's avatar
David Hankins committed
344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369
			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;

370 371 372 373 374 375 376 377 378
	/*
	 * 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
379 380 381 382 383 384 385 386
	v6_len = 0;
	for (;;) {
		if ((((token == NAME) || (token == NUMBER_OR_NAME)) && 
		     is_hex_string(val)) ||
		    (token == NUMBER) || 
		    (token == DOT) || 
		    (token == COLON)) {

387
			next_raw_token(&val, NULL, cfile);
David Hankins's avatar
David Hankins committed
388 389 390 391 392 393 394 395 396 397 398 399
			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;
		}
400
		token = peek_raw_token(&val, NULL, cfile);
David Hankins's avatar
David Hankins committed
401 402 403
	}
	v6[v6_len] = '\0';

404 405 406
	/*
	 * Use inet_pton() for actual work.
	 */
David Hankins's avatar
David Hankins committed
407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430
	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
431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471
/*
 * 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;
}

472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494
/*
 * 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) {
495
			skip_token(&val, NULL, cfile);
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 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
			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 */ 
}

564
/*
565
 * hardware-parameter :== HARDWARE hardware-type colon-separated-hex-list SEMI
566 567 568
 * 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.
569
 */
570 571

void parse_hardware_param (cfile, hardware)
572
	struct parse *cfile;
573 574
	struct hardware *hardware;
{
575
	const char *val;
576
	enum dhcp_token token;
577
	unsigned hlen;
578 579
	unsigned char *t;

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

			return;
		}
604 605 606 607 608 609 610 611 612 613
	}

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

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

void parse_lease_time (cfile, timep)
647
	struct parse *cfile;
648 649
	TIME *timep;
{
650
	const char *val;
651
	enum dhcp_token token;
652
	u_int32_t num;
653

654
	token = next_token (&val, (unsigned *)0, cfile);
655
	if (token != NUMBER) {
656
		parse_warn (cfile, "Expecting numeric lease time");
657 658 659
		skip_to_semi (cfile);
		return;
	}
660
	convert_num(cfile, (unsigned char *)&num, val, 10, 32);
661
	/* Unswap the number - convert_num returns stuff in NBO. */
662
	*timep = ntohl(num);
663 664 665 666 667

	parse_semi (cfile);
}

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

689 690

unsigned char *parse_numeric_aggregate (cfile, buf,
691
					max, separator, base, size)
692
	struct parse *cfile;
693
	unsigned char *buf;
694
	unsigned *max;
695
	int separator;
696
	int base;
697
	unsigned size;
698
{
699
	const char *val;
700
	enum dhcp_token token;
701
	unsigned char *bufp = buf, *s, *t;
702
	unsigned count = 0;
703 704 705
	pair c = (pair)0;

	if (!bufp && *max) {
Ted Lemon's avatar
Ted Lemon committed
706
		bufp = (unsigned char *)dmalloc (*max * size / 8, MDL);
707
		if (!bufp)
708
			log_fatal ("no space for numeric aggregate");
Shawn Routhier's avatar
Shawn Routhier committed
709 710
	}
	s = bufp;
711 712 713

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

734
		if (token == END_OF_FILE) {
735
			parse_warn (cfile, "unexpected end of file");
736 737 738 739 740 741
			break;
		}

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

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

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

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

913 914
/*
 * date :== NUMBER NUMBER SLASH NUMBER SLASH NUMBER 
915
 *		NUMBER COLON NUMBER COLON NUMBER |
916
 *          NUMBER NUMBER SLASH NUMBER SLASH NUMBER 
917 918
 *		NUMBER COLON NUMBER COLON NUMBER NUMBER |
 *          EPOCH NUMBER |
919
 *	    NEVER
920
 *
921
 * Dates are stored in UTC or with a timezone offset; first number is day
922 923 924
 * 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.
925
 */
926

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

942 943
	/* "never", "epoch" or day of week */
	token = peek_token(&val, NULL, cfile);
944
	if (token == NEVER) {
945
		skip_token(&val, NULL, cfile); /* consume NEVER */
946
		return(MAX_TIME);
947 948
	}

949 950
	/* This indicates 'local' time format. */
	if (token == EPOCH) {
951
		skip_token(&val, NULL, cfile); /* consume EPOCH */
952
		token = peek_token(&val, NULL, cfile);
953 954 955

		if (token != NUMBER) {
			if (token != SEMI)
956
				skip_token(&val, NULL, cfile);
957 958
			parse_warn(cfile, "Seconds since epoch expected.");
			return((TIME)0);
959 960
		}

961
		skip_token(&val, NULL, cfile); /* consume number */
962 963
		guess = atoi(val);

964
		return((TIME)guess);
965 966
	}

967 968
	if (token != NUMBER) {
		if (token != SEMI)
969
			skip_token(&val, NULL, cfile);
970 971
		parse_warn(cfile, "numeric day of week expected.");
		return((TIME)0);
972
	}
973
	skip_token(&val, NULL, cfile); /* consume day of week */
974
        /* we are not using this for anything */
975 976

	/* Year... */
977
	token = peek_token(&val, NULL, cfile);
978 979
	if (token != NUMBER) {
		if (token != SEMI)
980
			skip_token(&val, NULL, cfile);
981 982
		parse_warn(cfile, "numeric year expected.");
		return((TIME)0);
983
	}
984
	skip_token(&val, NULL, cfile); /* consume year */
985 986 987 988 989

	/* 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. */
990
	year = atoi(val);
991 992
	if (year > 1900)
		year -= 1900;
993

994
	/* Slash separating year from month... */
995
	token = peek_token(&val, NULL, cfile);
996 997
	if (token != SLASH) {
		if (token != SEMI)
998
			skip_token(&val, NULL, cfile);
999 1000 1001
		parse_warn(cfile,
			   "expected slash separating year from month.");
		return((TIME)0);
1002
	}
1003
	skip_token(&val, NULL, cfile); /* consume SLASH */
1004 1005

	/* Month... */
1006
	token = peek_token(&val, NULL, cfile);
1007 1008
	if (token != NUMBER) {
		if (token != SEMI)
1009
			skip_token(&val, NULL, cfile);
1010 1011
		parse_warn(cfile, "numeric month expected.");
		return((TIME)0);
Ted Lemon's avatar