summaryrefslogtreecommitdiffstats
path: root/dftest.c
diff options
context:
space:
mode:
Diffstat (limited to 'dftest.c')
-rw-r--r--dftest.c457
1 files changed, 457 insertions, 0 deletions
diff --git a/dftest.c b/dftest.c
new file mode 100644
index 00000000..9448f7f4
--- /dev/null
+++ b/dftest.c
@@ -0,0 +1,457 @@
+/* dftest.c
+ * Shows display filter byte-code, for debugging dfilter routines.
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include <config.h>
+#define WS_LOG_DOMAIN LOG_DOMAIN_MAIN
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <locale.h>
+#include <string.h>
+#include <errno.h>
+
+#include <glib.h>
+
+#include <ws_exit_codes.h>
+
+#include <epan/epan.h>
+#include <epan/timestamp.h>
+#include <epan/prefs.h>
+#include <epan/dfilter/dfilter.h>
+
+#ifdef HAVE_PLUGINS
+#include <wsutil/plugins.h>
+#endif
+#include <wsutil/filesystem.h>
+#include <wsutil/privileges.h>
+#include <wsutil/report_message.h>
+#include <wsutil/wslog.h>
+#include <wsutil/ws_getopt.h>
+
+#include <wiretap/wtap.h>
+
+#include "ui/util.h"
+#include "wsutil/cmdarg_err.h"
+#include "ui/failure_message.h"
+#include "wsutil/version_info.h"
+
+static int opt_verbose = 0;
+#define DFTEST_LOG_NONE 0
+#define DFTEST_LOG_DEBUG 1
+#define DFTEST_LOG_NOISY 2
+static int opt_log_level = DFTEST_LOG_NONE;
+static int opt_flex = 0;
+static int opt_lemon = 0;
+static int opt_syntax_tree = 0;
+static int opt_timer = 0;
+static long opt_optimize = 1;
+static int opt_show_types = 0;
+static int opt_dump_refs = 0;
+
+static gint64 elapsed_expand = 0;
+static gint64 elapsed_compile = 0;
+
+/*
+ * Report an error in command-line arguments.
+ */
+static void
+dftest_cmdarg_err(const char *fmt, va_list ap)
+{
+ fprintf(stderr, "dftest: ");
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n");
+}
+
+/*
+ * Report additional information for an error in command-line arguments.
+ */
+static void
+dftest_cmdarg_err_cont(const char *fmt, va_list ap)
+{
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n");
+}
+
+static void
+putloc(FILE *fp, df_loc_t loc)
+{
+ for (long i = 0; i < loc.col_start; i++) {
+ fputc(' ', fp);
+ }
+ fputc('^', fp);
+
+ for (size_t l = loc.col_len; l > 1; l--) {
+ fputc('~', fp);
+ }
+ fputc('\n', fp);
+}
+
+WS_NORETURN static void
+print_usage(int status)
+{
+ FILE *fp = stdout;
+ fprintf(fp, "\n");
+ fprintf(fp, "Usage: dftest [OPTIONS] -- EXPRESSION\n");
+ fprintf(fp, "Options:\n");
+ fprintf(fp, " -V, --verbose enable verbose mode\n");
+ fprintf(fp, " -d, --debug enable compiler debug logs\n");
+ fprintf(fp, " -f, --flex enable Flex debug trace\n");
+ fprintf(fp, " -l, --lemon enable Lemon debug trace\n");
+ fprintf(fp, " -s, --syntax print syntax tree\n");
+ fprintf(fp, " -t, --timer print elapsed compilation time\n");
+ fprintf(fp, " -0, --optimize=0 do not optimize (check syntax)\n");
+ fprintf(fp, " --types show field value types\n");
+ /* NOTE: References are loaded during runtime and dftest only does compilation.
+ * Unless some static reference data is hard-coded at compile time during
+ * development the --refs option to dftest is useless because it will just
+ * print empty reference vectors. */
+ fprintf(fp, " --refs dump some runtime data structures\n");
+ fprintf(fp, " -h, --help display this help and exit\n");
+ fprintf(fp, " -v, --version print version\n");
+ fprintf(fp, "\n");
+ ws_log_print_usage(fp);
+ exit(status);
+}
+
+static void
+print_syntax_tree(dfilter_t *df)
+{
+ printf("Syntax tree:\n%s\n\n", dfilter_syntax_tree(df));
+}
+
+static void
+print_warnings(dfilter_t *df)
+{
+ guint i;
+ GPtrArray *deprecated;
+ int count = 0;
+
+ for (GSList *l = dfilter_get_warnings(df); l != NULL; l = l->next) {
+ printf("\nWarning: %s.", (char *)l->data);
+ count++;
+ }
+
+ deprecated = dfilter_deprecated_tokens(df);
+ if (deprecated && deprecated->len) {
+ for (i = 0; i < deprecated->len; i++) {
+ const char *token = g_ptr_array_index(deprecated, i);
+ printf("\nWarning: Deprecated token \"%s\".", token);
+ count++;
+ }
+ }
+
+ if (count) {
+ printf("\n");
+ }
+}
+
+static void
+print_elapsed(void)
+{
+ printf("\nElapsed: %"PRId64" µs (%"PRId64" µs + %"PRId64" µs)\n",
+ elapsed_expand + elapsed_compile,
+ elapsed_expand,
+ elapsed_compile);
+}
+
+static char *
+expand_filter(const char *text)
+{
+ char *expanded = NULL;
+ df_error_t *err = NULL;
+ gint64 start;
+
+ start = g_get_monotonic_time();
+ expanded = dfilter_expand(text, &err);
+ if (expanded == NULL) {
+ fprintf(stderr, "Error: %s\n", err->msg);
+ df_error_free(&err);
+ }
+ elapsed_expand = g_get_monotonic_time() - start;
+ return expanded;
+}
+
+static gboolean
+compile_filter(const char *text, dfilter_t **dfp)
+{
+ unsigned df_flags = 0;
+ gboolean ok;
+ df_error_t *df_err = NULL;
+ gint64 start;
+
+ if (opt_optimize > 0)
+ df_flags |= DF_OPTIMIZE;
+ if (opt_syntax_tree)
+ df_flags |= DF_SAVE_TREE;
+ if (opt_flex)
+ df_flags |= DF_DEBUG_FLEX;
+ if (opt_lemon)
+ df_flags |= DF_DEBUG_LEMON;
+
+ start = g_get_monotonic_time();
+ ok = dfilter_compile_full(text, dfp, &df_err, df_flags, "dftest");
+ if (!ok) {
+ fprintf(stderr, "Error: %s\n", df_err->msg);
+ if (df_err->loc.col_start >= 0) {
+ fprintf(stderr, " %s\n ", text);
+ putloc(stderr, df_err->loc);
+ }
+ df_error_free(&df_err);
+ }
+ elapsed_compile = g_get_monotonic_time() - start;
+ return ok;
+}
+
+int
+main(int argc, char **argv)
+{
+ char *configuration_init_error;
+ char *text = NULL;
+ char *expanded_text = NULL;
+ dfilter_t *df = NULL;
+ int exit_status = EXIT_FAILURE;
+
+ /*
+ * Set the C-language locale to the native environment and set the
+ * code page to UTF-8 on Windows.
+ */
+#ifdef _WIN32
+ setlocale(LC_ALL, ".UTF-8");
+#else
+ setlocale(LC_ALL, "");
+#endif
+
+ cmdarg_err_init(dftest_cmdarg_err, dftest_cmdarg_err_cont);
+
+ /* Initialize log handler early for startup. */
+ ws_log_init("dftest", vcmdarg_err);
+
+ /* Early logging command-line initialization. */
+ ws_log_parse_args(&argc, argv, vcmdarg_err, 1);
+
+ ws_noisy("Finished log init and parsing command line log arguments");
+
+ ws_init_version_info("DFTest", NULL, NULL);
+
+ const char *optstring = "hvdflstV0";
+ static struct ws_option long_options[] = {
+ { "help", ws_no_argument, 0, 'h' },
+ { "version", ws_no_argument, 0, 'v' },
+ { "debug", ws_no_argument, 0, 'd' },
+ { "flex", ws_no_argument, 0, 'f' },
+ { "lemon", ws_no_argument, 0, 'l' },
+ { "syntax", ws_no_argument, 0, 's' },
+ { "timer", ws_no_argument, 0, 't' },
+ { "verbose", ws_no_argument, 0, 'V' },
+ { "optimize", ws_required_argument, 0, 1000 },
+ { "types", ws_no_argument, 0, 2000 },
+ { "refs", ws_no_argument, 0, 3000 },
+ { NULL, 0, 0, 0 }
+ };
+ int opt;
+
+ for (;;) {
+ opt = ws_getopt_long(argc, argv, optstring, long_options, NULL);
+ if (opt == -1)
+ break;
+
+ switch (opt) {
+ case 'V':
+ opt_verbose = 1;
+ break;
+ case 'd':
+ opt_log_level = DFTEST_LOG_NOISY;
+ break;
+ case 'f':
+ opt_flex = 1;
+ break;
+ case 'l':
+ opt_lemon = 1;
+ break;
+ case 's':
+ opt_syntax_tree = 1;
+ break;
+ case 't':
+ opt_timer = 1;
+ break;
+ case '0':
+ opt_optimize = 0;
+ break;
+ case 1000:
+ if (strlen(ws_optarg) > 1 || !g_ascii_isdigit(*ws_optarg)) {
+ printf("Error: \"%s\" is not a valid number 0-9\n", ws_optarg);
+ print_usage(WS_EXIT_INVALID_OPTION);
+ }
+ errno = 0;
+ opt_optimize = strtol(ws_optarg, NULL, 10);
+ if (errno) {
+ printf("Error: %s\n", g_strerror(errno));
+ print_usage(WS_EXIT_INVALID_OPTION);
+ }
+ break;
+ case 2000:
+ opt_show_types = 1;
+ break;
+ case 3000:
+ opt_dump_refs = 1;
+ break;
+ case 'v':
+ show_version();
+ exit(EXIT_SUCCESS);
+ break;
+ case 'h':
+ show_help_header(NULL);
+ print_usage(EXIT_SUCCESS);
+ break;
+ case '?':
+ print_usage(EXIT_FAILURE);
+ default:
+ ws_assert_not_reached();
+ }
+ }
+
+ /* Check for filter on command line */
+ if (argv[ws_optind] == NULL) {
+ printf("Error: Missing argument.\n");
+ print_usage(EXIT_FAILURE);
+ }
+
+ if (opt_log_level == DFTEST_LOG_NOISY) {
+ ws_log_set_noisy_filter(LOG_DOMAIN_DFILTER);
+ }
+ else if (opt_flex || opt_lemon) {
+ /* Enable some dfilter logs with flex/lemon traces for context. */
+ ws_log_set_debug_filter(LOG_DOMAIN_DFILTER);
+ opt_log_level = DFTEST_LOG_DEBUG;
+ }
+
+ /*
+ * Get credential information for later use.
+ */
+ init_process_policies();
+
+ /*
+ * Attempt to get the pathname of the directory containing the
+ * executable file.
+ */
+ configuration_init_error = configuration_init(argv[0], NULL);
+ if (configuration_init_error != NULL) {
+ fprintf(stderr, "Error: Can't get pathname of directory containing "
+ "the dftest program: %s.\n",
+ configuration_init_error);
+ g_free(configuration_init_error);
+ }
+
+ static const struct report_message_routines dftest_report_routines = {
+ failure_message,
+ failure_message,
+ open_failure_message,
+ read_failure_message,
+ write_failure_message,
+ cfile_open_failure_message,
+ cfile_dump_open_failure_message,
+ cfile_read_failure_message,
+ cfile_write_failure_message,
+ cfile_close_failure_message
+ };
+
+ init_report_message("dftest", &dftest_report_routines);
+
+ timestamp_set_type(TS_RELATIVE);
+ timestamp_set_seconds_type(TS_SECONDS_DEFAULT);
+
+ /*
+ * Libwiretap must be initialized before libwireshark is, so that
+ * dissection-time handlers for file-type-dependent blocks can
+ * register using the file type/subtype value for the file type.
+ */
+ wtap_init(TRUE);
+
+ /* Register all dissectors; we must do this before checking for the
+ "-g" flag, as the "-g" flag dumps a list of fields registered
+ by the dissectors, and we must do it before we read the preferences,
+ in case any dissectors register preferences. */
+ if (!epan_init(NULL, NULL, FALSE))
+ goto out;
+
+ /* Load libwireshark settings from the current profile. */
+ epan_load_settings();
+
+ /* notify all registered modules that have had any of their preferences
+ changed either from one of the preferences file or from the command
+ line that its preferences have changed. */
+ prefs_apply_all();
+
+ /* This is useful to prevent confusion with option parsing.
+ * Skips printing options and argv[0]. */
+ if (opt_verbose) {
+ for (int i = ws_optind; i < argc; i++) {
+ fprintf(stderr, "argv[%d]: %s\n", i, argv[i]);
+ }
+ fprintf(stderr, "\n");
+ }
+
+ /* Get filter text */
+ text = get_args_as_string(argc, argv, ws_optind);
+
+ printf("Filter:\n %s\n\n", text);
+
+ /* Expand macros. */
+ expanded_text = expand_filter(text);
+ if (expanded_text == NULL) {
+ exit_status = WS_EXIT_INVALID_FILTER;
+ goto out;
+ }
+
+ if (strcmp(text, expanded_text) != 0)
+ printf("Filter (after expansion):\n %s\n\n", expanded_text);
+
+ /* Compile it */
+ if (!compile_filter(expanded_text, &df)) {
+ exit_status = WS_EXIT_INVALID_FILTER;
+ goto out;
+ }
+
+ /* If logging is enabled add an empty line. */
+ if (opt_log_level > DFTEST_LOG_NONE) {
+ printf("\n");
+ }
+
+ if (df == NULL) {
+ printf("Filter is empty.\n");
+ exit_status = WS_EXIT_INVALID_FILTER;
+ goto out;
+ }
+
+ if (opt_syntax_tree)
+ print_syntax_tree(df);
+
+ uint16_t dump_flags = 0;
+ if (opt_show_types)
+ dump_flags |= DF_DUMP_SHOW_FTYPE;
+ if (opt_dump_refs)
+ dump_flags |= DF_DUMP_REFERENCES;
+
+ dfilter_dump(stdout, df, dump_flags);
+
+ print_warnings(df);
+
+ if (opt_timer)
+ print_elapsed();
+
+ exit_status = 0;
+
+out:
+ epan_cleanup();
+ dfilter_free(df);
+ g_free(text);
+ g_free(expanded_text);
+ exit(exit_status);
+}