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