1 /* $OpenBSD: parse.c,v 1.29 2024/06/27 16:39:31 florian Exp $ */
2
3 /*
4 * Copyright (c) 1995, 1996, 1997, 1998 The Internet Software Consortium.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of The Internet Software Consortium nor the names
17 * of its contributors may be used to endorse or promote products derived
18 * from this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
21 * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
22 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
23 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24 * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
25 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
28 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
29 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 *
34 * This software has been written for the Internet Software Consortium
35 * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
36 * Enterprises. To learn more about the Internet Software Consortium,
37 * see ``http://www.vix.com/isc''. To learn more about Vixie
38 * Enterprises, see ``http://www.vix.com''.
39 */
40
41 #include <sys/types.h>
42 #include <sys/socket.h>
43
44 #include <net/if.h>
45
46 #include <netinet/in.h>
47 #include <netinet/if_ether.h>
48
49 #include <ctype.h>
50 #include <errno.h>
51 #include <stdarg.h>
52 #include <stdint.h>
53 #include <stdio.h>
54 #include <stdlib.h>
55 #include <string.h>
56 #include <syslog.h>
57 #include <time.h>
58 #include <unistd.h>
59
60 #include "dhcp.h"
61 #include "tree.h"
62 #include "dhcpd.h"
63 #include "dhctoken.h"
64 #include "log.h"
65
66 /*
67 * Skip to the semicolon ending the current statement. If we encounter
68 * braces, the matching closing brace terminates the statement. If we
69 * encounter a right brace but haven't encountered a left brace, return
70 * leaving the brace in the token buffer for the caller. If we see a
71 * semicolon and haven't seen a left brace, return. This lets us skip
72 * over:
73 *
74 * statement;
75 * statement foo bar { }
76 * statement foo bar { statement { } }
77 * statement}
78 *
79 * ...et cetera.
80 */
81 void
skip_to_semi(FILE * cfile)82 skip_to_semi(FILE *cfile)
83 {
84 int token;
85 char *val;
86 int brace_count = 0;
87
88 do {
89 token = peek_token(&val, cfile);
90 if (token == '}') {
91 if (brace_count) {
92 token = next_token(&val, cfile);
93 if (!--brace_count)
94 return;
95 } else
96 return;
97 } else if (token == '{') {
98 brace_count++;
99 } else if (token == ';' && !brace_count) {
100 token = next_token(&val, cfile);
101 return;
102 } else if (token == '\n') {
103 /*
104 * EOL only happens when parsing
105 * /etc/resolv.conf, and we treat it like a
106 * semicolon because the resolv.conf file is
107 * line-oriented.
108 */
109 token = next_token(&val, cfile);
110 return;
111 }
112 token = next_token(&val, cfile);
113 } while (token != EOF);
114 }
115
116 int
parse_semi(FILE * cfile)117 parse_semi(FILE *cfile)
118 {
119 int token;
120 char *val;
121
122 token = next_token(&val, cfile);
123 if (token != ';') {
124 parse_warn("semicolon expected.");
125 skip_to_semi(cfile);
126 return (0);
127 }
128 return (1);
129 }
130
131 /*
132 * string-parameter :== STRING SEMI
133 */
134 char *
parse_string(FILE * cfile)135 parse_string(FILE *cfile)
136 {
137 char *val, *s;
138 int token;
139
140 token = next_token(&val, cfile);
141 if (token != TOK_STRING) {
142 parse_warn("filename must be a string");
143 skip_to_semi(cfile);
144 return (NULL);
145 }
146 s = strdup(val);
147 if (s == NULL)
148 fatalx("no memory for string %s.", val);
149
150 if (!parse_semi(cfile)) {
151 free(s);
152 return (NULL);
153 }
154 return (s);
155 }
156
157 /*
158 * hostname :== identifier | hostname DOT identifier
159 */
160 char *
parse_host_name(FILE * cfile)161 parse_host_name(FILE *cfile)
162 {
163 char *val, *s, *t;
164 int token, len = 0;
165 pair c = NULL;
166
167 /* Read a dotted hostname... */
168 do {
169 /* Read a token, which should be an identifier. */
170 token = next_token(&val, cfile);
171 if (!is_identifier(token)) {
172 parse_warn("expecting an identifier in hostname");
173 skip_to_semi(cfile);
174 return (NULL);
175 }
176 /* Store this identifier... */
177 s = strdup(val);
178 if (s == NULL)
179 fatalx("can't allocate temp space for hostname.");
180 c = cons((caddr_t) s, c);
181 len += strlen(s) + 1;
182 /*
183 * Look for a dot; if it's there, keep going, otherwise
184 * we're done.
185 */
186 token = peek_token(&val, cfile);
187 if (token == '.')
188 token = next_token(&val, cfile);
189 } while (token == '.');
190
191 /* Assemble the hostname together into a string. */
192 if (!(s = malloc(len)))
193 fatalx("can't allocate space for hostname.");
194 t = s + len;
195 *--t = '\0';
196 while (c) {
197 pair cdr = c->cdr;
198 int l = strlen((char *)c->car);
199
200 t -= l;
201 memcpy(t, (char *)c->car, l);
202 /* Free up temp space. */
203 free(c->car);
204 free(c);
205 c = cdr;
206 if (t != s)
207 *--t = '.';
208 }
209 return (s);
210 }
211
212 /*
213 * hardware-parameter :== HARDWARE ETHERNET csns SEMI
214 * csns :== NUMBER | csns COLON NUMBER
215 */
216 void
parse_hardware_param(FILE * cfile,struct hardware * hardware)217 parse_hardware_param(FILE *cfile, struct hardware *hardware)
218 {
219 char *val;
220 int token, hlen = 0;
221 unsigned char *e, *t = NULL;
222
223 token = next_token(&val, cfile);
224 switch (token) {
225 case TOK_ETHERNET:
226 hardware->htype = HTYPE_ETHER;
227 break;
228 case TOK_IPSEC_TUNNEL:
229 hardware->htype = HTYPE_IPSEC_TUNNEL;
230 break;
231 default:
232 parse_warn("expecting a network hardware type");
233 skip_to_semi(cfile);
234 return;
235 }
236
237
238 /* Try looking up in /etc/ethers first. */
239 if (hardware->htype == HTYPE_ETHER) {
240 token = peek_token(&val, cfile);
241 hlen = sizeof(struct ether_addr);
242 if ((e = malloc(hlen)) == NULL)
243 fatalx("can't allocate space for ethernet address.");
244 if (ether_hostton(val, (struct ether_addr *)e) == 0) {
245 (void)next_token(&val, cfile); /* consume token */
246 t = e;
247 } else
248 free(e);
249 }
250
251 /*
252 * Parse the hardware address information. Technically, it
253 * would make a lot of sense to restrict the length of the data
254 * we'll accept here to the length of a particular hardware
255 * address type. Unfortunately, there are some broken clients
256 * out there that put bogus data in the chaddr buffer, and we
257 * accept that data in the lease file rather than simply failing
258 * on such clients. Yuck.
259 */
260 if (!t)
261 t = parse_numeric_aggregate(cfile, NULL, &hlen, ':', 16, 8);
262
263 if (!t)
264 return;
265 if (hlen > sizeof(hardware->haddr)) {
266 free(t);
267 parse_warn("hardware address too long");
268 } else {
269 hardware->hlen = hlen;
270 memcpy((unsigned char *)&hardware->haddr[0], t,
271 hardware->hlen);
272 if (hlen < sizeof(hardware->haddr))
273 memset(&hardware->haddr[hlen], 0,
274 sizeof(hardware->haddr) - hlen);
275 free(t);
276 }
277
278 token = next_token(&val, cfile);
279 if (token != ';') {
280 parse_warn("expecting semicolon.");
281 skip_to_semi(cfile);
282 }
283 }
284
285 /*
286 * lease-time :== NUMBER SEMI
287 */
288 void
parse_lease_time(FILE * cfile,time_t * timep)289 parse_lease_time(FILE *cfile, time_t *timep)
290 {
291 const char *errstr;
292 char *val;
293 uint32_t value;
294
295 next_token(&val, cfile);
296
297 value = strtonum(val, 0, UINT32_MAX, &errstr);
298 if (errstr) {
299 parse_warn("lease time is %s: %s", errstr, val);
300 skip_to_semi(cfile);
301 return;
302 }
303
304 *timep = value;
305
306 parse_semi(cfile);
307 }
308
309 /*
310 * No BNF for numeric aggregates - that's defined by the caller. What
311 * this function does is to parse a sequence of numbers separated by the
312 * token specified in separator. If max is zero, any number of numbers
313 * will be parsed; otherwise, exactly max numbers are expected. Base
314 * and size tell us how to internalize the numbers once they've been
315 * tokenized.
316 */
317 unsigned char *
parse_numeric_aggregate(FILE * cfile,unsigned char * buf,int * max,int separator,int base,int size)318 parse_numeric_aggregate(FILE *cfile, unsigned char *buf, int *max,
319 int separator, int base, int size)
320 {
321 char *val, *t;
322 int token, count = 0;
323 unsigned char *bufp = buf, *s = NULL;
324 pair c = NULL;
325
326 if (!bufp && *max) {
327 bufp = malloc(*max * size / 8);
328 if (!bufp)
329 fatalx("can't allocate space for numeric aggregate");
330 } else
331 s = bufp;
332
333 do {
334 if (count) {
335 token = peek_token(&val, cfile);
336 if (token != separator) {
337 if (!*max)
338 break;
339 if (token != '{' && token != '}')
340 token = next_token(&val, cfile);
341 parse_warn("too few numbers.");
342 if (token != ';')
343 skip_to_semi(cfile);
344 return (NULL);
345 }
346 token = next_token(&val, cfile);
347 }
348 token = next_token(&val, cfile);
349
350 if (token == EOF) {
351 parse_warn("unexpected end of file");
352 break;
353 }
354 if (token != TOK_NUMBER && token != TOK_NUMBER_OR_NAME) {
355 parse_warn("expecting numeric value.");
356 skip_to_semi(cfile);
357 return (NULL);
358 }
359 /*
360 * If we can, convert the number now; otherwise, build a
361 * linked list of all the numbers.
362 */
363 if (s) {
364 convert_num(s, val, base, size);
365 s += size / 8;
366 } else {
367 t = strdup(val);
368 if (t == NULL)
369 fatalx("no temp space for number.");
370 c = cons(t, c);
371 }
372 } while (++count != *max);
373
374 /* If we had to cons up a list, convert it now. */
375 if (c) {
376 bufp = malloc(count * size / 8);
377 if (!bufp)
378 fatalx("can't allocate space for numeric aggregate.");
379 s = bufp + count - size / 8;
380 *max = count;
381 }
382 while (c) {
383 pair cdr = c->cdr;
384 convert_num(s, (char *)c->car, base, size);
385 s -= size / 8;
386 /* Free up temp space. */
387 free(c->car);
388 free(c);
389 c = cdr;
390 }
391 return (bufp);
392 }
393
394 void
convert_num(unsigned char * buf,char * str,int base,int size)395 convert_num(unsigned char *buf, char *str, int base, int size)
396 {
397 int negative = 0, tval, max;
398 u_int32_t val = 0;
399 char *ptr = str;
400
401 if (*ptr == '-') {
402 negative = 1;
403 ptr++;
404 }
405
406 /* If base wasn't specified, figure it out from the data. */
407 if (!base) {
408 if (ptr[0] == '0') {
409 if (ptr[1] == 'x') {
410 base = 16;
411 ptr += 2;
412 } else if (isascii((unsigned char)ptr[1]) &&
413 isdigit((unsigned char)ptr[1])) {
414 base = 8;
415 ptr += 1;
416 } else
417 base = 10;
418 } else
419 base = 10;
420 }
421
422 do {
423 tval = *ptr++;
424 /* XXX assumes ASCII... */
425 if (tval >= 'a')
426 tval = tval - 'a' + 10;
427 else if (tval >= 'A')
428 tval = tval - 'A' + 10;
429 else if (tval >= '0')
430 tval -= '0';
431 else {
432 log_warnx("Bogus number: %s.", str);
433 break;
434 }
435 if (tval >= base) {
436 log_warnx("Bogus number: %s: digit %d not in base %d",
437 str, tval, base);
438 break;
439 }
440 val = val * base + tval;
441 } while (*ptr);
442
443 if (negative)
444 max = (1 << (size - 1));
445 else
446 max = (1 << (size - 1)) + ((1 << (size - 1)) - 1);
447 if (val > max) {
448 switch (base) {
449 case 8:
450 log_warnx("value %s%o exceeds max (%d) for precision.",
451 negative ? "-" : "", val, max);
452 break;
453 case 16:
454 log_warnx("value %s%x exceeds max (%d) for precision.",
455 negative ? "-" : "", val, max);
456 break;
457 default:
458 log_warnx("value %s%u exceeds max (%d) for precision.",
459 negative ? "-" : "", val, max);
460 break;
461 }
462 }
463
464 if (negative) {
465 switch (size) {
466 case 8:
467 *buf = -(unsigned long)val;
468 break;
469 case 16:
470 putShort(buf, -(unsigned long)val);
471 break;
472 case 32:
473 putLong(buf, -(unsigned long)val);
474 break;
475 default:
476 log_warnx("Unexpected integer size: %d", size);
477 break;
478 }
479 } else {
480 switch (size) {
481 case 8:
482 *buf = (u_int8_t)val;
483 break;
484 case 16:
485 putUShort(buf, (u_int16_t)val);
486 break;
487 case 32:
488 putULong(buf, val);
489 break;
490 default:
491 log_warnx("Unexpected integer size: %d", size);
492 break;
493 }
494 }
495 }
496
497 /*
498 * date :== NUMBER NUMBER SLASH NUMBER SLASH NUMBER
499 * NUMBER COLON NUMBER COLON NUMBER UTC SEMI
500 *
501 * Dates are always in UTC; first number is day of week; next is
502 * year/month/day; next is hours:minutes:seconds on a 24-hour
503 * clock.
504 */
505 time_t
parse_date(FILE * cfile)506 parse_date(FILE *cfile)
507 {
508 struct tm tm;
509 char timestr[26]; /* "w yyyy/mm/dd hh:mm:ss UTC" */
510 char *val, *p;
511 size_t n;
512 time_t guess;
513 int token;
514
515 memset(timestr, 0, sizeof(timestr));
516
517 do {
518 token = peek_token(NULL, cfile);
519 switch (token) {
520 case TOK_NAME:
521 case TOK_NUMBER:
522 case TOK_NUMBER_OR_NAME:
523 case '/':
524 case ':':
525 token = next_token(&val, cfile);
526 n = strlcat(timestr, val, sizeof(timestr));
527 if (n >= sizeof(timestr)) {
528 /* XXX Will break after year 9999! */
529 parse_warn("time string too long");
530 skip_to_semi(cfile);
531 return (0);
532 }
533 break;
534 case';':
535 break;
536 default:
537 parse_warn("invalid time string");
538 skip_to_semi(cfile);
539 return (0);
540 }
541 } while (token != ';');
542
543 parse_semi(cfile);
544
545 memset(&tm, 0, sizeof(tm)); /* 'cuz strptime ignores tm_isdt. */
546 p = strptime(timestr, DB_TIMEFMT, &tm);
547 if (p == NULL || *p != '\0') {
548 p = strptime(timestr, OLD_DB_TIMEFMT, &tm);
549 if (p == NULL || *p != '\0') {
550 parse_warn("unparseable time string");
551 return (0);
552 }
553 }
554
555 guess = timegm(&tm);
556 if (guess == -1) {
557 parse_warn("time could not be represented");
558 return (0);
559 }
560
561 return (guess);
562 }
563
564 int warnings_occurred;
565
566 int
parse_warn(char * fmt,...)567 parse_warn(char *fmt, ...)
568 {
569 static char fbuf[1024];
570 static char mbuf[1024];
571 static char spaces[81];
572 va_list list;
573 int i;
574
575 snprintf(fbuf, sizeof(fbuf), "%s line %d: %s", tlname, lexline, mbuf);
576 va_start(list, fmt);
577 vsnprintf(mbuf, sizeof(mbuf), fbuf, list);
578 va_end(list);
579
580 log_warnx("%s", mbuf);
581 log_warnx("%s", token_line);
582 if (lexchar < sizeof(spaces)) {
583 memset(spaces, 0, sizeof(spaces));
584 for (i = 0; i < lexchar - 1; i++) {
585 if (token_line[i] == '\t')
586 spaces[i] = '\t';
587 else
588 spaces[i] = ' ';
589 }
590 }
591 log_warnx("%s^", spaces);
592
593 warnings_occurred = 1;
594
595 return (0);
596 }
597