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 7
 * Copyright (c) 1995-2000 Internet Software Consortium.
 * 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[] =
46
"$Id: parse.c,v 1.96 2001/01/11 23:14:11 mellon Exp $ Copyright (c) 1995-2000 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 364 365 366 367
	}

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

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

void parse_lease_time (cfile, timep)
390
	struct parse *cfile;
391 392
	TIME *timep;
{
393
	const char *val;
394
	enum dhcp_token token;
395 396 397

	token = next_token (&val, cfile);
	if (token != NUMBER) {
398
		parse_warn (cfile, "Expecting numeric lease time");
399 400 401
		skip_to_semi (cfile);
		return;
	}
402
	convert_num (cfile, (unsigned char *)timep, val, 10, 32);
403 404 405 406 407 408 409 410 411 412 413 414 415 416 417
	/* 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)
418
	struct parse *cfile;
419
	unsigned char *buf;
420
	unsigned *max;
421 422
	int seperator;
	int base;
423
	unsigned size;
424
{
425
	const char *val;
426
	enum dhcp_token token;
427
	unsigned char *bufp = buf, *s, *t;
428
	unsigned count = 0;
429 430 431
	pair c = (pair)0;

	if (!bufp && *max) {
Ted Lemon's avatar
Ted Lemon committed
432
		bufp = (unsigned char *)dmalloc (*max * size / 8, MDL);
433
		if (!bufp)
434 435
			log_fatal ("no space for numeric aggregate");
		s = 0;
436 437 438 439 440 441 442 443 444 445 446
	} 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);
447
				parse_warn (cfile, "too few numbers.");
448 449 450 451 452 453 454 455 456
				if (token != SEMI)
					skip_to_semi (cfile);
				return (unsigned char *)0;
			}
			token = next_token (&val, cfile);
		}
		token = next_token (&val, cfile);

		if (token == EOF) {
457
			parse_warn (cfile, "unexpected end of file");
458 459 460 461 462 463
			break;
		}

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

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

502 503
void convert_num (cfile, buf, str, base, size)
	struct parse *cfile;
504
	unsigned char *buf;
505
	const char *str;
506
	int base;
507
	unsigned size;
508
{
509
	const char *ptr = str;
510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546
	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 {
547
			parse_warn (cfile, "Bogus number: %s.", str);
548 549 550
			break;
		}
		if (tval >= base) {
551 552
			parse_warn (cfile,
				    "Bogus number %s: digit %d not in base %d",
553
				    str, tval, base);
554 555 556 557 558 559 560 561 562 563 564 565
			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:
566
			parse_warn (cfile,
567
				    "%s%lo exceeds max (%d) for precision.",
Ted Lemon's avatar
Ted Lemon committed
568 569
				    negative ? "-" : "",
				    (unsigned long)val, max);
570 571
			break;
		      case 16:
572
			parse_warn (cfile,
573
				    "%s%lx exceeds max (%d) for precision.",
Ted Lemon's avatar
Ted Lemon committed
574 575
				    negative ? "-" : "",
				    (unsigned long)val, max);
576 577
			break;
		      default:
578
			parse_warn (cfile,
579
				    "%s%lu exceeds max (%d) for precision.",
Ted Lemon's avatar
Ted Lemon committed
580 581
				    negative ? "-" : "",
				    (unsigned long)val, max);
582 583 584 585 586 587 588 589 590 591
			break;
		}
	}

	if (negative) {
		switch (size) {
		      case 8:
			*buf = -(unsigned long)val;
			break;
		      case 16:
592
			putShort (buf, -(long)val);
593 594
			break;
		      case 32:
595
			putLong (buf, -(long)val);
596 597
			break;
		      default:
598 599
			parse_warn (cfile,
				    "Unexpected integer size: %d\n", size);
600 601 602 603 604 605 606 607 608 609 610 611 612 613
			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:
614 615
			parse_warn (cfile,
				    "Unexpected integer size: %d\n", size);
616 617 618 619 620
			break;
		}
	}
}

621 622
/*
 * date :== NUMBER NUMBER SLASH NUMBER SLASH NUMBER 
623 624 625
 *		NUMBER COLON NUMBER COLON NUMBER SEMI |
 *          NUMBER NUMBER SLASH NUMBER SLASH NUMBER 
 *		NUMBER COLON NUMBER COLON NUMBER NUMBER SEMI |
626
 *	    NEVER
627
 *
628 629 630 631
 * 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.
632
 */
633 634

TIME parse_date (cfile)
635
	struct parse *cfile;
636 637 638
{
	struct tm tm;
	int guess;
639
	int tzoff, wday, year, mon, mday, hour, min, sec;
640
	const char *val;
641
	enum dhcp_token token;
642 643 644
	static int months [11] = { 31, 59, 90, 120, 151, 181,
					  212, 243, 273, 304, 334 };

645
	/* Day of week, or "never"... */
646
	token = next_token (&val, cfile);
647 648 649 650 651 652
	if (token == NEVER) {
		if (!parse_semi (cfile))
			return 0;
		return MAX_TIME;
	}

653
	if (token != NUMBER) {
654
		parse_warn (cfile, "numeric day of week expected.");
655 656 657 658
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}
659
	wday = atoi (val);
660 661 662 663

	/* Year... */
	token = next_token (&val, cfile);
	if (token != NUMBER) {
664
		parse_warn (cfile, "numeric year expected.");
665 666 667 668
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}
669 670 671 672 673

	/* 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. */
674 675 676
	year = atoi (val);
	if (year > 1900)
		year -= 1900;
677 678 679 680

	/* Slash seperating year from month... */
	token = next_token (&val, cfile);
	if (token != SLASH) {
681 682
		parse_warn (cfile,
			    "expected slash seperating year from month.");
683 684 685 686 687 688 689 690
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}

	/* Month... */
	token = next_token (&val, cfile);
	if (token != NUMBER) {
691
		parse_warn (cfile, "numeric month expected.");
692 693 694 695
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}
696
	mon = atoi (val) - 1;
697 698 699 700

	/* Slash seperating month from day... */
	token = next_token (&val, cfile);
	if (token != SLASH) {
701 702
		parse_warn (cfile,
			    "expected slash seperating month from day.");
703 704 705 706 707 708 709 710
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}

	/* Month... */
	token = next_token (&val, cfile);
	if (token != NUMBER) {
711
		parse_warn (cfile, "numeric day of month expected.");
712 713 714 715
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}
716
	mday = atoi (val);
717 718 719 720

	/* Hour... */
	token = next_token (&val, cfile);
	if (token != NUMBER) {
721
		parse_warn (cfile, "numeric hour expected.");
722 723 724 725
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}
726
	hour = atoi (val);
727 728 729 730

	/* Colon seperating hour from minute... */
	token = next_token (&val, cfile);
	if (token != COLON) {
731 732
		parse_warn (cfile,
			    "expected colon seperating hour from minute.");
733 734 735 736 737 738 739 740
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}

	/* Minute... */
	token = next_token (&val, cfile);
	if (token != NUMBER) {
741
		parse_warn (cfile, "numeric minute expected.");
742 743 744 745
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}
746
	min = atoi (val);
747 748 749 750

	/* Colon seperating minute from second... */
	token = next_token (&val, cfile);
	if (token != COLON) {
751 752
		parse_warn (cfile,
			    "expected colon seperating hour from minute.");
753 754 755 756 757 758 759 760
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}

	/* Minute... */
	token = next_token (&val, cfile);
	if (token != NUMBER) {
761
		parse_warn (cfile, "numeric minute expected.");
762 763 764 765
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}
766
	sec = atoi (val);
767

768 769 770 771 772 773
	token = peek_token (&val, cfile);
	if (token == NUMBER) {
		token = next_token (&val, cfile);
		tzoff = atoi (val);
	} else
		tzoff = 0;
774 775

	/* Make sure the date ends in a semicolon... */
Ted Lemon's avatar
Ted Lemon committed
776
	if (!parse_semi (cfile))
777 778 779
		return 0;

	/* Guess the time value... */
780 781 782 783
	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]
784
		       : 0) +
