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

   Common parser code for dhcpd and dhclient. */

/*
Ted Lemon's avatar
Ted Lemon committed
6
 * Copyright (c) 1995-2001 Internet Software Consortium.
Ted Lemon's avatar
Ted Lemon committed
7
 * All rights reserved.
8
 *
Ted Lemon's avatar
Ted Lemon committed
9 10 11
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
12
 *
Ted Lemon's avatar
Ted Lemon committed
13 14 15 16 17 18 19 20
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of The Internet Software Consortium nor the names
 *    of its contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
21
 *
Ted Lemon's avatar
Ted Lemon committed
22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
 * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
 * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * This software has been written for the Internet Software Consortium
 * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
 * To learn more about the Internet Software Consortium, see
 * ``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''.
42 43 44 45
 */

#ifndef lint
static char copyright[] =
Ted Lemon's avatar
Ted Lemon committed
46
"$Id: parse.c,v 1.100 2001/02/12 19:44:54 mellon Exp $ Copyright (c) 1995-2001 The Internet Software Consortium.  All rights reserved.\n";
47 48 49 50
#endif /* not lint */

#include "dhcpd.h"

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 82 83 84 85 86 87 88 89
/* 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;
}

90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
/* 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)
105
	struct parse *cfile;
106 107 108 109 110
{
	skip_to_rbrace (cfile, 0);
}

void skip_to_rbrace (cfile, brace_count)
111
	struct parse *cfile;
112
	int brace_count;
113
{
114
	enum dhcp_token token;
115
	const char *val;
116

117 118 119
#if defined (DEBUG_TOKEN)
	log_error ("skip_to_rbrace: %d\n", brace_count);
#endif
120 121 122
	do {
		token = peek_token (&val, cfile);
		if (token == RBRACE) {
123
			token = next_token (&val, cfile);
124 125 126 127 128 129 130 131 132 133
			if (brace_count) {
				if (!--brace_count)
					return;
			} else
				return;
		} else if (token == LBRACE) {
			brace_count++;
		} else if (token == SEMI && !brace_count) {
			token = next_token (&val, cfile);
			return;
134 135 136 137 138 139
		} 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. */
			token = next_token (&val, cfile);
			return;
140 141 142 143 144 145
		}
		token = next_token (&val, cfile);
	} while (token != EOF);
}

int parse_semi (cfile)
146
	struct parse *cfile;
147
{
148
	enum dhcp_token token;
149
	const char *val;
150 151 152

	token = next_token (&val, cfile);
	if (token != SEMI) {
153
		parse_warn (cfile, "semicolon expected.");
154 155 156 157 158 159 160 161 162
		skip_to_semi (cfile);
		return 0;
	}
	return 1;
}

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

char *parse_string (cfile)
163
	struct parse *cfile;
164
{
165
	const char *val;
166
	enum dhcp_token token;
167 168 169 170
	char *s;

	token = next_token (&val, cfile);
	if (token != STRING) {
171
		parse_warn (cfile, "expecting a string");
172 173 174
		skip_to_semi (cfile);
		return (char *)0;
	}
Ted Lemon's avatar
Ted Lemon committed
175
	s = (char *)dmalloc (strlen (val) + 1, MDL);
176
	if (!s)
177
		log_fatal ("no memory for string %s.", val);
178 179 180 181 182 183 184
	strcpy (s, val);

	if (!parse_semi (cfile))
		return (char *)0;
	return s;
}

185 186 187 188 189
/*
 * hostname :== IDENTIFIER
 *		| IDENTIFIER DOT
 *		| hostname DOT IDENTIFIER
 */
190 191

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

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

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

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

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

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

	token = peek_token (&val, cfile);
	if (is_identifier (token)) {
		name = parse_host_name (cfile);
		if (!name)
275 276 277 278 279 280
			return 0;
		if (!make_host_lookup (expr, name))
			return 0;
		if (!uniform) {
			if (!make_limit (&x, *expr, 4))
				return 0;
281
			expression_dereference (expr, MDL);
282 283
			*expr = x;
		}
284 285
	} else if (token == NUMBER) {
		if (!parse_numeric_aggregate (cfile, addr, &len, DOT, 10, 8))
286 287
			return 0;
		return make_const_data (expr, addr, len, 0, 1);
288 289 290
	} else {
		if (token != RBRACE && token != LBRACE)
			token = next_token (&val, cfile);
291
		parse_warn (cfile, "%s (%d): expecting IP address or hostname",
292 293 294
			    val, token);
		if (token != SEMI)
			skip_to_semi (cfile);
295
		return 0;
296 297
	}

