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

   Common parser code for dhcpd and dhclient. */

/*
David Hankins's avatar
David Hankins committed
6
 * Copyright (c) 2004-2007 by Internet Systems Consortium, Inc. ("ISC")
7
 * Copyright (c) 1995-2003 by Internet Software Consortium
8
 *
9 10 11
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
12
 *
13 14 15 16 17 18 19
 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20
 *
21 22 23 24 25
 *   Internet Systems Consortium, Inc.
 *   950 Charter Street
 *   Redwood City, CA 94063
 *   <info@isc.org>
 *   http://www.isc.org/
Ted Lemon's avatar
Ted Lemon committed
26
 *
27
 * This software has been written for Internet Systems Consortium
Ted Lemon's avatar
Ted Lemon committed
28
 * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
29
 * To learn more about Internet Systems Consortium, see
Ted Lemon's avatar
Ted Lemon committed
30 31 32
 * ``http://www.isc.org/''.  To learn more about Vixie Enterprises,
 * see ``http://www.vix.com''.   To learn more about Nominum, Inc., see
 * ``http://www.nominum.com''.
33 34 35 36
 */

#ifndef lint
static char copyright[] =
37
"$Id: parse.c,v 1.129 2007/06/27 18:25:15 each Exp $ Copyright (c) 2004-2007 Internet Systems Consortium.  All rights reserved.\n";
38 39 40
#endif /* not lint */

#include "dhcpd.h"
41
#include <syslog.h>
42

43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
/* 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,
						  const char *value)
{
	struct enumeration *e;
	int i;

	e = find_enumeration (name, length);
	if (e) {
		for (i = 0; e -> values [i].name; i++) {
			if (!strcmp (value, e -> values [i].name))
				return &e -> values [i];
		}
	}
	return (struct enumeration_value *)0;
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

334
int parse_ip_addr (cfile, addr)
335
	struct parse *cfile;
336 337
	struct iaddr *addr;
{
338
	const char *val;
339
	enum dhcp_token token;
340 341 342 343 344 345 346 347

	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
348 349 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 376 377 378 379 380
/*
 * Return true if every character in the string is hexidecimal.
 */
