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