735e37caef9911a69c97e41de9fb3c96c80f1900
[metze/wireshark/wip.git] / epan / dfilter / scanner.l
1 %top {
2 /* Include this before everything else, for various large-file definitions */
3 #include "config.h"
4 }
5
6 /*
7  * We want a reentrant scanner.
8  */
9 %option reentrant
10
11 /*
12  * We don't use input, so don't generate code for it.
13  */
14 %option noinput
15
16 /*
17  * We don't use unput, so don't generate code for it.
18  */
19 %option nounput
20
21 /*
22  * We don't read interactively from the terminal.
23  */
24 %option never-interactive
25
26 /*
27  * Prefix scanner routines with "df_" rather than "yy", so this scanner
28  * can coexist with other scanners.
29  */
30 %option prefix="df_"
31
32 /*
33  * We're reading from a string, so we don't need yywrap.
34  */
35 %option noyywrap
36
37 /*
38  * The type for the state we keep for a scanner.
39  */
40 %option extra-type="df_scanner_state_t *"
41
42 /*
43  * We have to override the memory allocators so that we don't get
44  * "unused argument" warnings from the yyscanner argument (which
45  * we don't use, as we have a global memory allocator).
46  *
47  * We provide, as macros, our own versions of the routines generated by Flex,
48  * which just call malloc()/realloc()/free() (as the Flex versions do),
49  * discarding the extra argument.
50  */
51 %option noyyalloc
52 %option noyyrealloc
53 %option noyyfree
54
55 %{
56 /*
57  * Wireshark - Network traffic analyzer
58  * By Gerald Combs <gerald@wireshark.org>
59  * Copyright 2001 Gerald Combs
60  *
61  * SPDX-License-Identifier: GPL-2.0-or-later
62  */
63
64 #include <stdlib.h>
65 #include <errno.h>
66
67 #include "dfilter-int.h"
68 #include "syntax-tree.h"
69 #include "grammar.h"
70 #include "dfunctions.h"
71
72 /*
73  * Disable diagnostics in the code generated by Flex.
74  */
75 DIAG_OFF_FLEX
76
77 #define LVAL            df_lval
78 #define LVAL_TYPE       stnode_t*
79 #define LVAL_INIT_VAL   NULL
80 #define MODNAME         df
81 #define FLEX_YY_PREFIX  df_
82
83 #include <lemonflex-head.inc>
84
85 /*#undef YY_NO_UNPUT*/
86
87 static int set_lval(int token, gpointer data);
88 static int set_lval_int(dfwork_t *dfw, int token, char *s);
89 static int simple(int token);
90 static gboolean str_to_gint32(dfwork_t *dfw, char *s, gint32* pint);
91 static void mark_lval_deprecated(const char *s);
92
93 /*
94  * Sleazy hack to suppress compiler warnings in yy_fatal_error().
95  */
96 #define YY_EXIT_FAILURE ((void)yyscanner, 2)
97
98 /*
99  * Macros for the allocators, to discard the extra argument.
100  */
101 #define df_alloc(size, yyscanner)               (void *)malloc(size)
102 #define df_realloc(ptr, size, yyscanner)        (void *)realloc((char *)(ptr), (size))
103 #define df_free(ptr, yyscanner)                 free((char *)ptr)
104
105 %}
106
107 %x RANGE_INT
108 %x RANGE_PUNCT
109 %x DQUOTE
110 %x SQUOTE
111
112 %%
113
114 [[:blank:]\n]+          {
115         /* Ignore whitespace, unless set elements are being parsed. Perhaps it
116          * should have used commas from the beginning, but now we are stuck with
117          * whitespace as separators. */
118         if (yyextra->in_set) {
119                 return simple(TOKEN_WHITESPACE);
120         }
121 }
122
123
124
125 "("                             return simple(TOKEN_LPAREN);
126 ")"                             return simple(TOKEN_RPAREN);
127 ","                             return simple(TOKEN_COMMA);
128
129 "{"[[:blank:]\n]*       {
130         yyextra->in_set = TRUE;
131         return simple(TOKEN_LBRACE);
132 }
133 [[:blank:]\n]*".."[[:blank:]\n]*        return simple(TOKEN_DOTDOT);
134 [[:blank:]\n]*"}"       {
135         yyextra->in_set = FALSE;
136         return simple(TOKEN_RBRACE);
137 }
138
139 "=="                    return simple(TOKEN_TEST_EQ);
140 "eq"                    return simple(TOKEN_TEST_EQ);
141 "!="                    {
142         mark_lval_deprecated("!=");
143         return simple(TOKEN_TEST_NE);
144 }
145 "ne"                    {
146         mark_lval_deprecated("ne");
147         return simple(TOKEN_TEST_NE);
148 }
149 ">"                             return simple(TOKEN_TEST_GT);
150 "gt"                    return simple(TOKEN_TEST_GT);
151 ">="                    return simple(TOKEN_TEST_GE);
152 "ge"                    return simple(TOKEN_TEST_GE);
153 "<"                             return simple(TOKEN_TEST_LT);
154 "lt"                    return simple(TOKEN_TEST_LT);
155 "<="                    return simple(TOKEN_TEST_LE);
156 "le"                    return simple(TOKEN_TEST_LE);
157 "bitwise_and"   return simple(TOKEN_TEST_BITWISE_AND);
158 "&"                             return simple(TOKEN_TEST_BITWISE_AND);
159 "contains"              return simple(TOKEN_TEST_CONTAINS);
160 "~"                             return simple(TOKEN_TEST_MATCHES);
161 "matches"               return simple(TOKEN_TEST_MATCHES);
162 "!"                             return simple(TOKEN_TEST_NOT);
163 "not"                   return simple(TOKEN_TEST_NOT);
164 "&&"                    return simple(TOKEN_TEST_AND);
165 "and"                   return simple(TOKEN_TEST_AND);
166 "||"                    return simple(TOKEN_TEST_OR);
167 "or"                    return simple(TOKEN_TEST_OR);
168 "in"                    return simple(TOKEN_TEST_IN);
169
170
171 "["                                     {
172         BEGIN(RANGE_INT);
173         return simple(TOKEN_LBRACKET);
174 }
175
176 <RANGE_INT>[+-]?[[:digit:]]+            {
177         BEGIN(RANGE_PUNCT);
178         return set_lval_int(yyextra->dfw, TOKEN_INTEGER, yytext);
179 }
180
181 <RANGE_INT>[+-]?0x[[:xdigit:]]+         {
182         BEGIN(RANGE_PUNCT);
183         return set_lval_int(yyextra->dfw, TOKEN_INTEGER, yytext);
184 }
185
186 <RANGE_INT,RANGE_PUNCT>":"              {
187         BEGIN(RANGE_INT);
188         return simple(TOKEN_COLON);
189 }
190
191 <RANGE_PUNCT>"-"                        {
192         BEGIN(RANGE_INT);
193         return simple(TOKEN_HYPHEN);
194 }
195
196 <RANGE_INT,RANGE_PUNCT>","              {
197         BEGIN(RANGE_INT);
198         return simple(TOKEN_COMMA);
199 }
200
201 <RANGE_INT,RANGE_PUNCT>"]"              {
202         BEGIN(INITIAL);
203         return simple(TOKEN_RBRACKET);
204 }
205
206         /* Error if none of the above while scanning a range (slice) */
207
208 <RANGE_PUNCT>[^:\-,\]]+         {
209         dfilter_fail(yyextra->dfw, "Invalid string \"%s\" found while scanning slice.", yytext);
210         return SCAN_FAILED;
211 }
212
213         /* XXX It would be nice to be able to match an entire non-integer string,
214          * but beware of Flex's "match the most text" rule.
215          */
216
217 <RANGE_INT>.    {
218         dfilter_fail(yyextra->dfw, "Invalid character \"%s\" found while scanning slice; expected integer.", yytext);
219         return SCAN_FAILED;
220 }
221
222 \042                            {
223         /* start quote of a quoted string */
224         /* The example of how to scan for strings was taken from
225         the flex 2.5.4 manual, from the section "Start Conditions".
226         See:
227         http://www.gnu.org/software/flex/manual/html_node/flex_11.html */
228
229         BEGIN(DQUOTE);
230         /* A previous filter that failed to compile due to
231         a missing end quote will have left quoted_string set
232         to something. Clear it now that we are starting
233         a new quoted string. */
234         if (yyextra->quoted_string) {
235                 g_string_free(yyextra->quoted_string, TRUE);
236                 /* Don't set quoted_string to NULL, as we
237                 do in other quoted_string-cleanup code, as we're
238                 about to set it in the next line. */
239         }
240         yyextra->quoted_string = g_string_new("");
241 }
242
243 <DQUOTE><<EOF>>                         {
244         /* unterminated string */
245         /* The example of how to handle unclosed strings was taken from
246         the flex 2.5.4 manual, from the section "End-of-file rules".
247         See:
248         http://www.gnu.org/software/flex/manual/html_node/flex_13.html */
249
250         dfilter_fail(yyextra->dfw, "The final quote was missing from a quoted string.");
251         return SCAN_FAILED;
252 }
253
254 <DQUOTE>\042                    {
255         /* end quote */
256         int token;
257         BEGIN(INITIAL);
258         token = set_lval(TOKEN_STRING, yyextra->quoted_string->str);
259         g_string_free(yyextra->quoted_string, TRUE);
260         yyextra->quoted_string = NULL;
261         return token;
262 }
263
264 <DQUOTE>\\[0-7]{1,3} {
265         /* octal sequence */
266         unsigned long result;
267         result = strtoul(yytext + 1, NULL, 8);
268         if (result > 0xff) {
269                 g_string_free(yyextra->quoted_string, TRUE);
270                 yyextra->quoted_string = NULL;
271                 dfilter_fail(yyextra->dfw, "%s is larger than 255.", yytext);
272                 return SCAN_FAILED;
273         }
274         g_string_append_c(yyextra->quoted_string, (gchar) result);
275 }
276
277 <DQUOTE>\\x[[:xdigit:]]{1,2} {
278         /* hex sequence */
279         unsigned long result;
280         result = strtoul(yytext + 2, NULL, 16);
281         g_string_append_c(yyextra->quoted_string, (gchar) result);
282 }
283
284
285 <DQUOTE>\\.                             {
286         /* escaped character */
287         g_string_append_c(yyextra->quoted_string, yytext[1]);
288 }
289
290 <DQUOTE>[^\\\042]+                      {
291         /* non-escaped string */
292         g_string_append(yyextra->quoted_string, yytext);
293 }
294
295
296 \047                            {
297         /* start quote of a quoted character value */
298         /* The example of how to scan for strings was taken from
299         the Flex manual, from the section "Start Conditions".
300         See:
301         http://flex.sourceforge.net/manual/Start-Conditions.html#Start-Conditions */
302
303         BEGIN(SQUOTE);
304         /* A previous filter that failed to compile due to
305         a missing end quote will have left quoted_string set
306         to something. Clear it now that we are starting
307         a new quoted string. */
308         if (yyextra->quoted_string) {
309                 g_string_free(yyextra->quoted_string, TRUE);
310                 /* Don't set quoted_string to NULL, as we
311                 do in other quoted_string-cleanup code, as we're
312                 about to set it in the next line. */
313         }
314         yyextra->quoted_string = g_string_new("'");
315 }
316
317 <SQUOTE><<EOF>>                         {
318         /* unterminated character value */
319         /* The example of how to handle unclosed strings was taken from
320         the Flex manual, from the section "End-of-file rules".
321         See:
322         http://flex.sourceforge.net/manual/EOF.html#EOF.html */
323
324         dfilter_fail(yyextra->dfw, "The final quote was missing from a character constant.");
325         return SCAN_FAILED;
326 }
327
328 <SQUOTE>\047                    {
329         /* end quote */
330         int token;
331         BEGIN(INITIAL);
332         g_string_append_c(yyextra->quoted_string, '\'');
333         token = set_lval(TOKEN_CHARCONST, yyextra->quoted_string->str);
334         g_string_free(yyextra->quoted_string, TRUE);
335         yyextra->quoted_string = NULL;
336         return token;
337 }
338
339 <SQUOTE>\\.                             {
340         /* escaped character */
341         g_string_append(yyextra->quoted_string, yytext);
342 }
343
344 <SQUOTE>[^\\\047]+                      {
345         /* non-escaped string */
346         g_string_append(yyextra->quoted_string, yytext);
347 }
348
349
350
351 [-[:alnum:]_\.:]*\/[[:digit:]]+  {
352         /* CIDR */
353         return set_lval(TOKEN_UNPARSED, yytext);
354 }
355
356                 ([.][-+[:alnum:]_:]+)+[.]{0,2} |
357 [-+[:alnum:]_:]+([.][-+[:alnum:]_:]+)*[.]{0,2} {
358         /* Is it a field name or some other value (float, integer, bytes, ...)? */
359         header_field_info *hfinfo;
360         df_func_def_t *df_func_def;
361
362         /* Trailing dot is allowed for floats, but make sure that trailing ".."
363          * is interpreted as a token on its own. */
364         if (strstr(yytext, "..")) {
365                 yyless(yyleng-2);
366         }
367
368         hfinfo = proto_registrar_get_byname(yytext);
369         if (hfinfo) {
370                 /* Yes, it's a field name */
371                 return set_lval(TOKEN_FIELD, hfinfo);
372         }
373         else {
374                 /* Is it a function name? */
375                 df_func_def = df_func_lookup(yytext);
376                 if (df_func_def) {
377                     /* yes, it's a dfilter function */
378                     return set_lval(TOKEN_FUNCTION, df_func_def);
379                 }
380                 else {
381                     /* No, so treat it as an unparsed string */
382                     return set_lval(TOKEN_UNPARSED, yytext);
383                 }
384         }
385 }
386
387 . {
388         /* Default */
389         return set_lval(TOKEN_UNPARSED, yytext);
390 }
391
392
393 %%
394
395 /*
396  * Turn diagnostics back on, so we check the code that we've written.
397  */
398 DIAG_ON_FLEX
399
400 static int
401 simple(int token)
402 {
403         switch (token) {
404                 case TOKEN_LPAREN:
405                 case TOKEN_RPAREN:
406                 case TOKEN_LBRACKET:
407                 case TOKEN_RBRACKET:
408                 case TOKEN_LBRACE:
409                 case TOKEN_RBRACE:
410                 case TOKEN_COLON:
411                 case TOKEN_COMMA:
412                 case TOKEN_DOTDOT:
413                 case TOKEN_HYPHEN:
414                 case TOKEN_WHITESPACE:
415                 case TOKEN_TEST_EQ:
416                 case TOKEN_TEST_NE:
417                 case TOKEN_TEST_GT:
418                 case TOKEN_TEST_GE:
419                 case TOKEN_TEST_LT:
420                 case TOKEN_TEST_LE:
421                 case TOKEN_TEST_BITWISE_AND:
422                 case TOKEN_TEST_CONTAINS:
423                 case TOKEN_TEST_MATCHES:
424                 case TOKEN_TEST_NOT:
425                 case TOKEN_TEST_AND:
426                 case TOKEN_TEST_OR:
427                 case TOKEN_TEST_IN:
428                         break;
429                 default:
430                         g_assert_not_reached();
431         }
432         return token;
433 }
434
435 static int
436 set_lval(int token, gpointer data)
437 {
438         sttype_id_t     type_id = STTYPE_UNINITIALIZED;
439
440         switch (token) {
441                 case TOKEN_STRING:
442                         type_id = STTYPE_STRING;
443                         break;
444                 case TOKEN_CHARCONST:
445                         type_id = STTYPE_CHARCONST;
446                         break;
447                 case TOKEN_FIELD:
448                         type_id = STTYPE_FIELD;
449                         break;
450                 case TOKEN_UNPARSED:
451                         type_id = STTYPE_UNPARSED;
452                         break;
453                 case TOKEN_FUNCTION:
454                         type_id = STTYPE_FUNCTION;
455                         break;
456                 default:
457                         g_assert_not_reached();
458         }
459         stnode_init(df_lval, type_id, data);
460         return token;
461 }
462
463 static int
464 set_lval_int(dfwork_t *dfw, int token, char *s)
465 {
466         sttype_id_t     type_id = STTYPE_UNINITIALIZED;
467         gint32          val;
468
469         if (!str_to_gint32(dfw, s, &val)) {
470                 return SCAN_FAILED;
471         }
472
473         switch (token) {
474                 case TOKEN_INTEGER:
475                         type_id = STTYPE_INTEGER;
476                         break;
477                 default:
478                         g_assert_not_reached();
479         }
480
481         stnode_init_int(df_lval, type_id, val);
482         return token;
483 }
484
485
486 static gboolean
487 str_to_gint32(dfwork_t *dfw, char *s, gint32* pint)
488 {
489         char    *endptr;
490         long    integer;
491
492         errno = 0;
493         integer = strtol(s, &endptr, 0);
494
495         if (errno == EINVAL || endptr == s || *endptr != '\0') {
496                 /* This isn't a valid number. */
497                 dfilter_fail(dfw, "\"%s\" is not a valid number.", s);
498                 return FALSE;
499         }
500         if (errno == ERANGE) {
501                 if (integer == LONG_MAX) {
502                         dfilter_fail(dfw, "\"%s\" causes an integer overflow.", s);
503                 }
504                 else if (integer == LONG_MIN) {
505                         dfilter_fail(dfw, "\"%s\" causes an integer underflow.", s);
506                 }
507                 else {
508                         /*
509                          * XXX - can "strtol()" set errno to ERANGE without
510                          * returning LONG_MAX or LONG_MIN?
511                          */
512                         dfilter_fail(dfw, "\"%s\" is not an integer.", s);
513                 }
514                 return FALSE;
515         }
516         if (integer > G_MAXINT32) {
517                 /*
518                  * Fits in a long, but not in a gint32 (a long might be
519                  * 64 bits).
520                  */
521                 dfilter_fail(dfw, "\"%s\" causes an integer overflow.", s);
522                 return FALSE;
523         }
524         if (integer < G_MININT32) {
525                 /*
526                  * Fits in a long, but not in a gint32 (a long might be
527                  * 64 bits).
528                  */
529                 dfilter_fail(dfw, "\"%s\" causes an integer underflow.", s);
530                 return FALSE;
531         }
532
533         *pint = (gint32)integer;
534         return TRUE;
535 }
536
537 static void
538 mark_lval_deprecated(const char *s)
539 {
540         df_lval->deprecated_token = s;
541 }