2 * Shows display filter byte-code, for debugging dfilter routines.
4 * Wireshark - Network traffic analyzer
5 * By Gerald Combs <gerald@wireshark.org>
6 * Copyright 1998 Gerald Combs
8 * SPDX-License-Identifier: GPL-2.0-or-later
12 #define WS_LOG_DOMAIN LOG_DOMAIN_MAIN
22 #include <ws_exit_codes.h>
24 #include <epan/epan.h>
25 #include <epan/timestamp.h>
26 #include <epan/prefs.h>
27 #include <epan/dfilter/dfilter.h>
28 #include <epan/dfilter/dfilter-macro.h>
31 #include <wsutil/plugins.h>
33 #include <wsutil/filesystem.h>
34 #include <wsutil/file_util.h>
35 #include <wsutil/privileges.h>
36 #include <wsutil/wslog.h>
37 #include <wsutil/ws_getopt.h>
38 #include <wsutil/utf8_entities.h>
40 #include <wiretap/wtap.h>
43 #include "wsutil/cmdarg_err.h"
44 #include "ui/failure_message.h"
45 #include "wsutil/version_info.h"
47 static int opt_verbose
;
48 static int opt_debug_level
; /* currently up to 2 */
51 static int opt_syntax_tree
;
52 static int opt_return_vals
;
54 static long opt_optimize
= 1;
55 static int opt_show_types
;
56 static int opt_dump_refs
;
57 static int opt_dump_macros
;
59 static int64_t elapsed_expand
;
60 static int64_t elapsed_compile
;
63 * Report an error in command-line arguments.
66 dftest_cmdarg_err(const char *fmt
, va_list ap
)
68 fprintf(stderr
, "dftest: ");
69 vfprintf(stderr
, fmt
, ap
);
70 fprintf(stderr
, "\n");
74 * Report additional information for an error in command-line arguments.
77 dftest_cmdarg_err_cont(const char *fmt
, va_list ap
)
79 vfprintf(stderr
, fmt
, ap
);
80 fprintf(stderr
, "\n");
84 /* Maximum supported line length of a filter. */
85 #define MAX_LINELEN 4096
87 /** Read a line without trailing (CR)LF. Returns -1 on failure. */
89 fgetline(char *buf
, int size
, FILE *fp
)
91 if (fgets(buf
, size
, fp
)) {
92 int len
= (int)strcspn(buf
, "\r\n");
99 #endif /* HAVE_GETLINE */
102 putloc(FILE *fp
, df_loc_t loc
)
104 for (long i
= 0; i
< loc
.col_start
; i
++) {
109 for (size_t l
= loc
.col_len
; l
> 1; l
--) {
115 WS_NORETURN
static void
116 print_usage(int status
)
120 fprintf(fp
, "Usage: dftest [OPTIONS] -- EXPRESSION\n");
121 fprintf(fp
, "Options:\n");
122 fprintf(fp
, " -V, --verbose enable verbose mode\n");
123 fprintf(fp
, " -C <config profile> run with specified configuration profile\n");
124 fprintf(fp
, " -d, --debug[=N] increase or set debug level\n");
125 fprintf(fp
, " -D set maximum debug level\n");
126 fprintf(fp
, " -f, --flex enable Flex debug trace\n");
127 fprintf(fp
, " -l, --lemon enable Lemon debug trace\n");
128 fprintf(fp
, " -s, --syntax print syntax tree\n");
129 fprintf(fp
, " -m --macros print saved macros\n");
130 fprintf(fp
, " -t, --timer print elapsed compilation time\n");
131 fprintf(fp
, " -r --return-vals return field values for the tree root\n");
132 fprintf(fp
, " -0, --optimize=0 do not optimize (check syntax)\n");
133 fprintf(fp
, " --types show field value types\n");
134 /* NOTE: References are loaded during runtime and dftest only does compilation.
135 * Unless some static reference data is hard-coded at compile time during
136 * development the --refs option to dftest is useless because it will just
137 * print empty reference vectors. */
138 fprintf(fp
, " --refs dump some runtime data structures\n");
139 fprintf(fp
, " --file <path> read filters line-by-line from a file (use '-' for stdin)\n");
140 fprintf(fp
, " -h, --help display this help and exit\n");
141 fprintf(fp
, " -v, --version print version\n");
143 ws_log_print_usage(fp
);
148 print_syntax_tree(dfilter_t
*df
)
150 printf("Syntax tree:\n%s\n\n", dfilter_syntax_tree(df
));
156 if (dfilter_macro_table_count() == 0) {
157 printf("Macros: (empty)\n\n");
161 struct dfilter_macro_table_iter iter
;
162 const char *name
, *text
;
164 dfilter_macro_table_iter_init(&iter
);
166 while (dfilter_macro_table_iter_next(&iter
, &name
, &text
)) {
167 printf(" "UTF8_BULLET
" %s:\n", name
);
168 printf(" %s\n", text
);
174 print_warnings(dfilter_t
*df
)
177 GPtrArray
*deprecated
;
180 for (GSList
*l
= dfilter_get_warnings(df
); l
!= NULL
; l
= l
->next
) {
181 printf("\nWarning: %s.", (char *)l
->data
);
185 deprecated
= dfilter_deprecated_tokens(df
);
186 if (deprecated
&& deprecated
->len
) {
187 for (i
= 0; i
< deprecated
->len
; i
++) {
188 const char *token
= g_ptr_array_index(deprecated
, i
);
189 printf("\nWarning: Deprecated token \"%s\".", token
);
202 printf("\nElapsed: %"PRId64
" µs (%"PRId64
" µs + %"PRId64
" µs)\n",
203 elapsed_expand
+ elapsed_compile
,
209 expand_filter(const char *text
)
211 char *expanded
= NULL
;
212 df_error_t
*err
= NULL
;
215 start
= g_get_monotonic_time();
216 expanded
= dfilter_expand(text
, &err
);
217 if (expanded
== NULL
) {
218 fprintf(stderr
, "Error: %s\n", err
->msg
);
221 elapsed_expand
= g_get_monotonic_time() - start
;
226 compile_filter(const char *text
, dfilter_t
**dfp
)
228 unsigned df_flags
= 0;
230 df_error_t
*df_err
= NULL
;
233 if (opt_optimize
> 0)
234 df_flags
|= DF_OPTIMIZE
;
236 df_flags
|= DF_SAVE_TREE
;
238 df_flags
|= DF_DEBUG_FLEX
;
240 df_flags
|= DF_DEBUG_LEMON
;
242 df_flags
|= DF_RETURN_VALUES
;
244 start
= g_get_monotonic_time();
245 ok
= dfilter_compile_full(text
, dfp
, &df_err
, df_flags
, "dftest");
247 fprintf(stderr
, "Error: %s\n", df_err
->msg
);
248 if (df_err
->loc
.col_start
>= 0) {
249 fprintf(stderr
, " %s\n ", text
);
250 putloc(stderr
, df_err
->loc
);
252 df_error_free(&df_err
);
254 elapsed_compile
= g_get_monotonic_time() - start
;
259 optarg_to_digit(const char *arg
)
261 if (strlen(arg
) > 1 || !g_ascii_isdigit(*arg
)) {
262 printf("Error: \"%s\" is not a valid number 0-9\n", arg
);
263 print_usage(WS_EXIT_INVALID_OPTION
);
266 int digit
= (int)strtol(ws_optarg
, NULL
, 10);
268 printf("Error: %s\n", g_strerror(errno
));
269 print_usage(WS_EXIT_INVALID_OPTION
);
275 test_filter(const char *text
)
277 char *expanded_text
= NULL
;
278 dfilter_t
*df
= NULL
;
280 printf("Filter:\n %s\n\n", text
);
283 expanded_text
= expand_filter(text
);
284 if (expanded_text
== NULL
) {
288 if (strcmp(text
, expanded_text
) != 0)
289 printf("Filter (after expansion):\n %s\n\n", expanded_text
);
292 if (!compile_filter(expanded_text
, &df
)) {
296 /* If logging is enabled add an empty line. */
297 if (opt_debug_level
> 0) {
302 printf("Filter is empty.\n");
307 print_syntax_tree(df
);
309 uint16_t dump_flags
= 0;
311 dump_flags
|= DF_DUMP_SHOW_FTYPE
;
313 dump_flags
|= DF_DUMP_REFERENCES
;
315 dfilter_dump(stdout
, df
, dump_flags
);
322 g_free(expanded_text
);
328 g_free(expanded_text
);
330 return WS_EXIT_INVALID_FILTER
;
334 main(int argc
, char **argv
)
336 char *configuration_init_error
;
339 int exit_status
= EXIT_FAILURE
;
342 * Set the C-language locale to the native environment and set the
343 * code page to UTF-8 on Windows.
346 setlocale(LC_ALL
, ".UTF-8");
348 setlocale(LC_ALL
, "");
351 cmdarg_err_init(dftest_cmdarg_err
, dftest_cmdarg_err_cont
);
353 /* Initialize log handler early for startup. */
354 ws_log_init("dftest", vcmdarg_err
);
356 /* Early logging command-line initialization. */
357 ws_log_parse_args(&argc
, argv
, vcmdarg_err
, 1);
359 ws_noisy("Finished log init and parsing command line log arguments");
362 * Get credential information for later use.
364 init_process_policies();
367 * Attempt to get the pathname of the directory containing the
370 configuration_init_error
= configuration_init(argv
[0], NULL
);
371 if (configuration_init_error
!= NULL
) {
372 fprintf(stderr
, "Error: Can't get pathname of directory containing "
373 "the dftest program: %s.\n",
374 configuration_init_error
);
375 g_free(configuration_init_error
);
378 ws_init_version_info("DFTest", NULL
, NULL
);
380 const char *optstring
= "hvC:dDflsmrtV0";
381 static struct ws_option long_options
[] = {
382 { "help", ws_no_argument
, 0, 'h' },
383 { "version", ws_no_argument
, 0, 'v' },
384 { "debug", ws_optional_argument
, 0, 'd' },
385 { "flex", ws_no_argument
, 0, 'f' },
386 { "lemon", ws_no_argument
, 0, 'l' },
387 { "syntax", ws_no_argument
, 0, 's' },
388 { "macros", ws_no_argument
, 0, 'm' },
389 { "timer", ws_no_argument
, 0, 't' },
390 { "verbose", ws_no_argument
, 0, 'V' },
391 { "return-vals", ws_no_argument
, 0, 'r' },
392 { "optimize", ws_required_argument
, 0, 1000 },
393 { "types", ws_no_argument
, 0, 2000 },
394 { "refs", ws_no_argument
, 0, 3000 },
395 { "file", ws_required_argument
, 0, 4000 },
401 opt
= ws_getopt_long(argc
, argv
, optstring
, long_options
, NULL
);
411 opt_debug_level
= optarg_to_digit(ws_optarg
);
418 case 'C': /* Configuration Profile */
419 if (profile_exists (ws_optarg
, false)) {
420 set_profile_name (ws_optarg
);
422 cmdarg_err("Configuration Profile \"%s\" does not exist", ws_optarg
);
423 print_usage(WS_EXIT_INVALID_OPTION
);
454 opt_optimize
= optarg_to_digit(ws_optarg
);
470 show_help_header(NULL
);
471 print_usage(EXIT_SUCCESS
);
474 print_usage(EXIT_FAILURE
);
476 ws_assert_not_reached();
480 /* Check for filter on command line. */
481 if (argv
[ws_optind
] == NULL
) {
482 /* If not printing macros we need a filter expression to compile. */
483 if (!opt_dump_macros
&& !path
) {
484 printf("Error: Missing argument.\n");
485 print_usage(EXIT_FAILURE
);
489 /* Set dfilter domain logging. */
490 if (opt_debug_level
> 1) {
491 ws_log_set_noisy_filter(LOG_DOMAIN_DFILTER
);
493 else if (opt_debug_level
> 0 || opt_flex
|| opt_lemon
) {
494 /* Also enable some dfilter logs with flex/lemon traces for context. */
495 ws_log_set_debug_filter(LOG_DOMAIN_DFILTER
);
498 init_report_failure_message("dftest");
500 timestamp_set_type(TS_RELATIVE
);
501 timestamp_set_seconds_type(TS_SECONDS_DEFAULT
);
504 * Libwiretap must be initialized before libwireshark is, so that
505 * dissection-time handlers for file-type-dependent blocks can
506 * register using the file type/subtype value for the file type.
510 /* Register all dissectors; we must do this before checking for the
511 "-g" flag, as the "-g" flag dumps a list of fields registered
512 by the dissectors, and we must do it before we read the preferences,
513 in case any dissectors register preferences. */
514 if (!epan_init(NULL
, NULL
, true))
517 /* Load libwireshark settings from the current profile. */
518 epan_load_settings();
520 /* notify all registered modules that have had any of their preferences
521 changed either from one of the preferences file or from the command
522 line that its preferences have changed. */
525 if (opt_dump_macros
) {
527 if (argv
[ws_optind
] == NULL
) {
528 /* No filter expression, we're done. */
533 /* This is useful to prevent confusion with option parsing.
534 * Skips printing options and argv[0]. */
536 for (int i
= ws_optind
; i
< argc
; i
++) {
537 fprintf(stderr
, "argv[%d]: %s\n", i
, argv
[i
]);
539 fprintf(stderr
, "\n");
544 if (strcmp(path
, "-") == 0) {
547 filter_p
= ws_fopen(path
, "r");
548 if (filter_p
== NULL
) {
549 open_failure_message(path
, errno
, false);
550 exit_status
= WS_EXIT_INVALID_FILE
;
558 while (getline(&line
, &len
, filter_p
) >= 0) {
560 char line
[MAX_LINELEN
];
561 while (fgetline(line
, sizeof(line
), filter_p
) >= 0) {
568 exit_status
= test_filter(line
);
569 /* A keep going option could be added. */
570 if (exit_status
!= EXIT_SUCCESS
)
579 /* Check again for filter on command line */
580 if (argv
[ws_optind
] == NULL
) {
581 printf("Error: Missing argument.\n");
582 print_usage(EXIT_FAILURE
);
585 /* Get filter text */
586 text
= get_args_as_string(argc
, argv
, ws_optind
);
588 exit_status
= test_filter(text
);