785 786 787 788 789
		      (mon > 1 &&		/* Leap day this year */
		       !((year - 72) & 3)) +
		      mday - 1) * 24) +		/* Day of month */
		    hour) * 60) +
		  min) * 60) + sec + tzoff;
790 791 792 793 794 795 796 797 798 799 800 801

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

803 804 805 806 807
/*
 * option-name :== IDENTIFIER |
 		   IDENTIFIER . IDENTIFIER
 */

808
struct option *parse_option_name (cfile, allocate, known)
809
	struct parse *cfile;
810
	int allocate;
811
	int *known;
812
{
813
	const char *val;
814
	enum dhcp_token token;
815
	char *uname;
816 817 818 819 820
	struct universe *universe;
	struct option *option;

	token = next_token (&val, cfile);
	if (!is_identifier (token)) {
821 822
		parse_warn (cfile,
			    "expecting identifier after option keyword.");
823 824 825 826
		if (token != SEMI)
			skip_to_semi (cfile);
		return (struct option *)0;
	}
Ted Lemon's avatar
Ted Lemon committed
827
	uname = dmalloc (strlen (val) + 1, MDL);
828 829 830
	if (!uname)
		log_fatal ("no memory for uname information.");
	strcpy (uname, val);
831 832 833 834 835 836 837 838
	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)) {
839
			parse_warn (cfile, "expecting identifier after '.'");
840 841 842 843 844 845
			if (token != SEMI)
				skip_to_semi (cfile);
			return (struct option *)0;
		}

		/* Look up the option name hash table for the specified
846
		   uname. */
