1 /*	$OpenBSD: parse.c,v 1.18 2007/01/08 13:34:38 krw 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 __RCSID("$MirOS: src/sbin/dhclient/parse.c,v 1.5 2007/02/17 03:23:43 tg Exp $");
47 
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
skip_to_semi(FILE * cfile)64 skip_to_semi(FILE *cfile)
65 {
66 	int		 token;
67 	char		*val;
68 	int		 brace_count = 0;
69 
70 	do {
71 		token = peek_token(&val, cfile);
72 		if (token == '}') {
73 			if (brace_count) {
74 				token = next_token(&val, cfile);
75 				if (!--brace_count)
76 					return;
77 			} else
78 				return;
79 		} else if (token == '{') {
80 			brace_count++;
81 		} else if (token == ';' && !brace_count) {
82 			token = next_token(&val, cfile);
83 			return;
84 		} else if (token == '\n') {
85 			/*
86 			 * EOL only happens when parsing
87 			 * /etc/resolv.conf, and we treat it like a
88 			 * semicolon because the resolv.conf file is
89 			 * line-oriented.
90 			 */
91 			token = next_token(&val, cfile);
92 			return;
93 		}
94 		token = next_token(&val, cfile);
95 	} while (token != EOF);
96 }
97 
98 int
parse_semi(FILE * cfile)99 parse_semi(FILE *cfile)
100 {
101 	int token;
102 	char *val;
103 
104 	token = next_token(&val, cfile);
105 	if (token != ';') {
106 		parse_warn("semicolon expected.");
107 		skip_to_semi(cfile);
108 		return (0);
109 	}
110 	return (1);
111 }
112 
113 /*
114  * string-parameter :== STRING SEMI
115  */
116 char *
parse_string(FILE * cfile)117 parse_string(FILE *cfile)
118 {
119 	char *val, *s;
120 	int token;
121 
122 	token = next_token(&val, cfile);
123 	if (token != TOK_STRING) {
124 		parse_warn("filename must be a string");
125 		skip_to_semi(cfile);
126 		return (NULL);
127 	}
128 	s = malloc(strlen(val) + 1);
129 	if (!s)
130 		error("no memory for string %s.", val);
131 	strlcpy(s, val, strlen(val) + 1);
132 
133 	if (!parse_semi(cfile)) {
134 		free(s);
135 		return (NULL);
136 	}
137 	return (s);
138 }
139 
140 int
parse_ip_addr(FILE * cfile,struct iaddr * addr)141 parse_ip_addr(FILE *cfile, struct iaddr *addr)
142 {
143 	addr->len = 4;
144 	return (parse_numeric_aggregate(cfile, addr->iabuf, addr->len, '.',
145 	    10));
146 }
147 
148 /*
149  * hardware-parameter :== HARDWARE ETHERNET csns SEMI
150  * csns :== NUMBER | csns COLON NUMBER
151  */
152 void
parse_hardware_param(FILE * cfile,struct hardware * hardware)153 parse_hardware_param(FILE *cfile, struct hardware *hardware)
154 {
155 	int token;
156 	char *val;
157 
158 	token = next_token(&val, cfile);
159 	switch (token) {
160 	case TOK_ETHERNET:
161 		hardware->htype = HTYPE_ETHER;
162 		hardware->hlen = 6;
163 		break;
164 	case TOK_TOKEN_RING:
165 		hardware->htype = HTYPE_IEEE802;
166 		hardware->hlen = 6;
167 		break;
168 	case TOK_FDDI:
169 		hardware->htype = HTYPE_FDDI;
170 		hardware->hlen = 6;
171 		break;
172 	default:
173 		parse_warn("expecting a network hardware type");
174 		skip_to_semi(cfile);
175 		return;
176 	}
177 
178 	if (parse_numeric_aggregate(cfile, hardware->haddr, hardware->hlen,
179 	    ':', 16) == 0)
180 		return;
181 
182 	token = next_token(&val, cfile);
183 	if (token != ';') {
184 		parse_warn("expecting semicolon.");
185 		skip_to_semi(cfile);
186 	}
187 }
188 
189 /*
190  * lease-time :== NUMBER SEMI
191  */
192 void
parse_lease_time(FILE * cfile,time_t * timep)193 parse_lease_time(FILE *cfile, time_t *timep)
194 {
195 	char *val;
196 	int token;
197 	uint32_t tmp = 0;
198 
199 	token = next_token(&val, cfile);
200 	if (token != TOK_NUMBER) {
201 		parse_warn("Expecting numeric lease time");
202 		skip_to_semi(cfile);
203 		return;
204 	}
205 	convert_num((unsigned char *)&tmp, val, 10, 32);
206 	/* Unswap the number - convert_num returns stuff in NBO. */
207 	*timep = ntohl(tmp);
208 
209 	parse_semi(cfile);
210 }
211 
212 /*
213  * Parse a sequence of numbers separated by the token specified in separator.
214  * Exactly max numbers are expected.
215  */
216 int
parse_numeric_aggregate(FILE * cfile,unsigned char * buf,int max,int separator,int base)217 parse_numeric_aggregate(FILE *cfile, unsigned char *buf, int max, int separator,
218     int base)
219 {
220 	char *val;
221 	int token, count;
222 
223 	if (buf == NULL || max == 0)
224 		error("no space for numeric aggregate");
225 
226 	for (count = 0; count < max; count++, buf++) {
227 		if (count && (peek_token(&val, cfile) == separator))
228 			token = next_token(&val, cfile);
229 
230 		token = next_token(&val, cfile);
231 
232 		if (token == TOK_NUMBER || (base == 16 && token == TOK_NUMBER_OR_NAME))
233 			/* XXX Need to check if conversion was successful. */
234 			convert_num(buf, val, base, 8);
235 		else
236 			break;
237 	}
238 
239 	if (count < max) {
240 		parse_warn("numeric aggregate too short.");
241 		return (0);
242 	}
243 
244 	return (1);
245 }
246 
247 void
convert_num(unsigned char * buf,char * str,int base,int size)248 convert_num(unsigned char *buf, char *str, int base, int size)
249 {
250 	int negative = 0, tval, max;
251 	u_int32_t val = 0;
252 	char *ptr = str;
253 
254 	if (*ptr == '-') {
255 		negative = 1;
256 		ptr++;
257 	}
258 
259 	/* If base wasn't specified, figure it out from the data. */
260 	if (!base) {
261 		if (ptr[0] == '0') {
262 			if (ptr[1] == 'x') {
263 				base = 16;
264 				ptr += 2;
265 			} else if (isascii(ptr[1]) && isdigit(ptr[1])) {
266 				base = 8;
267 				ptr += 1;
268 			} else
269 				base = 10;
270 		} else
271 			base = 10;
272 	}
273 
274 	do {
275 		tval = *ptr++;
276 		/* XXX assumes ASCII... */
277 		if (tval >= 'a')
278 			tval = tval - 'a' + 10;
279 		else if (tval >= 'A')
280 			tval = tval - 'A' + 10;
281 		else if (tval >= '0')
282 			tval -= '0';
283 		else {
284 			warning("Bogus number: %s.", str);
285 			break;
286 		}
287 		if (tval >= base) {
288 			warning("Bogus number: %s: digit %d not in base %d",
289 			    str, tval, base);
290 			break;
291 		}
292 		val = val * base + tval;
293 	} while (*ptr);
294 
295 	if (negative)
296 		max = (1 << (size - 1));
297 	else
298 		max = (1 << (size - 1)) + ((1 << (size - 1)) - 1);
299 	if (val > max) {
300 		switch (base) {
301 		case 8:
302 			warning("value %s%o exceeds max (%d) for precision.",
303 			    negative ? "-" : "", val, max);
304 			break;
305 		case 16:
306 			warning("value %s%x exceeds max (%d) for precision.",
307 			    negative ? "-" : "", val, max);
308 			break;
309 		default:
310 			warning("value %s%u exceeds max (%d) for precision.",
311 			    negative ? "-" : "", val, max);
312 			break;
313 		}
314 	}
315 
316 	if (negative)
317 		switch (size) {
318 		case 8:
319 			*buf = -(unsigned long)val;
320 			break;
321 		case 16:
322 			putShort(buf, -(unsigned long)val);
323 			break;
324 		case 32:
325 			putLong(buf, -(unsigned long)val);
326 			break;
327 		default:
328 			warning("Unexpected integer size: %d", size);
329 			break;
330 		}
331 	else
332 		switch (size) {
333 		case 8:
334 			*buf = (u_int8_t)val;
335 			break;
336 		case 16:
337 			putUShort(buf, (u_int16_t)val);
338 			break;
339 		case 32:
340 			putULong(buf, val);
341 			break;
342 		default:
343 			warning("Unexpected integer size: %d", size);
344 			break;
345 		}
346 }
347 
348 /*
349  * date :== NUMBER NUMBER SLASH NUMBER SLASH NUMBER
350  *		NUMBER COLON NUMBER COLON NUMBER SEMI
351  *
352  * Dates are always in GMT; first number is day of week; next is
353  * year/month/day; next is hours:minutes:seconds on a 24-hour
354  * clock.
355  */
356 time_t
parse_date(FILE * cfile)357 parse_date(FILE *cfile)
358 {
359 	static int months[11] = { 31, 59, 90, 120, 151, 181,
360 	    212, 243, 273, 304, 334 };
361 	int guess, token;
362 	struct tm tm;
363 	char *val;
364 
365 	/* Day of week... */
366 	token = next_token(&val, cfile);
367 	if (token != TOK_NUMBER) {
368 		parse_warn("numeric day of week expected.");
369 		if (token != ';')
370 			skip_to_semi(cfile);
371 		return (0);
372 	}
373 	tm.tm_wday = atoi(val);
374 
375 	/* Year... */
376 	token = next_token(&val, cfile);
377 	if (token != TOK_NUMBER) {
378 		parse_warn("numeric year expected.");
379 		if (token != ';')
380 			skip_to_semi(cfile);
381 		return (0);
382 	}
383 	tm.tm_year = atoi(val);
384 	if (tm.tm_year > 1900)
385 		tm.tm_year -= 1900;
386 
387 	/* Slash separating year from month... */
388 	token = next_token(&val, cfile);
389 	if (token != '/') {
390 		parse_warn("expected slash separating year from month.");
391 		if (token != ';')
392 			skip_to_semi(cfile);
393 		return (0);
394 	}
395 
396 	/* Month... */
397 	token = next_token(&val, cfile);
398 	if (token != TOK_NUMBER) {
399 		parse_warn("numeric month expected.");
400 		if (token != ';')
401 			skip_to_semi(cfile);
402 		return (0);
403 	}
404 	tm.tm_mon = atoi(val) - 1;
405 
406 	/* Slash separating month from day... */
407 	token = next_token(&val, cfile);
408 	if (token != '/') {
409 		parse_warn("expected slash separating month from day.");
410 		if (token != ';')
411 			skip_to_semi(cfile);
412 		return (0);
413 	}
414 
415 	/* Day... */
416 	token = next_token(&val, cfile);
417 	if (token != TOK_NUMBER) {
418 		parse_warn("numeric day of month expected.");
419 		if (token != ';')
420 			skip_to_semi(cfile);
421 		return (0);
422 	}
423 	tm.tm_mday = atoi(val);
424 
425 	/* Hour... */
426 	token = next_token(&val, cfile);
427 	if (token != TOK_NUMBER) {
428 		parse_warn("numeric hour expected.");
429 		if (token != ';')
430 			skip_to_semi(cfile);
431 		return (0);
432 	}
433 	tm.tm_hour = atoi(val);
434 
435 	/* Colon separating hour from minute... */
436 	token = next_token(&val, cfile);
437 	if (token != ':') {
438 		parse_warn("expected colon separating hour from minute.");
439 		if (token != ';')
440 			skip_to_semi(cfile);
441 		return (0);
442 	}
443 
444 	/* Minute... */
445 	token = next_token(&val, cfile);
446 	if (token != TOK_NUMBER) {
447 		parse_warn("numeric minute expected.");
448 		if (token != ';')
449 			skip_to_semi(cfile);
450 		return (0);
451 	}
452 	tm.tm_min = atoi(val);
453 
454 	/* Colon separating minute from second... */
455 	token = next_token(&val, cfile);
456 	if (token != ':') {
457 		parse_warn("expected colon separating minute from second.");
458 		if (token != ';')
459 			skip_to_semi(cfile);
460 		return (0);
461 	}
462 
463 	/* Second... */
464 	token = next_token(&val, cfile);
465 	if (token != TOK_NUMBER) {
466 		parse_warn("numeric second expected.");
467 		if (token != ';')
468 			skip_to_semi(cfile);
469 		return (0);
470 	}
471 	tm.tm_sec = atoi(val);
472 	tm.tm_isdst = 0;
473 
474 	/* XXX: We assume that mktime does not use tm_yday. */
475 	tm.tm_yday = 0;
476 
477 	/* Make sure the date ends in a semicolon... */
478 	token = next_token(&val, cfile);
479 	if (token != ';') {
480 		parse_warn("semicolon expected.");
481 		skip_to_semi(cfile);
482 		return (0);
483 	}
484 
485 	/* Guess the time value... */
486 	guess = ((((((365 * (tm.tm_year - 70) +	/* Days in years since '70 */
487 	    (tm.tm_year - 69) / 4 +	/* Leap days since '70 */
488 	    (tm.tm_mon			/* Days in months this year */
489 	    ? months[tm.tm_mon - 1] : 0) +
490 	    (tm.tm_mon > 1 &&		/* Leap day this year */
491 	    !((tm.tm_year - 72) & 3)) +
492 	    tm.tm_mday - 1) * 24) +	/* Day of month */
493 	    tm.tm_hour) * 60) + tm.tm_min) * 60) + tm.tm_sec;
494 
495 	/*
496 	 * This guess could be wrong because of leap seconds or other
497 	 * weirdness we don't know about that the system does.   For
498 	 * now, we're just going to accept the guess, but at some point
499 	 * it might be nice to do a successive approximation here to get
500 	 * an exact value.   Even if the error is small, if the server
501 	 * is restarted frequently (and thus the lease database is
502 	 * reread), the error could accumulate into something
503 	 * significant.
504 	 */
505 	return (guess);
506 }
507