TODO epan/dissectors/asn1/kerberos/packet-kerberos-template.c new GSS flags
[wireshark-sm.git] / dftest.c
blobf585920391275c15500844806a32564014588faf
1 /* dftest.c
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
9 */
11 #include <config.h>
12 #define WS_LOG_DOMAIN LOG_DOMAIN_MAIN
14 #include <stdlib.h>
15 #include <stdio.h>
16 #include <locale.h>
17 #include <string.h>
18 #include <errno.h>
20 #include <glib.h>
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>
30 #ifdef HAVE_PLUGINS
31 #include <wsutil/plugins.h>
32 #endif
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>
42 #include "ui/util.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 */
49 static int opt_flex;
50 static int opt_lemon;
51 static int opt_syntax_tree;
52 static int opt_return_vals;
53 static int opt_timer;
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;
62 #ifndef HAVE_GETLINE
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. */
67 static int
68 fgetline(char *buf, int size, FILE *fp)
70 if (fgets(buf, size, fp)) {
71 int len = (int)strcspn(buf, "\r\n");
72 buf[len] = '\0';
73 return len;
75 return -1;
77 } /* fgetline */
78 #endif /* HAVE_GETLINE */
80 static void
81 putloc(FILE *fp, df_loc_t loc)
83 for (long i = 0; i < loc.col_start; i++) {
84 fputc(' ', fp);
86 fputc('^', fp);
88 for (size_t l = loc.col_len; l > 1; l--) {
89 fputc('~', fp);
91 fputc('\n', fp);
94 WS_NORETURN static void
95 print_usage(int status)
97 FILE *fp = stdout;
98 fprintf(fp, "\n");
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");
121 fprintf(fp, "\n");
122 ws_log_print_usage(fp);
123 exit(status);
126 static void
127 print_syntax_tree(dfilter_t *df)
129 printf("Syntax tree:\n%s\n\n", dfilter_syntax_tree(df));
132 static void
133 print_macros(void)
135 if (dfilter_macro_table_count() == 0) {
136 printf("Macros: (empty)\n\n");
137 return;
140 struct dfilter_macro_table_iter iter;
141 const char *name, *text;
143 dfilter_macro_table_iter_init(&iter);
144 printf("Macros:\n");
145 while (dfilter_macro_table_iter_next(&iter, &name, &text)) {
146 printf(" "UTF8_BULLET" %s:\n", name);
147 printf(" %s\n", text);
149 printf("\n");
152 static void
153 print_warnings(dfilter_t *df)
155 unsigned i;
156 GPtrArray *deprecated;
157 int count = 0;
159 for (GSList *l = dfilter_get_warnings(df); l != NULL; l = l->next) {
160 printf("\nWarning: %s.", (char *)l->data);
161 count++;
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);
169 count++;
173 if (count) {
174 printf("\n");
178 static void
179 print_elapsed(void)
181 printf("\nElapsed: %"PRId64" µs (%"PRId64" µs + %"PRId64" µs)\n",
182 elapsed_expand + elapsed_compile,
183 elapsed_expand,
184 elapsed_compile);
187 static char *
188 expand_filter(const char *text)
190 char *expanded = NULL;
191 df_error_t *err = NULL;
192 int64_t start;
194 start = g_get_monotonic_time();
195 expanded = dfilter_expand(text, &err);
196 if (expanded == NULL) {
197 fprintf(stderr, "Error: %s\n", err->msg);
198 df_error_free(&err);
200 elapsed_expand = g_get_monotonic_time() - start;
201 return expanded;
204 static bool
205 compile_filter(const char *text, dfilter_t **dfp)
207 unsigned df_flags = 0;
208 bool ok;
209 df_error_t *df_err = NULL;
210 int64_t start;
212 if (opt_optimize > 0)
213 df_flags |= DF_OPTIMIZE;
214 if (opt_syntax_tree)
215 df_flags |= DF_SAVE_TREE;
216 if (opt_flex)
217 df_flags |= DF_DEBUG_FLEX;
218 if (opt_lemon)
219 df_flags |= DF_DEBUG_LEMON;
220 if (opt_return_vals)
221 df_flags |= DF_RETURN_VALUES;
223 start = g_get_monotonic_time();
224 ok = dfilter_compile_full(text, dfp, &df_err, df_flags, "dftest");
225 if (!ok) {
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;
234 return ok;
237 static int
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);
244 errno = 0;
245 int digit = (int)strtol(ws_optarg, NULL, 10);
246 if (errno) {
247 printf("Error: %s\n", g_strerror(errno));
248 print_usage(WS_EXIT_INVALID_OPTION);
250 return digit;
253 static int
254 test_filter(const char *text)
256 char *expanded_text = NULL;
257 dfilter_t *df = NULL;
259 printf("Filter:\n %s\n\n", text);
261 /* Expand macros. */
262 expanded_text = expand_filter(text);
263 if (expanded_text == NULL) {
264 goto fail;
267 if (strcmp(text, expanded_text) != 0)
268 printf("Filter (after expansion):\n %s\n\n", expanded_text);
270 /* Compile it */
271 if (!compile_filter(expanded_text, &df)) {
272 goto fail;
275 /* If logging is enabled add an empty line. */
276 if (opt_debug_level > 0) {
277 printf("\n");
280 if (df == NULL) {
281 printf("Filter is empty.\n");
282 goto fail;
285 if (opt_syntax_tree)
286 print_syntax_tree(df);
288 uint16_t dump_flags = 0;
289 if (opt_show_types)
290 dump_flags |= DF_DUMP_SHOW_FTYPE;
291 if (opt_dump_refs)
292 dump_flags |= DF_DUMP_REFERENCES;
294 dfilter_dump(stdout, df, dump_flags);
296 print_warnings(df);
298 if (opt_timer)
299 print_elapsed();
301 g_free(expanded_text);
302 dfilter_free(df);
304 return EXIT_SUCCESS;
306 fail:
307 g_free(expanded_text);
308 dfilter_free(df);
309 return WS_EXIT_INVALID_FILTER;
313 main(int argc, char **argv)
315 char *configuration_init_error;
316 char *path = NULL;
317 char *text = NULL;
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.
327 #ifdef _WIN32
328 setlocale(LC_ALL, ".UTF-8");
329 #else
330 setlocale(LC_ALL, "");
331 #endif
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
350 * executable file.
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 },
378 { NULL, 0, 0, 0 }
380 int opt;
382 for (;;) {
383 opt = ws_getopt_long(argc, argv, optstring, long_options, NULL);
384 if (opt == -1)
385 break;
387 switch (opt) {
388 case 'V':
389 opt_verbose = 1;
390 break;
391 case 'd':
392 if (ws_optarg) {
393 opt_debug_level = optarg_to_digit(ws_optarg);
395 else {
396 opt_debug_level++;
398 opt_show_types = 1;
399 break;
400 case 'C': /* Configuration Profile */
401 if (profile_exists (ws_optarg, false)) {
402 set_profile_name (ws_optarg);
403 } else {
404 cmdarg_err("Configuration Profile \"%s\" does not exist", ws_optarg);
405 print_usage(WS_EXIT_INVALID_OPTION);
407 break;
408 case 'D':
409 opt_debug_level = 9;
410 opt_lemon = 1;
411 opt_flex = 1;
412 opt_show_types = 1;
413 break;
414 case 'f':
415 opt_flex = 1;
416 break;
417 case 'l':
418 opt_lemon = 1;
419 break;
420 case 's':
421 opt_syntax_tree = 1;
422 break;
423 case 'm':
424 opt_dump_macros = 1;
425 break;
426 case 't':
427 opt_timer = 1;
428 break;
429 case 'r':
430 opt_return_vals = 1;
431 break;
432 case '0':
433 opt_optimize = 0;
434 break;
435 case 1000:
436 opt_optimize = optarg_to_digit(ws_optarg);
437 break;
438 case 2000:
439 opt_show_types = 1;
440 break;
441 case 3000:
442 opt_dump_refs = 1;
443 break;
444 case 4000:
445 path = ws_optarg;
446 break;
447 case 'v':
448 show_version();
449 exit(EXIT_SUCCESS);
450 break;
451 case 'h':
452 show_help_header(NULL);
453 print_usage(EXIT_SUCCESS);
454 break;
455 case '?':
456 print_usage(EXIT_FAILURE);
457 default:
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.
490 wtap_init(true);
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))
497 goto out;
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. */
505 prefs_apply_all();
507 if (opt_dump_macros) {
508 print_macros();
509 if (argv[ws_optind] == NULL) {
510 /* No filter expression, we're done. */
511 exit(EXIT_SUCCESS);
515 /* This is useful to prevent confusion with option parsing.
516 * Skips printing options and argv[0]. */
517 if (opt_verbose) {
518 for (int i = ws_optind; i < argc; i++) {
519 fprintf(stderr, "argv[%d]: %s\n", i, argv[i]);
521 fprintf(stderr, "\n");
524 if (path) {
525 FILE *filter_p;
526 if (strcmp(path, "-") == 0) {
527 filter_p = stdin;
528 } else {
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;
533 goto out;
536 bool first = true;
537 #ifdef HAVE_GETLINE
538 char *line = NULL;
539 size_t len = 0;
540 while (getline(&line, &len, filter_p) >= 0) {
541 #else
542 char line[MAX_LINELEN];
543 while (fgetline(line, sizeof(line), filter_p) >= 0) {
544 #endif
545 if (first) {
546 first = false;
547 } else {
548 printf("\n");
550 exit_status = test_filter(line);
551 /* A keep going option could be added. */
552 if (exit_status != EXIT_SUCCESS)
553 break;
555 #ifdef HAVE_GETLINE
556 g_free(line);
557 #endif
558 fclose(filter_p);
559 } else {
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);
573 out:
574 epan_cleanup();
575 g_free(text);
576 exit(exit_status);