847 848 849
		universe = (struct universe *)0;
		if (!universe_hash_lookup (&universe, universe_hash,
					   uname, 0, MDL)) {
850
			parse_warn (cfile, "no option space named %s.", uname);
851 852 853 854 855 856
			skip_to_semi (cfile);
			return (struct option *)0;
		}
	} else {
		/* Use the default hash table, which contains all the
		   standard dhcp option names. */
857
		val = uname;
858 859 860 861
		universe = &dhcp_universe;
	}

	/* Look up the actual option info... */
862 863
	option = (struct option *)0;
	option_hash_lookup (&option, universe -> hash, val, 0, MDL);
864 865

	/* If we didn't get an option structure, it's an undefined option. */
866
	if (option) {
867 868
		if (known)
			*known = 1;
869
	} else {
870 871 872 873
		/* 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) {
874
			option = new_option (MDL);
875 876 877
			if (val == uname)
				option -> name = val;
			else {
878
				char *s;
Ted Lemon's avatar
Ted Lemon committed
879 880
				dfree (uname, MDL);
				s = dmalloc (strlen (val) + 1, MDL);
881
				if (!s)
882 883
				    log_fatal ("no memory for option %s.%s",
					       universe -> name, val);
884 885
				strcpy (s, val);
				option -> name = s;
886 887
			}
			option -> universe = universe;
888
			option -> code = 0;
889 890 891
			return option;
		}
		if (val == uname)
892
			parse_warn (cfile, "no option named %s", val);
893
		else
894
			parse_warn (cfile, "no option named %s in space %s",
895
				    val, uname);
896 897 898 899 900
		skip_to_semi (cfile);
		return (struct option *)0;
	}

	/* Free the initial identifier token. */
Ted Lemon's avatar
Ted Lemon committed
901
	dfree (uname, MDL);
902 903 904
	return option;
}

905 906 907
/* IDENTIFIER SEMI */

void parse_option_space_decl (cfile)
908
	struct parse *cfile;
909 910
{
	int token;
911
	const char *val;
912
	struct universe **ua, *nu;
913
	char *s;
914 915 916 917 918

	next_token (&val, cfile);	/* Discard the SPACE token, which was
					   checked by the caller. */
	token = next_token (&val, cfile);
	if (!is_identifier (token)) {
919
		parse_warn (cfile, "expecting identifier.");
920 921 922
		skip_to_semi (cfile);
		return;
	}
923
	nu = new_universe (MDL);
924 925 926 927
	if (!nu)
		log_fatal ("No memory for new option space.");

	/* Set up the server option universe... */
Ted Lemon's avatar
Ted Lemon committed
928
	s = dmalloc (strlen (val) + 1, MDL);
929
	if (!s)
930
		log_fatal ("No memory for new option space name.");
931 932
	strcpy (s, val);
	nu -> name = s;
933 934 935
	nu -> lookup_func = lookup_hashed_option;
	nu -> option_state_dereference =
		hashed_option_state_dereference;
936
	nu -> foreach = hashed_option_space_foreach;
937 938 939
	nu -> save_func = save_hashed_option;
	nu -> delete_func = delete_hashed_option;
	nu -> encapsulate = hashed_option_space_encapsulate;
940
	nu -> decode = parse_option_buffer;
941 942 943 944 945 946
	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
947
		ua = dmalloc (universe_max * 2 * sizeof *ua, MDL);
948 949 950 951
		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
952
		dfree (universes, MDL);
953 954 955
		universes = ua;
	}
	universes [nu -> index] = nu;
956
	nu -> hash = new_hash (0, 0, 1);
957 958
	if (!nu -> hash)
		log_fatal ("Can't allocate %s option hash table.", nu -> name);
959
	universe_hash_add (universe_hash, nu -> name, 0, nu, MDL);
960 961 962
	parse_semi (cfile);
}

963 964 965 966 967 968 969 970 971
/* 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 |
972
	    ARRAY OF ocsd_simple_type_sequence
973 974 975

   ocsd_type_sequence :== LBRACE ocsd_types RBRACE

976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992
   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 |
993 994
			STRING |
			ENCAPSULATE identifier */
995 996

int parse_option_code_definition (cfile, option)
997
	struct parse *cfile;
998 999
	struct option *option;
{
1000
	const char *val;
1001
	enum dhcp_token token;
1002
	unsigned arrayp = 0;
1003 1004 1005
	int recordp = 0;
	int no_more_in_record = 0;
	char tokbuf [128];
1006
	unsigned tokix = 0;
1007 1008 1009
	char type;
	int code;
	int is_signed;
1010
	char *s;
1011
	int has_encapsulation = 0;
1012 1013 1014 1015
	
	/* Parse the option code. */
	token = next_token (&val, cfile);
	if (token != NUMBER) {
1016
		parse_warn (cfile, "expecting option code number.");
1017 1018 1019 1020 1021 1022 1023
		skip_to_semi (cfile);
		return 0;
	}
	option -> code = atoi (val);

	token = next_token (&val, cfile);
	if (token != EQUAL) {
1024
		parse_warn (cfile, "expecting \"=\"");
1025 1026 1027 1028 1029 1030 1031 1032 1033
		skip_to_semi (cfile);
		return