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.127 2007/06/07 15:52:29 dhankins 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 278
	struct parse cfile0;
	int ipaddr = 0;
279

280
	token = peek_token (&val, (unsigned *)0, cfile);
281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303

	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.
		 */
		memcpy(&cfile0, cfile, sizeof(struct parse));
		(void) next_token(NULL, NULL, cfile);
		if (next_token(NULL, NULL, cfile) == DOT &&
		    next_token(NULL, NULL, cfile) == NUMBER)
			ipaddr = 1;
		memcpy(cfile, &cfile0, sizeof(struct parse));

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

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

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

	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
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 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430
/*
 * 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;

	v6_len = 0;
	for (;;) {
		token = peek_token(&val, NULL, cfile);
		if ((((token == NAME) || (token == NUMBER_OR_NAME)) && 
		     is_hex_string(val)) ||
		    (token == NUMBER) || 
		    (token == DOT) || 
		    (token == COLON)) {

			next_token(&val, NULL, cfile);
			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;
		}
	}
	v6[v6_len] = '\0';

	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);
}

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

523
/*
524
 * hardware-parameter :== HARDWARE hardware-type colon-separated-hex-list SEMI
525
 * hardware-type :== ETHERNET | TOKEN_RING | FDDI
526
 */
527 528

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

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

			return;
		}
558 559 560 561 562 563 564 565 566 567
	}

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

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

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

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

	parse_semi (cfile);
}

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

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

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

671
		if (token == END_OF_FILE) {
672
			parse_warn (cfile, "unexpected end of file");
673 674 675 676 677 678
			break;
		}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

992
	/* Second... */
993
	token = next_token (&val, (unsigned *)0, cfile);
994
	if (token != NUMBER) {
995
		parse_warn (cfile, "numeric second expected.");
996 997 998 999
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}
1000
	sec = atoi (val);
1001

1002
	token = peek_token (&val, (unsigned *)0, cfile);
1003
	if (token == NUMBER) {
Ted Lemon's avatar