dcerpc-nt: add UNION_ALIGN_TO... helpers
[wireshark-sm.git] / dftest.c
blobf78c3700417e7e607074c422ddc689367512ff6e
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;
63 * Report an error in command-line arguments.
65 static void
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.
76 static void
77 dftest_cmdarg_err_cont(const char *fmt, va_list ap)
79 vfprintf(stderr, fmt, ap);
80 fprintf(stderr, "\n");
83 #ifndef HAVE_GETLINE
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. */
88 static int
89 fgetline(char *buf, int size, FILE *fp)
91 if (fgets(buf, size, fp)) {
92 int len = (int)strcspn(buf, "\r\n");
93 buf[len] = '\0';
94 return len;
96 return -1;
98 } /* fgetline */
99 #endif /* HAVE_GETLINE */
101 static void
102 putloc(FILE *fp, df_loc_t loc)
104 for (long i = 0; i < loc.col_start; i++) {
105 fputc(' ', fp);
107 fputc('^', fp);
109 for (size_t l = loc.col_len; l > 1; l--) {
110 fputc('~', fp);
112 fputc('\n', fp);
115 WS_NORETURN static void
116 print_usage(int status)
118 FILE *fp = stdout;
119 fprintf(fp, "\n");
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");
142 fprintf(fp, "\n");
143 ws_log_print_usage(fp);
144 exit(status);
147 static void
148 print_syntax_tree(dfilter_t *df)
150 printf("Syntax tree:\n%s\n\n", dfilter_syntax_tree(df));
153 static void
154 print_macros(void)
156 if (dfilter_macro_table_count() == 0) {
157 printf("Macros: (empty)\n\n");
158 return;
161 struct dfilter_macro_table_iter iter;
162 const char *name, *text;
164 dfilter_macro_table_iter_init(&iter);
165 printf("Macros:\n");
166 while (dfilter_macro_table_iter_next(&iter, &name, &text)) {
167 printf(" "UTF8_BULLET" %s:\n", name);
168 printf(" %s\n", text);
170 printf("\n");
173 static void
174 print_warnings(dfilter_t *df)
176 unsigned i;
177 GPtrArray *deprecated;
178 int count = 0;
180 for (GSList *l = dfilter_get_warnings(df); l != NULL; l = l->next) {
181 printf("\nWarning: %s.", (char *)l->data);
182 count++;
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);
190 count++;
194 if (count) {
195 printf("\n");
199 static void
200 print_elapsed(void)
202 printf("\nElapsed: %"PRId64" µs (%"PRId64" µs + %"PRId64" µs)\n",
203 elapsed_expand + elapsed_compile,
204 elapsed_expand,
205 elapsed_compile);
208 static char *
209 expand_filter(const char *text)
211 char *expanded = NULL;
212 df_error_t *err = NULL;
213 int64_t start;
215 start = g_get_monotonic_time();
216 expanded = dfilter_expand(text, &err);
217 if (expanded == NULL) {
218 fprintf(stderr, "Error: %s\n", err->msg);
219 df_error_free(&err);
221 elapsed_expand = g_get_monotonic_time() - start;
222 return expanded;
225 static bool
226 compile_filter(const char *text, dfilter_t **dfp)
228 unsigned df_flags = 0;
229 bool ok;
230 df_error_t *df_err = NULL;
231 int64_t start;
233 if (opt_optimize > 0)
234 df_flags |= DF_OPTIMIZE;
235 if (opt_syntax_tree)
236 df_flags |= DF_SAVE_TREE;
237 if (opt_flex)
238 df_flags |= DF_DEBUG_FLEX;
239 if (opt_lemon)
240 df_flags |= DF_DEBUG_LEMON;
241 if (opt_return_vals)
242 df_flags |= DF_RETURN_VALUES;
244 start = g_get_monotonic_time();
245 ok = dfilter_compile_full(text, dfp, &df_err, df_flags, "dftest");
246 if (!ok) {
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;
255 return ok;
258 static int
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);
265 errno = 0;
266 int digit = (int)strtol(ws_optarg, NULL, 10);
267 if (errno) {
268 printf("Error: %s\n", g_strerror(errno));
269 print_usage(WS_EXIT_INVALID_OPTION);
271 return digit;
274 static int
275 test_filter(const char *text)
277 char *expanded_text = NULL;
278 dfilter_t *df = NULL;
280 printf("Filter:\n %s\n\n", text);
282 /* Expand macros. */
283 expanded_text = expand_filter(text);
284 if (expanded_text == NULL) {
285 goto fail;
288 if (strcmp(text, expanded_text) != 0)
289 printf("Filter (after expansion):\n %s\n\n", expanded_text);
291 /* Compile it */
292 if (!compile_filter(expanded_text, &df)) {
293 goto fail;
296 /* If logging is enabled add an empty line. */
297 if (opt_debug_level > 0) {
298 printf("\n");
301 if (df == NULL) {
302 printf("Filter is empty.\n");
303 goto fail;
306 if (opt_syntax_tree)
307 print_syntax_tree(df);
309 uint16_t dump_flags = 0;
310 if (opt_show_types)
311 dump_flags |= DF_DUMP_SHOW_FTYPE;
312 if (opt_dump_refs)
313 dump_flags |= DF_DUMP_REFERENCES;
315 dfilter_dump(stdout, df, dump_flags);
317 print_warnings(df);
319 if (opt_timer)
320 print_elapsed();
322 g_free(expanded_text);
323 dfilter_free(df);
325 return EXIT_SUCCESS;
327 fail:
328 g_free(expanded_text);
329 dfilter_free(df);
330 return WS_EXIT_INVALID_FILTER;
334 main(int argc, char **argv)
336 char *configuration_init_error;
337 char *path = NULL;
338 char *text = NULL;
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.
345 #ifdef _WIN32
346 setlocale(LC_ALL, ".UTF-8");
347 #else
348 setlocale(LC_ALL, "");
349 #endif
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
368 * executable file.
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 },
396 { NULL, 0, 0, 0 }
398 int opt;
400 for (;;) {
401 opt = ws_getopt_long(argc, argv, optstring, long_options, NULL);
402 if (opt == -1)
403 break;
405 switch (opt) {
406 case 'V':
407 opt_verbose = 1;
408 break;
409 case 'd':
410 if (ws_optarg) {
411 opt_debug_level = optarg_to_digit(ws_optarg);
413 else {
414 opt_debug_level++;
416 opt_show_types = 1;
417 break;
418 case 'C': /* Configuration Profile */
419 if (profile_exists (ws_optarg, false)) {
420 set_profile_name (ws_optarg);
421 } else {
422 cmdarg_err("Configuration Profile \"%s\" does not exist", ws_optarg);
423 print_usage(WS_EXIT_INVALID_OPTION);
425 break;
426 case 'D':
427 opt_debug_level = 9;
428 opt_lemon = 1;
429 opt_flex = 1;
430 opt_show_types = 1;
431 break;
432 case 'f':
433 opt_flex = 1;
434 break;
435 case 'l':
436 opt_lemon = 1;
437 break;
438 case 's':
439 opt_syntax_tree = 1;
440 break;
441 case 'm':
442 opt_dump_macros = 1;
443 break;
444 case 't':
445 opt_timer = 1;
446 break;
447 case 'r':
448 opt_return_vals = 1;
449 break;
450 case '0':
451 opt_optimize = 0;
452 break;
453 case 1000:
454 opt_optimize = optarg_to_digit(ws_optarg);
455 break;
456 case 2000:
457 opt_show_types = 1;
458 break;
459 case 3000:
460 opt_dump_refs = 1;
461 break;
462 case 4000:
463 path = ws_optarg;
464 break;
465 case 'v':
466 show_version();
467 exit(EXIT_SUCCESS);
468 break;
469 case 'h':
470 show_help_header(NULL);
471 print_usage(EXIT_SUCCESS);
472 break;
473 case '?':
474 print_usage(EXIT_FAILURE);
475 default:
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.
508 wtap_init(true);
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))
515 goto out;
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. */
523 prefs_apply_all();
525 if (opt_dump_macros) {
526 print_macros();
527 if (argv[ws_optind] == NULL) {
528 /* No filter expression, we're done. */
529 exit(EXIT_SUCCESS);
533 /* This is useful to prevent confusion with option parsing.
534 * Skips printing options and argv[0]. */
535 if (opt_verbose) {
536 for (int i = ws_optind; i < argc; i++) {
537 fprintf(stderr, "argv[%d]: %s\n", i, argv[i]);
539 fprintf(stderr, "\n");
542 if (path) {
543 FILE *filter_p;
544 if (strcmp(path, "-") == 0) {
545 filter_p = stdin;
546 } else {
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;
551 goto out;
554 bool first = true;
555 #ifdef HAVE_GETLINE
556 char *line = NULL;
557 size_t len = 0;
558 while (getline(&line, &len, filter_p) >= 0) {
559 #else
560 char line[MAX_LINELEN];
561 while (fgetline(line, sizeof(line), filter_p) >= 0) {
562 #endif
563 if (first) {
564 first = false;
565 } else {
566 printf("\n");
568 exit_status = test_filter(line);
569 /* A keep going option could be added. */
570 if (exit_status != EXIT_SUCCESS)
571 break;
573 #ifdef HAVE_GETLINE
574 g_free(line);
575 #endif
576 fclose(filter_p);
577 } else {
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);
591 out:
592 epan_cleanup();
593 g_free(text);
594 exit(exit_status);