aboutsummaryrefslogtreecommitdiffstats
path: root/epan
diff options
context:
space:
mode:
Diffstat (limited to 'epan')
-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);