aboutsummaryrefslogtreecommitdiffstats
path: root/epan/dfilter
diff options
context:
space:
mode:
authorJoão Valverde <j@v6e.pt>2022-04-12 16:13:08 +0100
committerA Wireshark GitLab Utility <gerald+gitlab-utility@wireshark.org>2022-04-14 13:07:41 +0000
commitcb2f085f14eee01bc57468db2f93800f8b817842 (patch)
tree1f223ce9b23bbfbf16fb60e861beb2b6907a2302 /epan/dfilter
parenta372497a851f71062ac1d279681826e68c66eddb (diff)
dfilter: Add max() and min() functions
Changes the function calling convention to pass the first register number plus the number of registers after that sequentially. This allows function with any number of arguments. Functions can still only return one value. Adds max() and min() function to select the maximum/minimum value from any number of arguments, all of the same type. The functions accept literals too. The return type is the same as the first argument (cannot be a literal).
Diffstat (limited to 'epan/dfilter')
-rw-r--r--epan/dfilter/CMakeLists.txt1
-rw-r--r--epan/dfilter/dfilter-int.h20
-rw-r--r--epan/dfilter/dfilter.c20
-rw-r--r--epan/dfilter/dfunctions.c156
-rw-r--r--epan/dfilter/dfunctions.h16
-rw-r--r--epan/dfilter/dfvm.c62
-rw-r--r--epan/dfilter/dfvm.h1
-rw-r--r--epan/dfilter/gencode.c103
-rw-r--r--epan/dfilter/semcheck.c107
-rw-r--r--epan/dfilter/sttype-function.c27
-rw-r--r--epan/dfilter/sttype-function.h4
-rw-r--r--epan/dfilter/sttype-pointer.c15
-rw-r--r--epan/dfilter/sttype-pointer.h20
-rw-r--r--epan/dfilter/syntax-tree.c18
-rw-r--r--epan/dfilter/syntax-tree.h4
15 files changed, 431 insertions, 143 deletions
diff --git a/epan/dfilter/CMakeLists.txt b/epan/dfilter/CMakeLists.txt
index 95ec4418ad..a7f542ceb9 100644
--- a/epan/dfilter/CMakeLists.txt
+++ b/epan/dfilter/CMakeLists.txt
@@ -23,6 +23,7 @@ set(DFILTER_HEADER_FILES
gencode.h
semcheck.h
sttype-function.h
+ sttype-pointer.h
sttype-range.h
sttype-set.h
sttype-test.h
diff --git a/epan/dfilter/dfilter-int.h b/epan/dfilter/dfilter-int.h
index dc50f6a75a..4d89277d8c 100644
--- a/epan/dfilter/dfilter-int.h
+++ b/epan/dfilter/dfilter-int.h
@@ -80,7 +80,7 @@ dfilter_fail(dfwork_t *dfw, stloc_t *err_loc,
WS_NORETURN
void
dfilter_fail_throw(dfwork_t *dfw, stloc_t *err_loc,
- long code, const char *format, ...) G_GNUC_PRINTF(4, 5);
+ const char *format, ...) G_GNUC_PRINTF(3, 4);
void
dfw_set_error_location(dfwork_t *dfw, stloc_t *err_loc);
@@ -97,6 +97,24 @@ DfilterTrace(FILE *TraceFILE, char *zTracePrompt);
header_field_info *
dfilter_resolve_unparsed(dfwork_t *dfw, const char *name);
+gboolean
+dfw_resolve_unparsed(dfwork_t *dfw, stnode_t *st);
+
+fvalue_t *
+dfilter_fvalue_from_unparsed(dfwork_t *dfw, ftenum_t ftype, stnode_t *st,
+ gboolean allow_partial_value, header_field_info *hfinfo_value_string);
+
+WS_RETNONNULL fvalue_t*
+dfilter_fvalue_from_literal(dfwork_t *dfw, ftenum_t ftype, stnode_t *st,
+ gboolean allow_partial_value, header_field_info *hfinfo_value_string);
+
+WS_RETNONNULL fvalue_t *
+dfilter_fvalue_from_string(dfwork_t *dfw, ftenum_t ftype, stnode_t *st,
+ header_field_info *hfinfo_value_string);
+
+WS_RETNONNULL fvalue_t *
+dfilter_fvalue_from_charconst(dfwork_t *dfw, ftenum_t ftype, stnode_t *st);
+
const char *tokenstr(int token);
#endif
diff --git a/epan/dfilter/dfilter.c b/epan/dfilter/dfilter.c
index 24198660d2..39adb1cedd 100644
--- a/epan/dfilter/dfilter.c
+++ b/epan/dfilter/dfilter.c
@@ -76,15 +76,14 @@ dfilter_fail(dfwork_t *dfw, stloc_t *loc,
}
void
-dfilter_fail_throw(dfwork_t *dfw, stloc_t *loc,
- long code, const char *format, ...)
+dfilter_fail_throw(dfwork_t *dfw, stloc_t *loc, const char *format, ...)
{
va_list args;
va_start(args, format);
dfilter_vfail(dfw, loc, format, args);
va_end(args);
- THROW(code);
+ THROW(TypeError);
}
void
@@ -122,6 +121,21 @@ dfilter_resolve_unparsed(dfwork_t *dfw, const char *name)
return NULL;
}
+gboolean
+dfw_resolve_unparsed(dfwork_t *dfw, stnode_t *st)
+{
+ if (stnode_type_id(st) != STTYPE_UNPARSED)
+ return FALSE;
+
+ header_field_info *hfinfo = dfilter_resolve_unparsed(dfw, stnode_data(st));
+ if (hfinfo != NULL) {
+ stnode_replace(st, STTYPE_FIELD, hfinfo);
+ return TRUE;
+ }
+ stnode_replace(st, STTYPE_LITERAL, g_strdup(stnode_data(st)));
+ return FALSE;
+}
+
/* Initialize the dfilter module */
void
dfilter_init(void)
diff --git a/epan/dfilter/dfunctions.c b/epan/dfilter/dfunctions.c
index 8f05c8e477..66250d7cb5 100644
--- a/epan/dfilter/dfunctions.c
+++ b/epan/dfilter/dfunctions.c
@@ -12,6 +12,7 @@
#include "dfilter-int.h"
#include "dfunctions.h"
+#include "sttype-pointer.h"
#include <string.h>
@@ -20,18 +21,19 @@
#include <wsutil/ws_assert.h>
#define FAIL(dfw, node, ...) \
- dfilter_fail_throw(dfw, stnode_location(node), TypeError, __VA_ARGS__)
+ dfilter_fail_throw(dfw, stnode_location(node), __VA_ARGS__)
/* Convert an FT_STRING using a callback function */
static gboolean
-string_walk(GSList* arg1list, GSList **retval, gchar(*conv_func)(gchar))
+string_walk(GSList **args, guint32 arg_count, GSList **retval, gchar(*conv_func)(gchar))
{
GSList *arg1;
fvalue_t *arg_fvalue;
fvalue_t *new_ft_string;
char *s, *c;
- arg1 = arg1list;
+ ws_assert(arg_count == 1);
+ arg1 = args[0];
while (arg1) {
arg_fvalue = (fvalue_t *)arg1->data;
/* XXX - it would be nice to handle FT_TVBUFF, too */
@@ -54,27 +56,28 @@ string_walk(GSList* arg1list, GSList **retval, gchar(*conv_func)(gchar))
/* dfilter function: lower() */
static gboolean
-df_func_lower(GSList* arg1list, GSList *arg2junk _U_, GSList **retval)
+df_func_lower(GSList **args, guint32 arg_count, GSList **retval)
{
- return string_walk(arg1list, retval, g_ascii_tolower);
+ return string_walk(args, arg_count, retval, g_ascii_tolower);
}
/* dfilter function: upper() */
static gboolean
-df_func_upper(GSList* arg1list, GSList *arg2junk _U_, GSList **retval)
+df_func_upper(GSList **args, guint32 arg_count, GSList **retval)
{
- return string_walk(arg1list, retval, g_ascii_toupper);
+ return string_walk(args, arg_count, retval, g_ascii_toupper);
}
/* dfilter function: len() */
static gboolean
-df_func_len(GSList* arg1list, GSList *arg2junk _U_, GSList **retval)
+df_func_len(GSList **args, guint32 arg_count, GSList **retval)
{
GSList *arg1;
fvalue_t *arg_fvalue;
fvalue_t *ft_len;
- arg1 = arg1list;
+ ws_assert(arg_count == 1);
+ arg1 = args[0];
while (arg1) {
arg_fvalue = (fvalue_t *)arg1->data;
ft_len = fvalue_new(FT_UINT32);
@@ -88,12 +91,15 @@ df_func_len(GSList* arg1list, GSList *arg2junk _U_, GSList **retval)
/* dfilter function: count() */
static gboolean
-df_func_count(GSList* arg1list, GSList *arg2junk _U_, GSList **retval)
+df_func_count(GSList **args, guint32 arg_count, GSList **retval)
{
+ GSList *arg1;
fvalue_t *ft_ret;
guint32 num_items;
- num_items = (guint32)g_slist_length(arg1list);
+ ws_assert(arg_count == 1);
+ arg1 = args[0];
+ num_items = (guint32)g_slist_length(arg1);
ft_ret = fvalue_new(FT_UINT32);
fvalue_set_uinteger(ft_ret, num_items);
@@ -104,13 +110,16 @@ df_func_count(GSList* arg1list, GSList *arg2junk _U_, GSList **retval)
/* dfilter function: string() */
static gboolean
-df_func_string(GSList* arg1list, GSList *arg2junk _U_, GSList **retval)
+df_func_string(GSList **args, guint32 arg_count, GSList **retval)
{
- GSList *arg1 = arg1list;
+ GSList *arg1;
fvalue_t *arg_fvalue;
fvalue_t *new_ft_string;
char *s;
+ ws_assert(arg_count == 1);
+ arg1 = args[0];
+
while (arg1) {
arg_fvalue = (fvalue_t *)arg1->data;
switch (fvalue_type_ftenum(arg_fvalue))
@@ -167,15 +176,53 @@ df_func_string(GSList* arg1list, GSList *arg2junk _U_, GSList **retval)
return TRUE;
}
+static gboolean
+df_func_compare(GSList **args, guint32 arg_count, GSList **retval,
+ gboolean (*fv_cmp)(const fvalue_t *a, const fvalue_t *b))
+{
+ fvalue_t *fv_ret = NULL;
+ GSList *l;
+ guint32 i;
+
+ for (i = 0; i < arg_count; i++) {
+ for (l = args[i]; l != NULL; l = l->next) {
+ if (fv_ret == NULL || fv_cmp(l->data, fv_ret)) {
+ fv_ret = l->data;
+ }
+ }
+ }
+
+ *retval = g_slist_append(NULL, fvalue_dup(fv_ret));
+
+ return TRUE;
+}
+
+/* Find maximum value. */
+static gboolean
+df_func_max(GSList **args, guint32 arg_count, GSList **retval)
+{
+ return df_func_compare(args, arg_count, retval, fvalue_gt);
+}
+
+/* Find minimum value. */
+static gboolean
+df_func_min(GSList **args, guint32 arg_count, GSList **retval)
+{
+ return df_func_compare(args, arg_count, retval, fvalue_lt);
+}
+
/* For upper() and lower() checks that the parameter passed to
* it is an FT_STRING */
static void
ul_semcheck_is_field_string(dfwork_t *dfw, const char *func_name,
- int param_num, stnode_t *st_node)
+ GSList *param_list, stloc_t *func_loc _U_)
{
header_field_info *hfinfo;
- ws_assert(param_num == 0);
+ ws_assert(g_slist_length(param_list) == 1);
+ stnode_t *st_node = param_list->data;
+
+ dfw_resolve_unparsed(dfw, st_node);
if (stnode_type_id(st_node) == STTYPE_FIELD) {
hfinfo = stnode_data(st_node);
@@ -188,9 +235,12 @@ ul_semcheck_is_field_string(dfwork_t *dfw, const char *func_name,
static void
ul_semcheck_is_field(dfwork_t *dfw, const char *func_name,
- int param_num, stnode_t *st_node)
+ GSList *param_list, stloc_t *func_loc _U_)
{
- ws_assert(param_num == 0);
+ ws_assert(g_slist_length(param_list) == 1);
+ stnode_t *st_node = param_list->data;
+
+ dfw_resolve_unparsed(dfw, st_node);
if (stnode_type_id(st_node) == STTYPE_FIELD)
return;
@@ -200,11 +250,14 @@ ul_semcheck_is_field(dfwork_t *dfw, const char *func_name,
static void
ul_semcheck_string_param(dfwork_t *dfw, const char *func_name,
- int param_num, stnode_t *st_node)
+ GSList *param_list, stloc_t *func_loc _U_)
{
header_field_info *hfinfo;
- ws_assert(param_num == 0);
+ ws_assert(g_slist_length(param_list) == 1);
+ stnode_t *st_node = param_list->data;
+
+ dfw_resolve_unparsed(dfw, st_node);
if (stnode_type_id(st_node) == STTYPE_FIELD) {
hfinfo = stnode_data(st_node);
@@ -250,6 +303,69 @@ ul_semcheck_string_param(dfwork_t *dfw, const char *func_name,
FAIL(dfw, st_node, "Only fields can be used as parameter for %s()", func_name);
}
+/* Check arguments are all the same type and they can be compared. */
+static void
+ul_semcheck_compare(dfwork_t *dfw, const char *func_name,
+ GSList *param_list, stloc_t *func_loc)
+{
+ stnode_t *arg;
+ ftenum_t ftype, ft_arg;
+ GSList *l;
+ const header_field_info *hfinfo;
+ fvalue_t *fv;
+
+ /* First argument must be a field not FT_NONE. */
+ arg = param_list->data;
+ dfw_resolve_unparsed(dfw, arg);
+ ftype = sttype_pointer_ftenum(arg);
+ if (ftype == FT_NONE) {
+ FAIL(dfw, arg, "First argument to %s() must be a field, not %s",
+ func_name, stnode_type_name(arg));
+ }
+
+ for (l = param_list; l != NULL; l = l->next) {
+ arg = l->data;
+ dfw_resolve_unparsed(dfw, arg);
+
+ switch (stnode_type_id(arg)) {
+ case STTYPE_FIELD:
+ case STTYPE_REFERENCE:
+ hfinfo = stnode_data(arg);
+ ft_arg = hfinfo->type;
+ break;
+ case STTYPE_LITERAL:
+ fv = dfilter_fvalue_from_literal(dfw, ftype, arg, FALSE, NULL);
+ stnode_replace(arg, STTYPE_FVALUE, fv);
+ ft_arg = fvalue_type_ftenum(stnode_data(arg));
+ break;
+ case STTYPE_FVALUE:
+ ft_arg = fvalue_type_ftenum(stnode_data(arg));
+ break;
+ default:
+ FAIL(dfw, arg, "Type %s is not valid for %s",
+ stnode_type_name(arg), func_name);
+ }
+ if (ft_arg == FT_NONE) {
+ dfilter_fail_throw(dfw, func_loc,
+ "Argument '%s' (FT_NONE) is not valid for %s()",
+ stnode_todisplay(arg), func_name);
+ }
+ if (ftype == FT_NONE) {
+ ftype = ft_arg;
+ }
+ if (ft_arg != ftype) {
+ dfilter_fail_throw(dfw, func_loc,
+ "Arguments to '%s' must have the same type",
+ func_name);
+ }
+ if (!ftype_can_cmp(ft_arg)) {
+ dfilter_fail_throw(dfw, func_loc,
+ "Argument '%s' to '%s' cannot be ordered",
+ stnode_todisplay(arg), func_name);
+ }
+ }
+}
+
/* The table of all display-filter functions */
static df_func_def_t
df_functions[] = {
@@ -258,6 +374,8 @@ df_functions[] = {
{ "len", df_func_len, FT_UINT32, 1, 1, ul_semcheck_is_field },
{ "count", df_func_count, FT_UINT32, 1, 1, ul_semcheck_is_field },
{ "string", df_func_string, FT_STRING, 1, 1, ul_semcheck_string_param },
+ { "max", df_func_max, /*Any*/ 0, 1, 0, ul_semcheck_compare },
+ { "min", df_func_min, /*Any*/ 0, 1, 0, ul_semcheck_compare },
{ NULL, NULL, FT_NONE, 0, 0, NULL }
};
diff --git a/epan/dfilter/dfunctions.h b/epan/dfilter/dfunctions.h
index d9f1de1daa..60cae3a33e 100644
--- a/epan/dfilter/dfunctions.h
+++ b/epan/dfilter/dfunctions.h
@@ -14,27 +14,23 @@
#include <ftypes/ftypes.h>
#include "syntax-tree.h"
+/* Functions take any number of arguments and return 1. */
+
/* The run-time logic of the dfilter function */
-typedef gboolean (*DFFuncType)(GSList *arg1list, GSList *arg2list, GSList **retval);
+typedef gboolean (*DFFuncType)(GSList **arg_list, guint32 arg_count, GSList **retval);
/* The semantic check for the dfilter function */
typedef void (*DFSemCheckType)(dfwork_t *dfw, const char *func_name,
- int param_num, stnode_t *st_node);
-
-/* If a function needs more args than this, increase
- * this macro and add more arg members to the dfvm_insn_t
- * struct in dfvm.h, and add some logic to dfw_append_function()
- * and dfvm_apply() */
-#define DFUNCTION_MAX_NARGS 2
+ GSList *param_list, stloc_t *func_loc);
/* This is a "function definition" record, holding everything
* we need to know about a function */
typedef struct {
const char *name;
DFFuncType function;
- ftenum_t retval_ftype;
+ ftenum_t retval_ftype; /* 0 means return value is the same as input argument(s) */
guint min_nargs;
- guint max_nargs;
+ guint max_nargs; /* 0 for no limit */
DFSemCheckType semcheck_param_function;
} df_func_def_t;
diff --git a/epan/dfilter/dfvm.c b/epan/dfilter/dfvm.c
index 0afe51e48b..eaaa1f4828 100644
--- a/epan/dfilter/dfvm.c
+++ b/epan/dfilter/dfvm.c
@@ -14,6 +14,9 @@
#include <ftypes/ftypes.h>
#include <wsutil/ws_assert.h>
+static void
+debug_register(GSList *reg, guint32 num);
+
dfvm_insn_t*
dfvm_insn_new(dfvm_opcode_t op)
{
@@ -170,11 +173,14 @@ dfvm_value_tostr(dfvm_value_t *v)
s = ws_strdup(ws_regex_pattern(v->value.pcre));
break;
case REGISTER:
- s = ws_strdup_printf("reg#%u", v->value.numeric);
+ s = ws_strdup_printf("reg#%"G_GUINT32_FORMAT, v->value.numeric);
break;
case FUNCTION_DEF:
s = ws_strdup(v->value.funcdef->name);
break;
+ case INTEGER:
+ s = ws_strdup_printf("%"G_GUINT32_FORMAT, v->value.numeric);
+ break;
default:
s = ws_strdup("FIXME");
}
@@ -226,14 +232,20 @@ dfvm_dump_str(wmem_allocator_t *alloc, dfilter_t *df, gboolean print_references)
id, arg1_str, arg2_str);
break;
+ case PUT_FVALUE:
+ wmem_strbuf_append_printf(buf, "%05d PUT_FVALUE\t%s -> %s\n",
+ id, arg1_str, arg2_str);
+ break;
+
case CALL_FUNCTION:
wmem_strbuf_append_printf(buf, "%05d CALL_FUNCTION\t%s(",
id, arg1_str);
if (arg3_str) {
wmem_strbuf_append_printf(buf, "%s", arg3_str);
}
- if (arg4_str) {
- wmem_strbuf_append_printf(buf, ", %s", arg4_str);
+ for (guint32 i = 1; i <= arg4->value.numeric; i++) {
+ wmem_strbuf_append_printf(buf, ", reg#%"G_GUINT32_FORMAT,
+ arg3->value.numeric + i);
}
wmem_strbuf_append_printf(buf, ") -> %s\n", arg2_str);
break;
@@ -697,28 +709,32 @@ mk_range(dfilter_t *df, dfvm_value_t *from_arg, dfvm_value_t *to_arg,
df->free_registers[to_arg->value.numeric] = (GDestroyNotify)fvalue_free;
}
+/*
+ * arg1: function def
+ * arg2: return register
+ * arg3: first input register
+ * arg4: number of input registers after first
+ */
static gboolean
call_function(dfilter_t *df, dfvm_value_t *arg1, dfvm_value_t *arg2,
dfvm_value_t *arg3, dfvm_value_t *arg4)
{
df_func_def_t *funcdef;
- GSList *param1 = NULL;
- GSList *param2 = NULL;
GSList *retval = NULL;
gboolean accum;
+ guint32 reg_return, reg_first_arg, more_args_count;
funcdef = arg1->value.funcdef;
- if (arg3) {
- param1 = df->registers[arg3->value.numeric];
- }
- if (arg4) {
- param2 = df->registers[arg4->value.numeric];
- }
- accum = funcdef->function(param1, param2, &retval);
+ reg_return = arg2->value.numeric;
+ reg_first_arg = arg3->value.numeric;
+ more_args_count = arg4->value.numeric;
+
+ accum = funcdef->function(&df->registers[reg_first_arg], 1 + more_args_count, &retval);
- df->registers[arg2->value.numeric] = retval;
+ /* Write return registers. */
+ df->registers[reg_return] = retval;
// functions create a new value, so own it.
- df->free_registers[arg2->value.numeric] = (GDestroyNotify)fvalue_free;
+ df->free_registers[reg_return] = (GDestroyNotify)fvalue_free;
return accum;
}
@@ -731,6 +747,8 @@ static void debug_op_error(fvalue_t *v1, fvalue_t *v2, const char *op, const cha
g_free(s2);
}
+/* Used for temporary debugging only, don't leave in production code (at
+ * a minimum WS_DEBUG_HERE must be replaced by another log level). */
static void _U_
debug_register(GSList *reg, guint32 num)
{
@@ -748,7 +766,7 @@ debug_register(GSList *reg, guint32 num)
wmem_strbuf_append_c(buf, ' ');
}
wmem_strbuf_append_c(buf, '}');
- ws_noisy("%s", wmem_strbuf_get_str(buf));
+ WS_DEBUG_HERE("%s", wmem_strbuf_get_str(buf));
wmem_strbuf_destroy(buf);
}
@@ -865,6 +883,16 @@ mk_minus(dfilter_t *df, dfvm_value_t *arg1, dfvm_value_t *to_arg)
df->free_registers[to_arg->value.numeric] = (GDestroyNotify)fvalue_free;
}
+static void
+put_fvalue(dfilter_t *df, dfvm_value_t *arg1, dfvm_value_t *to_arg)
+{
+ fvalue_t *fv = arg1->value.fvalue;
+ df->registers[to_arg->value.numeric] = g_slist_append(NULL, fv);
+
+ /* Memory is owned by the dfvm_value_t. */
+ df->free_registers[to_arg->value.numeric] = NULL;
+}
+
gboolean
dfvm_apply(dfilter_t *df, proto_tree *tree)
{
@@ -913,6 +941,10 @@ dfvm_apply(dfilter_t *df, proto_tree *tree)
accum = read_reference(df, arg1, arg2);
break;
+ case PUT_FVALUE:
+ put_fvalue(df, arg1, arg2);
+ break;
+
case CALL_FUNCTION:
accum = call_function(df, arg1, arg2, arg3, arg4);
break;
diff --git a/epan/dfilter/dfvm.h b/epan/dfilter/dfvm.h
index fc6902218f..565e5568fd 100644
--- a/epan/dfilter/dfvm.h
+++ b/epan/dfilter/dfvm.h
@@ -54,6 +54,7 @@ typedef enum {
RETURN,
READ_TREE,
READ_REFERENCE,
+ PUT_FVALUE,
ALL_EQ,
ANY_EQ,
ALL_NE,
diff --git a/epan/dfilter/gencode.c b/epan/dfilter/gencode.c
index d68ef697e2..eb90b6cbc3 100644
--- a/epan/dfilter/gencode.c
+++ b/epan/dfilter/gencode.c
@@ -38,12 +38,13 @@ dfw_append_insn(dfwork_t *dfw, dfvm_insn_t *insn)
/* returns register number */
static dfvm_value_t *
-dfw_append_read_tree(dfwork_t *dfw, header_field_info *hfinfo)
+dfw_append_read_tree(dfwork_t *dfw, header_field_info *hfinfo, gboolean reuse_register)
{
dfvm_insn_t *insn;
int reg = -1;
dfvm_value_t *reg_val, *val1;
gboolean added_new_hfinfo = FALSE;
+ void *loaded_key;
/* Rewind to find the first field of this name. */
while (hfinfo->same_name_prev_id != -1) {
@@ -53,14 +54,21 @@ dfw_append_read_tree(dfwork_t *dfw, header_field_info *hfinfo)
/* Keep track of which registers
* were used for which hfinfo's so that we
* can re-use registers. */
- reg = GPOINTER_TO_INT(
- g_hash_table_lookup(dfw->loaded_fields, hfinfo));
- if (reg) {
- /* Reg's are stored in has as reg+1, so
- * that the non-existence of a hfinfo in
- * the hash, or 0, can be differentiated from
- * a hfinfo being loaded into register #0. */
- reg--;
+ loaded_key = g_hash_table_lookup(dfw->loaded_fields, hfinfo);
+ if (loaded_key != NULL) {
+ /* Already loaded at least once. */
+ if (reuse_register) {
+ /*
+ * Reg's are stored in has as reg+1, so
+ * that the non-existence of a hfinfo in
+ * the hash, or 0, can be differentiated from
+ * a hfinfo being loaded into register #0.
+ */
+ reg = GPOINTER_TO_INT(loaded_key) - 1;
+ }
+ else {
+ reg = dfw->next_register++;
+ }
}
else {
reg = dfw->next_register++;
@@ -160,6 +168,23 @@ dfw_append_mk_range(dfwork_t *dfw, stnode_t *node, GSList **jumps_ptr)
return reg_val;
}
+/* returns register number */
+static dfvm_value_t *
+dfw_append_put_fvalue(dfwork_t *dfw, fvalue_t *fv)
+{
+ dfvm_insn_t *insn;
+ dfvm_value_t *reg_val, *val1;
+
+ insn = dfvm_insn_new(PUT_FVALUE);
+ val1 = dfvm_value_new_fvalue(fv);
+ insn->arg1 = dfvm_value_ref(val1);
+ reg_val = dfvm_value_new_register(dfw->next_register++);
+ insn->arg2 = dfvm_value_ref(reg_val);
+ dfw_append_insn(dfw, insn);
+
+ return reg_val;
+}
+
/* returns register number that the functions's result will be in. */
static dfvm_value_t *
dfw_append_function(dfwork_t *dfw, stnode_t *node, GSList **jumps_ptr)
@@ -167,8 +192,10 @@ dfw_append_function(dfwork_t *dfw, stnode_t *node, GSList **jumps_ptr)
GSList *params;
GSList *param_jumps = NULL;
dfvm_value_t *jmp;
- dfvm_insn_t *insn;
- dfvm_value_t *reg_val, *val1, *val3, *val4;
+ dfvm_insn_t *insn, *insn_jump;
+ dfvm_value_t *reg_val, *val1, *val3, *val4, *val_arg;
+ guint32 reg_first, more_args_count;
+ stnode_t *arg;
/* Create the new DFVM instruction */
insn = dfvm_insn_new(CALL_FUNCTION);
@@ -176,26 +203,56 @@ dfw_append_function(dfwork_t *dfw, stnode_t *node, GSList **jumps_ptr)
insn->arg1 = dfvm_value_ref(val1);
reg_val = dfvm_value_new_register(dfw->next_register++);
insn->arg2 = dfvm_value_ref(reg_val);
- insn->arg3 = NULL;
- insn->arg4 = NULL;
+ /* Create input arguments */
params = sttype_function_params(node);
- if (params) {
- val3 = gen_entity(dfw, params->data, &param_jumps);
- insn->arg3 = dfvm_value_ref(val3);
+ ws_assert(params);
+ val3 = dfw_append_read_tree(dfw, stnode_steal_data(params->data), FALSE);
+ insn->arg3 = dfvm_value_ref(val3);
+ /* Add a jump if reading argument failed. */
+ insn_jump = dfvm_insn_new(IF_FALSE_GOTO);
+ jmp = dfvm_value_new(INSN_NUMBER);
+ insn_jump->arg1 = dfvm_value_ref(jmp);
+ dfw_append_insn(dfw, insn_jump);
+ param_jumps = g_slist_prepend(param_jumps, jmp);
+
+ params = params->next;
+ reg_first = val3->value.numeric;
+ more_args_count = 0;
+ while (params) {
+ arg = params->data;
+ switch (stnode_type_id(arg)) {
+ case STTYPE_FVALUE:
+ dfw_append_put_fvalue(dfw, stnode_steal_data(arg));
+ break;
+ case STTYPE_FIELD:
+ /* We cannot reuse registers here because the function calling
+ * convention is to pass input arguments sequentially. */
+ val_arg = dfw_append_read_tree(dfw, stnode_data(arg), FALSE);
+ /* Assert the registers are numbered sequentially. */
+ ws_assert(val_arg->value.numeric == reg_first + more_args_count + 1);
+ /* Add a jump if reading argument failed. */
+ insn_jump = dfvm_insn_new(IF_FALSE_GOTO);
+ jmp = dfvm_value_new(INSN_NUMBER);
+ insn_jump->arg1 = dfvm_value_ref(jmp);
+ dfw_append_insn(dfw, insn_jump);
+ param_jumps = g_slist_prepend(param_jumps, jmp);
+ break;
+ default:
+ ws_assert_not_reached();
+ }
+ more_args_count++;
params = params->next;
}
- if (params) {
- val4 = gen_entity(dfw, params->data, &param_jumps);
- insn->arg4 = dfvm_value_ref(val4);
- }
- ws_assert(!g_slist_next(params));
+ val4 = dfvm_value_new(INTEGER);
+ val4->value.numeric = more_args_count;
+ insn->arg4 = dfvm_value_ref(val4);
dfw_append_insn(dfw, insn);
/* If any of our parameters failed, send them to
* our own failure instruction. This *has* to be done
- * after we caled dfw_append_insn above so that
+ * after we called dfw_append_insn above so that
* we know what the next DFVM insruction is, via
* dfw->next_insn_id */
g_slist_foreach(param_jumps, fixup_jumps, dfw);
@@ -390,7 +447,7 @@ gen_entity(dfwork_t *dfw, stnode_t *st_arg, GSList **jumps_ptr)
if (e_type == STTYPE_FIELD) {
hfinfo = stnode_data(st_arg);
- val = dfw_append_read_tree(dfw, hfinfo);
+ val = dfw_append_read_tree(dfw, hfinfo, TRUE);
insn = dfvm_insn_new(IF_FALSE_GOTO);
jmp = dfvm_value_new(INSN_NUMBER);
diff --git a/epan/dfilter/semcheck.c b/epan/dfilter/semcheck.c
index 7a4eb5452b..838d2d847c 100644
--- a/epan/dfilter/semcheck.c
+++ b/epan/dfilter/semcheck.c
@@ -32,8 +32,7 @@
#define FAIL(dfw, node, ...) \
do { \
ws_noisy("Semantic check failed here."); \
- dfilter_fail_throw(dfw, stnode_location(node), \
- TypeError, __VA_ARGS__); \
+ dfilter_fail_throw(dfw, stnode_location(node), __VA_ARGS__); \
} while (0)
static void
@@ -160,7 +159,7 @@ node_is_constant(stnode_t *node)
/* Gets an fvalue from a string, and sets the error message on failure. */
WS_RETNONNULL
-static fvalue_t*
+fvalue_t*
dfilter_fvalue_from_literal(dfwork_t *dfw, ftenum_t ftype, stnode_t *st,
gboolean allow_partial_value, header_field_info *hfinfo_value_string)
{
@@ -190,7 +189,7 @@ dfilter_fvalue_from_literal(dfwork_t *dfw, ftenum_t ftype, stnode_t *st,
return fv;
}
-static fvalue_t *
+fvalue_t *
dfilter_fvalue_from_unparsed(dfwork_t *dfw, ftenum_t ftype, stnode_t *st,
gboolean allow_partial_value, header_field_info *hfinfo_value_string)
{
@@ -243,7 +242,7 @@ dfilter_fvalue_from_unparsed(dfwork_t *dfw, ftenum_t ftype, stnode_t *st,
/* Gets an fvalue from a string, and sets the error message on failure. */
WS_RETNONNULL
-static fvalue_t *
+fvalue_t *
dfilter_fvalue_from_string(dfwork_t *dfw, ftenum_t ftype, stnode_t *st,
header_field_info *hfinfo_value_string)
{
@@ -271,21 +270,6 @@ dfilter_fvalue_from_string(dfwork_t *dfw, ftenum_t ftype, stnode_t *st,
return fv;
}
-static gboolean
-resolve_unparsed(dfwork_t *dfw, stnode_t *st)
-{
- if (stnode_type_id(st) != STTYPE_UNPARSED)
- return FALSE;
-
- header_field_info *hfinfo = dfilter_resolve_unparsed(dfw, stnode_data(st));
- if (hfinfo != NULL) {
- stnode_replace(st, STTYPE_FIELD, hfinfo);
- return TRUE;
- }
- stnode_replace(st, STTYPE_LITERAL, g_strdup(stnode_data(st)));
- return FALSE;
-}
-
/* Creates a FT_UINT32 fvalue with a given value. */
static fvalue_t*
mk_uint32_fvalue(guint32 val)
@@ -524,7 +508,7 @@ check_exists(dfwork_t *dfw, stnode_t *st_arg1)
{
LOG_NODE(st_arg1);
- resolve_unparsed(dfw, st_arg1);
+ dfw_resolve_unparsed(dfw, st_arg1);
switch (stnode_type_id(st_arg1)) {
case STTYPE_FIELD:
@@ -578,7 +562,7 @@ check_drange_sanity(dfwork_t *dfw, stnode_t *st)
entity1 = sttype_range_entity(st);
ws_assert(entity1);
- resolve_unparsed(dfw, entity1);
+ dfw_resolve_unparsed(dfw, entity1);
if (stnode_type_id(entity1) == STTYPE_FIELD) {
hfinfo1 = stnode_data(entity1);
@@ -589,15 +573,13 @@ check_drange_sanity(dfwork_t *dfw, stnode_t *st)
hfinfo1->abbrev, ftype_pretty_name(ftype1));
}
} else if (stnode_type_id(entity1) == STTYPE_FUNCTION) {
- df_func_def_t *funcdef = sttype_function_funcdef(entity1);
- ftype1 = funcdef->retval_ftype;
+ check_function(dfw, entity1);
+ ftype1 = sttype_function_retval_ftype(entity1);
if (!ftype_can_slice(ftype1)) {
FAIL(dfw, entity1, "Return value of function \"%s\" is a %s and cannot be converted into a sequence of bytes.",
- funcdef->name, ftype_pretty_name(ftype1));
+ sttype_function_name(entity1), ftype_pretty_name(ftype1));
}
-
- check_function(dfw, entity1);
} else if (stnode_type_id(entity1) == STTYPE_RANGE) {
/* Should this be rejected instead? */
check_drange_sanity(dfw, entity1);
@@ -627,7 +609,6 @@ check_function(dfwork_t *dfw, stnode_t *st_node)
{
df_func_def_t *funcdef;
GSList *params;
- guint iparam;
guint nparams;
LOG_NODE(st_node);
@@ -639,22 +620,17 @@ check_function(dfwork_t *dfw, stnode_t *st_node)
if (nparams < funcdef->min_nargs) {
FAIL(dfw, st_node, "Function %s needs at least %u arguments.",
funcdef->name, funcdef->min_nargs);
- } else if (nparams > funcdef->max_nargs) {
+ } else if (funcdef->max_nargs > 0 && nparams > funcdef->max_nargs) {
FAIL(dfw, st_node, "Function %s can only accept %u arguments.",
funcdef->name, funcdef->max_nargs);
}
- iparam = 0;
- while (params) {
- resolve_unparsed(dfw, params->data);
- funcdef->semcheck_param_function(dfw, funcdef->name, iparam, params->data);
- params = params->next;
- iparam++;
- }
+ funcdef->semcheck_param_function(dfw, funcdef->name, params,
+ stnode_location(st_node));
}
WS_RETNONNULL
-static fvalue_t *
+fvalue_t *
dfilter_fvalue_from_charconst(dfwork_t *dfw, ftenum_t ftype, stnode_t *st)
{
fvalue_t *fvalue;
@@ -681,7 +657,6 @@ check_relation_LHS_FIELD(dfwork_t *dfw, test_op_t st_op,
{
sttype_id_t type2;
header_field_info *hfinfo1, *hfinfo2;
- df_func_def_t *funcdef;
ftenum_t ftype1, ftype2;
fvalue_t *fvalue;
@@ -756,21 +731,19 @@ again:
}
}
else if (type2 == STTYPE_FUNCTION) {
- funcdef = sttype_function_funcdef(st_arg2);
- ftype2 = funcdef->retval_ftype;
+ check_function(dfw, st_arg2);
+ ftype2 = sttype_function_retval_ftype(st_arg2);
if (!compatible_ftypes(ftype1, ftype2)) {
FAIL(dfw, st_arg2, "%s (type=%s) and return value of %s() (type=%s) are not of compatible types.",
hfinfo1->abbrev, ftype_pretty_name(ftype1),
- funcdef->name, ftype_pretty_name(ftype2));
+ sttype_function_name(st_arg2), ftype_pretty_name(ftype2));
}
if (!can_func(ftype2)) {
FAIL(dfw, st_arg2, "return value of %s() (type=%s) cannot participate in specified comparison.",
- funcdef->name, ftype_pretty_name(ftype2));
+ sttype_function_name(st_arg2), ftype_pretty_name(ftype2));
}
-
- check_function(dfw, st_arg2);
}
else if (type2 == STTYPE_PCRE) {
ws_assert(st_op == TEST_OP_MATCHES);
@@ -851,21 +824,19 @@ again:
check_drange_sanity(dfw, st_arg2);
}
else if (type2 == STTYPE_FUNCTION) {
- df_func_def_t *funcdef = sttype_function_funcdef(st_arg2);
- ftype2 = funcdef->retval_ftype;
+ check_function(dfw, st_arg2);
+ ftype2 = sttype_function_retval_ftype(st_arg2);
if (!is_bytes_type(ftype2)) {
if (!ftype_can_slice(ftype2)) {
FAIL(dfw, st_arg2, "Return value of function \"%s\" is a %s and cannot be converted into a sequence of bytes.",
- funcdef->name,
+ sttype_function_name(st_arg2),
ftype_pretty_name(ftype2));
}
/* Convert function result to bytes */
convert_to_bytes(st_arg2);
}
-
- check_function(dfw, st_arg2);
}
else if (type2 == STTYPE_PCRE) {
ws_assert(st_op == TEST_OP_MATCHES);
@@ -900,19 +871,15 @@ check_relation_LHS_FUNCTION(dfwork_t *dfw, test_op_t st_op,
header_field_info *hfinfo2;
ftenum_t ftype1, ftype2;
fvalue_t *fvalue;
- df_func_def_t *funcdef;
- df_func_def_t *funcdef2;
- /* GSList *params; */
LOG_NODE(st_node);
check_function(dfw, st_arg1);
- funcdef = sttype_function_funcdef(st_arg1);
- ftype1 = funcdef->retval_ftype;
+ ftype1 = sttype_function_retval_ftype(st_arg1);
if (!can_func(ftype1)) {
FAIL(dfw, st_arg1, "Function %s (type=%s) cannot participate in %s comparison.",
- funcdef->name, ftype_pretty_name(ftype1),
+ sttype_function_name(st_arg1), ftype_pretty_name(ftype1),
stnode_todisplay(st_node));
}
@@ -925,7 +892,7 @@ again:
if (!compatible_ftypes(ftype1, ftype2)) {
FAIL(dfw, st_arg2, "Function %s and %s are not of compatible types.",
- funcdef->name, hfinfo2->abbrev);
+ sttype_function_name(st_arg2), hfinfo2->abbrev);
}
/* Do this check even though you'd think that if
* they're compatible, then can_func() would pass. */
@@ -959,7 +926,7 @@ again:
if (!is_bytes_type(ftype1)) {
if (!ftype_can_slice(ftype1)) {
FAIL(dfw, st_arg1, "Function \"%s\" is a %s and cannot be converted into a sequence of bytes.",
- funcdef->name,
+ sttype_function_name(st_arg1),
ftype_pretty_name(ftype1));
}
@@ -968,22 +935,20 @@ again:
}
}
else if (type2 == STTYPE_FUNCTION) {
- funcdef2 = sttype_function_funcdef(st_arg2);
- ftype2 = funcdef2->retval_ftype;
+ check_function(dfw, st_arg2);
+ ftype2 = sttype_function_retval_ftype(st_arg2);
if (!compatible_ftypes(ftype1, ftype2)) {
FAIL(dfw, st_arg2, "Return values of function %s (type=%s) and function %s (type=%s) are not of compatible types.",
- funcdef->name, ftype_pretty_name(ftype1), funcdef2->name, ftype_pretty_name(ftype2));
+ sttype_function_name(st_arg1), ftype_pretty_name(ftype1), sttype_function_name(st_arg1), ftype_pretty_name(ftype2));
}
/* Do this check even though you'd think that if
* they're compatible, then can_func() would pass. */
if (!can_func(ftype2)) {
FAIL(dfw, st_arg2, "Return value of %s (type=%s) cannot participate in specified comparison.",
- funcdef2->name, ftype_pretty_name(ftype2));
+ sttype_function_name(st_arg2), ftype_pretty_name(ftype2));
}
-
- check_function(dfw, st_arg2);
}
else if (type2 == STTYPE_PCRE) {
ws_assert(st_op == TEST_OP_MATCHES);
@@ -1046,7 +1011,7 @@ check_relation(dfwork_t *dfw, test_op_t st_op,
{
LOG_NODE(st_node);
- resolve_unparsed(dfw, st_arg1);
+ dfw_resolve_unparsed(dfw, st_arg1);
switch (stnode_type_id(st_arg1)) {
case STTYPE_FIELD:
@@ -1078,7 +1043,7 @@ check_relation_contains(dfwork_t *dfw, stnode_t *st_node,
{
LOG_NODE(st_node);
- resolve_unparsed(dfw, st_arg1);
+ dfw_resolve_unparsed(dfw, st_arg1);
switch (stnode_type_id(st_arg1)) {
case STTYPE_FIELD:
@@ -1111,7 +1076,7 @@ check_relation_matches(dfwork_t *dfw, stnode_t *st_node,
LOG_NODE(st_node);
- resolve_unparsed(dfw, st_arg1);
+ dfw_resolve_unparsed(dfw, st_arg1);
if (stnode_type_id(st_arg2) != STTYPE_STRING) {
FAIL(dfw, st_arg2, "Matches requires a double quoted string on the right side.");
@@ -1158,7 +1123,7 @@ check_relation_in(dfwork_t *dfw, stnode_t *st_node _U_,
LOG_NODE(st_node);
- resolve_unparsed(dfw, st_arg1);
+ dfw_resolve_unparsed(dfw, st_arg1);
if (stnode_type_id(st_arg1) != STTYPE_FIELD) {
FAIL(dfw, st_arg1, "Only a field may be tested for membership in a set.");
@@ -1261,7 +1226,7 @@ check_arithmetic_entity(dfwork_t *dfw, stnode_t *st_arg, ftenum_t lhs_ftype)
* is none we must have been passed an entity with a definite type
* (field, function, etc). */
- resolve_unparsed(dfw, st_arg);
+ dfw_resolve_unparsed(dfw, st_arg);
type = stnode_type_id(st_arg);
if (type == STTYPE_LITERAL) {
@@ -1277,9 +1242,7 @@ check_arithmetic_entity(dfwork_t *dfw, stnode_t *st_arg, ftenum_t lhs_ftype)
}
else if (type == STTYPE_FUNCTION) {
check_function(dfw, st_arg);
-
- df_func_def_t *funcdef = sttype_function_funcdef(st_arg);
- ftype = funcdef->retval_ftype;
+ ftype = sttype_function_retval_ftype(st_arg);
}
else if (type == STTYPE_RANGE) {
check_drange_sanity(dfw, st_arg);
@@ -1312,7 +1275,7 @@ check_arithmetic_expr(dfwork_t *dfw, stnode_t *st_node, ftenum_t lhs_ftype)
}
sttype_test_get(st_node, &st_op, &st_arg1, &st_arg2);
- resolve_unparsed(dfw, st_arg1);
+ dfw_resolve_unparsed(dfw, st_arg1);
/* On the LHS we require a field-like value as the first term. */
if (lhs_ftype == FT_NONE && node_is_constant(st_arg1)) {
diff --git a/epan/dfilter/sttype-function.c b/epan/dfilter/sttype-function.c
index e4036a30cd..bc1c6021f2 100644
--- a/epan/dfilter/sttype-function.c
+++ b/epan/dfilter/sttype-function.c
@@ -119,6 +119,33 @@ sttype_function_funcdef(stnode_t *node)
return stfuncrec->funcdef;
}
+ftenum_t
+sttype_function_retval_ftype(stnode_t *node)
+{
+ function_t *stfuncrec;
+
+ stfuncrec = stnode_data(node);
+ ws_assert_magic(stfuncrec, FUNCTION_MAGIC);
+ if (stfuncrec->funcdef->retval_ftype != 0)
+ return stfuncrec->funcdef->retval_ftype;
+
+ if (stfuncrec->params) {
+ stnode_t *first_arg = stfuncrec->params->data;
+ return stnode_ftenum(first_arg);
+ }
+ return FT_NONE;
+}
+
+const char *
+sttype_function_name(stnode_t *node)
+{
+ function_t *stfuncrec;
+
+ stfuncrec = stnode_data(node);
+ ws_assert_magic(stfuncrec, FUNCTION_MAGIC);
+ return stfuncrec->funcdef->name;
+}
+
/* Get the parameters for a function stnode_t. */
GSList*
sttype_function_params(stnode_t *node)
diff --git a/epan/dfilter/sttype-function.h b/epan/dfilter/sttype-function.h
index 68b9c57d4d..48294ce2d7 100644
--- a/epan/dfilter/sttype-function.h
+++ b/epan/dfilter/sttype-function.h
@@ -21,6 +21,10 @@ sttype_function_set_params(stnode_t *node, GSList *params);
/* Get the function-definition record for a function stnode_t. */
df_func_def_t* sttype_function_funcdef(stnode_t *node);
+ftenum_t sttype_function_retval_ftype(stnode_t *node);
+
+const char *sttype_function_name(stnode_t *node);
+
/* Get the parameters for a function stnode_t. */
GSList* sttype_function_params(stnode_t *node);
diff --git a/epan/dfilter/sttype-pointer.c b/epan/dfilter/sttype-pointer.c
index b61a82b048..e91634ad3b 100644
--- a/epan/dfilter/sttype-pointer.c
+++ b/epan/dfilter/sttype-pointer.c
@@ -8,6 +8,7 @@
*/
#include "config.h"
+#include "sttype-pointer.h"
#include "ftypes/ftypes.h"
#include "syntax-tree.h"
@@ -109,6 +110,20 @@ range_node_tostr(const void *data, gboolean pretty _U_)
return drange_node_tostr(data);
}
+ftenum_t
+sttype_pointer_ftenum(stnode_t *node)
+{
+ switch (node->type->id) {
+ case STTYPE_FIELD:
+ return ((header_field_info *)node->data)->type;
+ case STTYPE_FVALUE:
+ return fvalue_type_ftenum(node->data);
+ default:
+ break;
+ }
+ return FT_NONE;
+}
+
void
sttype_register_pointer(void)
{
diff --git a/epan/dfilter/sttype-pointer.h b/epan/dfilter/sttype-pointer.h
new file mode 100644
index 0000000000..54aa28edec
--- /dev/null
+++ b/epan/dfilter/sttype-pointer.h
@@ -0,0 +1,20 @@
+/** @file
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 2001 Gerald Combs
+ *
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef STTYPE_POINTER_H
+#define STTYPE_POINTER_H
+
+#include "dfilter-int.h"
+#include <epan/ftypes/ftypes.h>
+
+ftenum_t
+sttype_pointer_ftenum(stnode_t *node);
+
+#endif
diff --git a/epan/dfilter/syntax-tree.c b/epan/dfilter/syntax-tree.c
index 1ea5527349..bd72a74222 100644
--- a/epan/dfilter/syntax-tree.c
+++ b/epan/dfilter/syntax-tree.c
@@ -15,6 +15,8 @@
#include <wsutil/str_util.h>
#include <wsutil/glib-compat.h>
#include "sttype-test.h"
+#include "sttype-pointer.h"
+#include "sttype-function.h"
#include "dfilter-int.h"
/* Keep track of sttype_t's via their sttype_id_t number */
@@ -211,6 +213,22 @@ stnode_type_id(stnode_t *node)
return STTYPE_UNINITIALIZED;
}
+ftenum_t
+stnode_ftenum(stnode_t *node)
+{
+ ws_assert_magic(node, STNODE_MAGIC);
+ switch (node->type->id) {
+ case STTYPE_FVALUE:
+ case STTYPE_FIELD:
+ return sttype_pointer_ftenum(node);
+ case STTYPE_FUNCTION:
+ return sttype_function_retval_ftype(node);
+ default:
+ break;
+ }
+ return FT_NONE;
+}
+
gpointer
stnode_data(stnode_t *node)
{
diff --git a/epan/dfilter/syntax-tree.h b/epan/dfilter/syntax-tree.h
index c1ee49769f..796c70d349 100644
--- a/epan/dfilter/syntax-tree.h
+++ b/epan/dfilter/syntax-tree.h
@@ -15,6 +15,7 @@
#include <wsutil/ws_assert.h>
#include <wsutil/wslog.h>
+#include <epan/ftypes/ftypes.h>
/** @file
*/
@@ -137,6 +138,9 @@ stnode_type_name(stnode_t *node);
sttype_id_t
stnode_type_id(stnode_t *node);
+ftenum_t
+stnode_ftenum(stnode_t *node);
+
gpointer
stnode_data(stnode_t *node);