1 /* $FreeBSD$ */ 2 3 /* 4 * Copyright (C) 2012 by Darren Reed. 5 * 6 * See the IPFILTER.LICENCE file for details on licencing. 7 */ 8 #include <ctype.h> 9 #include "ipf.h" 10 #ifdef IPFILTER_SCAN 11 # include "netinet/ip_scan.h" 12 #endif 13 #include <sys/ioctl.h> 14 #include <syslog.h> 15 #ifdef TEST_LEXER 16 # define NO_YACC 17 union { 18 int num; 19 char *str; 20 struct in_addr ipa; 21 i6addr_t ip6; 22 } yylval; 23 #endif 24 #include "lexer.h" 25 #include "y.tab.h" 26 27 FILE *yyin; 28 29 #define ishex(c) (ISDIGIT(c) || ((c) >= 'a' && (c) <= 'f') || \ 30 ((c) >= 'A' && (c) <= 'F')) 31 #define TOOLONG -3 32 33 extern int string_start; 34 extern int string_end; 35 extern char *string_val; 36 extern int pos; 37 extern int yydebug; 38 39 char *yystr = NULL; 40 int yytext[YYBUFSIZ+1]; 41 char yychars[YYBUFSIZ+1]; 42 int yylineNum = 1; 43 int yypos = 0; 44 int yylast = -1; 45 int yydictfixed = 0; 46 int yyexpectaddr = 0; 47 int yybreakondot = 0; 48 int yyvarnext = 0; 49 int yytokentype = 0; 50 wordtab_t *yywordtab = NULL; 51 int yysavedepth = 0; 52 wordtab_t *yysavewords[30]; 53 54 55 static wordtab_t *yyfindkey(char *); 56 static int yygetc(int); 57 static void yyunputc(int); 58 static int yyswallow(int); 59 static char *yytexttostr(int, int); 60 static void yystrtotext(char *); 61 static char *yytexttochar(void); 62 63 static int 64 yygetc(int docont) 65 { 66 int c; 67 68 if (yypos < yylast) { 69 c = yytext[yypos++]; 70 if (c == '\n') 71 yylineNum++; 72 return (c); 73 } 74 75 if (yypos == YYBUFSIZ) 76 return (TOOLONG); 77 78 if (pos >= string_start && pos <= string_end) { 79 c = string_val[pos - string_start]; 80 yypos++; 81 } else { 82 c = fgetc(yyin); 83 if (docont && (c == '\\')) { 84 c = fgetc(yyin); 85 if (c == '\n') { 86 yylineNum++; 87 c = fgetc(yyin); 88 } 89 } 90 } 91 if (c == '\n') 92 yylineNum++; 93 yytext[yypos++] = c; 94 yylast = yypos; 95 yytext[yypos] = '\0'; 96 97 return (c); 98 } 99 100 101 static void 102 yyunputc(int c) 103 { 104 if (c == '\n') 105 yylineNum--; 106 yytext[--yypos] = c; 107 } 108 109 110 static int 111 yyswallow(int last) 112 { 113 int c; 114 115 while (((c = yygetc(0)) > '\0') && (c != last)) 116 ; 117 118 if (c != EOF) 119 yyunputc(c); 120 if (c == last) 121 return (0); 122 return (-1); 123 } 124 125 126 static char * 127 yytexttochar(void) 128 { 129 int i; 130 131 for (i = 0; i < yypos; i++) 132 yychars[i] = (char)(yytext[i] & 0xff); 133 yychars[i] = '\0'; 134 return (yychars); 135 } 136 137 138 static void 139 yystrtotext(char *str) 140 { 141 int len; 142 char *s; 143 144 len = strlen(str); 145 if (len > YYBUFSIZ) 146 len = YYBUFSIZ; 147 148 for (s = str; *s != '\0' && len > 0; s++, len--) 149 yytext[yylast++] = *s; 150 yytext[yylast] = '\0'; 151 } 152 153 154 static char * 155 yytexttostr(int offset, int max) 156 { 157 char *str; 158 int i; 159 160 if ((yytext[offset] == '\'' || yytext[offset] == '"') && 161 (yytext[offset] == yytext[offset + max - 1])) { 162 offset++; 163 max--; 164 } 165 166 if (max > yylast) 167 max = yylast; 168 str = malloc(max + 1); 169 if (str != NULL) { 170 for (i = offset; i < max; i++) 171 str[i - offset] = (char)(yytext[i] & 0xff); 172 str[i - offset] = '\0'; 173 } 174 return (str); 175 } 176 177 178 int 179 yylex(void) 180 { 181 static int prior = 0; 182 static int priornum = 0; 183 int c, n, isbuilding, rval, lnext, nokey = 0; 184 char *name; 185 int triedv6 = 0; 186 187 isbuilding = 0; 188 lnext = 0; 189 rval = 0; 190 191 if (yystr != NULL) { 192 free(yystr); 193 yystr = NULL; 194 } 195 196 nextchar: 197 c = yygetc(0); 198 if (yydebug > 1) 199 printf("yygetc = (%x) %c [%*.*s]\n", 200 c, c, yypos, yypos, yytexttochar()); 201 202 switch (c) 203 { 204 case '\n' : 205 lnext = 0; 206 nokey = 0; 207 case '\t' : 208 case '\r' : 209 case ' ' : 210 if (isbuilding == 1) { 211 yyunputc(c); 212 goto done; 213 } 214 if (yylast > yypos) { 215 bcopy(yytext + yypos, yytext, 216 sizeof(yytext[0]) * (yylast - yypos + 1)); 217 } 218 yylast -= yypos; 219 if (yyexpectaddr == 2) 220 yyexpectaddr = 0; 221 yypos = 0; 222 lnext = 0; 223 nokey = 0; 224 goto nextchar; 225 226 case '\\' : 227 if (lnext == 0) { 228 lnext = 1; 229 if (yylast == yypos) { 230 yylast--; 231 yypos--; 232 } else 233 yypos--; 234 if (yypos == 0) 235 nokey = 1; 236 goto nextchar; 237 } 238 break; 239 } 240 241 if (lnext == 1) { 242 lnext = 0; 243 if ((isbuilding == 0) && !ISALNUM(c)) { 244 prior = c; 245 return (c); 246 } 247 goto nextchar; 248 } 249 250 switch (c) 251 { 252 case '#' : 253 if (isbuilding == 1) { 254 yyunputc(c); 255 goto done; 256 } 257 yyswallow('\n'); 258 rval = YY_COMMENT; 259 goto done; 260 261 case '$' : 262 if (isbuilding == 1) { 263 yyunputc(c); 264 goto done; 265 } 266 n = yygetc(0); 267 if (n == '{') { 268 if (yyswallow('}') == -1) { 269 rval = -2; 270 goto done; 271 } 272 (void) yygetc(0); 273 } else { 274 if (!ISALPHA(n)) { 275 yyunputc(n); 276 break; 277 } 278 do { 279 n = yygetc(1); 280 } while (ISALPHA(n) || ISDIGIT(n) || n == '_'); 281 yyunputc(n); 282 } 283 284 name = yytexttostr(1, yypos); /* skip $ */ 285 286 if (name != NULL) { 287 string_val = get_variable(name, NULL, yylineNum); 288 free(name); 289 if (string_val != NULL) { 290 name = yytexttostr(yypos, yylast); 291 if (name != NULL) { 292 yypos = 0; 293 yylast = 0; 294 yystrtotext(string_val); 295 yystrtotext(name); 296 free(string_val); 297 free(name); 298 goto nextchar; 299 } 300 free(string_val); 301 } 302 } 303 break; 304 305 case '\'': 306 case '"' : 307 if (isbuilding == 1) { 308 goto done; 309 } 310 do { 311 n = yygetc(1); 312 if (n == EOF || n == TOOLONG) { 313 rval = -2; 314 goto done; 315 } 316 if (n == '\n') { 317 yyunputc(' '); 318 yypos++; 319 } 320 } while (n != c); 321 rval = YY_STR; 322 goto done; 323 /* NOTREACHED */ 324 325 case EOF : 326 yylineNum = 1; 327 yypos = 0; 328 yylast = -1; 329 yyexpectaddr = 0; 330 yybreakondot = 0; 331 yyvarnext = 0; 332 yytokentype = 0; 333 if (yydebug) 334 fprintf(stderr, "reset at EOF\n"); 335 prior = 0; 336 return (0); 337 } 338 339 if (strchr("=,/;{}()@", c) != NULL) { 340 if (isbuilding == 1) { 341 yyunputc(c); 342 goto done; 343 } 344 rval = c; 345 goto done; 346 } else if (c == '.') { 347 if (isbuilding == 0) { 348 rval = c; 349 goto done; 350 } 351 if (yybreakondot != 0) { 352 yyunputc(c); 353 goto done; 354 } 355 } 356 357 switch (c) 358 { 359 case '-' : 360 n = yygetc(0); 361 if (n == '>') { 362 isbuilding = 1; 363 goto done; 364 } 365 yyunputc(n); 366 if (yyexpectaddr) { 367 if (isbuilding == 1) 368 yyunputc(c); 369 else 370 rval = '-'; 371 goto done; 372 } 373 if (isbuilding == 1) 374 break; 375 rval = '-'; 376 goto done; 377 378 case '!' : 379 if (isbuilding == 1) { 380 yyunputc(c); 381 goto done; 382 } 383 n = yygetc(0); 384 if (n == '=') { 385 rval = YY_CMP_NE; 386 goto done; 387 } 388 yyunputc(n); 389 rval = '!'; 390 goto done; 391 392 case '<' : 393 if (yyexpectaddr) 394 break; 395 if (isbuilding == 1) { 396 yyunputc(c); 397 goto done; 398 } 399 n = yygetc(0); 400 if (n == '=') { 401 rval = YY_CMP_LE; 402 goto done; 403 } 404 if (n == '>') { 405 rval = YY_RANGE_OUT; 406 goto done; 407 } 408 yyunputc(n); 409 rval = YY_CMP_LT; 410 goto done; 411 412 case '>' : 413 if (yyexpectaddr) 414 break; 415 if (isbuilding == 1) { 416 yyunputc(c); 417 goto done; 418 } 419 n = yygetc(0); 420 if (n == '=') { 421 rval = YY_CMP_GE; 422 goto done; 423 } 424 if (n == '<') { 425 rval = YY_RANGE_IN; 426 goto done; 427 } 428 yyunputc(n); 429 rval = YY_CMP_GT; 430 goto done; 431 } 432 433 /* 434 * Now for the reason this is here...IPv6 address parsing. 435 * The longest string we can expect is of this form: 436 * 0000:0000:0000:0000:0000:0000:000.000.000.000 437 * not: 438 * 0000:0000:0000:0000:0000:0000:0000:0000 439 */ 440 #ifdef USE_INET6 441 if (yyexpectaddr != 0 && isbuilding == 0 && 442 (ishex(c) || isdigit(c) || c == ':')) { 443 char ipv6buf[45 + 1], *s, oc; 444 int start; 445 446 buildipv6: 447 start = yypos; 448 s = ipv6buf; 449 oc = c; 450 451 if (prior == YY_NUMBER && c == ':') { 452 snprintf(s, sizeof(s), "%d", priornum); 453 s += strlen(s); 454 } 455 456 /* 457 * Perhaps we should implement stricter controls on what we 458 * swallow up here, but surely it would just be duplicating 459 * the code in inet_pton() anyway. 460 */ 461 do { 462 *s++ = c; 463 c = yygetc(1); 464 } while ((ishex(c) || c == ':' || c == '.') && 465 (s - ipv6buf < 46)); 466 yyunputc(c); 467 *s = '\0'; 468 469 if (inet_pton(AF_INET6, ipv6buf, &yylval.ip6) == 1) { 470 rval = YY_IPV6; 471 yyexpectaddr = 0; 472 goto done; 473 } 474 yypos = start; 475 c = oc; 476 } 477 #endif 478 479 if ((c == ':') && (rval != YY_IPV6) && (triedv6 == 0)) { 480 #ifdef USE_INET6 481 yystr = yytexttostr(0, yypos - 1); 482 if (yystr != NULL) { 483 char *s; 484 485 for (s = yystr; *s && ishex(*s); s++) 486 ; 487 if (!*s && *yystr) { 488 isbuilding = 0; 489 c = *yystr; 490 free(yystr); 491 triedv6 = 1; 492 yypos = 1; 493 goto buildipv6; 494 } 495 free(yystr); 496 } 497 #endif 498 if (isbuilding == 1) { 499 yyunputc(c); 500 goto done; 501 } 502 rval = ':'; 503 goto done; 504 } 505 506 if (isbuilding == 0 && c == '0') { 507 n = yygetc(0); 508 if (n == 'x') { 509 do { 510 n = yygetc(1); 511 } while (ishex(n)); 512 yyunputc(n); 513 rval = YY_HEX; 514 goto done; 515 } 516 yyunputc(n); 517 } 518 519 /* 520 * No negative numbers with leading - sign.. 521 */ 522 if (isbuilding == 0 && ISDIGIT(c)) { 523 do { 524 n = yygetc(1); 525 } while (ISDIGIT(n)); 526 yyunputc(n); 527 rval = YY_NUMBER; 528 goto done; 529 } 530 531 isbuilding = 1; 532 goto nextchar; 533 534 done: 535 yystr = yytexttostr(0, yypos); 536 537 if (yydebug) 538 printf("isbuilding %d yyvarnext %d nokey %d fixed %d addr %d\n", 539 isbuilding, yyvarnext, nokey, yydictfixed, yyexpectaddr); 540 if (isbuilding == 1) { 541 wordtab_t *w; 542 543 w = NULL; 544 isbuilding = 0; 545 546 if ((yyvarnext == 0) && (nokey == 0)) { 547 w = yyfindkey(yystr); 548 if (w == NULL && yywordtab != NULL && !yydictfixed) { 549 yyresetdict(); 550 w = yyfindkey(yystr); 551 } 552 } else 553 yyvarnext = 0; 554 if (w != NULL) 555 rval = w->w_value; 556 else 557 rval = YY_STR; 558 } 559 560 if (rval == YY_STR) { 561 if (yysavedepth > 0 && !yydictfixed) 562 yyresetdict(); 563 if (yyexpectaddr != 0) 564 yyexpectaddr = 0; 565 } 566 567 yytokentype = rval; 568 569 if (yydebug) 570 printf("lexed(%s) %d,%d,%d [%d,%d,%d] => %d @%d\n", 571 yystr, isbuilding, yyexpectaddr, yysavedepth, 572 string_start, string_end, pos, rval, yysavedepth); 573 574 switch (rval) 575 { 576 case YY_NUMBER : 577 sscanf(yystr, "%u", &yylval.num); 578 break; 579 580 case YY_HEX : 581 sscanf(yystr, "0x%x", (u_int *)&yylval.num); 582 break; 583 584 case YY_STR : 585 yylval.str = strdup(yystr); 586 break; 587 588 default : 589 break; 590 } 591 592 if (yylast > 0) { 593 bcopy(yytext + yypos, yytext, 594 sizeof(yytext[0]) * (yylast - yypos + 1)); 595 yylast -= yypos; 596 yypos = 0; 597 } 598 599 if (rval == YY_NUMBER) 600 priornum = yylval.num; 601 prior = rval; 602 return (rval); 603 } 604 605 606 static wordtab_t *yyfindkey(key) 607 char *key; 608 { 609 wordtab_t *w; 610 611 if (yywordtab == NULL) 612 return (NULL); 613 614 for (w = yywordtab; w->w_word != 0; w++) 615 if (strcasecmp(key, w->w_word) == 0) 616 return (w); 617 return (NULL); 618 } 619 620 621 char * 622 yykeytostr(int num) 623 { 624 wordtab_t *w; 625 626 if (yywordtab == NULL) 627 return ("<unknown>"); 628 629 for (w = yywordtab; w->w_word; w++) 630 if (w->w_value == num) 631 return (w->w_word); 632 return ("<unknown>"); 633 } 634 635 636 wordtab_t * 637 yysettab(wordtab_t *words) 638 { 639 wordtab_t *save; 640 641 save = yywordtab; 642 yywordtab = words; 643 return (save); 644 } 645 646 647 void 648 yyerror(char *msg) 649 { 650 char *txt, letter[2]; 651 int freetxt = 0; 652 653 if (yytokentype < 256) { 654 letter[0] = yytokentype; 655 letter[1] = '\0'; 656 txt = letter; 657 } else if (yytokentype == YY_STR || yytokentype == YY_HEX || 658 yytokentype == YY_NUMBER) { 659 if (yystr == NULL) { 660 txt = yytexttostr(yypos, YYBUFSIZ); 661 freetxt = 1; 662 } else 663 txt = yystr; 664 } else { 665 txt = yykeytostr(yytokentype); 666 } 667 fprintf(stderr, "%s error at \"%s\", line %d\n", msg, txt, yylineNum); 668 if (freetxt == 1) 669 free(txt); 670 exit(1); 671 } 672 673 674 void 675 yysetfixeddict(wordtab_t *newdict) 676 { 677 if (yydebug) 678 printf("yysetfixeddict(%lx)\n", (u_long)newdict); 679 680 if (yysavedepth == sizeof(yysavewords)/sizeof(yysavewords[0])) { 681 fprintf(stderr, "%d: at maximum dictionary depth\n", 682 yylineNum); 683 return; 684 } 685 686 yysavewords[yysavedepth++] = yysettab(newdict); 687 if (yydebug) 688 printf("yysavedepth++ => %d\n", yysavedepth); 689 yydictfixed = 1; 690 } 691 692 693 void 694 yysetdict(wordtab_t *newdict) 695 { 696 if (yydebug) 697 printf("yysetdict(%lx)\n", (u_long)newdict); 698 699 if (yysavedepth == sizeof(yysavewords)/sizeof(yysavewords[0])) { 700 fprintf(stderr, "%d: at maximum dictionary depth\n", 701 yylineNum); 702 return; 703 } 704 705 yysavewords[yysavedepth++] = yysettab(newdict); 706 if (yydebug) 707 printf("yysavedepth++ => %d\n", yysavedepth); 708 } 709 710 void 711 yyresetdict(void) 712 { 713 if (yydebug) 714 printf("yyresetdict(%d)\n", yysavedepth); 715 if (yysavedepth > 0) { 716 yysettab(yysavewords[--yysavedepth]); 717 if (yydebug) 718 printf("yysavedepth-- => %d\n", yysavedepth); 719 } 720 yydictfixed = 0; 721 } 722 723 724 725 #ifdef TEST_LEXER 726 int 727 main(int argc, char *argv[]) 728 { 729 int n; 730 731 yyin = stdin; 732 733 while ((n = yylex()) != 0) 734 printf("%d.n = %d [%s] %d %d\n", 735 yylineNum, n, yystr, yypos, yylast); 736 } 737 #endif 738