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