xref: /openbsd-src/usr.sbin/dhcpd/parse.c (revision 43003dfe3ad45d1698bed8a37f2b0f5b14f20d4f)
1 /*	$OpenBSD: parse.c,v 1.12 2009/09/01 08:42:31 reyk 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 "dhcpd.h"
44 #include "dhctoken.h"
45 
46 /*
47  * Skip to the semicolon ending the current statement.   If we encounter
48  * braces, the matching closing brace terminates the statement.   If we
49  * encounter a right brace but haven't encountered a left brace, return
50  * leaving the brace in the token buffer for the caller.   If we see a
51  * semicolon and haven't seen a left brace, return.   This lets us skip
52  * over:
53  *
54  *	statement;
55  *	statement foo bar { }
56  *	statement foo bar { statement { } }
57  *	statement}
58  *
59  *	...et cetera.
60  */
61 void
62 skip_to_semi(FILE *cfile)
63 {
64 	int		 token;
65 	char		*val;
66 	int		 brace_count = 0;
67 
68 	do {
69 		token = peek_token(&val, cfile);
70 		if (token == '}') {
71 			if (brace_count) {
72 				token = next_token(&val, cfile);
73 				if (!--brace_count)
74 					return;
75 			} else
76 				return;
77 		} else if (token == '{') {
78 			brace_count++;
79 		} else if (token == ';' && !brace_count) {
80 			token = next_token(&val, cfile);
81 			return;
82 		} else if (token == '\n') {
83 			/*
84 			 * EOL only happens when parsing
85 			 * /etc/resolv.conf, and we treat it like a
86 			 * semicolon because the resolv.conf file is
87 			 * line-oriented.
88 			 */
89 			token = next_token(&val, cfile);
90 			return;
91 		}
92 		token = next_token(&val, cfile);
93 	} while (token != EOF);
94 }
95 
96 int
97 parse_semi(FILE *cfile)
98 {
99 	int token;
100 	char *val;
101 
102 	token = next_token(&val, cfile);
103 	if (token != ';') {
104 		parse_warn("semicolon expected.");
105 		skip_to_semi(cfile);
106 		return (0);
107 	}
108 	return (1);
109 }
110 
111 /*
112  * string-parameter :== STRING SEMI
113  */
114 char *
115 parse_string(FILE *cfile)
116 {
117 	char *val, *s;
118 	int token;
119 
120 	token = next_token(&val, cfile);
121 	if (token != TOK_STRING) {
122 		parse_warn("filename must be a string");
123 		skip_to_semi(cfile);
124 		return (NULL);
125 	}
126 	s = malloc(strlen(val) + 1);
127 	if (!s)
128 		error("no memory for string %s.", val);
129 	strlcpy(s, val, strlen(val) + 1);
130 
131 	if (!parse_semi(cfile)) {
132 		free(s);
133 		return (NULL);
134 	}
135 	return (s);
136 }
137 
138 /*
139  * hostname :== identifier | hostname DOT identifier
140  */
141 char *
142 parse_host_name(FILE *cfile)
143 {
144 	char *val, *s, *t;
145 	int token, len = 0;
146 	pair c = NULL;
147 
148 	/* Read a dotted hostname... */
149 	do {
150 		/* Read a token, which should be an identifier. */
151 		token = next_token(&val, cfile);
152 		if (!is_identifier(token) && token != TOK_NUMBER) {
153 			parse_warn("expecting an identifier in hostname");
154 			skip_to_semi(cfile);
155 			return (NULL);
156 		}
157 		/* Store this identifier... */
158 		if (!(s = malloc(strlen(val) + 1)))
159 			error("can't allocate temp space for hostname.");
160 		strlcpy(s, val, strlen(val) + 1);
161 		c = cons((caddr_t) s, c);
162 		len += strlen(s) + 1;
163 		/*
164 		 * Look for a dot; if it's there, keep going, otherwise
165 		 * we're done.
166 		 */
167 		token = peek_token(&val, cfile);
168 		if (token == '.')
169 			token = next_token(&val, cfile);
170 	} while (token == '.');
171 
172 	/* Assemble the hostname together into a string. */
173 	if (!(s = malloc(len)))
174 		error("can't allocate space for hostname.");
175 	t = s + len;
176 	*--t = '\0';
177 	while (c) {
178 		pair cdr = c->cdr;
179 		int l = strlen((char *)c->car);
180 
181 		t -= l;
182 		memcpy(t, (char *)c->car, l);
183 		/* Free up temp space. */
184 		free(c->car);
185 		free(c);
186 		c = cdr;
187 		if (t != s)
188 			*--t = '.';
189 	}
190 	return (s);
191 }
192 
193 /*
194  * hardware-parameter :== HARDWARE ETHERNET csns SEMI
195  * csns :== NUMBER | csns COLON NUMBER
196  */
197 void
198 parse_hardware_param(FILE *cfile, struct hardware *hardware)
199 {
200 	char *val;
201 	int token, hlen;
202 	unsigned char *t;
203 
204 	token = next_token(&val, cfile);
205 	switch (token) {
206 	case TOK_ETHERNET:
207 		hardware->htype = HTYPE_ETHER;
208 		break;
209 	case TOK_TOKEN_RING:
210 		hardware->htype = HTYPE_IEEE802;
211 		break;
212 	case TOK_FDDI:
213 		hardware->htype = HTYPE_FDDI;
214 		break;
215 	case TOK_IPSEC_TUNNEL:
216 		hardware->htype = HTYPE_IPSEC_TUNNEL;
217 		break;
218 	default:
219 		parse_warn("expecting a network hardware type");
220 		skip_to_semi(cfile);
221 		return;
222 	}
223 
224 	/*
225 	 * Parse the hardware address information.   Technically, it
226 	 * would make a lot of sense to restrict the length of the data
227 	 * we'll accept here to the length of a particular hardware
228 	 * address type.   Unfortunately, there are some broken clients
229 	 * out there that put bogus data in the chaddr buffer, and we
230 	 * accept that data in the lease file rather than simply failing
231 	 * on such clients.   Yuck.
232 	 */
233 	hlen = 0;
234 	t = parse_numeric_aggregate(cfile, NULL, &hlen, ':', 16, 8);
235 	if (!t)
236 		return;
237 	if (hlen > sizeof(hardware->haddr)) {
238 		free(t);
239 		parse_warn("hardware address too long");
240 	} else {
241 		hardware->hlen = hlen;
242 		memcpy((unsigned char *)&hardware->haddr[0], t, hardware->hlen);
243 		if (hlen < sizeof(hardware->haddr))
244 			memset(&hardware->haddr[hlen], 0,
245 			    sizeof(hardware->haddr) - hlen);
246 		free(t);
247 	}
248 
249 	token = next_token(&val, cfile);
250 	if (token != ';') {
251 		parse_warn("expecting semicolon.");
252 		skip_to_semi(cfile);
253 	}
254 }
255 
256 /*
257  * lease-time :== NUMBER SEMI
258  */
259 void
260 parse_lease_time(FILE *cfile, time_t *timep)
261 {
262 	char *val;
263 	int token;
264 
265 	token = next_token(&val, cfile);
266 	if (token != TOK_NUMBER) {
267 		parse_warn("Expecting numeric lease time");
268 		skip_to_semi(cfile);
269 		return;
270 	}
271 	convert_num((unsigned char *)timep, val, 10, 32);
272 	/* Unswap the number - convert_num returns stuff in NBO. */
273 	*timep = ntohl(*timep);	/* XXX */
274 
275 	parse_semi(cfile);
276 }
277 
278 /*
279  * No BNF for numeric aggregates - that's defined by the caller.  What
280  * this function does is to parse a sequence of numbers separated by the
281  * token specified in separator.  If max is zero, any number of numbers
282  * will be parsed; otherwise, exactly max numbers are expected.  Base
283  * and size tell us how to internalize the numbers once they've been
284  * tokenized.
285  */
286 unsigned char *
287 parse_numeric_aggregate(FILE *cfile, unsigned char *buf, int *max,
288     int separator, int base, int size)
289 {
290 	char *val, *t;
291 	int token, count = 0;
292 	unsigned char *bufp = buf, *s = NULL;
293 	pair c = NULL;
294 
295 	if (!bufp && *max) {
296 		bufp = malloc(*max * size / 8);
297 		if (!bufp)
298 			error("can't allocate space for numeric aggregate");
299 	} else
300 		s = bufp;
301 
302 	do {
303 		if (count) {
304 			token = peek_token(&val, cfile);
305 			if (token != separator) {
306 				if (!*max)
307 					break;
308 				if (token != '{' && token != '}')
309 					token = next_token(&val, cfile);
310 				parse_warn("too few numbers.");
311 				if (token != ';')
312 					skip_to_semi(cfile);
313 				return (NULL);
314 			}
315 			token = next_token(&val, cfile);
316 		}
317 		token = next_token(&val, cfile);
318 
319 		if (token == EOF) {
320 			parse_warn("unexpected end of file");
321 			break;
322 		}
323 		/* Allow NUMBER_OR_NAME if base is 16. */
324 		if (token != TOK_NUMBER &&
325 		    (base != 16 || token != TOK_NUMBER_OR_NAME)) {
326 			parse_warn("expecting numeric value.");
327 			skip_to_semi(cfile);
328 			return (NULL);
329 		}
330 		/*
331 		 * If we can, convert the number now; otherwise, build a
332 		 * linked list of all the numbers.
333 		 */
334 		if (s) {
335 			convert_num(s, val, base, size);
336 			s += size / 8;
337 		} else {
338 			t = malloc(strlen(val) + 1);
339 			if (!t)
340 				error("no temp space for number.");
341 			strlcpy(t, val, strlen(val) + 1);
342 			c = cons(t, c);
343 		}
344 	} while (++count != *max);
345 
346 	/* If we had to cons up a list, convert it now. */
347 	if (c) {
348 		bufp = malloc(count * size / 8);
349 		if (!bufp)
350 			error("can't allocate space for numeric aggregate.");
351 		s = bufp + count - size / 8;
352 		*max = count;
353 	}
354 	while (c) {
355 		pair		cdr = c->cdr;
356 		convert_num(s, (char *)c->car, base, size);
357 		s -= size / 8;
358 		/* Free up temp space. */
359 		free(c->car);
360 		free(c);
361 		c = cdr;
362 	}
363 	return (bufp);
364 }
365 
366 void
367 convert_num(unsigned char *buf, char *str, int base, int size)
368 {
369 	int negative = 0, tval, max;
370 	u_int32_t val = 0;
371 	char *ptr = str;
372 
373 	if (*ptr == '-') {
374 		negative = 1;
375 		ptr++;
376 	}
377 
378 	/* If base wasn't specified, figure it out from the data. */
379 	if (!base) {
380 		if (ptr[0] == '0') {
381 			if (ptr[1] == 'x') {
382 				base = 16;
383 				ptr += 2;
384 			} else if (isascii(ptr[1]) && isdigit(ptr[1])) {
385 				base = 8;
386 				ptr += 1;
387 			} else
388 				base = 10;
389 		} else
390 			base = 10;
391 	}
392 
393 	do {
394 		tval = *ptr++;
395 		/* XXX assumes ASCII... */
396 		if (tval >= 'a')
397 			tval = tval - 'a' + 10;
398 		else if (tval >= 'A')
399 			tval = tval - 'A' + 10;
400 		else if (tval >= '0')
401 			tval -= '0';
402 		else {
403 			warning("Bogus number: %s.", str);
404 			break;
405 		}
406 		if (tval >= base) {
407 			warning("Bogus number: %s: digit %d not in base %d",
408 			    str, tval, base);
409 			break;
410 		}
411 		val = val * base + tval;
412 	} while (*ptr);
413 
414 	if (negative)
415 		max = (1 << (size - 1));
416 	else
417 		max = (1 << (size - 1)) + ((1 << (size - 1)) - 1);
418 	if (val > max) {
419 		switch (base) {
420 		case 8:
421 			warning("value %s%o exceeds max (%d) for precision.",
422 			    negative ? "-" : "", val, max);
423 			break;
424 		case 16:
425 			warning("value %s%x exceeds max (%d) for precision.",
426 			    negative ? "-" : "", val, max);
427 			break;
428 		default:
429 			warning("value %s%u exceeds max (%d) for precision.",
430 			    negative ? "-" : "", val, max);
431 			break;
432 		}
433 	}
434 
435 	if (negative) {
436 		switch (size) {
437 		case 8:
438 			*buf = -(unsigned long)val;
439 			break;
440 		case 16:
441 			putShort(buf, -(unsigned long)val);
442 			break;
443 		case 32:
444 			putLong(buf, -(unsigned long)val);
445 			break;
446 		default:
447 			warning("Unexpected integer size: %d", size);
448 			break;
449 		}
450 	} else {
451 		switch (size) {
452 		case 8:
453 			*buf = (u_int8_t)val;
454 			break;
455 		case 16:
456 			putUShort(buf, (u_int16_t)val);
457 			break;
458 		case 32:
459 			putULong(buf, val);
460 			break;
461 		default:
462 			warning("Unexpected integer size: %d", size);
463 			break;
464 		}
465 	}
466 }
467 
468 /*
469  * date :== NUMBER NUMBER SLASH NUMBER SLASH NUMBER
470  *		NUMBER COLON NUMBER COLON NUMBER SEMI
471  *
472  * Dates are always in GMT; first number is day of week; next is
473  * year/month/day; next is hours:minutes:seconds on a 24-hour
474  * clock.
475  */
476 time_t
477 parse_date(FILE *cfile)
478 {
479 	static int months[11] = { 31, 59, 90, 120, 151, 181,
480 	    212, 243, 273, 304, 334 };
481 	int guess, token;
482 	struct tm tm;
483 	char *val;
484 
485 	/* Day of week... */
486 	token = next_token(&val, cfile);
487 	if (token != TOK_NUMBER) {
488 		parse_warn("numeric day of week expected.");
489 		if (token != ';')
490 			skip_to_semi(cfile);
491 		return (0);
492 	}
493 	tm.tm_wday = atoi(val);
494 
495 	/* Year... */
496 	token = next_token(&val, cfile);
497 	if (token != TOK_NUMBER) {
498 		parse_warn("numeric year expected.");
499 		if (token != ';')
500 			skip_to_semi(cfile);
501 		return (0);
502 	}
503 	tm.tm_year = atoi(val);
504 	if (tm.tm_year > 1900)
505 		tm.tm_year -= 1900;
506 
507 	/* Slash separating year from month... */
508 	token = next_token(&val, cfile);
509 	if (token != '/') {
510 		parse_warn("expected slash separating year from month.");
511 		if (token != ';')
512 			skip_to_semi(cfile);
513 		return (0);
514 	}
515 
516 	/* Month... */
517 	token = next_token(&val, cfile);
518 	if (token != TOK_NUMBER) {
519 		parse_warn("numeric month expected.");
520 		if (token != ';')
521 			skip_to_semi(cfile);
522 		return (0);
523 	}
524 	tm.tm_mon = atoi(val) - 1;
525 
526 	/* Slash separating month from day... */
527 	token = next_token(&val, cfile);
528 	if (token != '/') {
529 		parse_warn("expected slash separating month from day.");
530 		if (token != ';')
531 			skip_to_semi(cfile);
532 		return (0);
533 	}
534 
535 	/* Day... */
536 	token = next_token(&val, cfile);
537 	if (token != TOK_NUMBER) {
538 		parse_warn("numeric day of month expected.");
539 		if (token != ';')
540 			skip_to_semi(cfile);
541 		return (0);
542 	}
543 	tm.tm_mday = atoi(val);
544 
545 	/* Hour... */
546 	token = next_token(&val, cfile);
547 	if (token != TOK_NUMBER) {
548 		parse_warn("numeric hour expected.");
549 		if (token != ';')
550 			skip_to_semi(cfile);
551 		return (0);
552 	}
553 	tm.tm_hour = atoi(val);
554 
555 	/* Colon separating hour from minute... */
556 	token = next_token(&val, cfile);
557 	if (token != ':') {
558 		parse_warn("expected colon separating hour from minute.");
559 		if (token != ';')
560 			skip_to_semi(cfile);
561 		return (0);
562 	}
563 
564 	/* Minute... */
565 	token = next_token(&val, cfile);
566 	if (token != TOK_NUMBER) {
567 		parse_warn("numeric minute expected.");
568 		if (token != ';')
569 			skip_to_semi(cfile);
570 		return (0);
571 	}
572 	tm.tm_min = atoi(val);
573 
574 	/* Colon separating minute from second... */
575 	token = next_token(&val, cfile);
576 	if (token != ':') {
577 		parse_warn("expected colon separating minute from second.");
578 		if (token != ';')
579 			skip_to_semi(cfile);
580 		return (0);
581 	}
582 
583 	/* Second... */
584 	token = next_token(&val, cfile);
585 	if (token != TOK_NUMBER) {
586 		parse_warn("numeric second expected.");
587 		if (token != ';')
588 			skip_to_semi(cfile);
589 		return (0);
590 	}
591 	tm.tm_sec = atoi(val);
592 	tm.tm_isdst = 0;
593 
594 	/* XXX: We assume that mktime does not use tm_yday. */
595 	tm.tm_yday = 0;
596 
597 	/* Make sure the date ends in a semicolon... */
598 	token = next_token(&val, cfile);
599 	if (token != ';') {
600 		parse_warn("semicolon expected.");
601 		skip_to_semi(cfile);
602 		return (0);
603 	}
604 
605 	/* Guess the time value... */
606 	guess = ((((((365 * (tm.tm_year - 70) +	/* Days in years since '70 */
607 	    (tm.tm_year - 69) / 4 +	/* Leap days since '70 */
608 	    (tm.tm_mon			/* Days in months this year */
609 	    ? months[tm.tm_mon - 1] : 0) +
610 	    (tm.tm_mon > 1 &&		/* Leap day this year */
611 	    !((tm.tm_year - 72) & 3)) +
612 	    tm.tm_mday - 1) * 24) +	/* Day of month */
613 	    tm.tm_hour) * 60) + tm.tm_min) * 60) + tm.tm_sec;
614 
615 	/*
616 	 * This guess could be wrong because of leap seconds or other
617 	 * weirdness we don't know about that the system does.   For
618 	 * now, we're just going to accept the guess, but at some point
619 	 * it might be nice to do a successive approximation here to get
620 	 * an exact value.   Even if the error is small, if the server
621 	 * is restarted frequently (and thus the lease database is
622 	 * reread), the error could accumulate into something
623 	 * significant.
624 	 */
625 	return (guess);
626 }
627