summaryrefslogtreecommitdiffstats
path: root/epan/dfilter/dfilter-macro.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-10 20:34:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-10 20:34:10 +0000
commite4ba6dbc3f1e76890b22773807ea37fe8fa2b1bc (patch)
tree68cb5ef9081156392f1dd62a00c6ccc1451b93df /epan/dfilter/dfilter-macro.c
parentInitial commit. (diff)
downloadwireshark-e4ba6dbc3f1e76890b22773807ea37fe8fa2b1bc.tar.xz
wireshark-e4ba6dbc3f1e76890b22773807ea37fe8fa2b1bc.zip
Adding upstream version 4.2.2.upstream/4.2.2
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--epan/dfilter/dfilter-macro.c682
1 files changed, 682 insertions, 0 deletions
diff --git a/epan/dfilter/dfilter-macro.c b/epan/dfilter/dfilter-macro.c
new file mode 100644
index 0000000..1e479be
--- /dev/null
+++ b/epan/dfilter/dfilter-macro.c
@@ -0,0 +1,682 @@
+/* dfilter-macro.c
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 2001 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+
+#include "config.h"
+#define WS_LOG_DOMAIN LOG_DOMAIN_DFILTER
+#include "dfilter-macro.h"
+
+#ifdef DUMP_DFILTER_MACRO
+#include <stdio.h>
+#endif
+#include <string.h>
+
+#include "dfilter-int.h"
+#include <ftypes/ftypes.h>
+#include <epan/uat-int.h>
+#include <epan/proto.h>
+#include <wsutil/glib-compat.h>
+
+
+static uat_t* dfilter_macro_uat = NULL;
+static dfilter_macro_t* macros = NULL;
+static unsigned num_macros;
+
+/* #define DUMP_DFILTER_MACRO */
+#ifdef DUMP_DFILTER_MACRO
+void dump_dfilter_macro_t(const dfilter_macro_t *m, const char *function, const char *file, int line);
+#define DUMP_MACRO(m) dump_dfilter_macro_t(m, G_STRFUNC, __FILE__, __LINE__)
+#else
+#define DUMP_MACRO(m)
+#endif
+
+static char* dfilter_macro_resolve(char* name, char** args, df_error_t** error) {
+ GString* text;
+ int argc = 0;
+ dfilter_macro_t* m = NULL;
+ int* arg_pos_p;
+ char** parts;
+ char* ret;
+ unsigned i;
+
+ for (i = 0; i < num_macros; i++) {
+ dfilter_macro_t* c = &(macros[i]);
+ if ( c->usable && g_str_equal(c->name,name) ) {
+ m = c;
+ break;
+ }
+ }
+
+ if (!m) {
+ if (error != NULL)
+ *error = df_error_new_printf(DF_ERROR_GENERIC, NULL, "macro '%s' does not exist", name);
+ return NULL;
+ }
+
+ DUMP_MACRO(m);
+
+ if (args) {
+ while(args[argc]) argc++;
+ }
+
+ if (argc != m->argc) {
+ if (error != NULL) {
+ *error = df_error_new_printf(DF_ERROR_GENERIC, NULL,
+ "wrong number of arguments for macro '%s', expecting %d instead of %d",
+ name, m->argc, argc);
+ }
+ return NULL;
+ }
+
+ arg_pos_p = m->args_pos;
+ parts = m->parts;
+
+ text = g_string_new(*(parts++));
+
+ if (args) {
+ while (*parts) {
+ g_string_append_printf(text,"%s%s",
+ args[*(arg_pos_p++)],
+ *(parts++));
+ }
+ }
+
+ ret = wmem_strdup(NULL, text->str);
+
+ g_string_free(text,true);
+
+ return ret;
+}
+
+/* Start points to the first character after "${" */
+static bool start_is_field_reference(const char *start)
+{
+ const char *end;
+ char saved_c;
+ const header_field_info *hfinfo;
+
+ end = strchr(start, '#');
+ if (end == NULL)
+ end = strchr(start, '}');
+ if (end == NULL)
+ return false;
+
+ saved_c = *end;
+ /* This violates constness but we will restore the original string. */
+ *(char *)end = '\0';
+ /* Search for name in registered fields. */
+
+ if (start[0] == '@')
+ start++;
+
+ hfinfo = dfilter_resolve_unparsed(NULL, start);
+ /* Restore mangled string. */
+ *(char *)end = saved_c;
+
+ if (hfinfo == NULL)
+ return false;
+
+ if (hfinfo->type == FT_PROTOCOL || hfinfo->type == FT_NONE) {
+ /* Ignore these? */
+ return false;
+ }
+
+ /* It's a field reference so ignore it as a macro. */
+ ws_noisy("Ignore field reference ${%s}", start);
+ return true;
+}
+
+static char* dfilter_macro_apply_recurse(const char* text, unsigned depth, df_error_t** error) {
+ enum { OUTSIDE, STARTING, NAME, ARGS } state = OUTSIDE;
+ GString* out;
+ GString* name = NULL;
+ GString* arg = NULL;
+ GPtrArray* args = NULL;
+ char c;
+ const char* r = text;
+ bool changed = false;
+
+ if ( depth > 31) {
+ if (error != NULL)
+ *error = df_error_new_msg("too much nesting in macros");
+ return NULL;
+ }
+
+#define FGS(n) if (n) g_string_free(n,true); n = NULL
+
+#define FREE_ALL() \
+ do { \
+ FGS(name); \
+ FGS(arg); \
+ if (args) { \
+ while(args->len) { void* p = g_ptr_array_remove_index_fast(args,0); g_free(p); } \
+ g_ptr_array_free(args,true); \
+ args = NULL; \
+ } \
+ } while(0)
+
+ if (error != NULL)
+ *error = NULL;
+ out = g_string_sized_new(64);
+
+ while(1) {
+ c = *r++;
+
+ switch(state) {
+ case OUTSIDE: {
+ switch(c) {
+ case '\0': {
+ goto finish;
+ } case '$': {
+ state = STARTING;
+ break;
+ } default: {
+ g_string_append_c(out,c);
+ break;
+ }
+ }
+ break;
+ } case STARTING: {
+ switch (c) {
+ case '{': {
+ if (start_is_field_reference(r)) {
+ /* We have a field reference, preserve the name with ${} and bail. */
+ g_string_append(out,"${");
+ state = OUTSIDE;
+ break;
+ }
+
+ /* We have a macro, continue. */
+ args = g_ptr_array_new();
+ arg = g_string_sized_new(32);
+ name = g_string_sized_new(32);
+
+ state = NAME;
+
+ break;
+ } case '\0': {
+ g_string_append_c(out,'$');
+
+ goto finish;
+ } default: {
+ g_string_append_c(out,'$');
+ g_string_append_c(out,c);
+
+ state = OUTSIDE;
+
+ break;
+ }
+ }
+ break;
+ } case NAME: {
+ if ( g_ascii_isalnum(c) || c == '_' || c == '-' || c == '.' ) {
+ g_string_append_c(name,c);
+ } else if ( c == ':') {
+ state = ARGS;
+ } else if ( c == '}') {
+ char* resolved;
+
+ g_ptr_array_add(args,NULL);
+
+ resolved = dfilter_macro_resolve(name->str, (char**)args->pdata, error);
+ if (resolved == NULL)
+ goto on_error;
+
+ changed = true;
+
+ g_string_append(out,resolved);
+ wmem_free(NULL, resolved);
+
+ FREE_ALL();
+
+ state = OUTSIDE;
+ } else if ( c == '\0') {
+ if (error != NULL)
+ *error = df_error_new_msg("end of filter in the middle of a macro expression");
+ goto on_error;
+ } else {
+ if (error != NULL)
+ *error = df_error_new_msg("invalid character in macro name");
+ goto on_error;
+ }
+ break;
+ } case ARGS: {
+ switch(c) {
+ case '\0': {
+ if (error != NULL)
+ *error = df_error_new_msg("end of filter in the middle of a macro expression");
+ goto on_error;
+ } case ';': {
+ g_ptr_array_add(args,g_string_free(arg,false));
+
+ arg = g_string_sized_new(32);
+ break;
+ } case '\\': {
+ c = *r++;
+ if (c) {
+ g_string_append_c(arg,c);
+ break;
+ } else {
+ if (error != NULL)
+ *error = df_error_new_msg("end of filter in the middle of a macro expression");
+ goto on_error;
+ }
+ } default: {
+ g_string_append_c(arg,c);
+ break;
+ } case '}': {
+ char* resolved;
+ g_ptr_array_add(args,g_string_free(arg,false));
+ g_ptr_array_add(args,NULL);
+
+ arg = NULL;
+
+ resolved = dfilter_macro_resolve(name->str, (char**)args->pdata, error);
+ if (resolved == NULL)
+ goto on_error;
+
+ changed = true;
+
+ g_string_append(out,resolved);
+ wmem_free(NULL, resolved);
+
+ FREE_ALL();
+
+ state = OUTSIDE;
+ break;
+ }
+ }
+ break;
+ }
+ }
+ }
+
+finish:
+ {
+ FREE_ALL();
+
+ if (changed) {
+ char* resolved = dfilter_macro_apply_recurse(out->str, depth + 1, error);
+ g_string_free(out,true);
+ return resolved;
+ } else {
+ char* out_str = wmem_strdup(NULL, out->str);
+ g_string_free(out,true);
+ return out_str;
+ }
+ }
+on_error:
+ {
+ FREE_ALL();
+ if (error != NULL) {
+ if (*error == NULL)
+ *error = df_error_new_msg("unknown error in macro expression");
+ }
+ g_string_free(out,true);
+ return NULL;
+ }
+}
+
+char* dfilter_macro_apply(const char* text, df_error_t** error) {
+ return dfilter_macro_apply_recurse(text, 0, error);
+}
+
+static bool macro_update(void* mp, gchar** error) {
+ dfilter_macro_t* m = (dfilter_macro_t*)mp;
+ GPtrArray* parts;
+ GArray* args_pos;
+ const char* r;
+ char* w;
+ char* part;
+ int argc = 0;
+
+ DUMP_MACRO(m);
+
+ *error = NULL;
+
+ /* Invalidate the display filter in case it's in use */
+ if (dfilter_macro_uat && dfilter_macro_uat->post_update_cb)
+ dfilter_macro_uat->post_update_cb();
+
+ parts = g_ptr_array_new();
+ args_pos = g_array_new(false,false,sizeof(int));
+
+ m->priv = part = w = g_strdup(m->text);
+ r = m->text;
+ g_ptr_array_add(parts,part);
+
+ while (r && *r) {
+
+ switch (*r) {
+ default:
+ *(w++) = *(r++);
+ break;
+ case '\0':
+ *w = *r;
+ goto done;
+ case '\\':
+ *(w++) = *(r++);
+ if(*r)
+ *(w++) = *(r++);
+ break;
+ case '$': {
+ int cnt = 0;
+ int arg_pos = 0;
+ do {
+ char c = *(r+1);
+ if (c >= '0' && c <= '9') {
+ cnt++;
+ r++;
+ *(w++) = '\0';
+ arg_pos *= 10;
+ arg_pos += c - '0';
+ } else {
+ break;
+ }
+ } while(*r);
+
+ if (cnt) {
+ *(w++) = '\0';
+ r++;
+ argc = argc < arg_pos ? arg_pos : argc;
+ arg_pos--;
+ g_array_append_val(args_pos,arg_pos);
+ g_ptr_array_add(parts,w);
+ } else {
+ *(w++) = *(r++);
+ }
+ break;
+ }
+ }
+
+ }
+
+done:
+ g_ptr_array_add(parts,NULL);
+
+ g_free(m->parts);
+ m->parts = (char **)g_ptr_array_free(parts, false);
+
+ g_free(m->args_pos);
+ m->args_pos = (int*)(void *)g_array_free(args_pos, false);
+
+ m->argc = argc;
+
+ m->usable = true;
+
+ DUMP_MACRO(m);
+
+ return true;
+}
+
+static void macro_free(void* r) {
+ dfilter_macro_t* m = (dfilter_macro_t*)r;
+
+ DUMP_MACRO(r);
+
+ g_free(m->name);
+ g_free(m->text);
+ g_free(m->priv);
+ g_free(m->parts);
+ g_free(m->args_pos);
+}
+
+static void* macro_copy(void* dest, const void* orig, size_t len _U_) {
+ dfilter_macro_t* d = (dfilter_macro_t*)dest;
+ const dfilter_macro_t* m = (const dfilter_macro_t*)orig;
+
+ DUMP_MACRO(m);
+
+ d->name = g_strdup(m->name);
+ d->text = g_strdup(m->text);
+ d->usable = m->usable;
+
+ if (m->parts) {
+ unsigned nparts = 0;
+
+ /*
+ * Copy the contents of m->priv (a "cooked" version
+ * of m->text) into d->priv.
+ *
+ * First we clone m->text into d->priv, this gets
+ * us a NUL terminated string of the proper length.
+ *
+ * Then we loop copying bytes from m->priv into
+ * d-priv. Since m->priv contains internal ACSII NULs
+ * we use the length of m->text to stop the copy.
+ */
+
+ d->priv = g_strdup(m->text);
+ {
+ const char* oldText = m->text;
+ const char* oldPriv = (const char*)m->priv;
+ char* newPriv = (char*)d->priv;
+ while(oldText && *oldText) {
+ *(newPriv++) = *(oldPriv++);
+ oldText++;
+ }
+ }
+
+ /*
+ * The contents of the m->parts array contains pointers
+ * into various sections of m->priv. Since it's
+ * an argv style array of ponters, this array is
+ * actually one larger than the number of parts
+ * to hold the final NULL terminator.
+ *
+ * The following copy clones the original m->parts
+ * array into d->parts but then fixes-up the pointers
+ * so that they point into the appropriate sections
+ * of the d->priv.
+ */
+
+ do nparts++; while (m->parts[nparts]);
+ d->parts = (char **)g_memdup2(m->parts,(nparts+1)*(unsigned)sizeof(void*));
+ nparts = 0;
+ while(m->parts[nparts]) {
+ if(nparts) {
+ d->parts[nparts] = d->parts[nparts - 1] + (m->parts[nparts] - m->parts[nparts - 1]);
+ } else {
+ d->parts[nparts] = (char *)d->priv;
+ }
+ nparts++;
+ }
+
+ /*
+ * Clone the contents of m->args_pos into d->args_pos.
+ */
+
+ d->args_pos = (int *)g_memdup2(m->args_pos,(--nparts)*(unsigned)sizeof(int));
+ }
+
+ DUMP_MACRO(d);
+
+ return d;
+}
+
+static bool macro_name_chk(void *mp, const char *in_name, unsigned name_len,
+ const void *u1 _U_, const void *u2 _U_, char **error) {
+ dfilter_macro_t* m = (dfilter_macro_t*)mp;
+ unsigned i;
+
+ if (name_len == 0) {
+ *error = g_strdup("invalid name");
+ return false;
+ }
+
+ for (i=0; i < name_len; i++) {
+ if (!(in_name[i] == '_' || g_ascii_isalnum(in_name[i]) ) ) {
+ *error = g_strdup("invalid char in name");
+ return false;
+ }
+ }
+
+ /* When loading (!m->name) or when adding/changing the an item with a
+ * different name, check for uniqueness. NOTE: if a duplicate already
+ * exists (because the user manually edited the file), then this will
+ * not trigger a warning. */
+ if (!m->name || g_strcmp0(m->name, in_name)) {
+ for (i = 0; i < num_macros; i++) {
+ /* This a string field which is always NUL-terminated,
+ * so no need to check name_len. */
+ if (!g_strcmp0(in_name, macros[i].name)) {
+ *error = ws_strdup_printf("macro '%s' already exists",
+ in_name);
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+UAT_CSTRING_CB_DEF(macro,name,dfilter_macro_t)
+UAT_CSTRING_CB_DEF(macro,text,dfilter_macro_t)
+
+void dfilter_macro_init(void) {
+ static uat_field_t uat_fields[] = {
+ UAT_FLD_CSTRING_OTHER(macro,name,"Name",macro_name_chk,"The name of the macro."),
+ /* N.B. it would be nice if there was a field type for display filters (with
+ auto-completion & colouring), but this wouldn't work here as the filter string
+ will contain $1, etc... */
+ UAT_FLD_CSTRING_ISPRINT(macro,text,"Text","The text this macro resolves to."),
+ UAT_END_FIELDS
+ };
+
+ dfilter_macro_uat = uat_new("Display Filter Macros",
+ sizeof(dfilter_macro_t),
+ DFILTER_MACRO_FILENAME,
+ true,
+ &macros,
+ &num_macros,
+ UAT_AFFECTS_FIELDS,
+ "ChDisplayFilterMacrosSection",
+ macro_copy,
+ macro_update,
+ macro_free,
+ NULL, /* Note: This is set in macros_init () */
+ NULL,
+ uat_fields);
+}
+
+void dfilter_macro_get_uat(uat_t **dfmu_ptr_ptr) {
+ *dfmu_ptr_ptr = dfilter_macro_uat;
+}
+
+#ifdef DUMP_DFILTER_MACRO
+/*
+ * The dfilter_macro_t has several characteristics that are
+ * not immediately obvious. The dump_dfilter_filter_macro_t()
+ * function can be used to help "visualize" the contents of
+ * a dfilter_macro_t.
+ *
+ * Some non-obvious components of this struct include:
+ *
+ * m->parts is an argv style array of pointers into the
+ * m->priv string.
+ *
+ * The last pointer of an m->parts array should contain
+ * NULL to indicate the end of the parts pointer array.
+ *
+ * m->priv is a "cooked" copy of the m->text string.
+ * Any variable substitution indicators within m->text
+ * ("$1", "$2", ...) will have been replaced with ASCII
+ * NUL characters within m->priv.
+ *
+ * The first element of m->parts array (m-parts[0]) will
+ * usually have the same pointer value as m->priv (unless
+ * the dfilter-macro starts off with a variable
+ * substitution indicator (e.g. "$1").
+ */
+
+void dump_dfilter_macro_t(const dfilter_macro_t *m, const char *function, const char *file, int line)
+{
+ printf("\n<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
+
+ if(m == NULL) {
+ printf(" dfilter_macro_t * == NULL! (via: %s(): %s:%d)\n", function, file, line);
+ printf("\n>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
+ return;
+ }
+
+ printf("DUMP of dfilter_macro_t: %p (via: %s(): %s:%d)\n", m, function, file, line);
+
+ printf(" &dfilter_macro->name == %p\n", &m->name);
+ if(m->name == NULL) {
+ printf(" ->name == NULL\n");
+ } else {
+ printf(" ->name == %p\n", m->name);
+ printf(" ->name == <%s>\n", m->name);
+ }
+
+ printf(" &dfilter_macro->text == %p\n", &m->text);
+ if(m->text == NULL) {
+ printf(" ->text == NULL\n");
+ } else {
+ printf(" ->text == %p\n", m->text);
+ printf(" ->text == <%s>\n", m->text);
+ }
+
+ printf(" &dfilter_macro->usable == %p\n", &m->usable);
+ printf(" ->usable == %u\n", m->usable);
+
+ printf(" &dfilter_macro->parts == %p\n", &m->parts);
+
+ if(m->parts == NULL) {
+ printf(" ->parts == NULL\n");
+ } else {
+ int i = 0;
+
+ while (m->parts[i]) {
+ printf(" ->parts[%d] == %p\n", i, m->parts[i]);
+ printf(" ->parts[%d] == <%s>\n", i, m->parts[i]);
+ i++;
+ }
+ printf(" ->parts[%d] == NULL\n", i);
+ }
+
+ printf(" &dfilter_macro->args_pos == %p\n", &m->args_pos);
+ if(m->args_pos == NULL) {
+ printf(" ->args_pos == NULL\n");
+ } else {
+ printf(" ->args_pos == %p\n", m->args_pos);
+ /*printf(" ->args_pos == <%?>\n", m->args_pos);*/
+ }
+
+ printf(" &dfilter_macro->argc == %p\n", &m->argc);
+ printf(" ->argc == %d\n", m->argc);
+
+ printf(" &dfilter_macro->priv == %p\n", &m->priv);
+ if(m->priv == NULL) {
+ printf(" ->priv == NULL\n");
+ } else {
+ printf(" ->priv == %p\n", m->priv);
+ printf(" ->priv == <%s>\n", (char *)m->priv);
+ }
+
+ printf("\n>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
+}
+#endif
+
+void dfilter_macro_cleanup(void)
+{
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */