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
 */

#include "dhcpd.h"
36
#include <syslog.h>
37

38 39 40 41 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
/* 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;
}

77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
/* 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)
92
	struct parse *cfile;
93 94 95 96 97
{
	skip_to_rbrace (cfile, 0);
}

void skip_to_rbrace (cfile, brace_count)
98
	struct parse *cfile;
99
	int brace_count;
100
{
101
	enum dhcp_token token;
102
	const char *val;
103

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

int parse_semi (cfile)
133
	struct parse *cfile;
134
{
135
	enum dhcp_token token;
136
	const char *val;
137

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

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

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

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

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

183 184 185 186 187
/*
 * hostname :== IDENTIFIER
 *		| IDENTIFIER DOT
 *		| hostname DOT IDENTIFIER
 */
188 189

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

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

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

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

250 251 252 253 254
/* 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
255 256 257 258 259
   than one IP address.

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

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

274
	token = peek_token (&val, (unsigned *)0, cfile);
275 276 277 278 279 280 281 282 283

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

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

322
	return 1;
323 324 325 326 327 328
}	
	
/*
 * ip-address :== NUMBER DOT NUMBER DOT NUMBER DOT NUMBER
 */

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

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

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

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

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 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
/*
 * 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 */ 
}

526
/*
527
 * hardware-parameter :== HARDWARE hardware-type colon-separated-hex-list SEMI
528
 * hardware-type :== ETHERNET | TOKEN_RING | FDDI
529
 */
530 531

void parse_hardware_param (cfile, hardware)
532
	struct parse *cfile;
533 534
	struct hardware *hardware;
{
535
	const char *val;
536
	enum dhcp_token token;
537
	unsigned hlen;
538 539
	unsigned char *t;

540
	token = next_token (&val, (unsigned *)0, cfile);
541 542
	switch (token) {
	      case ETHERNET:
543
		hardware -> hbuf [0] = HTYPE_ETHER;
544 545
		break;
	      case TOKEN_RING:
546
		hardware -> hbuf [0] = HTYPE_IEEE802;
547
		break;
548
	      case FDDI:
549
		hardware -> hbuf [0] = HTYPE_FDDI;
550
		break;
551
	      default:
552
		if (!strncmp (val, "unknown-", 8)) {
553
			hardware -> hbuf [0] = atoi (&val [8]);
554 555 556 557 558 559 560
		} else {
			parse_warn (cfile,
				    "expecting a network hardware type");
			skip_to_semi (cfile);

			return;
		}
561 562 563 564 565 566 567 568 569 570
	}

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

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

void parse_lease_time (cfile, timep)
605
	struct parse *cfile;
606 607
	TIME *timep;
{
608
	const char *val;
609
	enum dhcp_token token;
610
	u_int32_t num;
611

612
	token = next_token (&val, (unsigned *)0, cfile);
613
	if (token != NUMBER) {
614
		parse_warn (cfile, "Expecting numeric lease time");
615 616 617
		skip_to_semi (cfile);
		return;
	}
618
	convert_num(cfile, (unsigned char *)&num, val, 10, 32);
619
	/* Unswap the number - convert_num returns stuff in NBO. */
620
	*timep = ntohl(num);
621 622 623 624 625

	parse_semi (cfile);
}

/* No BNF for numeric aggregates - that's defined by the caller.  What
626 627
   this function does is to parse a sequence of numbers separated by
   the token specified in separator.  If max is zero, any number of
628 629 630 631 632
   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,
633
					max, separator, base, size)
634
	struct parse *cfile;
635
	unsigned char *buf;
636
	unsigned *max;
637
	int separator;
638
	int base;
639
	unsigned size;
640
{
641
	const char *val;
642
	enum dhcp_token token;
643
	unsigned char *bufp = buf, *s, *t;
644
	unsigned count = 0;
645 646 647
	pair c = (pair)0;

	if (!bufp && *max) {
Ted Lemon's avatar
Ted Lemon committed
648
		bufp = (unsigned char *)dmalloc (*max * size / 8, MDL);
649
		if (!bufp)
650 651
			log_fatal ("no space for numeric aggregate");
		s = 0;
652 653 654 655 656
	} else
		s = bufp;

	do {
		if (count) {
657
			token = peek_token (&val, (unsigned *)0, cfile);
658
			if (token != separator) {
659 660 661
				if (!*max)
					break;
				if (token != RBRACE && token != LBRACE)
662 663 664
					token = next_token (&val,
							    (unsigned *)0,
							    cfile);
665
				parse_warn (cfile, "too few numbers.");
666 667 668 669
				if (token != SEMI)
					skip_to_semi (cfile);
				return (unsigned char *)0;
			}
670
			token = next_token (&val, (unsigned *)0, cfile);
671
		}
672
		token = next_token (&val, (unsigned *)0, cfile);
673

674
		if (token == END_OF_FILE) {
675
			parse_warn (cfile, "unexpected end of file");
676 677 678 679 680 681
			break;
		}

		/* Allow NUMBER_OR_NAME if base is 16. */
		if (token != NUMBER &&
		    (base != 16 || token != NUMBER_OR_NAME)) {
682
			parse_warn (cfile, "expecting numeric value.");
683 684 685 686 687 688
			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) {
689
			convert_num (cfile, s, val, base, size);
690 691
			s += size / 8;
		} else {
Ted Lemon's avatar
Ted Lemon committed
692
			t = (unsigned char *)dmalloc (strlen (val) + 1, MDL);
693
			if (!t)
694
				log_fatal ("no temp space for number.");
695 696
			strcpy ((char *)t, val);
			c = cons ((caddr_t)t, c);
697 698 699 700 701
		}
	} while (++count != *max);

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