298
	return 1;
299 300 301 302 303 304
}	
	
/*
 * ip-address :== NUMBER DOT NUMBER DOT NUMBER DOT NUMBER
 */

305
int parse_ip_addr (cfile, addr)
306
	struct parse *cfile;
307 308
	struct iaddr *addr;
{
309
	const char *val;
310
	enum dhcp_token token;
311 312 313 314 315 316 317 318

	addr -> len = 4;
	if (parse_numeric_aggregate (cfile, addr -> iabuf,
				     &addr -> len, DOT, 10, 8))
		return 1;
	return 0;
}	

319 320 321 322
/*
 * hardware-parameter :== HARDWARE hardware-type colon-seperated-hex-list SEMI
 * hardware-type :== ETHERNET | TOKEN_RING
 */
323 324

void parse_hardware_param (cfile, hardware)
325
	struct parse *cfile;
326 327
	struct hardware *hardware;
{
328
	const char *val;
329
	enum dhcp_token token;
330
	unsigned hlen;
331 332 333 334 335
	unsigned char *t;

	token = next_token (&val, cfile);
	switch (token) {
	      case ETHERNET:
336
		hardware -> hbuf [0] = HTYPE_ETHER;
337 338
		break;
	      case TOKEN_RING:
339
		hardware -> hbuf [0] = HTYPE_IEEE802;
340
		break;
341
	      case FDDI:
342
		hardware -> hbuf [0] = HTYPE_FDDI;
343
		break;
344
	      default:
345
		if (!strncmp (val, "unknown-", 8)) {
346
			hardware -> hbuf [0] = atoi (&val [8]);
347 348 349 350 351 352 353
		} else {
			parse_warn (cfile,
				    "expecting a network hardware type");
			skip_to_semi (cfile);

			return;
		}
354 355 356 357 358 359 360 361 362 363
	}

	/* 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;
364 365 366 367 368
	token = peek_token (&val, cfile);
	if (token == SEMI) {
		hardware -> hlen = 1;
		goto out;
	}
369 370
	t = parse_numeric_aggregate (cfile, (unsigned char *)0, &hlen,
				     COLON, 16, 8);
371 372
	if (!t) {
		hardware -> hlen = 1;
373
		return;
374
	}
375
	if (hlen + 1 > sizeof hardware -> hbuf) {
Ted Lemon's avatar
Ted Lemon committed
376
		dfree (t, MDL);
377
		parse_warn (cfile, "hardware address too long");
378
	} else {
379 380 381 382 383
		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
384
		dfree (t, MDL);
385 386
	}
	
387
      out:
388 389
	token = next_token (&val, cfile);
	if (token != SEMI) {
390
		parse_warn (cfile, "expecting semicolon.");
391 392 393 394 395 396 397
		skip_to_semi (cfile);
	}
}

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

void parse_lease_time (cfile, timep)
398
	struct parse *cfile;
399 400
	TIME *timep;
{
401
	const char *val;
402
	enum dhcp_token token;
403 404 405

	token = next_token (&val, cfile);
	if (token != NUMBER) {
406
		parse_warn (cfile, "Expecting numeric lease time");
407 408 409
		skip_to_semi (cfile);
		return;
	}
410
	convert_num (cfile, (unsigned char *)timep, val, 10, 32);
411 412 413 414 415 416 417 418 419 420 421 422 423 424 425
	/* Unswap the number - convert_num returns stuff in NBO. */
	*timep = ntohl (*timep); /* XXX */

	parse_semi (cfile);
}

