1 /* $OpenBSD: parse.c,v 1.17 2013/12/18 20:37:04 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_IPSEC_TUNNEL: 209 hardware->htype = HTYPE_IPSEC_TUNNEL; 210 break; 211 default: 212 parse_warn("expecting a network hardware type"); 213 skip_to_semi(cfile); 214 return; 215 } 216 217 /* 218 * Parse the hardware address information. Technically, it 219 * would make a lot of sense to restrict the length of the data 220 * we'll accept here to the length of a particular hardware 221 * address type. Unfortunately, there are some broken clients 222 * out there that put bogus data in the chaddr buffer, and we 223 * accept that data in the lease file rather than simply failing 224 * on such clients. Yuck. 225 */ 226 hlen = 0; 227 t = parse_numeric_aggregate(cfile, NULL, &hlen, ':', 16, 8); 228 if (!t) 229 return; 230 if (hlen > sizeof(hardware->haddr)) { 231 free(t); 232 parse_warn("hardware address too long"); 233 } else { 234 hardware->hlen = hlen; 235 memcpy((unsigned char *)&hardware->haddr[0], t, hardware->hlen); 236 if (hlen < sizeof(hardware->haddr)) 237 memset(&hardware->haddr[hlen], 0, 238 sizeof(hardware->haddr) - hlen); 239 free(t); 240 } 241 242 token = next_token(&val, cfile); 243 if (token != ';') { 244 parse_warn("expecting semicolon."); 245 skip_to_semi(cfile); 246 } 247 } 248 249 /* 250 * lease-time :== NUMBER SEMI 251 */ 252 void 253 parse_lease_time(FILE *cfile, time_t *timep) 254 { 255 char *val; 256 uint32_t value; 257 int token; 258 259 token = next_token(&val, cfile); 260 if (token != TOK_NUMBER) { 261 parse_warn("Expecting numeric lease time"); 262 skip_to_semi(cfile); 263 return; 264 } 265 convert_num((unsigned char *)&value, val, 10, 32); 266 /* Unswap the number - convert_num returns stuff in NBO. */ 267 *timep = ntohl(value); /* XXX */ 268 269 parse_semi(cfile); 270 } 271 272 /* 273 * No BNF for numeric aggregates - that's defined by the caller. What 274 * this function does is to parse a sequence of numbers separated by the 275 * token specified in separator. If max is zero, any number of numbers 276 * will be parsed; otherwise, exactly max numbers are expected. Base 277 * and size tell us how to internalize the numbers once they've been 278 * tokenized. 279 */ 280 unsigned char * 281 parse_numeric_aggregate(FILE *cfile, unsigned char *buf, int *max, 282 int separator, int base, int size) 283 { 284 char *val, *t; 285 int token, count = 0; 286 unsigned char *bufp = buf, *s = NULL; 287 pair c = NULL; 288 289 if (!bufp && *max) { 290 bufp = malloc(*max * size / 8); 291 if (!bufp) 292 error("can't allocate space for numeric aggregate"); 293 } else 294 s = bufp; 295 296 do { 297 if (count) { 298 token = peek_token(&val, cfile); 299 if (token != separator) { 300 if (!*max) 301 break; 302 if (token != '{' && token != '}') 303 token = next_token(&val, cfile); 304 parse_warn("too few numbers."); 305 if (token != ';') 306 skip_to_semi(cfile); 307 return (NULL); 308 } 309 token = next_token(&val, cfile); 310 } 311 token = next_token(&val, cfile); 312 313 if (token == EOF) { 314 parse_warn("unexpected end of file"); 315 break; 316 } 317 /* Allow NUMBER_OR_NAME if base is 16. */ 318 if (token != TOK_NUMBER && 319 (base != 16 || token != TOK_NUMBER_OR_NAME)) { 320 parse_warn("expecting numeric value."); 321 skip_to_semi(cfile); 322 return (NULL); 323 } 324 /* 325 * If we can, convert the number now; otherwise, build a 326 * linked list of all the numbers. 327 */ 328 if (s) { 329 convert_num(s, val, base, size); 330 s += size / 8; 331 } else { 332 t = strdup(val); 333 if (t == NULL) 334 error("no temp space for number."); 335 c = cons(t, c); 336 } 337 } while (++count != *max); 338 339 /* If we had to cons up a list, convert it now. */ 340 if (c) { 341 bufp = malloc(count * size / 8); 342 if (!bufp) 343 error("can't allocate space for numeric aggregate."); 344 s = bufp + count - size / 8; 345 *max = count; 346 } 347 while (c) { 348 pair cdr = c->cdr; 349 convert_num(s, (char *)c->car, base, size); 350 s -= size / 8; 351 /* Free up temp space. */ 352 free(c->car); 353 free(c); 354 c = cdr; 355 } 356 return (bufp); 357 } 358 359 void 360 convert_num(unsigned char *buf, char *str, int base, int size) 361 { 362 int negative = 0, tval, max; 363 u_int32_t val = 0; 364 char *ptr = str; 365 366 if (*ptr == '-') { 367 negative = 1; 368 ptr++; 369 } 370 371 /* If base wasn't specified, figure it out from the data. */ 372 if (!base) { 373 if (ptr[0] == '0') { 374 if (ptr[1] == 'x') { 375 base = 16; 376 ptr += 2; 377 } else if (isascii((unsigned char)ptr[1]) && 378 isdigit((unsigned char)ptr[1])) { 379 base = 8; 380 ptr += 1; 381 } else 382 base = 10; 383 } else 384 base = 10; 385 } 386 387 do { 388 tval = *ptr++; 389 /* XXX assumes ASCII... */ 390 if (tval >= 'a') 391 tval = tval - 'a' + 10; 392 else if (tval >= 'A') 393 tval = tval - 'A' + 10; 394 else if (tval >= '0') 395 tval -= '0'; 396 else { 397 warning("Bogus number: %s.", str); 398 break; 399 } 400 if (tval >= base) { 401 warning("Bogus number: %s: digit %d not in base %d", 402 str, tval, base); 403 break; 404 } 405 val = val * base + tval; 406 } while (*ptr); 407 408 if (negative) 409 max = (1 << (size - 1)); 410 else 411 max = (1 << (size - 1)) + ((1 << (size - 1)) - 1); 412 if (val > max) { 413 switch (base) { 414 case 8: 415 warning("value %s%o exceeds max (%d) for precision.", 416 negative ? "-" : "", val, max); 417 break; 418 case 16: 419 warning("value %s%x exceeds max (%d) for precision.", 420 negative ? "-" : "", val, max); 421 break; 422 default: 423 warning("value %s%u exceeds max (%d) for precision.", 424 negative ? "-" : "", val, max); 425 break; 426 } 427 } 428 429 if (negative) { 430 switch (size) { 431 case 8: 432 *buf = -(unsigned long)val; 433 break; 434 case 16: 435 putShort(buf, -(unsigned long)val); 436 break; 437 case 32: 438 putLong(buf, -(unsigned long)val); 439 break; 440 default: 441 warning("Unexpected integer size: %d", size); 442 break; 443 } 444 } else { 445 switch (size) { 446 case 8: 447 *buf = (u_int8_t)val; 448 break; 449 case 16: 450 putUShort(buf, (u_int16_t)val); 451 break; 452 case 32: 453 putULong(buf, val); 454 break; 455 default: 456 warning("Unexpected integer size: %d", size); 457 break; 458 } 459 } 460 } 461 462 /* 463 * date :== NUMBER NUMBER SLASH NUMBER SLASH NUMBER 464 * NUMBER COLON NUMBER COLON NUMBER UTC SEMI 465 * 466 * Dates are always in UTC; first number is day of week; next is 467 * year/month/day; next is hours:minutes:seconds on a 24-hour 468 * clock. 469 */ 470 time_t 471 parse_date(FILE *cfile) 472 { 473 struct tm tm; 474 char timestr[26]; /* "w yyyy/mm/dd hh:mm:ss UTC" */ 475 char *val, *p; 476 size_t n; 477 time_t guess; 478 int token; 479 480 memset(timestr, 0, sizeof(timestr)); 481 482 do { 483 token = peek_token(NULL, cfile); 484 switch (token) { 485 case TOK_NAME: 486 case TOK_NUMBER: 487 case '/': 488 case ':': 489 token = next_token(&val, cfile); 490 n = strlcat(timestr, val, sizeof(timestr)); 491 if (n >= sizeof(timestr)) { 492 /* XXX Will break after year 9999! */ 493 parse_warn("time string too long"); 494 skip_to_semi(cfile); 495 return (0); 496 } 497 break; 498 case';': 499 break; 500 default: 501 parse_warn("invalid time string"); 502 skip_to_semi(cfile); 503 return (0); 504 } 505 } while (token != ';'); 506 507 parse_semi(cfile); 508 509 memset(&tm, 0, sizeof(tm)); /* 'cuz strptime ignores tm_isdt. */ 510 p = strptime(timestr, DB_TIMEFMT, &tm); 511 if (p == NULL || *p != '\0') { 512 p = strptime(timestr, OLD_DB_TIMEFMT, &tm); 513 if (p == NULL || *p != '\0') { 514 parse_warn("unparseable time string"); 515 return (0); 516 } 517 } 518 519 guess = timegm(&tm); 520 if (guess == -1) { 521 parse_warn("time could not be represented"); 522 return (0); 523 } 524 525 return (guess); 526 } 527