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