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