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 /* Maximum supported line length of a filter. */
64 #define MAX_LINELEN 4096
66 /** Read a line without trailing (CR)LF. Returns -1 on failure. */
68 fgetline(char *buf
, int size
, FILE *fp
)
70 if (fgets(buf
, size
, fp
)) {
71 int len
= (int)strcspn(buf
, "\r\n");
78 #endif /* HAVE_GETLINE */
81 putloc(FILE *fp
, df_loc_t loc
)
83 for (long i
= 0; i
< loc
.col_start
; i
++) {
88 for (size_t l
= loc
.col_len
; l
> 1; l
--) {
94 WS_NORETURN
static void
95 print_usage(int status
)
99 fprintf(fp
, "Usage: dftest [OPTIONS] -- EXPRESSION\n");
100 fprintf(fp
, "Options:\n");
101 fprintf(fp
, " -V, --verbose enable verbose mode\n");
102 fprintf(fp
, " -C <config profile> run with specified configuration profile\n");
103 fprintf(fp
, " -d, --debug[=N] increase or set debug level\n");
104 fprintf(fp
, " -D set maximum debug level\n");
105 fprintf(fp
, " -f, --flex enable Flex debug trace\n");
106 fprintf(fp
, " -l, --lemon enable Lemon debug trace\n");
107 fprintf(fp
, " -s, --syntax print syntax tree\n");
108 fprintf(fp
, " -m --macros print saved macros\n");
109 fprintf(fp
, " -t, --timer print elapsed compilation time\n");
110 fprintf(fp
, " -r --return-vals return field values for the tree root\n");
111 fprintf(fp
, " -0, --optimize=0 do not optimize (check syntax)\n");
112 fprintf(fp
, " --types show field value types\n");
113 /* NOTE: References are loaded during runtime and dftest only does compilation.
114 * Unless some static reference data is hard-coded at compile time during
115 * development the --refs option to dftest is useless because it will just
116 * print empty reference vectors. */
117 fprintf(fp
, " --refs dump some runtime data structures\n");
118 fprintf(fp
, " --file <path> read filters line-by-line from a file (use '-' for stdin)\n");
119 fprintf(fp
, " -h, --help display this help and exit\n");
120 fprintf(fp
, " -v, --version print version\n");
122 ws_log_print_usage(fp
);
127 print_syntax_tree(dfilter_t
*df
)
129 printf("Syntax tree:\n%s\n\n", dfilter_syntax_tree(df
));
135 if (dfilter_macro_table_count() == 0) {
136 printf("Macros: (empty)\n\n");
140 struct dfilter_macro_table_iter iter
;
141 const char *name
, *text
;
143 dfilter_macro_table_iter_init(&iter
);
145 while (dfilter_macro_table_iter_next(&iter
, &name
, &text
)) {
146 printf(" "UTF8_BULLET
" %s:\n", name
);
147 printf(" %s\n", text
);
153 print_warnings(dfilter_t
*df
)
156 GPtrArray
*deprecated
;
159 for (GSList
*l
= dfilter_get_warnings(df
); l
!= NULL
; l
= l
->next
) {
160 printf("\nWarning: %s.", (char *)l
->data
);
164 deprecated
= dfilter_deprecated_tokens(df
);
165 if (deprecated
&& deprecated
->len
) {
166 for (i
= 0; i
< deprecated
->len
; i
++) {
167 const char *token
= g_ptr_array_index(deprecated
, i
);
168 printf("\nWarning: Deprecated token \"%s\".", token
);
181 printf("\nElapsed: %"PRId64
" µs (%"PRId64
" µs + %"PRId64
" µs)\n",
182 elapsed_expand
+ elapsed_compile
,
188 expand_filter(const char *text
)
190 char *expanded
= NULL
;
191 df_error_t
*err
= NULL
;
194 start
= g_get_monotonic_time();
195 expanded
= dfilter_expand(text
, &err
);
196 if (expanded
== NULL
) {
197 fprintf(stderr
, "Error: %s\n", err
->msg
);
200 elapsed_expand
= g_get_monotonic_time() - start
;
205 compile_filter(const char *text
, dfilter_t
**dfp
)
207 unsigned df_flags
= 0;
209 df_error_t
*df_err
= NULL
;
212 if (opt_optimize
> 0)
213 df_flags
|= DF_OPTIMIZE
;
215 df_flags
|= DF_SAVE_TREE
;
217 df_flags
|= DF_DEBUG_FLEX
;
219 df_flags
|= DF_DEBUG_LEMON
;
221 df_flags
|= DF_RETURN_VALUES
;
223 start
= g_get_monotonic_time();
224 ok
= dfilter_compile_full(text
, dfp
, &df_err
, df_flags
, "dftest");
226 fprintf(stderr
, "Error: %s\n", df_err
->msg
);
227 if (df_err
->loc
.col_start
>= 0) {
228 fprintf(stderr
, " %s\n ", text
);
229 putloc(stderr
, df_err
->loc
);
231 df_error_free(&df_err
);
233 elapsed_compile
= g_get_monotonic_time() - start
;
238 optarg_to_digit(const char *arg
)
240 if (strlen(arg
) > 1 || !g_ascii_isdigit(*arg
)) {
241 printf("Error: \"%s\" is not a valid number 0-9\n", arg
);
242 print_usage(WS_EXIT_INVALID_OPTION
);
245 int digit
= (int)strtol(ws_optarg
, NULL
, 10);
247 printf("Error: %s\n", g_strerror(errno
));
248 print_usage(WS_EXIT_INVALID_OPTION
);
254 test_filter(const char *text
)
256 char *expanded_text
= NULL
;
257 dfilter_t
*df
= NULL
;
259 printf("Filter:\n %s\n\n", text
);
262 expanded_text
= expand_filter(text
);
263 if (expanded_text
== NULL
) {
267 if (strcmp(text
, expanded_text
) != 0)
268 printf("Filter (after expansion):\n %s\n\n", expanded_text
);
271 if (!compile_filter(expanded_text
, &df
)) {
275 /* If logging is enabled add an empty line. */
276 if (opt_debug_level
> 0) {
281 printf("Filter is empty.\n");
286 print_syntax_tree(df
);
288 uint16_t dump_flags
= 0;
290 dump_flags
|= DF_DUMP_SHOW_FTYPE
;
292 dump_flags
|= DF_DUMP_REFERENCES
;
294 dfilter_dump(stdout
, df
, dump_flags
);
301 g_free(expanded_text
);
307 g_free(expanded_text
);
309 return WS_EXIT_INVALID_FILTER
;
313 main(int argc
, char **argv
)
315 char *configuration_init_error
;
318 int exit_status
= EXIT_FAILURE
;
320 /* Set the program name. */
321 g_set_prgname("dftest");
324 * Set the C-language locale to the native environment and set the
325 * code page to UTF-8 on Windows.
328 setlocale(LC_ALL
, ".UTF-8");
330 setlocale(LC_ALL
, "");
333 cmdarg_err_init(stderr_cmdarg_err
, stderr_cmdarg_err_cont
);
335 /* Initialize log handler early for startup. */
336 ws_log_init(vcmdarg_err
);
338 /* Early logging command-line initialization. */
339 ws_log_parse_args(&argc
, argv
, vcmdarg_err
, 1);
341 ws_noisy("Finished log init and parsing command line log arguments");
344 * Get credential information for later use.
346 init_process_policies();
349 * Attempt to get the pathname of the directory containing the
352 configuration_init_error
= configuration_init(argv
[0]);
353 if (configuration_init_error
!= NULL
) {
354 fprintf(stderr
, "Error: Can't get pathname of directory containing "
355 "the dftest program: %s.\n",
356 configuration_init_error
);
357 g_free(configuration_init_error
);
360 ws_init_version_info("DFTest", NULL
, NULL
);
362 const char *optstring
= "hvC:dDflsmrtV0";
363 static struct ws_option long_options
[] = {
364 { "help", ws_no_argument
, 0, 'h' },
365 { "version", ws_no_argument
, 0, 'v' },
366 { "debug", ws_optional_argument
, 0, 'd' },
367 { "flex", ws_no_argument
, 0, 'f' },
368 { "lemon", ws_no_argument
, 0, 'l' },
369 { "syntax", ws_no_argument
, 0, 's' },
370 { "macros", ws_no_argument
, 0, 'm' },
371 { "timer", ws_no_argument
, 0, 't' },
372 { "verbose", ws_no_argument
, 0, 'V' },
373 { "return-vals", ws_no_argument
, 0, 'r' },
374 { "optimize", ws_required_argument
, 0, 1000 },
375 { "types", ws_no_argument
, 0, 2000 },
376 { "refs", ws_no_argument
, 0, 3000 },
377 { "file", ws_required_argument
, 0, 4000 },
383 opt
= ws_getopt_long(argc
, argv
, optstring
, long_options
, NULL
);
393 opt_debug_level
= optarg_to_digit(ws_optarg
);
400 case 'C': /* Configuration Profile */
401 if (profile_exists (ws_optarg
, false)) {
402 set_profile_name (ws_optarg
);
404 cmdarg_err("Configuration Profile \"%s\" does not exist", ws_optarg
);
405 print_usage(WS_EXIT_INVALID_OPTION
);
436 opt_optimize
= optarg_to_digit(ws_optarg
);
452 show_help_header(NULL
);
453 print_usage(EXIT_SUCCESS
);
456 print_usage(EXIT_FAILURE
);
458 ws_assert_not_reached();
462 /* Check for filter on command line. */
463 if (argv
[ws_optind
] == NULL
) {
464 /* If not printing macros we need a filter expression to compile. */
465 if (!opt_dump_macros
&& !path
) {
466 printf("Error: Missing argument.\n");
467 print_usage(EXIT_FAILURE
);
471 /* Set dfilter domain logging. */
472 if (opt_debug_level
> 1) {
473 ws_log_set_noisy_filter(LOG_DOMAIN_DFILTER
);
475 else if (opt_debug_level
> 0 || opt_flex
|| opt_lemon
) {
476 /* Also enable some dfilter logs with flex/lemon traces for context. */
477 ws_log_set_debug_filter(LOG_DOMAIN_DFILTER
);
480 init_report_failure_message("dftest");
482 timestamp_set_type(TS_RELATIVE
);
483 timestamp_set_seconds_type(TS_SECONDS_DEFAULT
);
486 * Libwiretap must be initialized before libwireshark is, so that
487 * dissection-time handlers for file-type-dependent blocks can
488 * register using the file type/subtype value for the file type.
492 /* Register all dissectors; we must do this before checking for the
493 "-g" flag, as the "-g" flag dumps a list of fields registered
494 by the dissectors, and we must do it before we read the preferences,
495 in case any dissectors register preferences. */
496 if (!epan_init(NULL
, NULL
, true))
499 /* Load libwireshark settings from the current profile. */
500 epan_load_settings();
502 /* notify all registered modules that have had any of their preferences
503 changed either from one of the preferences file or from the command
504 line that its preferences have changed. */
507 if (opt_dump_macros
) {
509 if (argv
[ws_optind
] == NULL
) {
510 /* No filter expression, we're done. */
515 /* This is useful to prevent confusion with option parsing.
516 * Skips printing options and argv[0]. */
518 for (int i
= ws_optind
; i
< argc
; i
++) {
519 fprintf(stderr
, "argv[%d]: %s\n", i
, argv
[i
]);
521 fprintf(stderr
, "\n");
526 if (strcmp(path
, "-") == 0) {
529 filter_p
= ws_fopen(path
, "r");
530 if (filter_p
== NULL
) {
531 open_failure_message(path
, errno
, false);
532 exit_status
= WS_EXIT_INVALID_FILE
;
540 while (getline(&line
, &len
, filter_p
) >= 0) {
542 char line
[MAX_LINELEN
];
543 while (fgetline(line
, sizeof(line
), filter_p
) >= 0) {
550 exit_status
= test_filter(line
);
551 /* A keep going option could be added. */
552 if (exit_status
!= EXIT_SUCCESS
)
561 /* Check again for filter on command line */
562 if (argv
[ws_optind
] == NULL
) {
563 printf("Error: Missing argument.\n");
564 print_usage(EXIT_FAILURE
);
567 /* Get filter text */
568 text
= get_args_as_string(argc
, argv
, ws_optind
);
570 exit_status
= test_filter(text
);