/* No BNF for numeric aggregates - that's defined by the caller.  What
   this function does is to parse a sequence of numbers seperated by
   the token specified in seperator.  If max is zero, any number of
   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,
					max, seperator, base, size)
426
	struct parse *cfile;
427
	unsigned char *buf;
428
	unsigned *max;
429 430
	int seperator;
	int base;
431
	unsigned size;
432
{
433
	const char *val;
434
	enum dhcp_token token;
435
	unsigned char *bufp = buf, *s, *t;
436
	unsigned count = 0;
437 438 439
	pair c = (pair)0;

	if (!bufp && *max) {
Ted Lemon's avatar
Ted Lemon committed
440
		bufp = (unsigned char *)dmalloc (*max * size / 8, MDL);
441
		if (!bufp)
442 443
			log_fatal ("no space for numeric aggregate");
		s = 0;
444 445 446 447 448 449 450 451 452 453 454
	} else
		s = bufp;

	do {
		if (count) {
			token = peek_token (&val, cfile);
			if (token != seperator) {
				if (!*max)
					break;
				if (token != RBRACE && token != LBRACE)
					token = next_token (&val, cfile);
455
				parse_warn (cfile, "too few numbers.");
456 457 458 459 460 461 462 463 464
				if (token != SEMI)
					skip_to_semi (cfile);
				return (unsigned char *)0;
			}
			token = next_token (&val, cfile);
		}
		token = next_token (&val, cfile);

		if (token == EOF) {
465
			parse_warn (cfile, "unexpected end of file");
466 467 468 469 470 471
			break;
		}

		/* Allow NUMBER_OR_NAME if base is 16. */
		if (token != NUMBER &&
		    (base != 16 || token != NUMBER_OR_NAME)) {
472
			parse_warn (cfile, "expecting numeric value.");
473 474 475 476 477 478
			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) {
479
			convert_num (cfile, s, val, base, size);
480 481
			s += size / 8;
		} else {
Ted Lemon's avatar
Ted Lemon committed
482
			t = (unsigned char *)dmalloc (strlen (val) + 1, MDL);
483
			if (!t)
484
				log_fatal ("no temp space for number.");
485 486
			strcpy ((char *)t, val);
			c = cons ((caddr_t)t, c);
487 488 489 490 491
		}
	} while (++count != *max);

	/* If we had to cons up a list, convert it now. */
	if (c) {
Ted Lemon's avatar
Ted Lemon committed
492
		bufp = (unsigned char *)dmalloc (count * size / 8, MDL);
493
		if (!bufp)
494
			log_fatal ("no space for numeric aggregate.");
495 496 497 498 499
		s = bufp + count - size / 8;
		*max = count;
	}
	while (c) {
		pair cdr = c -> cdr;
500
		convert_num (cfile, s, (char *)(c -> car), base, size);
501 502
		s -= size / 8;
		/* Free up temp space. */
Ted Lemon's avatar
Ted Lemon committed
503 504
		dfree (c -> car, MDL);
		dfree (c, MDL);
505 506 507 508 509
		c = cdr;
	}
	return bufp;
}

510 511
void convert_num (cfile, buf, str, base, size)
	struct parse *cfile;
512
	unsigned char *buf;
513
	const char *str;
514
	int base;
515
	unsigned size;
516
{
517
	const char *ptr = str;
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
	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 {
555
			parse_warn (cfile, "Bogus number: %s.", str);
556 557 558
			break;
		}
		if (tval >= base) {
559 560
			parse_warn (cfile,
				    "Bogus number %s: digit %d not in base %d",
561
				    str, tval, base);
562 563 564 565 566 567 568 569 570 571 572 573
			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:
574
			parse_warn (cfile,
575
				    "%s%lo exceeds max (%d) for precision.",
Ted Lemon's avatar
Ted Lemon committed
576 577
				    negative ? "-" : "",
				    (unsigned long)val, max);
578 579
			break;
		      case 16:
580
			parse_warn (cfile,
581
				    "%s%lx exceeds max (%d) for precision.",
Ted Lemon's avatar
Ted Lemon committed
582 583
				    negative ? "-" : "",
				    (unsigned long)val, max);
584 585
			break;
		      default:
586
			parse_warn (cfile,
587
				    "%s%lu exceeds max (%d) for precision.",
Ted Lemon's avatar
Ted Lemon committed
588 589
				    negative ? "-" : "",
				    (unsigned long)val, max);
590 591 592 593 594 595 596 597 598 599
			break;
		}
	}

	if (negative) {
		switch (size) {
		      case 8:
			*buf = -(unsigned long)val;
			break;
		      case 16:
600
			putShort (buf, -(long)val);
601 602
			break;
		      case 32:
603
			putLong (buf, -(long)val);
604 605
			break;
		      default:
606 607
			parse_warn (cfile,
				    "Unexpected integer size: %d\n", size);
608 609 610 611 612 613 614 615 616 617 618 619 620 621
			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:
622 623
			parse_warn (cfile,
				    "Unexpected integer size: %d\n", size);
624 625 626 627 628
			break;
		}
	}
}

