parse.c 93.9 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.71 2000/04/14 16:26:37 mellon Exp $ Copyright (c) 1995-2000 The Internet Software Consortium.  All rights reserved.\n";
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
#endif /* not lint */

#include "dhcpd.h"

/* 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)
66
	struct parse *cfile;
67
68
69
70
71
{
	skip_to_rbrace (cfile, 0);
}

void skip_to_rbrace (cfile, brace_count)
72
	struct parse *cfile;
73
	int brace_count;
74
{
75
	enum dhcp_token token;
76
	const char *val;
77

78
79
80
#if defined (DEBUG_TOKEN)
	log_error ("skip_to_rbrace: %d\n", brace_count);
#endif
81
82
83
	do {
		token = peek_token (&val, cfile);
		if (token == RBRACE) {
84
			token = next_token (&val, cfile);
85
86
87
88
89
90
91
92
93
94
			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;
95
96
97
98
99
100
		} 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;
101
102
103
104
105
106
		}
		token = next_token (&val, cfile);
	} while (token != EOF);
}

int parse_semi (cfile)
107
	struct parse *cfile;
108
{
109
	enum dhcp_token token;
110
	const char *val;
111
112
113

	token = next_token (&val, cfile);
	if (token != SEMI) {
114
		parse_warn (cfile, "semicolon expected.");
115
116
117
118
119
120
121
122
123
		skip_to_semi (cfile);
		return 0;
	}
	return 1;
}

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

char *parse_string (cfile)
124
	struct parse *cfile;
125
{
126
	const char *val;
127
	enum dhcp_token token;
128
129
130
131
	char *s;

	token = next_token (&val, cfile);
	if (token != STRING) {
132
		parse_warn (cfile, "filename must be a string");
133
134
135
		skip_to_semi (cfile);
		return (char *)0;
	}
Ted Lemon's avatar
Ted Lemon committed
136
	s = (char *)dmalloc (strlen (val) + 1, MDL);
137
	if (!s)
138
		log_fatal ("no memory for string %s.", val);
139
140
141
142
143
144
145
	strcpy (s, val);

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

146
147
148
149
150
/*
 * hostname :== IDENTIFIER
 *		| IDENTIFIER DOT
 *		| hostname DOT IDENTIFIER
 */
151
152

char *parse_host_name (cfile)
153
	struct parse *cfile;
154
{
155
	const char *val;
156
	enum dhcp_token token;
157
	unsigned len = 0;
158
159
160
161
162
163
164
	char *s;
	char *t;
	pair c = (pair)0;
	
	/* Read a dotted hostname... */
	do {
		/* Read a token, which should be an identifier. */
Ted Lemon's avatar
Ted Lemon committed
165
166
167
		token = peek_token (&val, cfile);
		if (!is_identifier (token) && token != NUMBER)
			break;
168
		token = next_token (&val, cfile);
Ted Lemon's avatar
Ted Lemon committed
169

170
		/* Store this identifier... */
Ted Lemon's avatar
Ted Lemon committed
171
		if (!(s = (char *)dmalloc (strlen (val) + 1, MDL)))
172
			log_fatal ("can't allocate temp space for hostname.");
173
174
175
176
177
178
179
180
181
182
		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);
		if (token == DOT)
			token = next_token (&val, cfile);
	} while (token == DOT);

Ted Lemon's avatar
Ted Lemon committed
183
184
185
186
	/* Should be at least one token. */
	if (!len)
		return (char *)0;

187
	/* Assemble the hostname together into a string. */
Ted Lemon's avatar
Ted Lemon committed
188
	if (!(s = (char *)dmalloc (len, MDL)))
189
		log_fatal ("can't allocate space for hostname.");
190
191
192
193
	t = s + len;
	*--t = 0;
	while (c) {
		pair cdr = c -> cdr;
194
		unsigned l = strlen ((char *)(c -> car));
195
196
197
		t -= l;
		memcpy (t, (char *)(c -> car), l);
		/* Free up temp space. */
Ted Lemon's avatar
Ted Lemon committed
198
199
		dfree (c -> car, MDL);
		dfree (c, MDL);
200
201
202
203
204
205
206
		c = cdr;
		if (t != s)
			*--t = '.';
	}
	return s;
}

207
208
209
210
211
212
213
/* 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. */

214
215
int parse_ip_addr_or_hostname (expr, cfile, uniform)
	struct expression **expr;
216
	struct parse *cfile;
217
218
	int uniform;
{
219
	const char *val;
220
	enum dhcp_token token;
221
	unsigned char addr [4];
222
	unsigned len = sizeof addr;
223
	char *name;
224
	struct expression *x = (struct expression *)0;
225
226
227
228
229

	token = peek_token (&val, cfile);
	if (is_identifier (token)) {
		name = parse_host_name (cfile);
		if (!name)
230
231
232
233
234
235
			return 0;
		if (!make_host_lookup (expr, name))
			return 0;
		if (!uniform) {
			if (!make_limit (&x, *expr, 4))
				return 0;
236
			expression_dereference (expr, MDL);
237
238
			*expr = x;
		}
239
240
	} else if (token == NUMBER) {
		if (!parse_numeric_aggregate (cfile, addr, &len, DOT, 10, 8))
241
242
			return 0;
		return make_const_data (expr, addr, len, 0, 1);
243
244
245
	} else {
		if (token != RBRACE && token != LBRACE)
			token = next_token (&val, cfile);
246
		parse_warn (cfile, "%s (%d): expecting IP address or hostname",
247
248
249
			    val, token);
		if (token != SEMI)
			skip_to_semi (cfile);
250
		return 0;
251
252
	}

253
	return 1;
254
255
256
257
258
259
}	
	
/*
 * ip-address :== NUMBER DOT NUMBER DOT NUMBER DOT NUMBER
 */

260
int parse_ip_addr (cfile, addr)
261
	struct parse *cfile;
262
263
	struct iaddr *addr;
{
264
	const char *val;
265
	enum dhcp_token token;
266
267
268
269
270
271
272
273

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

274
275
276
277
/*
 * hardware-parameter :== HARDWARE hardware-type colon-seperated-hex-list SEMI
 * hardware-type :== ETHERNET | TOKEN_RING
 */
278
279

void parse_hardware_param (cfile, hardware)
280
	struct parse *cfile;
281
282
	struct hardware *hardware;
{
283
	const char *val;
284
	enum dhcp_token token;
285
	unsigned hlen;
286
287
288
289
290
	unsigned char *t;

	token = next_token (&val, cfile);
	switch (token) {
	      case ETHERNET:
291
		hardware -> hbuf [0] = HTYPE_ETHER;
292
293
		break;
	      case TOKEN_RING:
294
		hardware -> hbuf [0] = HTYPE_IEEE802;
295
		break;
296
	      case FDDI:
297
		hardware -> hbuf [0] = HTYPE_FDDI;
298
		break;
299
	      default:
300
		parse_warn (cfile, "expecting a network hardware type");
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
		skip_to_semi (cfile);
		return;
	}

	/* 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;
317
	if (hlen + 1 > sizeof hardware -> hbuf) {
Ted Lemon's avatar
Ted Lemon committed
318
		dfree (t, MDL);
319
		parse_warn (cfile, "hardware address too long");
320
	} else {
321
322
323
324
325
		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
326
		dfree (t, MDL);
327
328
329
330
	}
	
	token = next_token (&val, cfile);
	if (token != SEMI) {
331
		parse_warn (cfile, "expecting semicolon.");
332
333
334
335
336
337
338
		skip_to_semi (cfile);
	}
}

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

void parse_lease_time (cfile, timep)
339
	struct parse *cfile;
340
341
	TIME *timep;
{
342
	const char *val;
343
	enum dhcp_token token;
344
345
346

	token = next_token (&val, cfile);
	if (token != NUMBER) {
347
		parse_warn (cfile, "Expecting numeric lease time");
348
349
350
		skip_to_semi (cfile);
		return;
	}
351
	convert_num (cfile, (unsigned char *)timep, val, 10, 32);
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
	/* 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)
367
	struct parse *cfile;
368
	unsigned char *buf;
369
	unsigned *max;
370
371
	int seperator;
	int base;
372
	unsigned size;
373
{
374
	const char *val;
375
	enum dhcp_token token;
376
	unsigned char *bufp = buf, *s, *t;
377
	unsigned count = 0;
378
379
380
	pair c = (pair)0;

	if (!bufp && *max) {
Ted Lemon's avatar
Ted Lemon committed
381
		bufp = (unsigned char *)dmalloc (*max * size / 8, MDL);
382
		if (!bufp)
383
384
			log_fatal ("no space for numeric aggregate");
		s = 0;
385
386
387
388
389
390
391
392
393
394
395
	} 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);
396
				parse_warn (cfile, "too few numbers.");
397
398
399
400
401
402
403
404
405
				if (token != SEMI)
					skip_to_semi (cfile);
				return (unsigned char *)0;
			}
			token = next_token (&val, cfile);
		}
		token = next_token (&val, cfile);

		if (token == EOF) {
406
			parse_warn (cfile, "unexpected end of file");
407
408
409
410
411
412
			break;
		}

		/* Allow NUMBER_OR_NAME if base is 16. */
		if (token != NUMBER &&
		    (base != 16 || token != NUMBER_OR_NAME)) {
413
			parse_warn (cfile, "expecting numeric value.");
414
415
416
417
418
419
			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) {
420
			convert_num (cfile, s, val, base, size);
421
422
			s += size / 8;
		} else {
Ted Lemon's avatar
Ted Lemon committed
423
			t = (unsigned char *)dmalloc (strlen (val) + 1, MDL);
424
			if (!t)
425
				log_fatal ("no temp space for number.");
426
427
			strcpy ((char *)t, val);
			c = cons ((caddr_t)t, c);
428
429
430
431
432
		}
	} while (++count != *max);

	/* If we had to cons up a list, convert it now. */
	if (c) {
Ted Lemon's avatar
Ted Lemon committed
433
		bufp = (unsigned char *)dmalloc (count * size / 8, MDL);
434
		if (!bufp)
435
			log_fatal ("no space for numeric aggregate.");
436
437
438
439
440
		s = bufp + count - size / 8;
		*max = count;
	}
	while (c) {
		pair cdr = c -> cdr;
441
		convert_num (cfile, s, (char *)(c -> car), base, size);
442
443
		s -= size / 8;
		/* Free up temp space. */
Ted Lemon's avatar
Ted Lemon committed
444
445
		dfree (c -> car, MDL);
		dfree (c, MDL);
446
447
448
449
450
		c = cdr;
	}
	return bufp;
}

451
452
void convert_num (cfile, buf, str, base, size)
	struct parse *cfile;
453
	unsigned char *buf;
454
	const char *str;
455
	int base;
456
	unsigned size;
457
{
458
	const char *ptr = str;
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
	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 {
496
			parse_warn (cfile, "Bogus number: %s.", str);
497
498
499
			break;
		}
		if (tval >= base) {
500
501
			parse_warn (cfile,
				    "Bogus number %s: digit %d not in base %d",
502
				    str, tval, base);
503
504
505
506
507
508
509
510
511
512
513
514
			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:
515
			parse_warn (cfile,
516
				    "%s%lo exceeds max (%d) for precision.",
Ted Lemon's avatar
Ted Lemon committed
517
518
				    negative ? "-" : "",
				    (unsigned long)val, max);
519
520
			break;
		      case 16:
521
			parse_warn (cfile,
522
				    "%s%lx exceeds max (%d) for precision.",
Ted Lemon's avatar
Ted Lemon committed
523
524
				    negative ? "-" : "",
				    (unsigned long)val, max);
525
526
			break;
		      default:
527
			parse_warn (cfile,
528
				    "%s%lu exceeds max (%d) for precision.",
Ted Lemon's avatar
Ted Lemon committed
529
530
				    negative ? "-" : "",
				    (unsigned long)val, max);
531
532
533
534
535
536
537
538
539
540
			break;
		}
	}

	if (negative) {
		switch (size) {
		      case 8:
			*buf = -(unsigned long)val;
			break;
		      case 16:
541
			putShort (buf, -(long)val);
542
543
			break;
		      case 32:
544
			putLong (buf, -(long)val);
545
546
			break;
		      default:
547
548
			parse_warn (cfile,
				    "Unexpected integer size: %d\n", size);
549
550
551
552
553
554
555
556
557
558
559
560
561
562
			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:
563
564
			parse_warn (cfile,
				    "Unexpected integer size: %d\n", size);
565
566
567
568
569
			break;
		}
	}
}

570
571
/*
 * date :== NUMBER NUMBER SLASH NUMBER SLASH NUMBER 
572
573
574
 *		NUMBER COLON NUMBER COLON NUMBER SEMI |
 *          NUMBER NUMBER SLASH NUMBER SLASH NUMBER 
 *		NUMBER COLON NUMBER COLON NUMBER NUMBER SEMI |
575
 *	    NEVER
576
 *
577
578
579
580
 * 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.
581
 */
582
583

TIME parse_date (cfile)
584
	struct parse *cfile;
585
586
587
{
	struct tm tm;
	int guess;
588
	int tzoff, wday, year, mon, mday, hour, min, sec;
589
	const char *val;
590
	enum dhcp_token token;
591
592
593
	static int months [11] = { 31, 59, 90, 120, 151, 181,
					  212, 243, 273, 304, 334 };

594
	/* Day of week, or "never"... */
595
	token = next_token (&val, cfile);
596
597
598
599
600
601
	if (token == NEVER) {
		if (!parse_semi (cfile))
			return 0;
		return MAX_TIME;
	}

602
	if (token != NUMBER) {
603
		parse_warn (cfile, "numeric day of week expected.");
604
605
606
607
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}
608
	wday = atoi (val);
609
610
611
612

	/* Year... */
	token = next_token (&val, cfile);
	if (token != NUMBER) {
613
		parse_warn (cfile, "numeric year expected.");
614
615
616
617
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}
618
619
620
621
622

	/* 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. */
623
624
625
	year = atoi (val);
	if (year > 1900)
		year -= 1900;
626
627
628
629

	/* Slash seperating year from month... */
	token = next_token (&val, cfile);
	if (token != SLASH) {
630
631
		parse_warn (cfile,
			    "expected slash seperating year from month.");
632
633
634
635
636
637
638
639
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}

	/* Month... */
	token = next_token (&val, cfile);
	if (token != NUMBER) {
640
		parse_warn (cfile, "numeric month expected.");
641
642
643
644
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}
645
	mon = atoi (val) - 1;
646
647
648
649

	/* Slash seperating month from day... */
	token = next_token (&val, cfile);
	if (token != SLASH) {
650
651
		parse_warn (cfile,
			    "expected slash seperating month from day.");
652
653
654
655
656
657
658
659
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}

	/* Month... */
	token = next_token (&val, cfile);
	if (token != NUMBER) {
660
		parse_warn (cfile, "numeric day of month expected.");
661
662
663
664
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}
665
	mday = atoi (val);
666
667
668
669

	/* Hour... */
	token = next_token (&val, cfile);
	if (token != NUMBER) {
670
		parse_warn (cfile, "numeric hour expected.");
671
672
673
674
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}
675
	hour = atoi (val);
676
677
678
679

	/* Colon seperating hour from minute... */
	token = next_token (&val, cfile);
	if (token != COLON) {
680
681
		parse_warn (cfile,
			    "expected colon seperating hour from minute.");
682
683
684
685
686
687
688
689
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}

	/* Minute... */
	token = next_token (&val, cfile);
	if (token != NUMBER) {
690
		parse_warn (cfile, "numeric minute expected.");
691
692
693
694
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}
695
	min = atoi (val);
696
697
698
699

	/* Colon seperating minute from second... */
	token = next_token (&val, cfile);
	if (token != COLON) {
700
701
		parse_warn (cfile,
			    "expected colon seperating hour from minute.");
702
703
704
705
706
707
708
709
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}

	/* Minute... */
	token = next_token (&val, cfile);
	if (token != NUMBER) {
710
		parse_warn (cfile, "numeric minute expected.");
711
712
713
714
		if (token != SEMI)
			skip_to_semi (cfile);
		return (TIME)0;
	}
715
	sec = atoi (val);
716

717
718
719
720
721
722
	token = peek_token (&val, cfile);
	if (token == NUMBER) {
		token = next_token (&val, cfile);
		tzoff = atoi (val);
	} else
		tzoff = 0;
723
724

	/* Make sure the date ends in a semicolon... */
Ted Lemon's avatar
Ted Lemon committed
725
	if (!parse_semi (cfile))
726
727
728
		return 0;

	/* Guess the time value... */
729
730
731
732
	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]
733
		       : 0) +
734
735
736
737
738
		      (mon > 1 &&		/* Leap day this year */
		       !((year - 72) & 3)) +
		      mday - 1) * 24) +		/* Day of month */
		    hour) * 60) +
		  min) * 60) + sec + tzoff;
739
740
741
742
743
744
745
746
747
748
749
750

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

752
753
754
755
756
/*
 * option-name :== IDENTIFIER |
 		   IDENTIFIER . IDENTIFIER
 */

757
struct option *parse_option_name (cfile, allocate, known)
758
	struct parse *cfile;
759
	int allocate;
760
	int *known;
761
{
762
	const char *val;
763
	enum dhcp_token token;
764
	char *uname;
765
766
767
768
769
	struct universe *universe;
	struct option *option;

	token = next_token (&val, cfile);
	if (!is_identifier (token)) {
770
771
		parse_warn (cfile,
			    "expecting identifier after option keyword.");
772
773
774
775
		if (token != SEMI)
			skip_to_semi (cfile);
		return (struct option *)0;
	}
Ted Lemon's avatar
Ted Lemon committed
776
	uname = dmalloc (strlen (val) + 1, MDL);
777
778
779
	if (!uname)
		log_fatal ("no memory for uname information.");
	strcpy (uname, val);
780
781
782
783
784
785
786
787
	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)) {
788
			parse_warn (cfile, "expecting identifier after '.'");
789
790
791
792
793
794
			if (token != SEMI)
				skip_to_semi (cfile);
			return (struct option *)0;
		}

		/* Look up the option name hash table for the specified
795
		   uname. */
796
		universe = ((struct universe *)
Ted Lemon's avatar
Ted Lemon committed
797
			    hash_lookup (universe_hash,
798
					 (unsigned char *)uname, 0));
