3 Common parser code for dhcpd and dhclient. */
6 * Copyright (c) 1995, 1996, 1997, 1998 The Internet Software Consortium.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
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.
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
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''.
44 static char copyright[] =
45 "$Id: parse.c,v 1.2.2.4 1999/03/29 22:18:53 mellon Exp $ Copyright (c) 1995, 1996, 1997, 1998 The Internet Software Consortium. All rights reserved.\n";
51 /* Skip to the semicolon ending the current statement. If we encounter
52 braces, the matching closing brace terminates the statement. If we
53 encounter a right brace but haven't encountered a left brace, return
54 leaving the brace in the token buffer for the caller. If we see a
55 semicolon and haven't seen a left brace, return. This lets us skip
60 statement foo bar { statement { } }
65 void skip_to_semi (cfile)
73 token = peek_token (&val, cfile);
74 if (token == RBRACE) {
76 token = next_token (&val, cfile);
81 } else if (token == LBRACE) {
83 } else if (token == SEMI && !brace_count) {
84 token = next_token (&val, cfile);
86 } else if (token == EOL) {
87 /* EOL only happens when parsing /etc/resolv.conf,
88 and we treat it like a semicolon because the
89 resolv.conf file is line-oriented. */
90 token = next_token (&val, cfile);
93 token = next_token (&val, cfile);
94 } while (token != EOF);
97 int parse_semi (cfile)
103 token = next_token (&val, cfile);
105 parse_warn ("semicolon expected.");
106 skip_to_semi (cfile);
112 /* string-parameter :== STRING SEMI */
114 char *parse_string (cfile)
121 token = next_token (&val, cfile);
122 if (token != STRING) {
123 parse_warn ("filename must be a string");
124 skip_to_semi (cfile);
127 s = (char *)malloc (strlen (val) + 1);
129 error ("no memory for string %s.", val);
132 if (!parse_semi (cfile))
137 /* hostname :== identifier | hostname DOT identifier */
139 char *parse_host_name (cfile)
149 /* Read a dotted hostname... */
151 /* Read a token, which should be an identifier. */
152 token = next_token (&val, cfile);
153 if (!is_identifier (token) && token != NUMBER) {
154 parse_warn ("expecting an identifier in hostname");
155 skip_to_semi (cfile);
158 /* Store this identifier... */
159 if (!(s = (char *)malloc (strlen (val) + 1)))
160 error ("can't allocate temp space for hostname.");
162 c = cons ((caddr_t)s, c);
163 len += strlen (s) + 1;
164 /* Look for a dot; if it's there, keep going, otherwise
166 token = peek_token (&val, cfile);
168 token = next_token (&val, cfile);
169 } while (token == DOT);
171 /* Assemble the hostname together into a string. */
172 if (!(s = (char *)malloc (len)))
173 error ("can't allocate space for hostname.");
178 int l = strlen ((char *)(c -> car));
180 memcpy (t, (char *)(c -> car), l);
181 /* Free up temp space. */
191 int parse_ip_addr (cfile, addr)
196 if (parse_numeric_aggregate (cfile, addr -> iabuf,
197 &addr -> len, DOT, 10, 8))
202 /* hardware-parameter :== HARDWARE ETHERNET csns SEMI
203 csns :== NUMBER | csns COLON NUMBER */
205 void parse_hardware_param (cfile, hardware)
207 struct hardware *hardware;
214 token = next_token (&val, cfile);
217 hardware -> htype = HTYPE_ETHER;
220 hardware -> htype = HTYPE_IEEE802;
223 hardware -> htype = HTYPE_FDDI;
226 parse_warn ("expecting a network hardware type");
227 skip_to_semi (cfile);
231 /* Parse the hardware address information. Technically,
232 it would make a lot of sense to restrict the length of the
233 data we'll accept here to the length of a particular hardware
234 address type. Unfortunately, there are some broken clients
235 out there that put bogus data in the chaddr buffer, and we accept
236 that data in the lease file rather than simply failing on such
239 t = parse_numeric_aggregate (cfile, (unsigned char *)0, &hlen,
243 if (hlen > sizeof hardware -> haddr) {
245 parse_warn ("hardware address too long");
247 hardware -> hlen = hlen;
248 memcpy ((unsigned char *)&hardware -> haddr [0],
249 t, hardware -> hlen);
250 if (hlen < sizeof hardware -> haddr)
251 memset (&hardware -> haddr [hlen], 0,
252 (sizeof hardware -> haddr) - hlen);
256 token = next_token (&val, cfile);
258 parse_warn ("expecting semicolon.");
259 skip_to_semi (cfile);
263 /* lease-time :== NUMBER SEMI */
265 void parse_lease_time (cfile, timep)
272 token = next_token (&val, cfile);
273 if (token != NUMBER) {
274 parse_warn ("Expecting numeric lease time");
275 skip_to_semi (cfile);
278 convert_num ((unsigned char *)timep, val, 10, 32);
279 /* Unswap the number - convert_num returns stuff in NBO. */
280 *timep = ntohl (*timep); /* XXX */
285 /* No BNF for numeric aggregates - that's defined by the caller. What
286 this function does is to parse a sequence of numbers seperated by
287 the token specified in seperator. If max is zero, any number of
288 numbers will be parsed; otherwise, exactly max numbers are
289 expected. Base and size tell us how to internalize the numbers
290 once they've been tokenized. */
292 unsigned char *parse_numeric_aggregate (cfile, buf,
293 max, seperator, base, size)
303 unsigned char *bufp = buf, *s;
309 bufp = (unsigned char *)malloc (*max * size / 8);
311 error ("can't allocate space for numeric aggregate");
317 token = peek_token (&val, cfile);
318 if (token != seperator) {
321 if (token != RBRACE && token != LBRACE)
322 token = next_token (&val, cfile);
323 parse_warn ("too few numbers.");
325 skip_to_semi (cfile);
326 return (unsigned char *)0;
328 token = next_token (&val, cfile);
330 token = next_token (&val, cfile);
333 parse_warn ("unexpected end of file");
337 /* Allow NUMBER_OR_NAME if base is 16. */
338 if (token != NUMBER &&
339 (base != 16 || token != NUMBER_OR_NAME)) {
340 parse_warn ("expecting numeric value.");
341 skip_to_semi (cfile);
342 return (unsigned char *)0;
344 /* If we can, convert the number now; otherwise, build
345 a linked list of all the numbers. */
347 convert_num (s, val, base, size);
350 t = (char *)malloc (strlen (val) + 1);
352 error ("no temp space for number.");
356 } while (++count != *max);
358 /* If we had to cons up a list, convert it now. */
360 bufp = (unsigned char *)malloc (count * size / 8);
362 error ("can't allocate space for numeric aggregate.");
363 s = bufp + count - size / 8;
368 convert_num (s, (char *)(c -> car), base, size);
370 /* Free up temp space. */
378 void convert_num (buf, str, base, size)
395 /* If base wasn't specified, figure it out from the data. */
397 if (ptr [0] == '0') {
398 if (ptr [1] == 'x') {
401 } else if (isascii (ptr [1]) && isdigit (ptr [1])) {
414 /* XXX assumes ASCII... */
416 tval = tval - 'a' + 10;
417 else if (tval >= 'A')
418 tval = tval - 'A' + 10;
419 else if (tval >= '0')
422 warn ("Bogus number: %s.", str);
426 warn ("Bogus number: %s: digit %d not in base %d\n",
430 val = val * base + tval;
434 max = (1 << (size - 1));
436 max = (1 << (size - 1)) + ((1 << (size - 1)) - 1);
440 warn ("value %s%o exceeds max (%d) for precision.",
441 negative ? "-" : "", val, max);
444 warn ("value %s%x exceeds max (%d) for precision.",
445 negative ? "-" : "", val, max);
448 warn ("value %s%u exceeds max (%d) for precision.",
449 negative ? "-" : "", val, max);
457 *buf = -(unsigned long)val;
460 putShort (buf, -(unsigned long)val);
463 putLong (buf, -(unsigned long)val);
466 warn ("Unexpected integer size: %d\n", size);
472 *buf = (u_int8_t)val;
475 putUShort (buf, (u_int16_t)val);
481 warn ("Unexpected integer size: %d\n", size);
487 /* date :== NUMBER NUMBER SLASH NUMBER SLASH NUMBER
488 NUMBER COLON NUMBER COLON NUMBER SEMI
490 Dates are always in GMT; first number is day of week; next is
491 year/month/day; next is hours:minutes:seconds on a 24-hour
494 TIME parse_date (cfile)
501 static int months [11] = { 31, 59, 90, 120, 151, 181,
502 212, 243, 273, 304, 334 };
505 token = next_token (&val, cfile);
506 if (token != NUMBER) {
507 parse_warn ("numeric day of week expected.");
509 skip_to_semi (cfile);
512 tm.tm_wday = atoi (val);
515 token = next_token (&val, cfile);
516 if (token != NUMBER) {
517 parse_warn ("numeric year expected.");
519 skip_to_semi (cfile);
522 tm.tm_year = atoi (val);
523 if (tm.tm_year > 1900)
526 /* Slash seperating year from month... */
527 token = next_token (&val, cfile);
528 if (token != SLASH) {
529 parse_warn ("expected slash seperating year from month.");
531 skip_to_semi (cfile);
536 token = next_token (&val, cfile);
537 if (token != NUMBER) {
538 parse_warn ("numeric month expected.");
540 skip_to_semi (cfile);
543 tm.tm_mon = atoi (val) - 1;
545 /* Slash seperating month from day... */
546 token = next_token (&val, cfile);
547 if (token != SLASH) {
548 parse_warn ("expected slash seperating month from day.");
550 skip_to_semi (cfile);
555 token = next_token (&val, cfile);
556 if (token != NUMBER) {
557 parse_warn ("numeric day of month expected.");
559 skip_to_semi (cfile);
562 tm.tm_mday = atoi (val);
565 token = next_token (&val, cfile);
566 if (token != NUMBER) {
567 parse_warn ("numeric hour expected.");
569 skip_to_semi (cfile);
572 tm.tm_hour = atoi (val);
574 /* Colon seperating hour from minute... */
575 token = next_token (&val, cfile);
576 if (token != COLON) {
577 parse_warn ("expected colon seperating hour from minute.");
579 skip_to_semi (cfile);
584 token = next_token (&val, cfile);
585 if (token != NUMBER) {
586 parse_warn ("numeric minute expected.");
588 skip_to_semi (cfile);
591 tm.tm_min = atoi (val);
593 /* Colon seperating minute from second... */
594 token = next_token (&val, cfile);
595 if (token != COLON) {
596 parse_warn ("expected colon seperating hour from minute.");
598 skip_to_semi (cfile);
603 token = next_token (&val, cfile);
604 if (token != NUMBER) {
605 parse_warn ("numeric minute expected.");
607 skip_to_semi (cfile);
610 tm.tm_sec = atoi (val);
613 /* XXX */ /* We assume that mktime does not use tm_yday. */
616 /* Make sure the date ends in a semicolon... */
617 token = next_token (&val, cfile);
619 parse_warn ("semicolon expected.");
620 skip_to_semi (cfile);
624 /* Guess the time value... */
625 guess = ((((((365 * (tm.tm_year - 70) + /* Days in years since '70 */
626 (tm.tm_year - 69) / 4 + /* Leap days since '70 */
627 (tm.tm_mon /* Days in months this year */
628 ? months [tm.tm_mon - 1]
630 (tm.tm_mon > 1 && /* Leap day this year */
631 !((tm.tm_year - 72) & 3)) +
632 tm.tm_mday - 1) * 24) + /* Day of month */
634 tm.tm_min) * 60) + tm.tm_sec;
636 /* This guess could be wrong because of leap seconds or other
637 weirdness we don't know about that the system does. For
638 now, we're just going to accept the guess, but at some point
639 it might be nice to do a successive approximation here to
640 get an exact value. Even if the error is small, if the
641 server is restarted frequently (and thus the lease database
642 is reread), the error could accumulate into something