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