1/*	$OpenBSD: parse.c,v 1.11 2004/05/05 23:07:47 deraadt Exp $	*/
2
3/* Common parser code for dhcpd and dhclient. */
4
5/*
6 * Copyright (c) 1995, 1996, 1997, 1998 The Internet Software Consortium.
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 *
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 * 3. Neither the name of The Internet Software Consortium nor the names
19 *    of its contributors may be used to endorse or promote products derived
20 *    from this software without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
23 * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
24 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
25 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
26 * DISCLAIMED.  IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
27 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
29 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
30 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
31 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
32 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
33 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 *
36 * This software has been written for the Internet Software Consortium
37 * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
38 * Enterprises.  To learn more about the Internet Software Consortium,
39 * see ``http://www.vix.com/isc''.  To learn more about Vixie
40 * Enterprises, see ``http://www.vix.com''.
41 */
42
43#include <sys/cdefs.h>
44__FBSDID("$FreeBSD$");
45
46#include "dhcpd.h"
47#include "dhctoken.h"
48
49/* Skip to the semicolon ending the current statement.   If we encounter
50 * braces, the matching closing brace terminates the statement.   If we
51 * encounter a right brace but haven't encountered a left brace, return
52 * leaving the brace in the token buffer for the caller.   If we see a
53 * semicolon and haven't seen a left brace, return.   This lets us skip
54 * over:
55 *
56 *	statement;
57 *	statement foo bar { }
58 *	statement foo bar { statement { } }
59 *	statement}
60 *
61 *	...et cetera.
62 */
63void
64skip_to_semi(FILE *cfile)
65{
66	int brace_count = 0, token;
67	char *val;
68
69	do {
70		token = peek_token(&val, cfile);
71		if (token == RBRACE) {
72			if (brace_count) {
73				token = next_token(&val, cfile);
74				if (!--brace_count)
75					return;
76			} else
77				return;
78		} else if (token == LBRACE) {
79			brace_count++;
80		} else if (token == SEMI && !brace_count) {
81			token = next_token(&val, cfile);
82			return;
83		} else if (token == '\n') {
84			/*
85			 * EOL only happens when parsing
86			 * /etc/resolv.conf, and we treat it like a
87			 * semicolon because the resolv.conf file is
88			 * line-oriented.
89			 */
90			token = next_token(&val, cfile);
91			return;
92		}
93		token = next_token(&val, cfile);
94	} while (token != EOF);
95}
96
97int
98parse_semi(FILE *cfile)
99{
100	int token;
101	char *val;
102
103	token = next_token(&val, cfile);
104	if (token != SEMI) {
105		parse_warn("semicolon expected.");
106		skip_to_semi(cfile);
107		return (0);
108	}
109	return (1);
110}
111
112/*
113 * string-parameter :== STRING SEMI
114 */
115char *
116parse_string(FILE *cfile)
117{
118	char *val, *s;
119	size_t valsize;
120	int token;
121
122	token = next_token(&val, cfile);
123	if (token != STRING) {
124		parse_warn("filename must be a string");
125		skip_to_semi(cfile);
126		return (NULL);
127	}
128	valsize = strlen(val) + 1;
129	s = malloc(valsize);
130	if (!s)
131		error("no memory for string %s.", val);
132	memcpy(s, val, valsize);
133
134	if (!parse_semi(cfile))
135		return (NULL);
136	return (s);
137}
138
139int
140parse_ip_addr(FILE *cfile, struct iaddr *addr)
141{
142	addr->len = 4;
143	if (parse_numeric_aggregate(cfile, addr->iabuf,
144	    &addr->len, DOT, 10, 8))
145		return (1);
146	return (0);
147}
148
149/*
150 * hardware-parameter :== HARDWARE ETHERNET csns SEMI
151 * csns :== NUMBER | csns COLON NUMBER
152 */
153void
154parse_hardware_param(FILE *cfile, struct hardware *hardware)
155{
156	unsigned char *t;
157	int token, hlen;
158	char *val;
159
160	token = next_token(&val, cfile);
161	switch (token) {
162	case ETHERNET:
163		hardware->htype = HTYPE_ETHER;
164		break;
165	case TOKEN_RING:
166		hardware->htype = HTYPE_IEEE802;
167		break;
168	case FDDI:
169		hardware->htype = HTYPE_FDDI;
170		break;
171	default:
172		parse_warn("expecting a network hardware type");
173		skip_to_semi(cfile);
174		return;
175	}
176
177	/*
178	 * Parse the hardware address information.   Technically, it
179	 * would make a lot of sense to restrict the length of the data
180	 * we'll accept here to the length of a particular hardware
181	 * address type.   Unfortunately, there are some broken clients
182	 * out there that put bogus data in the chaddr buffer, and we
183	 * accept that data in the lease file rather than simply failing
184	 * on such clients.   Yuck.
185	 */
186	hlen = 0;
187	t = parse_numeric_aggregate(cfile, NULL, &hlen, COLON, 16, 8);
188	if (!t)
189		return;
190	if (hlen > sizeof(hardware->haddr)) {
191		free(t);
192		parse_warn("hardware address too long");
193	} else {
194		hardware->hlen = hlen;
195		memcpy((unsigned char *)&hardware->haddr[0], t,
196		    hardware->hlen);
197		if (hlen < sizeof(hardware->haddr))
198			memset(&hardware->haddr[hlen], 0,
199			    sizeof(hardware->haddr) - hlen);
200		free(t);
201	}
202
203	token = next_token(&val, cfile);
204	if (token != SEMI) {
205		parse_warn("expecting semicolon.");
206		skip_to_semi(cfile);
207	}
208}
209
210/*
211 * lease-time :== NUMBER SEMI
212 */
213void
214parse_lease_time(FILE *cfile, time_t *timep)
215{
216	char *val;
217	int token;
218
219	token = next_token(&val, cfile);
220	if (token != NUMBER) {
221		parse_warn("Expecting numeric lease time");
222		skip_to_semi(cfile);
223		return;
224	}
225	convert_num((unsigned char *)timep, val, 10, 32);
226	/* Unswap the number - convert_num returns stuff in NBO. */
227	*timep = ntohl(*timep); /* XXX */
228
229	parse_semi(cfile);
230}
231
232/*
233 * No BNF for numeric aggregates - that's defined by the caller.  What
234 * this function does is to parse a sequence of numbers separated by the
235 * token specified in separator.  If max is zero, any number of numbers
236 * will be parsed; otherwise, exactly max numbers are expected.  Base
237 * and size tell us how to internalize the numbers once they've been
238 * tokenized.
239 */
240unsigned char *
241parse_numeric_aggregate(FILE *cfile, unsigned char *buf, int *max,
242    int separator, int base, int size)
243{
244	unsigned char *bufp = buf, *s = NULL;
245	int token, count = 0;
246	char *val, *t;
247	size_t valsize;
248	pair c = NULL;
249
250	if (!bufp && *max) {
251		bufp = malloc(*max * size / 8);
252		if (!bufp)
253			error("can't allocate space for numeric aggregate");
254	} else
255		s = bufp;
256
257	do {
258		if (count) {
259			token = peek_token(&val, cfile);
260			if (token != separator) {
261				if (!*max)
262					break;
263				if (token != RBRACE && token != LBRACE)
264					token = next_token(&val, cfile);
265				parse_warn("too few numbers.");
266				if (token != SEMI)
267					skip_to_semi(cfile);
268				return (NULL);
269			}
270			token = next_token(&val, cfile);
271		}
272		token = next_token(&val, cfile);
273
274		if (token == EOF) {
275			parse_warn("unexpected end of file");
276			break;
277		}
278
279		/* Allow NUMBER_OR_NAME if base is 16. */
280		if (token != NUMBER &&
281		    (base != 16 || token != NUMBER_OR_NAME)) {
282			parse_warn("expecting numeric value.");
283			skip_to_semi(cfile);
284			return (NULL);
285		}
286		/*
287		 * If we can, convert the number now; otherwise, build a
288		 * linked list of all the numbers.
289		 */
290		if (s) {
291			convert_num(s, val, base, size);
292			s += size / 8;
293		} else {
294			valsize = strlen(val) + 1;
295			t = malloc(valsize);
296			if (!t)
297				error("no temp space for number.");
298			memcpy(t, val, valsize);
299			c = cons(t, c);
300		}
301	} while (++count != *max);
302
303	/* If we had to cons up a list, convert it now. */
304	if (c) {
305		bufp = malloc(count * size / 8);
306		if (!bufp)
307			error("can't allocate space for numeric aggregate.");
308		s = bufp + count - size / 8;
309		*max = count;
310	}
311	while (c) {
312		pair cdr = c->cdr;
313		convert_num(s, (char *)c->car, base, size);
314		s -= size / 8;
315		/* Free up temp space. */
316		free(c->car);
317		free(c);
318		c = cdr;
319	}
320	return (bufp);
321}
322
323void
324convert_num(unsigned char *buf, char *str, int base, int size)
325{
326	int negative = 0, tval, max;
327	u_int32_t val = 0;
328	char *ptr = str;
329
330	if (*ptr == '-') {
331		negative = 1;
332		ptr++;
333	}
334
335	/* If base wasn't specified, figure it out from the data. */
336	if (!base) {
337		if (ptr[0] == '0') {
338			if (ptr[1] == 'x') {
339				base = 16;
340				ptr += 2;
341			} else if (isascii(ptr[1]) && isdigit(ptr[1])) {
342				base = 8;
343				ptr += 1;
344			} else
345				base = 10;
346		} else
347			base = 10;
348	}
349
350	do {
351		tval = *ptr++;
352		/* XXX assumes ASCII... */
353		if (tval >= 'a')
354			tval = tval - 'a' + 10;
355		else if (tval >= 'A')
356			tval = tval - 'A' + 10;
357		else if (tval >= '0')
358			tval -= '0';
359		else {
360			warning("Bogus number: %s.", str);
361			break;
362		}
363		if (tval >= base) {
364			warning("Bogus number: %s: digit %d not in base %d",
365			    str, tval, base);
366			break;
367		}
368		val = val * base + tval;
369	} while (*ptr);
370
371	if (negative)
372		max = (1 << (size - 1));
373	else
374		max = (1 << (size - 1)) + ((1 << (size - 1)) - 1);
375	if (val > max) {
376		switch (base) {
377		case 8:
378			warning("value %s%o exceeds max (%d) for precision.",
379			    negative ? "-" : "", val, max);
380			break;
381		case 16:
382			warning("value %s%x exceeds max (%d) for precision.",
383			    negative ? "-" : "", val, max);
384			break;
385		default:
386			warning("value %s%u exceeds max (%d) for precision.",
387			    negative ? "-" : "", val, max);
388			break;
389		}
390	}
391
392	if (negative)
393		switch (size) {
394		case 8:
395			*buf = -(unsigned long)val;
396			break;
397		case 16:
398			putShort(buf, -(unsigned long)val);
399			break;
400		case 32:
401			putLong(buf, -(unsigned long)val);
402			break;
403		default:
404			warning("Unexpected integer size: %d", size);
405			break;
406		}
407	else
408		switch (size) {
409		case 8:
410			*buf = (u_int8_t)val;
411			break;
412		case 16:
413			putUShort(buf, (u_int16_t)val);
414			break;
415		case 32:
416			putULong(buf, val);
417			break;
418		default:
419			warning("Unexpected integer size: %d", size);
420			break;
421		}
422}
423
424/*
425 * date :== NUMBER NUMBER SLASH NUMBER SLASH NUMBER
426 *		NUMBER COLON NUMBER COLON NUMBER SEMI
427 *
428 * Dates are always in GMT; first number is day of week; next is
429 * year/month/day; next is hours:minutes:seconds on a 24-hour
430 * clock.
431 */
432time_t
433parse_date(FILE *cfile)
434{
435	static int months[11] = { 31, 59, 90, 120, 151, 181,
436	    212, 243, 273, 304, 334 };
437	int guess, token;
438	struct tm tm;
439	char *val;
440
441	/* Day of week... */
442	token = next_token(&val, cfile);
443	if (token != NUMBER) {
444		parse_warn("numeric day of week expected.");
445		if (token != SEMI)
446			skip_to_semi(cfile);
447		return (0);
448	}
449	tm.tm_wday = atoi(val);
450
451	/* Year... */
452	token = next_token(&val, cfile);
453	if (token != NUMBER) {
454		parse_warn("numeric year expected.");
455		if (token != SEMI)
456			skip_to_semi(cfile);
457		return (0);
458	}
459	tm.tm_year = atoi(val);
460	if (tm.tm_year > 1900)
461		tm.tm_year -= 1900;
462
463	/* Slash separating year from month... */
464	token = next_token(&val, cfile);
465	if (token != SLASH) {
466		parse_warn("expected slash separating year from month.");
467		if (token != SEMI)
468			skip_to_semi(cfile);
469		return (0);
470	}
471
472	/* Month... */
473	token = next_token(&val, cfile);
474	if (token != NUMBER) {
475		parse_warn("numeric month expected.");
476		if (token != SEMI)
477			skip_to_semi(cfile);
478		return (0);
479	}
480	tm.tm_mon = atoi(val) - 1;
481
482	/* Slash separating month from day... */
483	token = next_token(&val, cfile);
484	if (token != SLASH) {
485		parse_warn("expected slash separating month from day.");
486		if (token != SEMI)
487			skip_to_semi(cfile);
488		return (0);
489	}
490
491	/* Month... */
492	token = next_token(&val, cfile);
493	if (token != NUMBER) {
494		parse_warn("numeric day of month expected.");
495		if (token != SEMI)
496			skip_to_semi(cfile);
497		return (0);
498	}
499	tm.tm_mday = atoi(val);
500
501	/* Hour... */
502	token = next_token(&val, cfile);
503	if (token != NUMBER) {
504		parse_warn("numeric hour expected.");
505		if (token != SEMI)
506			skip_to_semi(cfile);
507		return (0);
508	}
509	tm.tm_hour = atoi(val);
510
511	/* Colon separating hour from minute... */
512	token = next_token(&val, cfile);
513	if (token != COLON) {
514		parse_warn("expected colon separating hour from minute.");
515		if (token != SEMI)
516			skip_to_semi(cfile);
517		return (0);
518	}
519
520	/* Minute... */
521	token = next_token(&val, cfile);
522	if (token != NUMBER) {
523		parse_warn("numeric minute expected.");
524		if (token != SEMI)
525			skip_to_semi(cfile);
526		return (0);
527	}
528	tm.tm_min = atoi(val);
529
530	/* Colon separating minute from second... */
531	token = next_token(&val, cfile);
532	if (token != COLON) {
533		parse_warn("expected colon separating hour from minute.");
534		if (token != SEMI)
535			skip_to_semi(cfile);
536		return (0);
537	}
538
539	/* Minute... */
540	token = next_token(&val, cfile);
541	if (token != NUMBER) {
542		parse_warn("numeric minute expected.");
543		if (token != SEMI)
544			skip_to_semi(cfile);
545		return (0);
546	}
547	tm.tm_sec = atoi(val);
548	tm.tm_isdst = 0;
549
550	/* XXX: We assume that mktime does not use tm_yday. */
551	tm.tm_yday = 0;
552
553	/* Make sure the date ends in a semicolon... */
554	token = next_token(&val, cfile);
555	if (token != SEMI) {
556		parse_warn("semicolon expected.");
557		skip_to_semi(cfile);
558		return (0);
559	}
560
561	/* Guess the time value... */
562	guess = ((((((365 * (tm.tm_year - 70) +	/* Days in years since '70 */
563		    (tm.tm_year - 69) / 4 +	/* Leap days since '70 */
564		    (tm.tm_mon			/* Days in months this year */
565		    ? months[tm.tm_mon - 1]
566		    : 0) +
567		    (tm.tm_mon > 1 &&		/* Leap day this year */
568		    !((tm.tm_year - 72) & 3)) +
569		    tm.tm_mday - 1) * 24) +	/* Day of month */
570		    tm.tm_hour) * 60) +
571		    tm.tm_min) * 60) + tm.tm_sec;
572
573	/*
574	 * This guess could be wrong because of leap seconds or other
575	 * weirdness we don't know about that the system does.   For
576	 * now, we're just going to accept the guess, but at some point
577	 * it might be nice to do a successive approximation here to get
578	 * an exact value.   Even if the error is small, if the server
579	 * is restarted frequently (and thus the lease database is
580	 * reread), the error could accumulate into something
581	 * significant.
582	 */
583	return (guess);
584}
585