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