720 721
void convert_num (cfile, buf, str, base, size)
	struct parse *cfile;
722
	unsigned char *buf;
723
	const char *str;
724
	int base;
725
	unsigned size;
726
{
727
	const unsigned char *ptr = (const unsigned char *)str;
728 729 730 731 732 733 734 735 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
	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 {
765
			parse_warn (cfile, "Bogus number: %s.", str);
766 767 768
			break;
		}
		if (tval >= base) {
769 770
			parse_warn (cfile,
				    "Bogus number %s: digit %d not in base %d",
771
				    str, tval, base);
772 773 774 775 776 777 778 779 780 781 782 783
			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:
784
			parse_warn (cfile,
785
				    "%s%lo exceeds max (%d) for precision.",
Ted Lemon's avatar
Ted Lemon committed
786 787
				    negative ? "-" : "",
				    (unsigned long)val, max);
788 789
			break;
		      case 16:
790
			parse_warn (cfile,
791
				    "%s%lx exceeds max (%d) for precision.",
Ted Lemon's avatar
Ted Lemon committed
792 793
				    negative ? "-" : "",
				    (unsigned long)val, max);
794 795
			break;
		      default:
796
			parse_warn (cfile,
797
				    "%s%lu exceeds max (%d) for precision.",
Ted Lemon's avatar
Ted Lemon committed
798 799
				    negative ? "-" : "",
				    (unsigned long)val, max);
800 801 802 803 804 805 806 807 808 809
			break;
		}
	}

	if (negative) {
		switch (size) {
		      case 8:
			*buf = -(unsigned long)val;
			break;
		      case 16:
810
			putShort (buf, -(long)val);
811 812
			break;
		      case 32:
813
			putLong (buf, -(long)val);
814 815
			break;
		      default:
816 817
			parse_warn (cfile,
				    "Unexpected integer size: %d\n", size);
818 819 820 821 822 823 824 825 826 827 828 829 830 831
			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:
832 833
			parse_warn (cfile,
				    "Unexpected integer size: %d\n", size);
834 835 836 837 838
			break;
		}
	}
}

839 840
/*
 * date :== NUMBER NUMBER SLASH NUMBER SLASH NUMBER 
841 842 843
 *		NUMBER COLON NUMBER COLON NUMBER SEMI |
 *          NUMBER NUMBER SLASH NUMBER SLASH NUMBER 
 *		NUMBER COLON NUMBER COLON NUMBER NUMBER SEMI |
844
 *	    NEVER
845
 *
846 847 848 849
 * 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.
850
 */
851 852

TIME parse_date (cfile)
853
	struct parse *cfile;
854 855
{
	int guess;
856
	int tzoff, wday, year, mon, mday, hour, min, sec;
857
	const char *val;
858
	enum dhcp_token token;
859 860 861
	static int months [11] = { 31, 59, 90, 120, 151, 181,
					  212, 243, 273, 304, 334 };

862
	/* Day of week, or "never"... */
863
	token = next_token (&val, (unsigned *)0, cfile);
864 865 866 867 868 869
	if (token == NEVER) {
		if (!parse_semi (cfile))
			return 0;
		return MAX_TIME;
	}

870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888
	/* 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;
	}

889
	if (token != NUMBER) {
890
		parse_warn (cfile, "numeric day of week expected.");
891 892 893 894
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}
895
	wday = atoi (val);
896 897

	/* Year... */
898
	token = next_token (&val, (unsigned *)0, cfile);
899
	if (token != NUMBER) {
900
		parse_warn (cfile, "numeric year expected.");
901 902 903 904
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}
905 906 907 908 909

	/* 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. */
910 911 912
	year = atoi (val);
	if (year > 1900)
		year -= 1900;
913

914
	/* Slash separating year from month... */
915
	token = next_token (&val, (unsigned *)0, cfile);
916
	if (token != SLASH) {
917
		parse_warn (cfile,
918
			    "expected slash separating year from month.");
919 920 921 922 923 924
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}

	/* Month... */
925
	token = next_token (&val, (unsigned *)0, cfile);
926
	if (token != NUMBER) {
927
		parse_warn (cfile, "numeric month expected.");
928 929 930 931
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}
932
	mon = atoi (val) - 1;
933

934
	/* Slash separating month from day... */
935
	token = next_token (&val, (unsigned *)0, cfile);
936
	if (token != SLASH) {
937
		parse_warn (cfile,
938
			    "expected slash separating month from day.");
939 940 941 942 943
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}

944
	/* Day of month... */
945
	token = next_token (&val, (unsigned *)0, cfile);
946
	if (token != NUMBER) {
947
		parse_warn (cfile, "numeric day of month expected.");
948 949 950 951
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}
952
	mday = atoi (val);
953 954

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

964
	/* Colon separating hour from minute... */
965
	token = next_token (&val, (unsigned *)0, cfile);
966
	if (token != COLON) {
967
		parse_warn (cfile,
968
			    "expected colon separating hour from minute.");
969 970 971 972 973 974
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}

	/* Minute... */
975
	token = next_token (&val, (unsigned *)0, cfile);
976
	if (token != NUMBER) {
977
		parse_warn (cfile, "numeric minute expected.");
978 979 980 981
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}
982
	min = atoi (val);
983

984
	/* Colon separating minute from second... */
985
	token = next_token (&val, (unsigned *)0, cfile);
986
	if (token != COLON) {
987
		parse_warn (cfile,
988
			    "expected colon separating minute from second.");
989 990 991 992 993
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}

994
	/* Second... */
995
	token = next_token (&val, (unsigned *)0, cfile);
996
	if (token != NUMBER) {