1 /*        $NetBSD: lexer.c,v 1.2 2014/12/20 13:15:48 prlw1 Exp $      */
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 __P((char *));
56 static    int                 yygetc __P((int));
57 static    void                yyunputc __P((int));
58 static    int                 yyswallow __P((int));
59 static    char                *yytexttostr __P((int, int));
60 static    void                yystrtotext __P((char *));
61 static    char                *yytexttochar __P((void));
62 
yygetc(docont)63 static int yygetc(docont)
64           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 
yyunputc(c)101 static void yyunputc(c)
102           int c;
103 {
104           if (c == '\n')
105                     yylineNum--;
106           yytext[--yypos] = c;
107 }
108 
109 
yyswallow(last)110 static int yyswallow(last)
111           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 
yytexttochar()126 static char *yytexttochar()
127 {
128           int i;
129 
130           for (i = 0; i < yypos; i++)
131                     yychars[i] = (char)(yytext[i] & 0xff);
132           yychars[i] = '\0';
133           return yychars;
134 }
135 
136 
yystrtotext(str)137 static void yystrtotext(str)
138           char *str;
139 {
140           int len;
141           char *s;
142 
143           len = strlen(str);
144           if (len > YYBUFSIZ)
145                     len = YYBUFSIZ;
146 
147           for (s = str; *s != '\0' && len > 0; s++, len--)
148                     yytext[yylast++] = *s;
149           yytext[yylast] = '\0';
150 }
151 
152 
yytexttostr(offset,max)153 static char *yytexttostr(offset, max)
154           int offset, max;
155 {
156           char *str;
157           int i;
158 
159           if ((yytext[offset] == '\'' || yytext[offset] == '"') &&
160               (yytext[offset] == yytext[offset + max - 1])) {
161                     offset++;
162                     max--;
163           }
164 
165           if (max > yylast)
166                     max = yylast;
167           str = malloc(max + 1);
168           if (str != NULL) {
169                     for (i = offset; i < max; i++)
170                               str[i - offset] = (char)(yytext[i] & 0xff);
171                     str[i - offset] = '\0';
172           }
173           return str;
174 }
175 
176 
yylex()177 int yylex()
178 {
179 #ifdef    USE_INET6
180           static int prior = 0;
181           static int priornum = 0;
182 #endif
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 #ifdef    USE_INET6
245                               prior = c;
246 #endif
247                               return c;
248                     }
249                     goto nextchar;
250           }
251 
252           switch (c)
253           {
254           case '#' :
255                     if (isbuilding == 1) {
256                               yyunputc(c);
257                               goto done;
258                     }
259                     yyswallow('\n');
260                     rval = YY_COMMENT;
261                     goto done;
262 
263           case '$' :
264                     if (isbuilding == 1) {
265                               yyunputc(c);
266                               goto done;
267                     }
268                     n = yygetc(0);
269                     if (n == '{') {
270                               if (yyswallow('}') == -1) {
271                                         rval = -2;
272                                         goto done;
273                               }
274                               (void) yygetc(0);
275                     } else {
276                               if (!ISALPHA(n)) {
277                                         yyunputc(n);
278                                         break;
279                               }
280                               do {
281                                         n = yygetc(1);
282                               } while (ISALPHA(n) || ISDIGIT(n) || n == '_');
283                               yyunputc(n);
284                     }
285 
286                     name = yytexttostr(1, yypos);           /* skip $ */
287 
288                     if (name != NULL) {
289                               string_val = get_variable(name, NULL, yylineNum);
290                               free(name);
291                               if (string_val != NULL) {
292                                         name = yytexttostr(yypos, yylast);
293                                         if (name != NULL) {
294                                                   yypos = 0;
295                                                   yylast = 0;
296                                                   yystrtotext(string_val);
297                                                   yystrtotext(name);
298                                                   free(string_val);
299                                                   free(name);
300                                                   goto nextchar;
301                                         }
302                                         free(string_val);
303                               }
304                     }
305                     break;
306 
307           case '\'':
308           case '"' :
309                     if (isbuilding == 1) {
310                               goto done;
311                     }
312                     do {
313                               n = yygetc(1);
314                               if (n == EOF || n == TOOLONG) {
315                                         rval = -2;
316                                         goto done;
317                               }
318                               if (n == '\n') {
319                                         yyunputc(' ');
320                                         yypos++;
321                               }
322                     } while (n != c);
323                     rval = YY_STR;
324                     goto done;
325                     /* NOTREACHED */
326 
327           case EOF :
328                     yylineNum = 1;
329                     yypos = 0;
330                     yylast = -1;
331                     yyexpectaddr = 0;
332                     yybreakondot = 0;
333                     yyvarnext = 0;
334                     yytokentype = 0;
335                     if (yydebug)
336                               fprintf(stderr, "reset at EOF\n");
337 #ifdef    USE_INET6
338                     prior = 0;
339 #endif
340                     return 0;
341           }
342 
343           if (strchr("=,/;{}()@", c) != NULL) {
344                     if (isbuilding == 1) {
345                               yyunputc(c);
346                               goto done;
347                     }
348                     rval = c;
349                     goto done;
350           } else if (c == '.') {
351                     if (isbuilding == 0) {
352                               rval = c;
353                               goto done;
354                     }
355                     if (yybreakondot != 0) {
356                               yyunputc(c);
357                               goto done;
358                     }
359           }
360 
361           switch (c)
362           {
363           case '-' :
364                     n = yygetc(0);
365                     if (n == '>') {
366                               isbuilding = 1;
367                               goto done;
368                     }
369                     yyunputc(n);
370                     if (yyexpectaddr) {
371                               if (isbuilding == 1)
372                                         yyunputc(c);
373                               else
374                                         rval = '-';
375                               goto done;
376                     }
377                     if (isbuilding == 1)
378                               break;
379                     rval = '-';
380                     goto done;
381 
382           case '!' :
383                     if (isbuilding == 1) {
384                               yyunputc(c);
385                               goto done;
386                     }
387                     n = yygetc(0);
388                     if (n == '=') {
389                               rval = YY_CMP_NE;
390                               goto done;
391                     }
392                     yyunputc(n);
393                     rval = '!';
394                     goto done;
395 
396           case '<' :
397                     if (yyexpectaddr)
398                               break;
399                     if (isbuilding == 1) {
400                               yyunputc(c);
401                               goto done;
402                     }
403                     n = yygetc(0);
404                     if (n == '=') {
405                               rval = YY_CMP_LE;
406                               goto done;
407                     }
408                     if (n == '>') {
409                               rval = YY_RANGE_OUT;
410                               goto done;
411                     }
412                     yyunputc(n);
413                     rval = YY_CMP_LT;
414                     goto done;
415 
416           case '>' :
417                     if (yyexpectaddr)
418                               break;
419                     if (isbuilding == 1) {
420                               yyunputc(c);
421                               goto done;
422                     }
423                     n = yygetc(0);
424                     if (n == '=') {
425                               rval = YY_CMP_GE;
426                               goto done;
427                     }
428                     if (n == '<') {
429                               rval = YY_RANGE_IN;
430                               goto done;
431                     }
432                     yyunputc(n);
433                     rval = YY_CMP_GT;
434                     goto done;
435           }
436 
437           /*
438            * Now for the reason this is here...IPv6 address parsing.
439            * The longest string we can expect is of this form:
440            * 0000:0000:0000:0000:0000:0000:000.000.000.000
441            * not:
442            * 0000:0000:0000:0000:0000:0000:0000:0000
443            */
444 #ifdef    USE_INET6
445           if (yyexpectaddr != 0 && isbuilding == 0 &&
446               (ishex(c) || isdigit(c) || c == ':')) {
447                     char ipv6buf[45 + 1], *s, oc;
448                     int start;
449 
450 buildipv6:
451                     start = yypos;
452                     s = ipv6buf;
453                     oc = c;
454 
455                     if (prior == YY_NUMBER && c == ':') {
456                               sprintf(s, "%d", priornum);
457                               s += strlen(s);
458                     }
459 
460                     /*
461                      * Perhaps we should implement stricter controls on what we
462                      * swallow up here, but surely it would just be duplicating
463                      * the code in inet_pton() anyway.
464                      */
465                     do {
466                               *s++ = c;
467                               c = yygetc(1);
468                     } while ((ishex(c) || c == ':' || c == '.') &&
469                                (s - ipv6buf < 46));
470                     yyunputc(c);
471                     *s = '\0';
472 
473                     if (inet_pton(AF_INET6, ipv6buf, &yylval.ip6) == 1) {
474                               rval = YY_IPV6;
475                               yyexpectaddr = 0;
476                               goto done;
477                     }
478                     yypos = start;
479                     c = oc;
480           }
481 #endif
482 
483           if ((c == ':') && (rval != YY_IPV6) && (triedv6 == 0)) {
484 #ifdef    USE_INET6
485                     yystr = yytexttostr(0, yypos - 1);
486                     if (yystr != NULL) {
487                               char *s;
488 
489                               for (s = yystr; *s && ishex(*s); s++)
490                                         ;
491                               if (!*s && *yystr) {
492                                         isbuilding = 0;
493                                         c = *yystr;
494                                         free(yystr);
495                                         triedv6 = 1;
496                                         yypos = 1;
497                                         goto buildipv6;
498                               }
499                               free(yystr);
500                     }
501 #endif
502                     if (isbuilding == 1) {
503                               yyunputc(c);
504                               goto done;
505                     }
506                     rval = ':';
507                     goto done;
508           }
509 
510           if (isbuilding == 0 && c == '0') {
511                     n = yygetc(0);
512                     if (n == 'x') {
513                               do {
514                                         n = yygetc(1);
515                               } while (ishex(n));
516                               yyunputc(n);
517                               rval = YY_HEX;
518                               goto done;
519                     }
520                     yyunputc(n);
521           }
522 
523           /*
524            * No negative numbers with leading - sign..
525            */
526           if (isbuilding == 0 && ISDIGIT(c)) {
527                     do {
528                               n = yygetc(1);
529                     } while (ISDIGIT(n));
530                     yyunputc(n);
531                     rval = YY_NUMBER;
532                     goto done;
533           }
534 
535           isbuilding = 1;
536           goto nextchar;
537 
538 done:
539           yystr = yytexttostr(0, yypos);
540 
541           if (yydebug)
542                     printf("isbuilding %d yyvarnext %d nokey %d fixed %d addr %d\n",
543                            isbuilding, yyvarnext, nokey, yydictfixed, yyexpectaddr);
544           if (isbuilding == 1) {
545                     wordtab_t *w;
546 
547                     w = NULL;
548                     isbuilding = 0;
549 
550                     if ((yyvarnext == 0) && (nokey == 0)) {
551                               w = yyfindkey(yystr);
552                               if (w == NULL && yywordtab != NULL && !yydictfixed) {
553                                         yyresetdict();
554                                         w = yyfindkey(yystr);
555                               }
556                     } else
557                               yyvarnext = 0;
558                     if (w != NULL)
559                               rval = w->w_value;
560                     else
561                               rval = YY_STR;
562           }
563 
564           if (rval == YY_STR) {
565                     if (yysavedepth > 0 && !yydictfixed)
566                               yyresetdict();
567                     if (yyexpectaddr != 0)
568                               yyexpectaddr = 0;
569           }
570 
571           yytokentype = rval;
572 
573           if (yydebug)
574                     printf("lexed(%s) %d,%d,%d [%d,%d,%d] => %d @%d\n",
575                            yystr, isbuilding, yyexpectaddr, yysavedepth,
576                            string_start, string_end, pos, rval, yysavedepth);
577 
578           switch (rval)
579           {
580           case YY_NUMBER :
581                     sscanf(yystr, "%u", &yylval.num);
582                     break;
583 
584           case YY_HEX :
585                     sscanf(yystr, "0x%x", (u_int *)&yylval.num);
586                     break;
587 
588           case YY_STR :
589                     yylval.str = strdup(yystr);
590                     break;
591 
592           default :
593                     break;
594           }
595 
596           if (yylast > 0) {
597                     bcopy(yytext + yypos, yytext,
598                           sizeof(yytext[0]) * (yylast - yypos + 1));
599                     yylast -= yypos;
600                     yypos = 0;
601           }
602 
603 #ifdef    USE_INET6
604           if (rval == YY_NUMBER)
605                     priornum = yylval.num;
606           prior = rval;
607 #endif
608           return rval;
609 }
610 
611 
yyfindkey(key)612 static wordtab_t *yyfindkey(key)
613           char *key;
614 {
615           wordtab_t *w;
616 
617           if (yywordtab == NULL)
618                     return NULL;
619 
620           for (w = yywordtab; w->w_word != 0; w++)
621                     if (strcasecmp(key, w->w_word) == 0)
622                               return w;
623           return NULL;
624 }
625 
626 
yykeytostr(num)627 char *yykeytostr(num)
628           int num;
629 {
630           wordtab_t *w;
631 
632           if (yywordtab == NULL)
633                     return "<unknown>";
634 
635           for (w = yywordtab; w->w_word; w++)
636                     if (w->w_value == num)
637                               return w->w_word;
638           return "<unknown>";
639 }
640 
641 
yysettab(words)642 wordtab_t *yysettab(words)
643           wordtab_t *words;
644 {
645           wordtab_t *save;
646 
647           save = yywordtab;
648           yywordtab = words;
649           return save;
650 }
651 
652 
yyerror(msg)653 void yyerror(msg)
654           char *msg;
655 {
656           char *txt, letter[2];
657           int freetxt = 0;
658 
659           if (yytokentype < 256) {
660                     letter[0] = yytokentype;
661                     letter[1] = '\0';
662                     txt =  letter;
663           } else if (yytokentype == YY_STR || yytokentype == YY_HEX ||
664                        yytokentype == YY_NUMBER) {
665                     if (yystr == NULL) {
666                               txt = yytexttostr(yypos, YYBUFSIZ);
667                               freetxt = 1;
668                     } else
669                               txt = yystr;
670           } else {
671                     txt = yykeytostr(yytokentype);
672           }
673           fprintf(stderr, "%s error at \"%s\", line %d\n", msg, txt, yylineNum);
674           if (freetxt == 1)
675                     free(txt);
676           exit(1);
677 }
678 
679 
yysetfixeddict(newdict)680 void yysetfixeddict(newdict)
681           wordtab_t *newdict;
682 {
683           if (yydebug)
684                     printf("yysetfixeddict(%lx)\n", (u_long)newdict);
685 
686           if (yysavedepth == sizeof(yysavewords)/sizeof(yysavewords[0])) {
687                     fprintf(stderr, "%d: at maximum dictionary depth\n",
688                               yylineNum);
689                     return;
690           }
691 
692           yysavewords[yysavedepth++] = yysettab(newdict);
693           if (yydebug)
694                     printf("yysavedepth++ => %d\n", yysavedepth);
695           yydictfixed = 1;
696 }
697 
698 
yysetdict(newdict)699 void yysetdict(newdict)
700           wordtab_t *newdict;
701 {
702           if (yydebug)
703                     printf("yysetdict(%lx)\n", (u_long)newdict);
704 
705           if (yysavedepth == sizeof(yysavewords)/sizeof(yysavewords[0])) {
706                     fprintf(stderr, "%d: at maximum dictionary depth\n",
707                               yylineNum);
708                     return;
709           }
710 
711           yysavewords[yysavedepth++] = yysettab(newdict);
712           if (yydebug)
713                     printf("yysavedepth++ => %d\n", yysavedepth);
714 }
715 
yyresetdict()716 void yyresetdict()
717 {
718           if (yydebug)
719                     printf("yyresetdict(%d)\n", yysavedepth);
720           if (yysavedepth > 0) {
721                     yysettab(yysavewords[--yysavedepth]);
722                     if (yydebug)
723                               printf("yysavedepth-- => %d\n", yysavedepth);
724           }
725           yydictfixed = 0;
726 }
727 
728 
729 
730 #ifdef    TEST_LEXER
main(argc,argv)731 int main(argc, argv)
732           int argc;
733           char *argv[];
734 {
735           int n;
736 
737           yyin = stdin;
738 
739           while ((n = yylex()) != 0)
740                     printf("%d.n = %d [%s] %d %d\n",
741                               yylineNum, n, yystr, yypos, yylast);
742 }
743 #endif
744