629 630
/*
 * date :== NUMBER NUMBER SLASH NUMBER SLASH NUMBER 
631 632 633
 *		NUMBER COLON NUMBER COLON NUMBER SEMI |
 *          NUMBER NUMBER SLASH NUMBER SLASH NUMBER 
 *		NUMBER COLON NUMBER COLON NUMBER NUMBER SEMI |
634
 *	    NEVER
635
 *
636 637 638 639
 * 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.
640
 */
641 642

TIME parse_date (cfile)
643
	struct parse *cfile;
644 645 646
{
	struct tm tm;
	int guess;
647
	int tzoff, wday, year, mon, mday, hour, min, sec;
648
	const char *val;
649
	enum dhcp_token token;
650 651 652
	static int months [11] = { 31, 59, 90, 120, 151, 181,
					  212, 243, 273, 304, 334 };

653
	/* Day of week, or "never"... */
654
	token = next_token (&val, cfile);
655 656 657 658 659 660
	if (token == NEVER) {
		if (!parse_semi (cfile))
			return 0;
		return MAX_TIME;
	}

661
	if (token != NUMBER) {
662
		parse_warn (cfile, "numeric day of week expected.");
663 664 665 666
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}
667
	wday = atoi (val);
668 669 670 671

	/* Year... */
	token = next_token (&val, cfile);
	if (token != NUMBER) {
672
		parse_warn (cfile, "numeric year expected.");
673 674 675 676
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}
677 678 679 680 681

	/* 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. */
682 683 684
	year = atoi (val);
	if (year > 1900)
		year -= 1900;
685 686 687 688

	/* Slash seperating year from month... */
	token = next_token (&val, cfile);
	if (token != SLASH) {
689 690
		parse_warn (cfile,
			    "expected slash seperating year from month.");
691 692 693 694 695 696 697 698
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}

	/* Month... */
	token = next_token (&val, cfile);
	if (token != NUMBER) {
699
		parse_warn (cfile, "numeric month expected.");
700 701 702 703
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}
704
	mon = atoi (val) - 1;
705 706 707 708

	/* Slash seperating month from day... */
	token = next_token (&val, cfile);
	if (token != SLASH) {
709 710
		parse_warn (cfile,
			    "expected slash seperating month from day.");
711 712 713 714 715 716 717 718
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}

	/* Month... */
	token = next_token (&val, cfile);
	if (token != NUMBER) {
719
		parse_warn (cfile, "numeric day of month expected.");
720 721 722 723
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}
724
	mday = atoi (val);
725 726 727 728

	/* Hour... */
	token = next_token (&val, cfile);
	if (token != NUMBER) {
729
		parse_warn (cfile, "numeric hour expected.");
730 731 732 733
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}
734
	hour = atoi (val);
735 736 737 738

	/* Colon seperating hour from minute... */
	token = next_token (&val, cfile);
	if (token != COLON) {
739 740
		parse_warn (cfile,
			    "expected colon seperating hour from minute.");
741 742 743 744 745 746 747 748
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}

	/* Minute... */
	token = next_token (&val, cfile);
	if (token != NUMBER) {
749
		parse_warn (cfile, "numeric minute expected.");
750 751 752 753
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}
754
	min = atoi (val);
755 756 757 758

	/* Colon seperating minute from second... */
	token = next_token (&val, cfile);
	if (token != COLON) {
759 760
		parse_warn (cfile,
			    "expected colon seperating hour from minute.");
761 762 763 764 765 766 767 768
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}

	/* Minute... */
	token = next_token (&val, cfile);
	if (token != NUMBER) {
769
		parse_warn (cfile, "numeric minute expected.");
770 771 772 773
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}
774
	sec = atoi (val);
775

776 777 778 779 780 781
	token = peek_token (&val, cfile);
	if (token == NUMBER) {
		token = next_token (&val, cfile);
		tzoff = atoi (val);
	} else
		tzoff = 0;
782 783

	/* Make sure the date ends in a semicolon... */
Ted Lemon's avatar
Ted Lemon committed
784
	if (!parse_semi (cfile))
785 786 787
		return 0;

	/* Guess the time value... */
788 789 790 791
	guess = ((((((365 * (year - 70) +	/* Days in years since '70 */
		      (year - 69) / 4 +		/* Leap days since '70 */
		      (mon			/* Days in months this year */
		       ? months [mon - 1]
792
		       : 0) +
793 794 795 796 797
		      (mon > 1 &&		/* Leap day this year */
		       !((year - 72) & 3)) +
		      mday - 1) * 24) +		/* Day of month */
		    hour) * 60) +
		  min) * 60) + sec + tzoff;
798 799 800 801 802 803 804 805 806 807 808 809

	/* This guess could be wrong because of leap seconds or other
	   weirdness we don't know about that the system does.   For
	   now, we're just going to accept the guess, but at some point
	   it might be nice to do a successive approximation here to
	   get an exact value.   Even if the error is small, if the
	   server is restarted frequently (and thus the lease database
	   is reread), the error could accumulate into something
	   significant. */

	return guess;
}
810

