]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/isc-dhcp/common/parse.c
This commit was generated by cvs2svn to compensate for changes in r53657,
[FreeBSD/FreeBSD.git] / contrib / isc-dhcp / common / parse.c
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 #ifndef lint
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";
46 #endif /* not lint */
47
48 #include "dhcpd.h"
49 #include "dhctoken.h"
50
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
56    over:
57
58         statement;
59         statement foo bar { }
60         statement foo bar { statement { } }
61         statement}
62  
63         ...et cetera. */
64
65 void skip_to_semi (cfile)
66         FILE *cfile;
67 {
68         int token;
69         char *val;
70         int brace_count = 0;
71
72         do {
73                 token = peek_token (&val, cfile);
74                 if (token == RBRACE) {
75                         if (brace_count) {
76                                 token = next_token (&val, cfile);
77                                 if (!--brace_count)
78                                         return;
79                         } else
80                                 return;
81                 } else if (token == LBRACE) {
82                         brace_count++;
83                 } else if (token == SEMI && !brace_count) {
84                         token = next_token (&val, cfile);
85                         return;
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);
91                         return;
92                 }
93                 token = next_token (&val, cfile);
94         } while (token != EOF);
95 }
96
97 int parse_semi (cfile)
98         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 /* string-parameter :== STRING SEMI */
113
114 char *parse_string (cfile)
115         FILE *cfile;
116 {
117         char *val;
118         int token;
119         char *s;
120
121         token = next_token (&val, cfile);
122         if (token != STRING) {
123                 parse_warn ("filename must be a string");
124                 skip_to_semi (cfile);
125                 return (char *)0;
126         }
127         s = (char *)malloc (strlen (val) + 1);
128         if (!s)
129                 error ("no memory for string %s.", val);
130         strcpy (s, val);
131
132         if (!parse_semi (cfile))
133                 return (char *)0;
134         return s;
135 }
136
137 /* hostname :== identifier | hostname DOT identifier */
138
139 char *parse_host_name (cfile)
140         FILE *cfile;
141 {
142         char *val;
143         int token;
144         int len = 0;
145         char *s;
146         char *t;
147         pair c = (pair)0;
148         
149         /* Read a dotted hostname... */
150         do {
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);
156                         return (char *)0;
157                 }
158                 /* Store this identifier... */
159                 if (!(s = (char *)malloc (strlen (val) + 1)))
160                         error ("can't allocate temp space for hostname.");
161                 strcpy (s, val);
162                 c = cons ((caddr_t)s, c);
163                 len += strlen (s) + 1;
164                 /* Look for a dot; if it's there, keep going, otherwise
165                    we're done. */
166                 token = peek_token (&val, cfile);
167                 if (token == DOT)
168                         token = next_token (&val, cfile);
169         } while (token == DOT);
170
171         /* Assemble the hostname together into a string. */
172         if (!(s = (char *)malloc (len)))
173                 error ("can't allocate space for hostname.");
174         t = s + len;
175         *--t = 0;
176         while (c) {
177                 pair cdr = c -> cdr;
178                 int l = strlen ((char *)(c -> car));
179                 t -= l;
180                 memcpy (t, (char *)(c -> car), l);
181                 /* Free up temp space. */
182                 free (c -> car);
183                 free (c);
184                 c = cdr;
185                 if (t != s)
186                         *--t = '.';
187         }
188         return s;
189 }
190
191 int parse_ip_addr (cfile, addr)
192         FILE *cfile;
193         struct iaddr *addr;
194 {
195         addr -> len = 4;
196         if (parse_numeric_aggregate (cfile, addr -> iabuf,
197                                      &addr -> len, DOT, 10, 8))
198                 return 1;
199         return 0;
200 }       
201
202 /* hardware-parameter :== HARDWARE ETHERNET csns SEMI
203    csns :== NUMBER | csns COLON NUMBER */
204
205 void parse_hardware_param (cfile, hardware)
206         FILE *cfile;
207         struct hardware *hardware;
208 {
209         char *val;
210         int token;
211         int hlen;
212         unsigned char *t;
213
214         token = next_token (&val, cfile);
215         switch (token) {
216               case ETHERNET:
217                 hardware -> htype = HTYPE_ETHER;
218                 break;
219               case TOKEN_RING:
220                 hardware -> htype = HTYPE_IEEE802;
221                 break;
222               case FDDI:
223                 hardware -> htype = HTYPE_FDDI;
224                 break;
225               default:
226                 parse_warn ("expecting a network hardware type");
227                 skip_to_semi (cfile);
228                 return;
229         }
230
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
237            clients.   Yuck. */
238         hlen = 0;
239         t = parse_numeric_aggregate (cfile, (unsigned char *)0, &hlen,
240                                      COLON, 16, 8);
241         if (!t)
242                 return;
243         if (hlen > sizeof hardware -> haddr) {
244                 free (t);
245                 parse_warn ("hardware address too long");
246         } else {
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);
253                 free (t);
254         }
255         
256         token = next_token (&val, cfile);
257         if (token != SEMI) {
258                 parse_warn ("expecting semicolon.");
259                 skip_to_semi (cfile);
260         }
261 }
262
263 /* lease-time :== NUMBER SEMI */
264
265 void parse_lease_time (cfile, timep)
266         FILE *cfile;
267         TIME *timep;
268 {
269         char *val;
270         int token;
271
272         token = next_token (&val, cfile);
273         if (token != NUMBER) {
274                 parse_warn ("Expecting numeric lease time");
275                 skip_to_semi (cfile);
276                 return;
277         }
278         convert_num ((unsigned char *)timep, val, 10, 32);
279         /* Unswap the number - convert_num returns stuff in NBO. */
280         *timep = ntohl (*timep); /* XXX */
281
282         parse_semi (cfile);
283 }
284
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. */
291
292 unsigned char *parse_numeric_aggregate (cfile, buf,
293                                         max, seperator, base, size)
294         FILE *cfile;
295         unsigned char *buf;
296         int *max;
297         int seperator;
298         int base;
299         int size;
300 {
301         char *val;
302         int token;
303         unsigned char *bufp = buf, *s;
304         char *t;
305         int count = 0;
306         pair c = (pair)0;
307
308         if (!bufp && *max) {
309                 bufp = (unsigned char *)malloc (*max * size / 8);
310                 if (!bufp)
311                         error ("can't allocate space for numeric aggregate");
312         } else
313                 s = bufp;
314
315         do {
316                 if (count) {
317                         token = peek_token (&val, cfile);
318                         if (token != seperator) {
319                                 if (!*max)
320                                         break;
321                                 if (token != RBRACE && token != LBRACE)
322                                         token = next_token (&val, cfile);
323                                 parse_warn ("too few numbers.");
324                                 if (token != SEMI)
325                                         skip_to_semi (cfile);
326                                 return (unsigned char *)0;
327                         }
328                         token = next_token (&val, cfile);
329                 }
330                 token = next_token (&val, cfile);
331
332                 if (token == EOF) {
333                         parse_warn ("unexpected end of file");
334                         break;
335                 }
336
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;
343                 }
344                 /* If we can, convert the number now; otherwise, build
345                    a linked list of all the numbers. */
346                 if (s) {
347                         convert_num (s, val, base, size);
348                         s += size / 8;
349                 } else {
350                         t = (char *)malloc (strlen (val) + 1);
351                         if (!t)
352                                 error ("no temp space for number.");
353                         strcpy (t, val);
354                         c = cons (t, c);
355                 }
356         } while (++count != *max);
357
358         /* If we had to cons up a list, convert it now. */
359         if (c) {
360                 bufp = (unsigned char *)malloc (count * size / 8);
361                 if (!bufp)
362                         error ("can't allocate space for numeric aggregate.");
363                 s = bufp + count - size / 8;
364                 *max = count;
365         }
366         while (c) {
367                 pair cdr = c -> cdr;
368                 convert_num (s, (char *)(c -> car), base, size);
369                 s -= size / 8;
370                 /* Free up temp space. */
371                 free (c -> car);
372                 free (c);
373                 c = cdr;
374         }
375         return bufp;
376 }
377
378 void convert_num (buf, str, base, size)
379         unsigned char *buf;
380         char *str;
381         int base;
382         int size;
383 {
384         char *ptr = str;
385         int negative = 0;
386         u_int32_t val = 0;
387         int tval;
388         int max;
389
390         if (*ptr == '-') {
391                 negative = 1;
392                 ++ptr;
393         }
394
395         /* If base wasn't specified, figure it out from the data. */
396         if (!base) {
397                 if (ptr [0] == '0') {
398                         if (ptr [1] == 'x') {
399                                 base = 16;
400                                 ptr += 2;
401                         } else if (isascii (ptr [1]) && isdigit (ptr [1])) {
402                                 base = 8;
403                                 ptr += 1;
404                         } else {
405                                 base = 10;
406                         }
407                 } else {
408                         base = 10;
409                 }
410         }
411
412         do {
413                 tval = *ptr++;
414                 /* XXX assumes ASCII... */
415                 if (tval >= 'a')
416                         tval = tval - 'a' + 10;
417                 else if (tval >= 'A')
418                         tval = tval - 'A' + 10;
419                 else if (tval >= '0')
420                         tval -= '0';
421                 else {
422                         warn ("Bogus number: %s.", str);
423                         break;
424                 }
425                 if (tval >= base) {
426                         warn ("Bogus number: %s: digit %d not in base %d\n",
427                               str, tval, base);
428                         break;
429                 }
430                 val = val * base + tval;
431         } while (*ptr);
432
433         if (negative)
434                 max = (1 << (size - 1));
435         else
436                 max = (1 << (size - 1)) + ((1 << (size - 1)) - 1);
437         if (val > max) {
438                 switch (base) {
439                       case 8:
440                         warn ("value %s%o exceeds max (%d) for precision.",
441                               negative ? "-" : "", val, max);
442                         break;
443                       case 16:
444                         warn ("value %s%x exceeds max (%d) for precision.",
445                               negative ? "-" : "", val, max);
446                         break;
447                       default:
448                         warn ("value %s%u exceeds max (%d) for precision.",
449                               negative ? "-" : "", val, max);
450                         break;
451                 }
452         }
453
454         if (negative) {
455                 switch (size) {
456                       case 8:
457                         *buf = -(unsigned long)val;
458                         break;
459                       case 16:
460                         putShort (buf, -(unsigned long)val);
461                         break;
462                       case 32:
463                         putLong (buf, -(unsigned long)val);
464                         break;
465                       default:
466                         warn ("Unexpected integer size: %d\n", size);
467                         break;
468                 }
469         } else {
470                 switch (size) {
471                       case 8:
472                         *buf = (u_int8_t)val;
473                         break;
474                       case 16:
475                         putUShort (buf, (u_int16_t)val);
476                         break;
477                       case 32:
478                         putULong (buf, val);
479                         break;
480                       default:
481                         warn ("Unexpected integer size: %d\n", size);
482                         break;
483                 }
484         }
485 }
486
487 /* date :== NUMBER NUMBER SLASH NUMBER SLASH NUMBER 
488                 NUMBER COLON NUMBER COLON NUMBER SEMI
489
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
492    clock. */
493
494 TIME parse_date (cfile)
495         FILE *cfile;
496 {
497         struct tm tm;
498         int guess;
499         char *val;
500         int token;
501         static int months [11] = { 31, 59, 90, 120, 151, 181,
502                                           212, 243, 273, 304, 334 };
503
504         /* Day of week... */
505         token = next_token (&val, cfile);
506         if (token != NUMBER) {
507                 parse_warn ("numeric day of week expected.");
508                 if (token != SEMI)
509                         skip_to_semi (cfile);
510                 return (TIME)0;
511         }
512         tm.tm_wday = atoi (val);
513
514         /* Year... */
515         token = next_token (&val, cfile);
516         if (token != NUMBER) {
517                 parse_warn ("numeric year expected.");
518                 if (token != SEMI)
519                         skip_to_semi (cfile);
520                 return (TIME)0;
521         }
522         tm.tm_year = atoi (val);
523         if (tm.tm_year > 1900)
524                 tm.tm_year -= 1900;
525
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.");
530                 if (token != SEMI)
531                         skip_to_semi (cfile);
532                 return (TIME)0;
533         }
534
535         /* Month... */
536         token = next_token (&val, cfile);
537         if (token != NUMBER) {
538                 parse_warn ("numeric month expected.");
539                 if (token != SEMI)
540                         skip_to_semi (cfile);
541                 return (TIME)0;
542         }
543         tm.tm_mon = atoi (val) - 1;
544
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.");
549                 if (token != SEMI)
550                         skip_to_semi (cfile);
551                 return (TIME)0;
552         }
553
554         /* Month... */
555         token = next_token (&val, cfile);
556         if (token != NUMBER) {
557                 parse_warn ("numeric day of month expected.");
558                 if (token != SEMI)
559                         skip_to_semi (cfile);
560                 return (TIME)0;
561         }
562         tm.tm_mday = atoi (val);
563
564         /* Hour... */
565         token = next_token (&val, cfile);
566         if (token != NUMBER) {
567                 parse_warn ("numeric hour expected.");
568                 if (token != SEMI)
569                         skip_to_semi (cfile);
570                 return (TIME)0;
571         }
572         tm.tm_hour = atoi (val);
573
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.");
578                 if (token != SEMI)
579                         skip_to_semi (cfile);
580                 return (TIME)0;
581         }
582
583         /* Minute... */
584         token = next_token (&val, cfile);
585         if (token != NUMBER) {
586                 parse_warn ("numeric minute expected.");
587                 if (token != SEMI)
588                         skip_to_semi (cfile);
589                 return (TIME)0;
590         }
591         tm.tm_min = atoi (val);
592
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.");
597                 if (token != SEMI)
598                         skip_to_semi (cfile);
599                 return (TIME)0;
600         }
601
602         /* Minute... */
603         token = next_token (&val, cfile);
604         if (token != NUMBER) {
605                 parse_warn ("numeric minute expected.");
606                 if (token != SEMI)
607                         skip_to_semi (cfile);
608                 return (TIME)0;
609         }
610         tm.tm_sec = atoi (val);
611         tm.tm_isdst = 0;
612
613         /* XXX */ /* We assume that mktime does not use tm_yday. */
614         tm.tm_yday = 0;
615
616         /* Make sure the date ends in a semicolon... */
617         token = next_token (&val, cfile);
618         if (token != SEMI) {
619                 parse_warn ("semicolon expected.");
620                 skip_to_semi (cfile);
621                 return 0;
622         }
623
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]
629                        : 0) +
630                       (tm.tm_mon > 1 &&         /* Leap day this year */
631                        !((tm.tm_year - 72) & 3)) +
632                       tm.tm_mday - 1) * 24) +   /* Day of month */
633                     tm.tm_hour) * 60) +
634                   tm.tm_min) * 60) + tm.tm_sec;
635
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
643            significant. */
644
645         return guess;
646 }