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