1 /* $OpenBSD: parse.c,v 1.13 2010/03/27 14:11: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 /* 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 char *val; 66 int brace_count = 0; 67 68 do { 69 token = peek_token(&val, cfile); 70 if (token == '}') { 71 if (brace_count) { 72 token = next_token(&val, cfile); 73 if (!--brace_count) 74 return; 75 } else 76 return; 77 } else if (token == '{') { 78 brace_count++; 79 } else if (token == ';' && !brace_count) { 80 token = next_token(&val, cfile); 81 return; 82 } else if (token == '\n') { 83 /* 84 * EOL only happens when parsing 85 * /etc/resolv.conf, and we treat it like a 86 * semicolon because the resolv.conf file is 87 * line-oriented. 88 */ 89 token = next_token(&val, cfile); 90 return; 91 } 92 token = next_token(&val, cfile); 93 } while (token != EOF); 94 } 95 96 int 97 parse_semi(FILE *cfile) 98 { 99 int token; 100 char *val; 101 102 token = next_token(&val, cfile); 103 if (token != ';') { 104 parse_warn("semicolon expected."); 105 skip_to_semi(cfile); 106 return (0); 107 } 108 return (1); 109 } 110 111 /* 112 * string-parameter :== STRING SEMI 113 */ 114 char * 115 parse_string(FILE *cfile) 116 { 117 char *val, *s; 118 int token; 119 120 token = next_token(&val, cfile); 121 if (token != TOK_STRING) { 122 parse_warn("filename must be a string"); 123 skip_to_semi(cfile); 124 return (NULL); 125 } 126 s = strdup(val); 127 if (s == NULL) 128 error("no memory for string %s.", val); 129 130 if (!parse_semi(cfile)) { 131 free(s); 132 return (NULL); 133 } 134 return (s); 135 } 136 137 /* 138 * hostname :== identifier | hostname DOT identifier 139 */ 140 char * 141 parse_host_name(FILE *cfile) 142 { 143 char *val, *s, *t; 144 int token, len = 0; 145 pair c = NULL; 146 147 /* Read a dotted hostname... */ 148 do { 149 /* Read a token, which should be an identifier. */ 150 token = next_token(&val, cfile); 151 if (!is_identifier(token) && token != TOK_NUMBER) { 152 parse_warn("expecting an identifier in hostname"); 153 skip_to_semi(cfile); 154 return (NULL); 155 } 156 /* Store this identifier... */ 157 s = strdup(val); 158 if (s == NULL) 159 error("can't allocate temp space for hostname."); 160 c = cons((caddr_t) s, c); 161 len += strlen(s) + 1; 162 /* 163 * Look for a dot; if it's there, keep going, otherwise 164 * we're done. 165 */ 166 token = peek_token(&val, cfile); 167 if (token == '.') 168 token = next_token(&val, cfile); 169 } while (token == '.'); 170 171 /* Assemble the hostname together into a string. */ 172 if (!(s = malloc(len))) 173 error("can't allocate space for hostname."); 174 t = s + len; 175 *--t = '\0'; 176 while (c) { 177 pair cdr = c->cdr; 178 int l = strlen((char *)c->car); 179 180 t -= l; 181 memcpy(t, (char *)c->car, l); 182 /* Free up temp space. */ 183 free(c->car); 184 free(c); 185 c = cdr; 186 if (t != s) 187 *--t = '.'; 188 } 189 return (s); 190 } 191 192 /* 193 * hardware-parameter :== HARDWARE ETHERNET csns SEMI 194 * csns :== NUMBER | csns COLON NUMBER 195 */ 196 void 197 parse_hardware_param(FILE *cfile, struct hardware *hardware) 198 { 199 char *val; 200 int token, hlen; 201 unsigned char *t; 202 203 token = next_token(&val, cfile); 204 switch (token) { 205 case TOK_ETHERNET: 206 hardware->htype = HTYPE_ETHER; 207 break; 208 case TOK_TOKEN_RING: 209 hardware->htype = HTYPE_IEEE802; 210 break; 211 case TOK_FDDI: 212 hardware->htype = HTYPE_FDDI; 213 break; 214 case TOK_IPSEC_TUNNEL: 215 hardware->htype = HTYPE_IPSEC_TUNNEL; 216 break; 217 default: 218 parse_warn("expecting a network hardware type"); 219 skip_to_semi(cfile); 220 return; 221 } 222 223 /* 224 * Parse the hardware address information. Technically, it 225 * would make a lot of sense to restrict the length of the data 226 * we'll accept here to the length of a particular hardware 227 * address type. Unfortunately, there are some broken clients 228 * out there that put bogus data in the chaddr buffer, and we 229 * accept that data in the lease file rather than simply failing 230 * on such clients. Yuck. 231 */ 232 hlen = 0; 233 t = parse_numeric_aggregate(cfile, NULL, &hlen, ':', 16, 8); 234 if (!t) 235 return; 236 if (hlen > sizeof(hardware->haddr)) { 237 free(t); 238 parse_warn("hardware address too long"); 239 } else { 240 hardware->hlen = hlen; 241 memcpy((unsigned char *)&hardware->haddr[0], t, hardware->hlen); 242 if (hlen < sizeof(hardware->haddr)) 243 memset(&hardware->haddr[hlen], 0, 244 sizeof(hardware->haddr) - hlen); 245 free(t); 246 } 247 248 token = next_token(&val, cfile); 249 if (token != ';') { 250 parse_warn("expecting semicolon."); 251 skip_to_semi(cfile); 252 } 253 } 254 255 /* 256 * lease-time :== NUMBER SEMI 257 */ 258 void 259 parse_lease_time(FILE *cfile, time_t *timep) 260 { 261 char *val; 262 int token; 263 264 token = next_token(&val, cfile); 265 if (token != TOK_NUMBER) { 266 parse_warn("Expecting numeric lease time"); 267 skip_to_semi(cfile); 268 return; 269 } 270 convert_num((unsigned char *)timep, val, 10, 32); 271 /* Unswap the number - convert_num returns stuff in NBO. */ 272 *timep = ntohl(*timep); /* XXX */ 273 274 parse_semi(cfile); 275 } 276 277 /* 278 * No BNF for numeric aggregates - that's defined by the caller. What 279 * this function does is to parse a sequence of numbers separated by the 280 * token specified in separator. If max is zero, any number of numbers 281 * will be parsed; otherwise, exactly max numbers are expected. Base 282 * and size tell us how to internalize the numbers once they've been 283 * tokenized. 284 */ 285 unsigned char * 286 parse_numeric_aggregate(FILE *cfile, unsigned char *buf, int *max, 287 int separator, int base, int size) 288 { 289 char *val, *t; 290 int token, count = 0; 291 unsigned char *bufp = buf, *s = NULL; 292 pair c = NULL; 293 294 if (!bufp && *max) { 295 bufp = malloc(*max * size / 8); 296 if (!bufp) 297 error("can't allocate space for numeric aggregate"); 298 } else 299 s = bufp; 300 301 do { 302 if (count) { 303 token = peek_token(&val, cfile); 304 if (token != separator) { 305 if (!*max) 306 break; 307 if (token != '{' && token != '}') 308 token = next_token(&val, cfile); 309 parse_warn("too few numbers."); 310 if (token != ';') 311 skip_to_semi(cfile); 312 return (NULL); 313 } 314 token = next_token(&val, cfile); 315 } 316 token = next_token(&val, cfile); 317 318 if (token == EOF) { 319 parse_warn("unexpected end of file"); 320 break; 321 } 322 /* Allow NUMBER_OR_NAME if base is 16. */ 323 if (token != TOK_NUMBER && 324 (base != 16 || token != TOK_NUMBER_OR_NAME)) { 325 parse_warn("expecting numeric value."); 326 skip_to_semi(cfile); 327 return (NULL); 328 } 329 /* 330 * If we can, convert the number now; otherwise, build a 331 * linked list of all the numbers. 332 */ 333 if (s) { 334 convert_num(s, val, base, size); 335 s += size / 8; 336 } else { 337 t = strdup(val); 338 if (t == NULL) 339 error("no temp space for number."); 340 c = cons(t, c); 341 } 342 } while (++count != *max); 343 344 /* If we had to cons up a list, convert it now. */ 345 if (c) { 346 bufp = malloc(count * size / 8); 347 if (!bufp) 348 error("can't allocate space for numeric aggregate."); 349 s = bufp + count - size / 8; 350 *max = count; 351 } 352 while (c) { 353 pair cdr = c->cdr; 354 convert_num(s, (char *)c->car, base, size); 355 s -= size / 8; 356 /* Free up temp space. */ 357 free(c->car); 358 free(c); 359 c = cdr; 360 } 361 return (bufp); 362 } 363 364 void 365 convert_num(unsigned char *buf, char *str, int base, int size) 366 { 367 int negative = 0, tval, max; 368 u_int32_t val = 0; 369 char *ptr = str; 370 371 if (*ptr == '-') { 372 negative = 1; 373 ptr++; 374 } 375 376 /* If base wasn't specified, figure it out from the data. */ 377 if (!base) { 378 if (ptr[0] == '0') { 379 if (ptr[1] == 'x') { 380 base = 16; 381 ptr += 2; 382 } else if (isascii(ptr[1]) && isdigit(ptr[1])) { 383 base = 8; 384 ptr += 1; 385 } else 386 base = 10; 387 } else 388 base = 10; 389 } 390 391 do { 392 tval = *ptr++; 393 /* XXX assumes ASCII... */ 394 if (tval >= 'a') 395 tval = tval - 'a' + 10; 396 else if (tval >= 'A') 397 tval = tval - 'A' + 10; 398 else if (tval >= '0') 399 tval -= '0'; 400 else { 401 warning("Bogus number: %s.", str); 402 break; 403 } 404 if (tval >= base) { 405 warning("Bogus number: %s: digit %d not in base %d", 406 str, tval, base); 407 break; 408 } 409 val = val * base + tval; 410 } while (*ptr); 411 412 if (negative) 413 max = (1 << (size - 1)); 414 else 415 max = (1 << (size - 1)) + ((1 << (size - 1)) - 1); 416 if (val > max) { 417 switch (base) { 418 case 8: 419 warning("value %s%o exceeds max (%d) for precision.", 420 negative ? "-" : "", val, max); 421 break; 422 case 16: 423 warning("value %s%x exceeds max (%d) for precision.", 424 negative ? "-" : "", val, max); 425 break; 426 default: 427 warning("value %s%u exceeds max (%d) for precision.", 428 negative ? "-" : "", val, max); 429 break; 430 } 431 } 432 433 if (negative) { 434 switch (size) { 435 case 8: 436 *buf = -(unsigned long)val; 437 break; 438 case 16: 439 putShort(buf, -(unsigned long)val); 440 break; 441 case 32: 442 putLong(buf, -(unsigned long)val); 443 break; 444 default: 445 warning("Unexpected integer size: %d", size); 446 break; 447 } 448 } else { 449 switch (size) { 450 case 8: 451 *buf = (u_int8_t)val; 452 break; 453 case 16: 454 putUShort(buf, (u_int16_t)val); 455 break; 456 case 32: 457 putULong(buf, val); 458 break; 459 default: 460 warning("Unexpected integer size: %d", size); 461 break; 462 } 463 } 464 } 465 466 /* 467 * date :== NUMBER NUMBER SLASH NUMBER SLASH NUMBER 468 * NUMBER COLON NUMBER COLON NUMBER SEMI 469 * 470 * Dates are always in GMT; first number is day of week; next is 471 * year/month/day; next is hours:minutes:seconds on a 24-hour 472 * clock. 473 */ 474 time_t 475 parse_date(FILE *cfile) 476 { 477 static int months[11] = { 31, 59, 90, 120, 151, 181, 478 212, 243, 273, 304, 334 }; 479 int guess, token; 480 struct tm tm; 481 char *val; 482 483 /* Day of week... */ 484 token = next_token(&val, cfile); 485 if (token != TOK_NUMBER) { 486 parse_warn("numeric day of week expected."); 487 if (token != ';') 488 skip_to_semi(cfile); 489 return (0); 490 } 491 tm.tm_wday = atoi(val); 492 493 /* Year... */ 494 token = next_token(&val, cfile); 495 if (token != TOK_NUMBER) { 496 parse_warn("numeric year expected."); 497 if (token != ';') 498 skip_to_semi(cfile); 499 return (0); 500 } 501 tm.tm_year = atoi(val); 502 if (tm.tm_year > 1900) 503 tm.tm_year -= 1900; 504 505 /* Slash separating year from month... */ 506 token = next_token(&val, cfile); 507 if (token != '/') { 508 parse_warn("expected slash separating year from month."); 509 if (token != ';') 510 skip_to_semi(cfile); 511 return (0); 512 } 513 514 /* Month... */ 515 token = next_token(&val, cfile); 516 if (token != TOK_NUMBER) { 517 parse_warn("numeric month expected."); 518 if (token != ';') 519 skip_to_semi(cfile); 520 return (0); 521 } 522 tm.tm_mon = atoi(val) - 1; 523 524 /* Slash separating month from day... */ 525 token = next_token(&val, cfile); 526 if (token != '/') { 527 parse_warn("expected slash separating month from day."); 528 if (token != ';') 529 skip_to_semi(cfile); 530 return (0); 531 } 532 533 /* Day... */ 534 token = next_token(&val, cfile); 535 if (token != TOK_NUMBER) { 536 parse_warn("numeric day of month expected."); 537 if (token != ';') 538 skip_to_semi(cfile); 539 return (0); 540 } 541 tm.tm_mday = atoi(val); 542 543 /* Hour... */ 544 token = next_token(&val, cfile); 545 if (token != TOK_NUMBER) { 546 parse_warn("numeric hour expected."); 547 if (token != ';') 548 skip_to_semi(cfile); 549 return (0); 550 } 551 tm.tm_hour = atoi(val); 552 553 /* Colon separating hour from minute... */ 554 token = next_token(&val, cfile); 555 if (token != ':') { 556 parse_warn("expected colon separating hour from minute."); 557 if (token != ';') 558 skip_to_semi(cfile); 559 return (0); 560 } 561 562 /* Minute... */ 563 token = next_token(&val, cfile); 564 if (token != TOK_NUMBER) { 565 parse_warn("numeric minute expected."); 566 if (token != ';') 567 skip_to_semi(cfile); 568 return (0); 569 } 570 tm.tm_min = atoi(val); 571 572 /* Colon separating minute from second... */ 573 token = next_token(&val, cfile); 574 if (token != ':') { 575 parse_warn("expected colon separating minute from second."); 576 if (token != ';') 577 skip_to_semi(cfile); 578 return (0); 579 } 580 581 /* Second... */ 582 token = next_token(&val, cfile); 583 if (token != TOK_NUMBER) { 584 parse_warn("numeric second expected."); 585 if (token != ';') 586 skip_to_semi(cfile); 587 return (0); 588 } 589 tm.tm_sec = atoi(val); 590 tm.tm_isdst = 0; 591 592 /* XXX: We assume that mktime does not use tm_yday. */ 593 tm.tm_yday = 0; 594 595 /* Make sure the date ends in a semicolon... */ 596 token = next_token(&val, cfile); 597 if (token != ';') { 598 parse_warn("semicolon expected."); 599 skip_to_semi(cfile); 600 return (0); 601 } 602 603 /* Guess the time value... */ 604 guess = ((((((365 * (tm.tm_year - 70) + /* Days in years since '70 */ 605 (tm.tm_year - 69) / 4 + /* Leap days since '70 */ 606 (tm.tm_mon /* Days in months this year */ 607 ? months[tm.tm_mon - 1] : 0) + 608 (tm.tm_mon > 1 && /* Leap day this year */ 609 !((tm.tm_year - 72) & 3)) + 610 tm.tm_mday - 1) * 24) + /* Day of month */ 611 tm.tm_hour) * 60) + tm.tm_min) * 60) + tm.tm_sec; 612 613 /* 614 * This guess could be wrong because of leap seconds or other 615 * weirdness we don't know about that the system does. For 616 * now, we're just going to accept the guess, but at some point 617 * it might be nice to do a successive approximation here to get 618 * an exact value. Even if the error is small, if the server 619 * is restarted frequently (and thus the lease database is 620 * reread), the error could accumulate into something 621 * significant. 622 */ 623 return (guess); 624 } 625