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