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