811 812 813 814 815
/*
 * option-name :== IDENTIFIER |
 		   IDENTIFIER . IDENTIFIER
 */

816
struct option *parse_option_name (cfile, allocate, known)
817
	struct parse *cfile;
818
	int allocate;
819
	int *known;
820
{
821
	const char *val;
822
	enum dhcp_token token;
823
	char *uname;
824 825 826 827 828
	struct universe *universe;
	struct option *option;

	token = next_token (&val, cfile);
	if (!is_identifier (token)) {
829 830
		parse_warn (cfile,
			    "expecting identifier after option keyword.");
831 832 833 834
		if (token != SEMI)
			skip_to_semi (cfile);
		return (struct option *)0;
	}
Ted Lemon's avatar
Ted Lemon committed
835
	uname = dmalloc (strlen (val) + 1, MDL);
836 837 838
	if (!uname)
		log_fatal ("no memory for uname information.");
	strcpy (uname, val);
839 840 841 842 843 844 845 846
	token = peek_token (&val, cfile);
	if (token == DOT) {
		/* Go ahead and take the DOT token... */
		token = next_token (&val, cfile);

		/* The next token should be an identifier... */
		token = next_token (&val, cfile);
		if (!is_identifier (token)) {
847
			parse_warn (cfile, "expecting identifier after '.'");
848 849 850 851 852 853
			if (token != SEMI)
				skip_to_semi (cfile);
			return (struct option *)0;
		}

		/* Look up the option name hash table for the specified
854
		   uname. */
855 856 857
		universe = (struct universe *)0;
		if (!universe_hash_lookup (&universe, universe_hash,
					   uname, 0, MDL)) {
858
			parse_warn (cfile, "no option space named %s.", uname);
859 860 861 862 863 864
			skip_to_semi (cfile);
			return (struct option *)0;
		}
	} else {
		/* Use the default hash table, which contains all the
		   standard dhcp option names. */
865
		val = uname;
866 867 868 869
		universe = &dhcp_universe;
	}

	/* Look up the actual option info... */
870 871
	option = (struct option *)0;
	option_hash_lookup (&option, universe -> hash, val, 0, MDL);
872 873

	/* If we didn't get an option structure, it's an undefined option. */
874
	if (option) {
875 876
		if (known)
			*known = 1;
877
	} else {
878 879 880 881
		/* If we've been told to allocate, that means that this
		   (might) be an option code definition, so we'll create
		   an option structure just in case. */
		if (allocate) {
882
			option = new_option (MDL);
883 884 885
			if (val == uname)
				option -> name = val;
			else {
886
				char *s;
Ted Lemon's avatar
Ted Lemon committed
887 888
				dfree (uname, MDL);
				s = dmalloc (strlen (val) + 1, MDL);
889
				if (!s)
890 891
				    log_fatal ("no memory for option %s.%s",
					       universe -> name, val);
892 893
				strcpy (s, val);
				option -> name = s;
894 895
			}
			option -> universe = universe;
896
			option -> code = 0;
897 898 899
			return option;
		}
		if (val == uname)
900
			parse_warn (cfile, "no option named %s", val);
901
		else
902
			parse_warn (cfile, "no option named %s in space %s",
903
				    val, uname);
904 905 906 907 908
		skip_to_semi (cfile);
		return (struct option *)0;
	}

	/* Free the initial identifier token. */
Ted Lemon's avatar
Ted Lemon committed
909
	dfree (uname, MDL);
910 911 912
	return option;
}

