xref: /dragonfly/sbin/dhclient/parse.c (revision 277a81d9fe2047f1a89439c4bc1659f09a1cdea0)
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
skip_to_semi(FILE * cfile)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
parse_semi(FILE * cfile)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 *
parse_string(FILE * cfile)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
parse_ip_addr(FILE * cfile,struct iaddr * addr)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
parse_hardware_param(FILE * cfile,struct hardware * hardware)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
parse_lease_time(FILE * cfile,time_t * timep)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
parse_numeric_aggregate(FILE * cfile,unsigned char * buf,int max,int separator,int base)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
convert_num(unsigned char * buf,char * str,int base,int size)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
parse_date(FILE * cfile)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 }
500