799
800
801
		/* If it's not there, we can't parse the rest of the
		   declaration. */
		if (!universe) {
802
			parse_warn (cfile, "no option space named %s.", uname);
803
804
805
806
807
808
			skip_to_semi (cfile);
			return (struct option *)0;
		}
	} else {
		/* Use the default hash table, which contains all the
		   standard dhcp option names. */
809
		val = uname;
810
811
812
813
814
		universe = &dhcp_universe;
	}

	/* Look up the actual option info... */
	option = (struct option *)hash_lookup (universe -> hash,
815
					       (const unsigned char *)val, 0);
816
817

	/* If we didn't get an option structure, it's an undefined option. */
818
819
820
	if (option) {
		*known = 1;
	} else {
821
822
823
824
		/* 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) {
825
			option = new_option (MDL);
826
827
828
			if (val == uname)
				option -> name = val;
			else {
829
				char *s;
Ted Lemon's avatar
Ted Lemon committed
830
831
				dfree (uname, MDL);
				s = dmalloc (strlen (val) + 1, MDL);
832
				if (!s)
833
834
				    log_fatal ("no memory for option %s.%s",
					       universe -> name, val);
835
836
				strcpy (s, val);
				option -> name = s;
837
838
			}
			option -> universe = universe;
839
			option -> code = 0;
840
841
842
			return option;
		}
		if (val == uname)
843
			parse_warn (cfile, "no option named %s", val);
844
		else
845
			parse_warn (cfile, "no option named %s in space %s",
846
				    val, uname);
847
848
849
850
851
		skip_to_semi (cfile);
		return (struct option *)0;
	}

	/* Free the initial identifier token. */
Ted Lemon's avatar
Ted Lemon committed
852
	dfree (uname, MDL);
853
854
855
	return option;
}

856
857
858
/* IDENTIFIER SEMI */

void parse_option_space_decl (cfile)
859
	struct parse *cfile;
860
861
{
	int token;
862
	const char *val;
863
	struct universe **ua, *nu;
864
	char *s;
865
866
867
868
869

	next_token (&val, cfile);	/* Discard the SPACE token, which was
					   checked by the caller. */
	token = next_token (&val, cfile);
	if (!is_identifier (token)) {
870
		parse_warn (cfile, "expecting identifier.");
871
872
873
		skip_to_semi (cfile);
		return;
	}
874
	nu = new_universe (MDL);
875
876
877
878
	if (!nu)
		log_fatal ("No memory for new option space.");

	/* Set up the server option universe... */
Ted Lemon's avatar
Ted Lemon committed
879
	s = dmalloc (strlen (val) + 1, MDL);
880
	if (!s)
881
		log_fatal ("No memory for new option space name.");
882
883
	strcpy (s, val);
	nu -> name = s;
884
885
886
887
888
889
890
891
892
893
894
895
896
897
	nu -> lookup_func = lookup_hashed_option;
	nu -> option_state_dereference =
		hashed_option_state_dereference;
	nu -> get_func = hashed_option_get;
	nu -> set_func = hashed_option_set;
	nu -> save_func = save_hashed_option;
	nu -> delete_func = delete_hashed_option;
	nu -> encapsulate = hashed_option_space_encapsulate;
	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
898
		ua = dmalloc (universe_max * 2 * sizeof *ua, MDL);
899
900
901
902
		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
903
		dfree (universes, MDL);
904
905
906
		universes = ua;
	}
	universes [nu -> index] = nu;
907
	nu -> hash = new_hash (0, 0, 1);
908
909
	if (!nu -> hash)
		log_fatal ("Can't allocate %s option hash table.", nu -> name);
Ted Lemon's avatar
Ted Lemon committed
910
	add_hash (universe_hash,
911
		  (const unsigned char *)nu -> name, 0, (unsigned char *)nu);
912
913
914
	parse_semi (cfile);
}

915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
/* 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 |
	    ARRAY OF ocsd_type |
	    ARRAY OF ocsd_type_sequence

   ocsd_type :== BOOLEAN |
		 INTEGER NUMBER |
		 SIGNED INTEGER NUMBER |
		 UNSIGNED INTEGER NUMBER |
		 IP-ADDRESS |
		 TEXT |
		 STRING

   ocsd_type_sequence :== LBRACE ocsd_types RBRACE

   ocsd_type :== ocsd_type |
		 ocsd_types ocsd_type */

int parse_option_code_definition (cfile, option)
941
	struct parse *cfile;
942
943
	struct option *option;
{
944
	const char *val;
945
	enum dhcp_token token;
946
	unsigned arrayp = 0;
947
948
949
	int recordp = 0;
	int no_more_in_record = 0;
	char tokbuf [128];
950
	unsigned tokix = 0;
951
952
953
	char type;
	int code;
	int is_signed;
954
	char *s;
955
956
957
958
	