913 914 915
/* IDENTIFIER SEMI */

void parse_option_space_decl (cfile)
916
	struct parse *cfile;
917 918
{
	int token;
919
	const char *val;
920
	struct universe **ua, *nu;
921
	char *s;
922 923 924 925 926

	next_token (&val, cfile);	/* Discard the SPACE token, which was
					   checked by the caller. */
	token = next_token (&val, cfile);
	if (!is_identifier (token)) {
927
		parse_warn (cfile, "expecting identifier.");
928 929 930
		skip_to_semi (cfile);
		return;
	}
931
	nu = new_universe (MDL);
932 933 934 935
	if (!nu)
		log_fatal ("No memory for new option space.");

	/* Set up the server option universe... */
Ted Lemon's avatar
Ted Lemon committed
936
	s = dmalloc (strlen (val) + 1, MDL);
937
	if (!s)
938
		log_fatal ("No memory for new option space name.");
939 940
	strcpy (s, val);
	nu -> name = s;
941 942 943
	nu -> lookup_func = lookup_hashed_option;
	nu -> option_state_dereference =
		hashed_option_state_dereference;
944
	nu -> foreach = hashed_option_space_foreach;
945 946 947
	nu -> save_func = save_hashed_option;
	nu -> delete_func = delete_hashed_option;
	nu -> encapsulate = hashed_option_space_encapsulate;
948
	nu -> decode = parse_option_buffer;
949 950 951 952 953 954
	nu -> length_size = 1;
	nu -> tag_size = 1;
	nu -> store_tag = putUChar;
	nu -> store_length = putUChar;
	nu -> index = universe_count++;
	if (nu -> index >= universe_max) {
Ted Lemon's avatar
Ted Lemon committed
955
		ua = dmalloc (universe_max * 2 * sizeof *ua, MDL);
956 957 958 959
		if (!ua)
			log_fatal ("No memory to expand option space array.");
		memcpy (ua, universes, universe_max * sizeof *ua);
		universe_max *= 2;
Ted Lemon's avatar
Ted Lemon committed
960
		dfree (universes, MDL);
961 962 963
		universes = ua;
	}
	universes [nu -> index] = nu;
964
	nu -> hash = new_hash (0, 0, 1);
965 966
	if (!nu -> hash)
		log_fatal ("Can't allocate %s option hash table.", nu -> name);
967
	universe_hash_add (universe_hash, nu -> name, 0, nu, MDL);
968 969 970
	parse_semi (cfile);
}

971 972 973 974 975 976 977 978 979
/* This is faked up to look good right now.   Ideally, this should do a
   recursive parse and allow arbitrary data structure definitions, but for
   now it just allows you to specify a single type, an array of single types,
   a sequence of types, or an array of sequences of types.

   ocd :== NUMBER EQUALS ocsd SEMI

   ocsd :== ocsd_type |
	    ocsd_type_sequence |
980
	    ARRAY OF ocsd_simple_type_sequence
981 982 983

   ocsd_type_sequence :== LBRACE ocsd_types RBRACE

984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000
   ocsd_simple_type_sequence :== LBRACE ocsd_simple_types RBRACE

   ocsd_types :== ocsd_type |
		  ocsd_types ocsd_type

   ocsd_type :== ocsd_simple_type |
		 ARRAY OF ocsd_simple_type

   ocsd_simple_types :== ocsd_simple_type |
			 ocsd_simple_types ocsd_simple_type

   ocsd_simple_type :== BOOLEAN |
			INTEGER NUMBER |
			SIGNED INTEGER NUMBER |
			UNSIGNED INTEGER NUMBER |
			IP-ADDRESS |
			TEXT |
1001 1002
			STRING |
			ENCAPSULATE identifier */
1003 1004

int parse_option_code_definition (cfile, option)
1005
	struct parse *cfile;
1006 1007
	struct option *option;
{
1008
	const char *val;
1009
	enum dhcp_token token;
1010
	unsigned arrayp = 0;
1011 1012 1013
	int recordp = 0;
	int no_more_in_record = 0;
	char tokbuf [128];
1014
	unsigned tokix = 0;
1015 1016 1017
	char type;
	int code;
	int is_signed;
1018
	char *s;
1019
	int has_encapsulation = 0;
Ted Lemon's avatar