static int
is_hex_string(const char *s) {
	while (*s != '\0') {
		if (!isxdigit(*s)) {
			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;

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

442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533
/*
 * ip-address-with-subnet :== ip-address |
 *                          ip-address "/" NUMBER
 */

int
parse_ip_addr_with_subnet(cfile, match)
	struct parse *cfile;
	struct iaddrmatch *match;
{
	const char *val, *orig;
	enum dhcp_token token;
	int prefixlen;
	int fflen;
	unsigned char newval, warnmask=0;

	if (parse_ip_addr(cfile, &match->addr)) {
		/* default to host mask */
		prefixlen = match->addr.len * 8;

		token = peek_token(&val, NULL, cfile);

		if (token == SLASH) {
			next_token(&val, NULL, cfile);
			token = next_token(&val, NULL, cfile);

			if (token != NUMBER) {
				parse_warn(cfile, "Invalid CIDR prefix length:"
						  " expecting a number.");
				return 0;
			}

			prefixlen = atoi(val);

			if (prefixlen < 0 ||
			    prefixlen > (match->addr.len * 8)) {
				parse_warn(cfile, "subnet prefix is out of "
						  "range [0..%d].",
						  match->addr.len * 8);
				return 0;
			}
		}

		/* construct a suitable mask field */

		/* copy length */
		match->mask.len = match->addr.len;

		/* count of 0xff bytes in mask */
		fflen = prefixlen / 8;

		/* set leading mask */
		memset(match->mask.iabuf, 0xff, fflen);

		/* set zeroes */
		if (fflen < match->mask.len) {
			match->mask.iabuf[fflen] =
			    "\x00\x80\xc0\xe0\xf0\xf8\xfc\xfe"[prefixlen % 8];

			memset(match->mask.iabuf+fflen+1, 0x00, 
			       match->mask.len - fflen - 1);

			/* AND-out insignificant bits from supplied netmask. */
			orig = piaddr(match->addr);
			do {
				newval = match->addr.iabuf[fflen] &
					 match->mask.iabuf[fflen];

				if (newval != match->addr.iabuf[fflen]) {
					warnmask = 1;
					match->addr.iabuf[fflen] = newval;
				}
			} while (++fflen < match->mask.len);

			if (warnmask) {
				log_error("Warning: Extraneous bits removed "
					  "in address component of %s/%d.",
					  orig, prefixlen);
				log_error("New value: %s/%d.",
					  piaddr(match->addr), prefixlen);
			}
		}

		return 1;
	}

	parse_warn(cfile,
		   "expecting ip-address or ip-address/prefixlen");

	return 0;  /* let caller pick up pieces */ 
}

534
/*
535
 * hardware-parameter :== HARDWARE hardware-type colon-separated-hex-list SEMI
536
 * hardware-type :== ETHERNET | TOKEN_RING | FDDI
537
 */
538 539

void parse_hardware_param (cfile, hardware)
540
	struct parse *cfile;
541 542
	struct hardware *hardware;
{
543
	const char *val;
544
	enum dhcp_token token;
545
	unsigned hlen;
546 547
	unsigned char *t;

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

			return;
		}
569 570 571 572 573 574 575 576 577 578
	}

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

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

void parse_lease_time (cfile, timep)
613
	struct parse *cfile;
614 615
	TIME *timep;
{
616
	const char *val;
617
	enum dhcp_token token;
618
	u_int32_t num;
619

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

	parse_semi (cfile);
}

/* No BNF for numeric aggregates - that's defined by the caller.  What
634 635
   this function does is to parse a sequence of numbers separated by
   the token specified in separator.  If max is zero, any number of
636 637 638 639 640
   numbers will be parsed; otherwise, exactly max numbers are
   expected.  Base and size tell us how to internalize the numbers
   once they've been tokenized. */

unsigned char *parse_numeric_aggregate (cfile, buf,
641
					max, separator, base, size)
642
	struct parse *cfile;
643
	unsigned char *buf;
644
	unsigned *max;
645
	int separator;
646
	int base;
647
	unsigned size;
648
{
649
	const char *val;
650
	enum dhcp_token token;
651
	unsigned char *bufp = buf, *s, *t;
652
	unsigned count = 0;
653 654 655
	pair c = (pair)0;

	if (!bufp && *max) {
Ted Lemon's avatar
Ted Lemon committed
656
		bufp = (unsigned char *)dmalloc (*max * size / 8, MDL);
657
		if (!bufp)
658 659
			log_fatal ("no space for numeric aggregate");
		s = 0;
660 661 662 663 664
	} else
		s = bufp;

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

682
		if (token == END_OF_FILE) {
683
			parse_warn (cfile, "unexpected end of file");
684 685 686 687 688 689
			break;
		}

		/* Allow NUMBER_OR_NAME if base is 16. */
		if (token != NUMBER &&
		    (base != 16 || token != NUMBER_OR_NAME)) {
690
			parse_warn (cfile, "expecting numeric value.");
691 692 693 694 695 696
			skip_to_semi (cfile);
			return (unsigned char *)0;
		}
		/* If we can, convert the number now; otherwise, build
		   a linked list of all the numbers. */
		if (s) {
697
			convert_num (cfile, s, val, base, size);
698 699
			s += size / 8;
		} else {
Ted Lemon's avatar
Ted Lemon committed
700
			t = (unsigned char *)dmalloc (strlen (val) + 1, MDL);
701
			if (!t)
702
				log_fatal ("no temp space for number.");
703 704
			strcpy ((char *)t, val);
			c = cons ((caddr_t)t, c);
705 706 707 708 709
		}
	} while (++count != *max);

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

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

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

847 848
/*
 * date :== NUMBER NUMBER SLASH NUMBER SLASH NUMBER 
849 850 851
 *		NUMBER COLON NUMBER COLON NUMBER SEMI |
 *          NUMBER NUMBER SLASH NUMBER SLASH NUMBER 
 *		NUMBER COLON NUMBER COLON NUMBER NUMBER SEMI |
852
 *	    NEVER
853
 *
854 855 856 857
 * Dates are stored in GMT or with a timezone offset; first number is day
 * 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.
858
 */
859 860

TIME parse_date (cfile)
861
	struct parse *cfile;
862 863 864
{
	struct tm tm;
	int guess;
865
	int tzoff, wday, year, mon, mday, hour, min, sec;
866
	const char *val;
867
	enum dhcp_token token;
868 869 870
	static int months [11] = { 31, 59, 90, 120, 151, 181,
					  212, 243, 273, 304, 334 };

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

879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897
	/* This indicates 'local' time format. */
	if (token == EPOCH) {
		token = next_token(&val, NULL, cfile);

		if (token != NUMBER) {
			parse_warn(cfile, "Seconds since epoch expected.");
			if (token != SEMI)
				skip_to_semi(cfile);
			return (TIME)0;
		}

		guess = atoi(val);

		if (!parse_semi(cfile))
			return (TIME)0;

		return guess;
	}

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

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

	/* 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. */
919 920 921
	year = atoi (val);
	if (year > 1900)
		year -= 1900;
922

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

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

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

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

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

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

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

993
	/* Colon separating minute from second... */
994
	token = next_token (&val, (unsigned *)0, cfile);
995
	if (token != COLON) {
996
		parse_warn (cfile,
997
			    "expected colon separating minute from second.");
998