	/* Parse the option code. */
	token = next_token (&val, cfile);
	if (token != NUMBER) {
959
		parse_warn (cfile, "expecting option code number.");
960
961
962
963
964
965
966
		skip_to_semi (cfile);
		return 0;
	}
	option -> code = atoi (val);

	token = next_token (&val, cfile);
	if (token != EQUAL) {
967
		parse_warn (cfile, "expecting \"=\"");
968
969
970
971
972
973
974
975
976
		skip_to_semi (cfile);
		return 0;
	}

	/* See if this is an array. */
	token = next_token (&val, cfile);
	if (token == ARRAY) {
		token = next_token (&val, cfile);
		if (token != OF) {
977
			parse_warn (cfile, "expecting \"of\".");
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
			skip_to_semi (cfile);
			return 0;
		}
		arrayp = 1;
		token = next_token (&val, cfile);
	}

	if (token == LBRACE) {
		recordp = 1;
		token = next_token (&val, cfile);
	}

	/* At this point we're expecting a data type. */
      next_type:
	switch (token) {
	      case BOOLEAN:
		type = 'f';
		break;
	      case INTEGER:
		is_signed = 1;
	      parse_integer:
		token = next_token (&val, cfile);
		if (token != NUMBER) {
1001
			parse_warn (cfile, "expecting number.");
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
			skip_to_rbrace (cfile, recordp);
			if (recordp)
				skip_to_semi (cfile);
			return 0;
		}
		switch (atoi (val)) {
		      case 8:
			type = is_signed ? 'b' : 'B';
			break;
		      case 16:
			type = is_signed ? 's' : 'S';
			break;
		      case 32:
			type = is_signed ? 'l' : 'L';
			break;
		      default:
1018
1019
			parse_warn (cfile,
				    "%s bit precision is not supported.", val);
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
			skip_to_rbrace (cfile, recordp);
			if (recordp)
				skip_to_semi (cfile);
			return 0;
		}
		break;
	      case SIGNED:
		is_signed = 1;
	      parse_signed:
		token = next_token (&val, cfile);
		if (token != INTEGER) {
1031
			parse_warn (cfile, "expecting \"integer\" keyword.");
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
			skip_to_rbrace (cfile, recordp);
			if (recordp)
				skip_to_semi (cfile);
			return 0;
		}
		goto parse_integer;
	      case UNSIGNED:
		is_signed = 0;
		goto parse_signed;

	      case IP_ADDRESS:
		type = 'I';
		break;
	      case TEXT:
		type = 't';
	      no_arrays:
		if (arrayp) {
1049
			parse_warn (cfile, "arrays of text strings not %s",
1050
1051
1052
1053
1054
1055
1056
1057
				    "yet supported.");
			skip_to_rbrace (cfile, recordp);
			if (recordp)
				skip_to_semi (cfile);
			return 0;
		}
		no_more_in_record = 1;
		break;
Ted Lemon's avatar
Ted Lemon committed
1058
	      case STRING_TOKEN:
1059
1060
1061
1062
		type = 'X';
		goto no_arrays;

	      default:
1063
		parse_warn (cfile, "unknown data type %s", val);
1064
1065
1066
1067
1068
1069
1070
		skip_to_rbrace (cfile, recordp);
		if (recordp)
			skip_to_semi (cfile);
		return 0;
	}

	if (tokix == sizeof tokbuf) {
1071
		parse_warn (cfile, "too many types in record.");
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
		skip_to_rbrace (cfile, recordp);
		if (recordp)
			skip_to_semi (cfile);
		return 0;
	}
	tokbuf [tokix++] = type;

	if (recordp) {
		token = next_token (&val, cfile);
		if (token == COMMA) {
			if (no_more_in_record) {
1083
1084
				parse_warn (cfile,
					    "%s must be at end of record.",
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
					    type == 't' ? "text" : "string");
				skip_to_rbrace (cfile, 1);
				if (recordp)
					skip_to_semi (cfile);
				return 0;
			}
			token = next_token (&val, cfile);
			goto next_type;
		}
		if (token != RBRACE) {
1095
			parse_warn (cfile, "expecting right brace.");
1096
1097
1098
1099
1100
1101
1102
			skip_to_rbrace (cfile, 1);
			if (recordp)
				skip_to_semi (cfile);
			return 0;
		}
	}
	if (!parse_semi (cfile)) {
1103
		parse_warn (cfile, "semicolon expected.");
1104
1105
1106
1107
1108
		skip_to_semi (cfile);
		if (recordp)
			skip_to_semi (cfile);
		return 0;
	}
Ted Lemon's avatar
Ted Lemon committed
1109
	s = dmalloc (tokix + arrayp + 1, MDL);
1110
	if (!s)
1111
		log_fatal ("no memory for option format.");
1112
	memcpy (s, tokbuf, tokix);
1113
	if (arrayp)
1114
1115
1116
		s [tokix++] = 'A';
	s [tokix] = 0;
	option -> format = s;
1117
1118
1119
1120
1121
1122
	if (option -> universe -> options [option -> code]) {
		/* XXX Free the option, but we can't do that now because they
		   XXX may start out static. */
	}
	option -> universe -> options [option -> code] = option;
	add_hash (option -> universe -> hash,
1123
1124
		  (const unsigned char *)option -> name,
		  0, (unsigned char *)option);
1125
1126
1127
	return 1;
}

1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
/*
 * base64 :== NUMBER_OR_STRING
 */

int parse_base64 (data, cfile)
	struct data_string *data;
	struct parse *cfile;
{
	enum dhcp_token token;
	const char *val;
	int i, j;
	unsigned acc = 0;
	static char from64 [] = {64, 64, 64, 64, 64, 64, 64, 64, /*  \"#$%&' */
				 64, 64, 64, 62, 64, 64, 64, 63, /* ()*+,-./ */
				 52, 53, 54, 55, 56, 57, 58, 59, /* 01234567 */
				 60, 61, 64, 64, 64, 64, 64, 64, /* 90:;<=>? */
				 64, 0, 1, 2, 3, 4, 5, 6,	 /* @ABCDEFG */
				 7, 8, 9, 10, 11, 12, 13, 14,	 /* HIJKLMNO */
				 15, 16, 17, 18, 19, 20, 21, 22, /* PQRSTUVW */
				 23, 24, 25, 64, 64, 64, 64, 64, /* XYZ[\]^_ */
				 64, 26, 27, 28, 29, 30, 31, 32, /* 'abcdefg */
				 33, 34, 35, 36, 37, 38, 39, 40, /* hijklmno */
				 41, 42, 43, 44, 45, 46, 47, 48, /* pqrstuvw */
				 59, 50, 51, 64, 64, 64, 64, 64};/* xyz{|}~  */
	
	token = next_token (&val, cfile);
	if (token == STRING) {
		data -> len = strlen (val) + 1;
		if (!buffer_allocate (&data -> buffer, data -> len, MDL)) {
			parse_warn (cfile, "can't allocate string buffer");
			return 0;
		}
1160
		strcpy ((char *)data -> buffer -> data, val);
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
		data -> terminated = 1;
		data -> data = data -> buffer -> data;
		return 1;
	}

	data -> len = strlen (val);
	data -> len = (data -> len * 3) / 4;
	if (!buffer_allocate (&data -> buffer, data -> len, MDL)) {
		parse_warn (cfile, "can't allocate buffer for base64 data.");
		data -> len = 0;
		data -> data = (unsigned char *)0;
		return 0;
	}
		
	j = 0;
	for (i = 0; val [i] != '=' && val [i]; i++) {
		unsigned foo = val [i];
		if (foo < ' ' || foo > 'z') {
		      bad64:
			parse_warn (cfile,
				    "invalid base64 character %d.", val [i]);
			data_string_forget (data, MDL);
			return 0;
		}
		foo = from64 [foo - ' '];
		if (foo == 64)
			goto bad64;
		acc = (acc << 6) + foo;
		switch (i % 4) {
		      case 0:
			break;
		      case 1:
			data -> buffer -> data [j++] = (acc >> 4);
			acc = acc & 0x0f;
			break;

		      case 2:
			data -> buffer -> data [j++] = (acc >> 2);
			acc = acc & 0x03;
			break;
		      case 3:
			data -> buffer -> data [j++] = acc;
			acc = 0;
			break;
		}
	}
	if (i % 4)
		parse_warn (cfile, "partial base64 value left over: %d.", acc);
	data -> len = j;
	data -> data = data -> buffer -> data;
	return 1;
}


1215
1216
1217
1218
1219
/*
 * colon-seperated-hex-list :== NUMBER |
 *				NUMBER COLON colon-seperated-hex-list
 */

1220
1221
int parse_cshl (data, cfile)
	struct data_string *data;
1222
	struct parse *cfile;
1223
{
Ted Lemon's avatar
Ted Lemon committed
1224
	u_int8_t ibuf [128];
1225
1226
	unsigned ilen = 0;
	unsigned tlen = 0;
1227
1228
	struct option_tag *sl = (struct option_tag *)0;
	struct option_tag *next, **last = &sl;
1229
	enum dhcp_token token;
1230
	const char *val;
1231
	unsigned char *rvp;
1232
1233
1234
1235

	do {
		token = next_token (&val, cfile);
		if (token != NUMBER && token != NUMBER_OR_NAME) {
1236
			parse_warn (cfile, "expecting hexadecimal number.");
1237
1238
1239
			skip_to_semi (cfile);
			for (; sl; sl = next) {
				next = sl -> next;
Ted Lemon's avatar
Ted Lemon committed
1240
				dfree (sl, MDL);
1241
			}
1242
			return 0;
1243
1244
1245
1246
		}
		if (ilen == sizeof ibuf) {
			next = (struct option_tag *)
				dmalloc (ilen - 1 +
Ted Lemon's avatar
Ted Lemon committed
1247
					 sizeof (struct option_tag), MDL);
1248
			if (!next)
1249
				log_fatal ("no memory for string list.");
1250
1251
1252
1253
1254
1255
			memcpy (next -> data, ibuf, ilen);
			*last = next;
			last = &next -> next;
			tlen += ilen;
			ilen = 0;
		}
1256
		convert_num (cfile, &ibuf [ilen++], val, 16, 8);
1257
1258
1259
1260
1261
1262
1263

		token = peek_token (&val, cfile);
		if (token != COLON)
			break;
		token = next_token (&val, cfile);
	} while (1);

1264
	if (!buffer_allocate (&data -> buffer, tlen + ilen, MDL))
1265
		log_fatal ("no memory to store octet data.");
1266
1267
1268
1269
	data -> data = &data -> buffer -> data [0];
	data -> len = tlen + ilen;
	data -> terminated = 0;

1270
	rvp = &data -> buffer -> data [0];
1271
1272
1273
1274
	while (sl) {
		next = sl -> next;
		memcpy (rvp, sl -> data, sizeof ibuf);
		rvp += sizeof ibuf;
Ted Lemon's avatar
Ted Lemon committed
1275
		dfree (sl, MDL);
1276
1277
1278
1279
		sl = next;
	}
	
	memcpy (rvp, ibuf, ilen);
1280
	return 1;
1281
}
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296

/*
 * executable-statements :== executable-statement executable-statements |
 *			     executable-statement
 *
 * executable-statement :==
 *	IF if-statement |
 * 	ADD class-name SEMI |
 *	BREAK SEMI |
 *	OPTION option-parameter SEMI |
 *	SUPERSEDE option-parameter SEMI |
 *	PREPEND option-parameter SEMI |
 *	APPEND option-parameter SEMI
 */

1297
int parse_executable_statements (statements, cfile, lose, case_context)
1298
	struct executable_statement **statements;
1299
	struct parse *cfile;
1300
	int *lose;
1301
	enum expression_context case_context;
1302
{
1303
	struct executable_statement **next;
1304

1305
	next = statements;
1306
	while (parse_executable_statement (next, cfile, lose, case_context))
1307
		next = &((*next) -> next);
1308
	if (!*lose)
1309
1310
		return 1;
	return 0;
1311
1312
}

1313
int parse_executable_statement (result, cfile, lose, case_context)
1314
	struct executable_statement **result;
1315
	struct parse *cfile;
1316
	int *lose;
1317
	enum expression_context case_context;
1318
{
1319
	enum dhcp_token token;
1320
	const char *val;
1321
	struct executable_statement base;
1322
1323
	struct class *cta;
	struct option *option;
1324
	struct option_cache *cache;
1325
	int known;
1326
	int flag;
Ted Lemon's avatar
Ted Lemon committed
1327
1328
	struct dns_zone *zone;
	isc_result_t status;
1329

1330
1331
	token = peek_token (&val, cfile);
	switch (token) {
1332
	      case IF:
1333
		next_token (&val, cfile);
1334
1335
		return parse_if_statement (result, cfile, lose);

Ted Lemon's avatar
Ted Lemon committed
1336
	      case TOKEN_ADD:
1337
		token = next_token (&val, cfile);
1338
1339
		token = next_token (&val, cfile);
		if (token != STRING) {
1340
			parse_warn (cfile, "expecting class name.");
1341
1342
			skip_to_semi (cfile);
			*lose = 1;
1343
			return 0;
1344
1345
1346
		}
		cta = find_class (val);
		if (!cta) {
1347
			parse_warn (cfile, "unknown class %s.", val);
1348
1349
			skip_to_semi (cfile);
			*lose = 1;
1350
			return 0;
1351
1352
1353
		}
		if (!parse_semi (cfile)) {
			*lose = 1;
1354
			return 0;
1355
		}
1356
		if (!executable_statement_allocate (result, MDL))
1357
1358
1359
			log_fatal ("no memory for new statement.");
		(*result) -> op = add_statement;
		(*result) -> data.add = cta;