diff --git a/doc/README.developer b/doc/README.developer index abbd330298..9b760648f1 100644 --- a/doc/README.developer +++ b/doc/README.developer @@ -3209,6 +3209,70 @@ field_info structures that are interesting to the display filter. This makes lookup of those field_info structures during the filtering process faster. +5.4 Display Filter Functions + +You define a desplay filte function by adding an entry to +the df_functions table in epan/dfilter/dfunctions.c. The record struct +is defined in defunctions.h, and shown here: + +typedef struct { + char *name; + DFFuncType function; + ftenum_t retval_ftype; + guint min_nargs; + guint max_nargs; + DFSemCheckType semcheck_param_function; +} df_func_def_t; + +name - the name of the function; this is how the user will call your + function in the display filter language + +function - this is the run-time processing of your function. + +retval_ftype - what type of FT_* type does your function return? + +min_nargs - minimum number of arguments your function accepts +max_nargs - maximum number of arguments your function accepts + +semcheck_param_function - called during the semantic check of the + display filter string. + +DFFuncType function +------------------- +typedef gboolean (*DFFuncType)(GList *arg1list, GList *arg2list, GList **retval); + +The return value of your function is a gboolean; TRUE if processing went fine, +or FALSE if there was some sort of exception. + +For now, display filter functions can accept a maximum of 2 arguments. +The "arg1list" parameter is the GList for the first argument. The +'arg2list" parameter is the GList for the second argument. All arguments +to display filter functions are lists. This is because in the display +filter language a protocol field may have multiple instances. For example, +a field like "ip.addr" will exist more than once in a single frame. So +when the user invokes this display filter: + + somefunc(ip.addr) == TRUE + +even though "ip.addr" is a single argument, the "somefunc" function will +receive a GList of *all* the values of "ip.addr" in the frame. + +Similarly, the return value of the function needs to be a GList, since all +values in the display filter language are lists. The GList** retval argument +is passed to your function so you can set the pointer to your return value. + +DFSemCheckType +-------------- +typedef void (*DFSemCheckType)(int param_num, stnode_t *st_node); + +For each parameter in the syntax tree, this function will be called. +"param_num" will indicate the number of the parameter, starting with 0. +The "stnode_t" is the syntax-tree node representing that parameter. +If everything is okay with the value of that stnode_t, your function +does nothing --- it merely returns. If something is wrong, however, +it should THROW a TypeError exception. + + 6.0 Adding new capabilities. diff --git a/doc/ethereal-filter.pod.template b/doc/ethereal-filter.pod.template index f4b2956474..b560a8b3e4 100644 --- a/doc/ethereal-filter.pod.template +++ b/doc/ethereal-filter.pod.template @@ -93,6 +93,19 @@ have been compiled with the PCRE library. This can be checked by running: or selecting the "About Ethereal" item from the "Help" menu in B. +=head2 Functions + +The filter language has the following functions: + + upper(string-field) - converts a string field to uppercase + lower(string-field) - converts a string field to lowercase + +upper() and lower() are useful for performing case-insensitive string +comparisons. For example: + + upper(ncp.nds_stream_name) contains "MACRO" + lower(mount.dump.hostname) == "angel" + =head2 Protocol field types Each protocol field is typed. The types are: diff --git a/epan/dfilter/Makefile.am b/epan/dfilter/Makefile.am index 139c5a661d..7e1614320d 100644 --- a/epan/dfilter/Makefile.am +++ b/epan/dfilter/Makefile.am @@ -46,6 +46,8 @@ libdfilter_la_SOURCES = \ dfilter.c \ dfilter.h \ dfilter-int.h \ + dfunctions.c \ + dfunctions.h \ dfvm.c \ dfvm.h \ drange.c \ @@ -59,6 +61,8 @@ libdfilter_la_SOURCES = \ scanner.c \ semcheck.c \ semcheck.h \ + sttype-function.c \ + sttype-function.h \ sttype-integer.c \ sttype-pointer.c \ sttype-range.c \ diff --git a/epan/dfilter/Makefile.nmake b/epan/dfilter/Makefile.nmake index 8593e39dbe..38ac1d7c53 100644 --- a/epan/dfilter/Makefile.nmake +++ b/epan/dfilter/Makefile.nmake @@ -20,6 +20,7 @@ CVARSDLL=-DWIN32 -DNULL=0 -D_MT -D_DLL OBJECTS = \ dfilter.obj \ + dfunctions.obj \ dfvm.obj \ drange.obj \ gencode.obj \ @@ -27,6 +28,7 @@ OBJECTS = \ grammar.obj \ scanner.obj \ semcheck.obj \ + sttype-function.obj \ sttype-integer.obj \ sttype-pointer.obj \ sttype-range.obj \ diff --git a/epan/dfilter/dfunctions.c b/epan/dfilter/dfunctions.c new file mode 100644 index 0000000000..1b949b2ea5 --- /dev/null +++ b/epan/dfilter/dfunctions.c @@ -0,0 +1,158 @@ +/* + * $Id$ + * + * Ethereal - Network traffic analyzer + * + * Copyright 2006 Gilbert Ramirez + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "dfunctions.h" +#include "dfilter-int.h" + +#include +#include + +#include +#include + +/* lowercase an ASCII character. + * (thanks to Guy Harris for the function) */ +static gchar +string_ascii_to_lower(gchar c) +{ + return ((c & 0x80) ? c : tolower(c)); +} + +/* uppercase an ASCII character. */ +static gchar +string_ascii_to_upper(gchar c) +{ + return ((c & 0x80) ? c : toupper(c)); +} + + +/* Convert an FT_STRING using a callback function */ +static gboolean +string_walk(GList* arg1list, GList **retval, gchar(*conv_func)(gchar)) +{ + GList *arg1; + fvalue_t *arg_fvalue; + fvalue_t *new_ft_string; + char *s, *c; + + arg1 = arg1list; + while (arg1) { + arg_fvalue = arg1->data; + switch (fvalue_ftype(arg_fvalue)->ftype) { + case FT_STRING: + s = g_strdup(fvalue_get(arg1->data)); + for (c = s; *c; c++) { + /**c = string_ascii_to_lower(*c);*/ + *c = conv_func(*c); + } + + new_ft_string = fvalue_new(FT_STRING); + fvalue_set(new_ft_string, s, TRUE); + *retval = g_list_append(*retval, new_ft_string); + break; + + /* XXX - it would be nice to handle FT_TVBUFF, too */ + + default: + break; + } + arg1 = arg1->next; + } + + return TRUE; +} + +/* dfilter function: lower() */ +static gboolean +df_func_lower(GList* arg1list, GList *arg2junk _U_, GList **retval) +{ + return string_walk(arg1list, retval, string_ascii_to_lower); +} + +/* dfilter function: upper() */ +static gboolean +df_func_upper(GList* arg1list, GList *arg2junk _U_, GList **retval) +{ + return string_walk(arg1list, retval, string_ascii_to_upper); +} + +/* For upper() and lower(), checks that the parameter passed to + * it is an FT_STRING */ +static void +ul_semcheck_params(int param_num, stnode_t *st_node) +{ + sttype_id_t type; + ftenum_t ftype; + header_field_info *hfinfo; + + type = stnode_type_id(st_node); + + if (param_num == 0) { + switch(type) { + case STTYPE_FIELD: + hfinfo = stnode_data(st_node); + ftype = hfinfo->type; + if (ftype != FT_STRING && ftype != FT_STRINGZ + && ftype != FT_UINT_STRING) { + dfilter_fail("Only strings can be used in upper() or lower()"); + THROW(TypeError); + } + break; + default: + dfilter_fail("Only string-type fields can be used in upper() or lower()"); + THROW(TypeError); + } + } + else { + g_assert_not_reached(); + } +} + +/* The table of all display-filter functions */ +static df_func_def_t +df_functions[] = { + { "lower", df_func_lower, FT_STRING, 1, 1, ul_semcheck_params }, + { "upper", df_func_upper, FT_STRING, 1, 1, ul_semcheck_params }, + { NULL, NULL, 0, 0, 0, NULL } +}; + +/* Lookup a display filter function record by name */ +df_func_def_t* +df_func_lookup(char *name) +{ + df_func_def_t *func_def; + + func_def = df_functions; + while (func_def->function != NULL) { + if (strcmp(func_def->name, name) == 0) { + return func_def; + } + func_def++; + } + return NULL; +} diff --git a/epan/dfilter/dfunctions.h b/epan/dfilter/dfunctions.h new file mode 100644 index 0000000000..3e7ca3f0de --- /dev/null +++ b/epan/dfilter/dfunctions.h @@ -0,0 +1,56 @@ +/* + * $Id$ + * + * Ethereal - Network traffic analyzer + * + * Copyright 2006 Gilbert Ramirez + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef DFUNCTIONS_H +#define DFUNCTIONS_H + +#include +#include +#include "syntax-tree.h" + +/* The run-time logic of the dfilter function */ +typedef gboolean (*DFFuncType)(GList *arg1list, GList *arg2list, GList **retval); + +/* The semantic check for the dfilter function */ +typedef void (*DFSemCheckType)(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 + +/* This is a "function definition" record, holding everything + * we need to know about a function */ +typedef struct { + char *name; + DFFuncType function; + ftenum_t retval_ftype; + guint min_nargs; + guint max_nargs; + DFSemCheckType semcheck_param_function; +} df_func_def_t; + +/* Return the function definition record for a function of named "name" */ +df_func_def_t* df_func_lookup(char *name); + +#endif diff --git a/epan/dfilter/dfvm.c b/epan/dfilter/dfvm.c index eb1521e8e5..ad3a467243 100644 --- a/epan/dfilter/dfvm.c +++ b/epan/dfilter/dfvm.c @@ -118,6 +118,18 @@ dfvm_dump(FILE *f, GPtrArray *insns) arg2->value.numeric); break; + case CALL_FUNCTION: + fprintf(f, "%05d CALL_FUNCTION\t%s (", + id, arg1->value.funcdef->name); + if (arg3) { + fprintf(f, "reg#%u", arg3->value.numeric); + } + if (arg4) { + fprintf(f, ", reg#%u", arg4->value.numeric); + } + fprintf(f, ") --> reg#%u\n", arg2->value.numeric); + break; + case PUT_FVALUE: value_str = fvalue_to_string_repr(arg1->value.fvalue, FTREPR_DFILTER, NULL); @@ -373,8 +385,11 @@ dfvm_apply(dfilter_t *df, proto_tree *tree) dfvm_insn_t *insn; dfvm_value_t *arg1; dfvm_value_t *arg2; - dfvm_value_t *arg3; + dfvm_value_t *arg3 = NULL; + dfvm_value_t *arg4 = NULL; header_field_info *hfinfo; + GList *param1; + GList *param2; g_assert(tree); @@ -414,6 +429,21 @@ dfvm_apply(dfilter_t *df, proto_tree *tree) arg1->value.hfinfo, arg2->value.numeric); break; + case CALL_FUNCTION: + arg3 = insn->arg3; + arg4 = insn->arg4; + param1 = NULL; + param2 = NULL; + if (arg3) { + param1 = df->registers[arg3->value.numeric]; + } + if (arg4) { + param2 = df->registers[arg4->value.numeric]; + } + accum = arg1->value.funcdef->function(param1, param2, + &df->registers[arg2->value.numeric]); + break; + case PUT_FVALUE: accum = put_fvalue(df, arg1->value.fvalue, arg2->value.numeric); diff --git a/epan/dfilter/dfvm.h b/epan/dfilter/dfvm.h index 862bb0a313..da773b7759 100644 --- a/epan/dfilter/dfvm.h +++ b/epan/dfilter/dfvm.h @@ -28,6 +28,7 @@ #include "dfilter-int.h" #include "syntax-tree.h" #include "drange.h" +#include "dfunctions.h" typedef enum { EMPTY, @@ -36,7 +37,8 @@ typedef enum { INSN_NUMBER, REGISTER, INTEGER, - DRANGE + DRANGE, + FUNCTION_DEF } dfvm_value_type_t; typedef struct { @@ -47,6 +49,7 @@ typedef struct { guint32 numeric; drange *drange; header_field_info *hfinfo; + df_func_def_t *funcdef; } value; } dfvm_value_t; @@ -70,13 +73,13 @@ typedef enum { ANY_BITWISE_AND, ANY_CONTAINS, ANY_MATCHES, - MK_RANGE + MK_RANGE, + CALL_FUNCTION } dfvm_opcode_t; typedef struct { int id; - int LHS; dfvm_opcode_t op; dfvm_value_t *arg1; dfvm_value_t *arg2; diff --git a/epan/dfilter/gencode.c b/epan/dfilter/gencode.c index c48329ba2f..7d3ef8c162 100644 --- a/epan/dfilter/gencode.c +++ b/epan/dfilter/gencode.c @@ -30,11 +30,15 @@ #include "syntax-tree.h" #include "sttype-range.h" #include "sttype-test.h" +#include "sttype-function.h" #include "ftypes/ftypes.h" static void gencode(dfwork_t *dfw, stnode_t *st_node); +static int +gen_entity(dfwork_t *dfw, stnode_t *st_arg, dfvm_value_t **p_jmp); + static void dfw_append_insn(dfwork_t *dfw, dfvm_insn_t *insn) { @@ -155,58 +159,98 @@ dfw_append_mk_range(dfwork_t *dfw, stnode_t *node) return reg; } +/* returns register number that the functions's result will be in. */ +static int +dfw_append_function(dfwork_t *dfw, stnode_t *node, dfvm_value_t **p_jmp) +{ + GSList *params; + int i, num_params, reg; + dfvm_value_t **jmps; + dfvm_insn_t *insn; + dfvm_value_t *val1, *val2, *val; + + params = sttype_function_params(node); + num_params = g_slist_length(params); + + /* Array to hold the instructions that need to jump to + * an instruction if they fail. */ + jmps = g_malloc(num_params * sizeof(dfvm_value_t*)); + + /* Create the new DFVM instruction */ + insn = dfvm_insn_new(CALL_FUNCTION); + + val1 = dfvm_value_new(FUNCTION_DEF); + val1->value.funcdef = sttype_function_funcdef(node); + insn->arg1 = val1; + val2 = dfvm_value_new(REGISTER); + val2->value.numeric = dfw->next_register++; + insn->arg2 = val2; + insn->arg3 = NULL; + insn->arg4 = NULL; + + i = 0; + while (params) { + jmps[i] = NULL; + reg = gen_entity(dfw, params->data, &jmps[i]); + + val = dfvm_value_new(REGISTER); + val->value.numeric = reg; + + switch(i) { + case 0: + insn->arg3 = val; + break; + case 1: + insn->arg4 = val; + break; + default: + g_assert_not_reached(); + } + + params = params->next; + i++; + } + + 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 + * we know what the next DFVM insruction is, via + * dfw->next_insn_id */ + for (i = 0; i < num_params; i++) { + if (jmps[i]) { + jmps[i]->value.numeric = dfw->next_insn_id; + } + } + + /* We need another instruction to jump to another exit + * place, if the call() of our function failed for some reaosn */ + insn = dfvm_insn_new(IF_FALSE_GOTO); + g_assert(p_jmp); + *p_jmp = dfvm_value_new(INSN_NUMBER); + insn->arg1 = *p_jmp; + dfw_append_insn(dfw, insn); + + g_free(jmps); + + return val2->value.numeric; +} + static void gen_relation(dfwork_t *dfw, dfvm_opcode_t op, stnode_t *st_arg1, stnode_t *st_arg2) { - sttype_id_t type1, type2; dfvm_insn_t *insn; dfvm_value_t *val1, *val2; dfvm_value_t *jmp1 = NULL, *jmp2 = NULL; int reg1 = -1, reg2 = -1; - header_field_info *hfinfo; - type1 = stnode_type_id(st_arg1); - type2 = stnode_type_id(st_arg2); - - if (type1 == STTYPE_FIELD) { - hfinfo = stnode_data(st_arg1); - reg1 = dfw_append_read_tree(dfw, hfinfo); - - insn = dfvm_insn_new(IF_FALSE_GOTO); - jmp1 = dfvm_value_new(INSN_NUMBER); - insn->arg1 = jmp1; - dfw_append_insn(dfw, insn); - } - else if (type1 == STTYPE_FVALUE) { - reg1 = dfw_append_put_fvalue(dfw, stnode_data(st_arg1)); - } - else if (type1 == STTYPE_RANGE) { - reg1 = dfw_append_mk_range(dfw, st_arg1); - } - else { - g_assert_not_reached(); - } - - if (type2 == STTYPE_FIELD) { - hfinfo = stnode_data(st_arg2); - reg2 = dfw_append_read_tree(dfw, hfinfo); - - insn = dfvm_insn_new(IF_FALSE_GOTO); - jmp2 = dfvm_value_new(INSN_NUMBER); - insn->arg1 = jmp2; - dfw_append_insn(dfw, insn); - } - else if (type2 == STTYPE_FVALUE) { - reg2 = dfw_append_put_fvalue(dfw, stnode_data(st_arg2)); - } - else if (type2 == STTYPE_RANGE) { - reg2 = dfw_append_mk_range(dfw, st_arg2); - } - else { - g_assert_not_reached(); - } + /* Create code for the LHS and RHS of the relation */ + reg1 = gen_entity(dfw, st_arg1, &jmp1); + reg2 = gen_entity(dfw, st_arg2, &jmp2); + /* Then combine them in a DFVM insruction */ insn = dfvm_insn_new(op); val1 = dfvm_value_new(REGISTER); val1->value.numeric = reg1; @@ -216,6 +260,8 @@ gen_relation(dfwork_t *dfw, dfvm_opcode_t op, stnode_t *st_arg1, stnode_t *st_ar insn->arg2 = val2; dfw_append_insn(dfw, insn); + /* If either of the relation argumnents need an "exit" instruction + * to jump to (on failure), mark them */ if (jmp1) { jmp1->value.numeric = dfw->next_insn_id; } @@ -225,6 +271,45 @@ gen_relation(dfwork_t *dfw, dfvm_opcode_t op, stnode_t *st_arg1, stnode_t *st_ar } } +/* Parse an entity, returning the reg that it gets put into. + * p_jmp will be set if it has to be set by the calling code; it should + * be set tothe place to jump to, to return to the calling code, + * if the load of a field from the proto_tree fails. */ +static int +gen_entity(dfwork_t *dfw, stnode_t *st_arg, dfvm_value_t **p_jmp) +{ + sttype_id_t e_type; + dfvm_insn_t *insn; + header_field_info *hfinfo; + e_type = stnode_type_id(st_arg); + int reg = -1; + + if (e_type == STTYPE_FIELD) { + hfinfo = stnode_data(st_arg); + reg = dfw_append_read_tree(dfw, hfinfo); + + insn = dfvm_insn_new(IF_FALSE_GOTO); + g_assert(p_jmp); + *p_jmp = dfvm_value_new(INSN_NUMBER); + insn->arg1 = *p_jmp; + dfw_append_insn(dfw, insn); + } + else if (e_type == STTYPE_FVALUE) { + reg = dfw_append_put_fvalue(dfw, stnode_data(st_arg)); + } + else if (e_type == STTYPE_RANGE) { + reg = dfw_append_mk_range(dfw, st_arg); + } + else if (e_type == STTYPE_FUNCTION) { + reg = dfw_append_function(dfw, st_arg, p_jmp); + } + else { + printf("sttype_id is %u\n", e_type); + g_assert_not_reached(); + } + return reg; +} + static void gen_test(dfwork_t *dfw, stnode_t *st_node) diff --git a/epan/dfilter/grammar.lemon b/epan/dfilter/grammar.lemon index 08169369c2..225dd73110 100644 --- a/epan/dfilter/grammar.lemon +++ b/epan/dfilter/grammar.lemon @@ -9,6 +9,7 @@ #include "syntax-tree.h" #include "sttype-range.h" #include "sttype-test.h" +#include "sttype-function.h" #include "drange.h" #include "grammar.h" @@ -51,6 +52,9 @@ %type drnode_list {GSList*} %destructor drnode_list {drange_node_free_list($$);} +%type funcparams {GSList*} +%destructor funcparams {st_funcparams_free($$);} + /* This is called as soon as a syntax error happens. After that, any "error" symbols are shifted, if possible. */ %syntax_error { @@ -85,6 +89,9 @@ any "error" symbols are shifted, if possible. */ hfinfo = stnode_data(TOKEN); dfilter_fail("Syntax error near \"%s\".", hfinfo->abbrev); break; + case STTYPE_FUNCTION: + dfilter_fail("The function s was unexpected in this context."); + break; /* These aren't handed to use as terminal tokens from the scanner, so was can assert that we'll never @@ -120,11 +127,6 @@ sentence ::= . { dfw->st_root = NULL; } expr(X) ::= relation_test(R). { X = R; } expr(X) ::= logical_test(L). { X = L; } -expr(X) ::= LPAREN expr(Y) RPAREN. -{ - X = Y; -} - /* Logical tests */ logical_test(T) ::= expr(E) TEST_AND expr(F). @@ -254,4 +256,35 @@ rel_op2(O) ::= TEST_CONTAINS. { O = TEST_OP_CONTAINS; } rel_op2(O) ::= TEST_MATCHES. { O = TEST_OP_MATCHES; } +/* Functions */ + +/* A function can have one or more parameters */ +entity(E) ::= FUNCTION(F) LPAREN funcparams(P) RPAREN. +{ + E = F; + sttype_function_set_params(E, P); +} + +/* A function can have zero parameters. */ +entity(E) ::= FUNCTION(F) LPAREN RPAREN. +{ + E = F; +} + +funcparams(P) ::= entity(E). +{ + P = g_slist_append(NULL, E); +} + +funcparams(P) ::= funcparams(L) COMMA entity(E). +{ + P = g_slist_append(L, E); +} + + +/* Any expression inside parens is simply that expression */ +expr(X) ::= LPAREN expr(Y) RPAREN. +{ + X = Y; +} diff --git a/epan/dfilter/scanner.l b/epan/dfilter/scanner.l index a621292699..545c923c85 100644 --- a/epan/dfilter/scanner.l +++ b/epan/dfilter/scanner.l @@ -32,6 +32,7 @@ #include "dfilter-int.h" #include "syntax-tree.h" #include "grammar.h" +#include "dfunctions.h" /* * GLib 1.2[.x] doesn't define G_MAXINT32 or G_MININT32; if they're not @@ -75,6 +76,7 @@ GString* quoted_string = NULL; "(" return simple(TOKEN_LPAREN); ")" return simple(TOKEN_RPAREN); +"," return simple(TOKEN_COMMA); "==" return simple(TOKEN_TEST_EQ); "eq" return simple(TOKEN_TEST_EQ); @@ -232,9 +234,10 @@ GString* quoted_string = NULL; return set_lval(TOKEN_UNPARSED, yytext); } -[-\+[:alnum:]_.,:]+ { +[-\+[:alnum:]_.:]+ { /* Is it a field name? */ header_field_info *hfinfo; + df_func_def_t *df_func_def; hfinfo = proto_registrar_get_byname(yytext); if (hfinfo) { @@ -242,8 +245,16 @@ GString* quoted_string = NULL; return set_lval(TOKEN_FIELD, hfinfo); } else { - /* No, so treat it as an unparsed string */ - return set_lval(TOKEN_UNPARSED, yytext); + /* Is it a function name? */ + df_func_def = df_func_lookup(yytext); + if (df_func_def) { + /* yes, it's a dfilter function */ + return set_lval(TOKEN_FUNCTION, df_func_def); + } + else { + /* No, so treat it as an unparsed string */ + return set_lval(TOKEN_UNPARSED, yytext); + } } } @@ -300,10 +311,12 @@ set_lval(int token, gpointer data) case TOKEN_UNPARSED: type_id = STTYPE_UNPARSED; break; + case TOKEN_FUNCTION: + type_id = STTYPE_FUNCTION; + break; default: g_assert_not_reached(); } - stnode_init(df_lval, type_id, data); return token; } diff --git a/epan/dfilter/semcheck.c b/epan/dfilter/semcheck.c index b575be7a96..9e91bff0e5 100644 --- a/epan/dfilter/semcheck.c +++ b/epan/dfilter/semcheck.c @@ -31,6 +31,7 @@ #include "syntax-tree.h" #include "sttype-range.h" #include "sttype-test.h" +#include "sttype-function.h" #include #include @@ -307,6 +308,13 @@ check_exists(stnode_t *st_arg1) THROW(TypeError); break; + case STTYPE_FUNCTION: + /* XXX - Maybe we should change functions so they can return fields, + * in which case the 'exist' should be fine. */ + dfilter_fail("You cannot test whether a function is present."); + THROW(TypeError); + break; + case STTYPE_UNINITIALIZED: case STTYPE_TEST: case STTYPE_INTEGER: @@ -751,6 +759,161 @@ check_relation_LHS_RANGE(const char *relation_string, FtypeCanFunc can_func _U_, } } +static stnode_t* +check_param_entity(stnode_t *st_node) +{ + sttype_id_t e_type; + stnode_t *new_st; + fvalue_t *fvalue; + char *s; + + e_type = stnode_type_id(st_node); + /* If there's an unparsed string, change it to an FT_STRING */ + if (e_type == STTYPE_UNPARSED) { + s = stnode_data(st_node); + fvalue = fvalue_from_unparsed(FT_STRING, s, FALSE, dfilter_fail); + if (!fvalue) { + THROW(TypeError); + } + + new_st = stnode_new(STTYPE_FVALUE, fvalue); + stnode_free(st_node); + return new_st; + } + + return st_node; +} + + +/* If the LHS of a relation test is a FUNCTION, run some checks + * and possibly some modifications of syntax tree nodes. */ +static void +check_relation_LHS_FUNCTION(const char *relation_string, FtypeCanFunc can_func, + gboolean allow_partial_value, + stnode_t *st_node, stnode_t *st_arg1, stnode_t *st_arg2) +{ + stnode_t *new_st; + sttype_id_t type2; + header_field_info *hfinfo2; + ftenum_t ftype1, ftype2; + fvalue_t *fvalue; + char *s; + int param_i; + drange_node *rn; + df_func_def_t *funcdef; + guint num_params; + GSList *params; + + type2 = stnode_type_id(st_arg2); + + funcdef = sttype_function_funcdef(st_arg1); + ftype1 = funcdef->retval_ftype; + + params = sttype_function_params(st_arg1); + num_params = g_slist_length(params); + if (num_params < funcdef->min_nargs) { + dfilter_fail("Function %s needs at least %u arguments.", + funcdef->name, funcdef->min_nargs); + THROW(TypeError); + } + else if (num_params > funcdef->max_nargs) { + dfilter_fail("Function %s can only accept %u arguments.", + funcdef->name, funcdef->max_nargs); + THROW(TypeError); + } + + param_i = 0; + while (params) { + params->data = check_param_entity(params->data); + funcdef->semcheck_param_function(param_i, params->data); + params = params->next; + } + + DebugLog((" 5 check_relation_LHS_FUNCTION(%s)\n", relation_string)); + + if (!can_func(ftype1)) { + dfilter_fail("Function %s (type=%s) cannot participate in '%s' comparison.", + funcdef->name, ftype_pretty_name(ftype1), + relation_string); + THROW(TypeError); + } + + if (type2 == STTYPE_FIELD) { + hfinfo2 = stnode_data(st_arg2); + ftype2 = hfinfo2->type; + + if (!compatible_ftypes(ftype1, ftype2)) { + dfilter_fail("Function %s and %s are not of compatible types.", + funcdef->name, hfinfo2->abbrev); + THROW(TypeError); + } + /* Do this check even though you'd think that if + * they're compatible, then can_func() would pass. */ + if (!can_func(ftype2)) { + dfilter_fail("%s (type=%s) cannot participate in specified comparison.", + hfinfo2->abbrev, ftype_pretty_name(ftype2)); + THROW(TypeError); + } + } + else if (type2 == STTYPE_STRING) { + s = stnode_data(st_arg2); + if (strcmp(relation_string, "matches") == 0) { + /* Convert to a FT_PCRE */ + fvalue = fvalue_from_string(FT_PCRE, s, dfilter_fail); + } else { + fvalue = fvalue_from_string(ftype1, s, dfilter_fail); + } + if (!fvalue) { + THROW(TypeError); + } + + new_st = stnode_new(STTYPE_FVALUE, fvalue); + sttype_test_set2_args(st_node, st_arg1, new_st); + stnode_free(st_arg2); + } + else if (type2 == STTYPE_UNPARSED) { + s = stnode_data(st_arg2); + if (strcmp(relation_string, "matches") == 0) { + /* Convert to a FT_PCRE */ + fvalue = fvalue_from_unparsed(FT_PCRE, s, FALSE, dfilter_fail); + } else { + fvalue = fvalue_from_unparsed(ftype1, s, allow_partial_value, dfilter_fail); + } + if (!fvalue) { + THROW(TypeError); + } + + new_st = stnode_new(STTYPE_FVALUE, fvalue); + sttype_test_set2_args(st_node, st_arg1, new_st); + stnode_free(st_arg2); + } + else if (type2 == STTYPE_RANGE) { + check_drange_sanity(st_arg2); + if (!is_bytes_type(ftype1)) { + if (!ftype_can_slice(ftype1)) { + dfilter_fail("Function \"%s\" is a %s and cannot be converted into a sequence of bytes.", + funcdef->name, + ftype_pretty_name(ftype1)); + THROW(TypeError); + } + + /* Convert entire field to bytes */ + new_st = stnode_new(STTYPE_RANGE, NULL); + + rn = drange_node_new(); + drange_node_set_start_offset(rn, 0); + drange_node_set_to_the_end(rn); + /* st_arg1 is freed in this step */ + sttype_range_set1(new_st, st_arg1, rn); + + sttype_test_set2_args(st_node, new_st, st_arg2); + } + } + else { + g_assert_not_reached(); + } +} + /* Check the semantics of any relational test. */ static void @@ -795,12 +958,16 @@ header_field_info *hfinfo; check_relation_LHS_UNPARSED(relation_string, can_func, allow_partial_value, st_node, st_arg1, st_arg2); break; + case STTYPE_FUNCTION: + check_relation_LHS_FUNCTION(relation_string, can_func, + allow_partial_value, st_node, st_arg1, st_arg2); + break; case STTYPE_UNINITIALIZED: case STTYPE_TEST: case STTYPE_INTEGER: case STTYPE_FVALUE: - case STTYPE_NUM_TYPES: + default: g_assert_not_reached(); } } diff --git a/epan/dfilter/sttype-function.c b/epan/dfilter/sttype-function.c new file mode 100644 index 0000000000..c6c8a5af83 --- /dev/null +++ b/epan/dfilter/sttype-function.c @@ -0,0 +1,125 @@ +/* + * $Id$ + * + * Copyright (c) 2006 by Gilbert Ramirez + * + * Ethereal - Network traffic analyzer + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "syntax-tree.h" +#include "sttype-function.h" + +typedef struct { + guint32 magic; + df_func_def_t *funcdef; + GSList *params; +} function_t; + +#define FUNCTION_MAGIC 0xe10f0f99 + +static gpointer +function_new(gpointer funcdef) +{ + function_t *stfuncrec; + + g_assert(funcdef != NULL); + + stfuncrec = g_new(function_t, 1); + + stfuncrec->magic = FUNCTION_MAGIC; + stfuncrec->funcdef = funcdef; + stfuncrec->params = NULL; + + return (gpointer) stfuncrec; +} + +static void +slist_stnode_free(gpointer data, gpointer user_data _U_) +{ + stnode_free(data); +} + +void +st_funcparams_free(GSList *params) +{ + g_slist_foreach(params, slist_stnode_free, NULL); + g_slist_free(params); +} + +static void +function_free(gpointer value) +{ + function_t *stfuncrec = value; + assert_magic(stfuncrec, FUNCTION_MAGIC); + st_funcparams_free(stfuncrec->params); + g_free(stfuncrec); +} + + +/* Set the parameters for a function stnode_t. */ +void +sttype_function_set_params(stnode_t *node, GSList *params) +{ + + function_t *stfuncrec; + + stfuncrec = stnode_data(node); + assert_magic(stfuncrec, FUNCTION_MAGIC); + + stfuncrec->params = params; +} + +/* Get the function-definition record for a function stnode_t. */ +df_func_def_t* +sttype_function_funcdef(stnode_t *node) +{ + function_t *stfuncrec; + + stfuncrec = stnode_data(node); + assert_magic(stfuncrec, FUNCTION_MAGIC); + return stfuncrec->funcdef; +} + +/* Get the parameters for a function stnode_t. */ +GSList* +sttype_function_params(stnode_t *node) +{ + function_t *stfuncrec; + + stfuncrec = stnode_data(node); + assert_magic(stfuncrec, FUNCTION_MAGIC); + return stfuncrec->params; +} + + +void +sttype_register_function(void) +{ + static sttype_t function_type = { + STTYPE_FUNCTION, + "FUNCTION", + function_new, + function_free, + }; + + sttype_register(&function_type); +} + diff --git a/epan/dfilter/sttype-function.h b/epan/dfilter/sttype-function.h new file mode 100644 index 0000000000..b93f2e3273 --- /dev/null +++ b/epan/dfilter/sttype-function.h @@ -0,0 +1,42 @@ +/* + * $Id: sttype-test.h 11400 2004-07-18 00:24:25Z guy $ + * + * Ethereal - Network traffic analyzer + * By Gerald Combs + * Copyright 2001 Gerald Combs + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef STTYPE_FUNCTION_H +#define STTYPE_FUNCTION_H + +#include "dfunctions.h" + +/* Set the parameters for a function stnode_t. */ +void +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); + +/* Get the parameters for a function stnode_t. */ +GSList* sttype_function_params(stnode_t *node); + +/* Free the memory of a param list */ +void st_funcparams_free(GSList *params); + +#endif diff --git a/epan/dfilter/syntax-tree.c b/epan/dfilter/syntax-tree.c index 2fef146adf..62b54d97fe 100644 --- a/epan/dfilter/syntax-tree.c +++ b/epan/dfilter/syntax-tree.c @@ -36,6 +36,7 @@ static sttype_t* type_list[STTYPE_NUM_TYPES]; void sttype_init(void) { + sttype_register_function(); sttype_register_integer(); sttype_register_pointer(); sttype_register_range(); diff --git a/epan/dfilter/syntax-tree.h b/epan/dfilter/syntax-tree.h index 5fd5dbbe14..d57c850f90 100644 --- a/epan/dfilter/syntax-tree.h +++ b/epan/dfilter/syntax-tree.h @@ -35,6 +35,7 @@ typedef enum { STTYPE_FVALUE, STTYPE_INTEGER, STTYPE_RANGE, + STTYPE_FUNCTION, STTYPE_NUM_TYPES } sttype_id_t; @@ -62,6 +63,7 @@ typedef struct { } stnode_t; /* These are the sttype_t registration function prototypes. */ +void sttype_register_function(void); void sttype_register_integer(void); void sttype_register_pointer(void); void sttype_register_range(void); diff --git a/epan/ftypes/ftype-bytes.c b/epan/ftypes/ftype-bytes.c index 6de2616359..d67883cd53 100644 --- a/epan/ftypes/ftype-bytes.c +++ b/epan/ftypes/ftype-bytes.c @@ -480,6 +480,7 @@ ftype_register_bytes(void) { static ftype_t bytes_type = { + FT_BYTES, /* ftype */ "FT_BYTES", /* name */ "sequence of bytes", /* pretty_name */ 0, /* wire_size */ @@ -515,6 +516,7 @@ ftype_register_bytes(void) }; static ftype_t uint_bytes_type = { + FT_UINT_BYTES, /* ftype */ "FT_UINT_BYTES", /* name */ "sequence of bytes", /* pretty_name */ 0, /* wire_size */ @@ -550,6 +552,7 @@ ftype_register_bytes(void) }; static ftype_t ether_type = { + FT_ETHER, /* ftype */ "FT_ETHER", /* name */ "Ethernet or other MAC address",/* pretty_name */ ETHER_LEN, /* wire_size */ @@ -585,6 +588,7 @@ ftype_register_bytes(void) }; static ftype_t ipv6_type = { + FT_IPv6, /* ftype */ "FT_IPv6", /* name */ "IPv6 address", /* pretty_name */ IPv6_LEN, /* wire_size */ @@ -620,6 +624,7 @@ ftype_register_bytes(void) }; static ftype_t oid_type = { + FT_OID, /* ftype */ "OID", /* name */ "OBJECT IDENTIFIER", /* pretty_name */ 0, /* wire_size */ diff --git a/epan/ftypes/ftype-double.c b/epan/ftypes/ftype-double.c index af45990cdc..460b4c5b13 100644 --- a/epan/ftypes/ftype-double.c +++ b/epan/ftypes/ftype-double.c @@ -157,6 +157,7 @@ ftype_register_double(void) { static ftype_t float_type = { + FT_FLOAT, /* ftype */ "FT_FLOAT", /* name */ "floating point (single-precision)", /* pretty_name */ 0, /* wire_size */ @@ -192,6 +193,7 @@ ftype_register_double(void) }; static ftype_t double_type = { + FT_DOUBLE, /* ftype */ "FT_DOUBLE", /* name */ "floating point (double-precision)", /* pretty_name */ 0, /* wire_size */ diff --git a/epan/ftypes/ftype-guid.c b/epan/ftypes/ftype-guid.c index 08104453c8..4a5e9645d5 100644 --- a/epan/ftypes/ftype-guid.c +++ b/epan/ftypes/ftype-guid.c @@ -129,6 +129,7 @@ ftype_register_guid(void) { static ftype_t guid_type = { + FT_GUID, /* ftype */ "GUID", /* name */ "Globally Unique Identifier", /* pretty_name */ GUID_LEN, /* wire_size */ diff --git a/epan/ftypes/ftype-integer.c b/epan/ftypes/ftype-integer.c index dc2954bb9f..9656facbbf 100644 --- a/epan/ftypes/ftype-integer.c +++ b/epan/ftypes/ftype-integer.c @@ -444,6 +444,7 @@ ftype_register_integers(void) { static ftype_t uint8_type = { + FT_UINT8, /* ftype */ "FT_UINT8", /* name */ "unsigned, 1 byte", /* pretty name */ 1, /* wire_size */ @@ -478,6 +479,7 @@ ftype_register_integers(void) NULL, /* slice */ }; static ftype_t uint16_type = { + FT_UINT16, /* ftype */ "FT_UINT16", /* name */ "unsigned, 2 bytes", /* pretty_name */ 2, /* wire_size */ @@ -512,6 +514,7 @@ ftype_register_integers(void) NULL, /* slice */ }; static ftype_t uint24_type = { + FT_UINT24, /* ftype */ "FT_UINT24", /* name */ "unsigned, 3 bytes", /* pretty_name */ 3, /* wire_size */ @@ -546,6 +549,7 @@ ftype_register_integers(void) NULL, /* slice */ }; static ftype_t uint32_type = { + FT_UINT32, /* ftype */ "FT_UINT32", /* name */ "unsigned, 4 bytes", /* pretty_name */ 4, /* wire_size */ @@ -580,6 +584,7 @@ ftype_register_integers(void) NULL, /* slice */ }; static ftype_t uint64_type = { + FT_UINT64, /* ftype */ "FT_UINT64", /* name */ "unsigned, 8 bytes", /* pretty_name */ 8, /* wire_size */ @@ -614,6 +619,7 @@ ftype_register_integers(void) NULL, }; static ftype_t int8_type = { + FT_INT8, /* ftype */ "FT_INT8", /* name */ "signed, 1 byte", /* pretty_name */ 1, /* wire_size */ @@ -648,6 +654,7 @@ ftype_register_integers(void) NULL, /* slice */ }; static ftype_t int16_type = { + FT_INT16, /* ftype */ "FT_INT16", /* name */ "signed, 2 bytes", /* pretty_name */ 2, /* wire_size */ @@ -682,6 +689,7 @@ ftype_register_integers(void) NULL, /* slice */ }; static ftype_t int24_type = { + FT_INT24, /* ftype */ "FT_INT24", /* name */ "signed, 3 bytes", /* pretty_name */ 3, /* wire_size */ @@ -716,6 +724,7 @@ ftype_register_integers(void) NULL, /* slice */ }; static ftype_t int32_type = { + FT_INT32, /* ftype */ "FT_INT32", /* name */ "signed, 4 bytes", /* pretty_name */ 4, /* wire_size */ @@ -750,6 +759,7 @@ ftype_register_integers(void) NULL, /* slice */ }; static ftype_t int64_type = { + FT_INT64, /* ftype */ "FT_INT64", /* name */ "signed, 8 bytes", /* pretty_name */ 8, /* wire_size */ @@ -784,6 +794,7 @@ ftype_register_integers(void) NULL, }; static ftype_t boolean_type = { + FT_BOOLEAN, /* ftype */ "FT_BOOLEAN", /* name */ "Boolean", /* pretty_name */ 0, /* wire_size */ @@ -819,6 +830,7 @@ ftype_register_integers(void) }; static ftype_t ipxnet_type = { + FT_IPXNET, /* ftype */ "FT_IPXNET", /* name */ "IPX network number", /* pretty_name */ 4, /* wire_size */ @@ -854,6 +866,7 @@ ftype_register_integers(void) }; static ftype_t framenum_type = { + FT_FRAMENUM, /* ftype */ "FT_FRAMENUM", /* name */ "frame number", /* pretty_name */ 4, /* wire_size */ diff --git a/epan/ftypes/ftype-ipv4.c b/epan/ftypes/ftype-ipv4.c index 65b8961dc8..d57894325c 100644 --- a/epan/ftypes/ftype-ipv4.c +++ b/epan/ftypes/ftype-ipv4.c @@ -199,6 +199,7 @@ ftype_register_ipv4(void) { static ftype_t ipv4_type = { + FT_IPv4, /* ftype */ "FT_IPv4", /* name */ "IPv4 address", /* pretty_name */ 4, /* wire_size */ diff --git a/epan/ftypes/ftype-none.c b/epan/ftypes/ftype-none.c index 4eb037eed9..471e0a4d23 100644 --- a/epan/ftypes/ftype-none.c +++ b/epan/ftypes/ftype-none.c @@ -32,6 +32,7 @@ ftype_register_none(void) { static ftype_t none_type = { + FT_NONE, /* ftype */ "FT_NONE", /* name */ "label", /* pretty_name */ 0, /* wire_size */ diff --git a/epan/ftypes/ftype-pcre.c b/epan/ftypes/ftype-pcre.c index 05db17e28a..82bcc196de 100644 --- a/epan/ftypes/ftype-pcre.c +++ b/epan/ftypes/ftype-pcre.c @@ -177,6 +177,7 @@ void ftype_register_pcre(void) { static ftype_t pcre_type = { + FT_PCRE, /* ftype */ "FT_PCRE", /* name */ "Compiled Perl-Compatible Regular Expression object", /* pretty_name */ 0, /* wire_size */ @@ -219,6 +220,7 @@ void ftype_register_pcre(void) { static ftype_t pcre_type = { + FT_PCRE, /* ftype */ "FT_PCRE", /* name */ "Compiled Perl-Compatible Regular Expression object", /* pretty_name */ 0, /* wire_size */ diff --git a/epan/ftypes/ftype-string.c b/epan/ftypes/ftype-string.c index 70649b77b8..d4525fc279 100644 --- a/epan/ftypes/ftype-string.c +++ b/epan/ftypes/ftype-string.c @@ -301,6 +301,7 @@ ftype_register_string(void) { static ftype_t string_type = { + FT_STRING, /* ftype */ "FT_STRING", /* name */ "character string", /* pretty_name */ 0, /* wire_size */ @@ -335,6 +336,7 @@ ftype_register_string(void) slice, }; static ftype_t stringz_type = { + FT_STRINGZ, /* ftype */ "FT_STRINGZ", /* name */ "character string", /* pretty name */ 0, /* wire_size */ @@ -369,6 +371,7 @@ ftype_register_string(void) slice, }; static ftype_t uint_string_type = { + FT_UINT_STRING, /* ftype */ "FT_UINT_STRING", /* name */ "character string", /* pretty_name */ 0, /* wire_size */ diff --git a/epan/ftypes/ftype-time.c b/epan/ftypes/ftype-time.c index ed90960952..2427ccb5d5 100644 --- a/epan/ftypes/ftype-time.c +++ b/epan/ftypes/ftype-time.c @@ -345,6 +345,7 @@ ftype_register_time(void) { static ftype_t abstime_type = { + FT_ABSOLUTE_TIME, /* ftype */ "FT_ABSOLUTE_TIME", /* name */ "date/time", /* pretty_name */ 0, /* wire_size */ @@ -379,6 +380,7 @@ ftype_register_time(void) NULL }; static ftype_t reltime_type = { + FT_RELATIVE_TIME, /* ftype */ "FT_RELATIVE_TIME", /* name */ "time offset", /* pretty_name */ 0, /* wire_size */ diff --git a/epan/ftypes/ftype-tvbuff.c b/epan/ftypes/ftype-tvbuff.c index f4c84b5d65..8e4a705059 100644 --- a/epan/ftypes/ftype-tvbuff.c +++ b/epan/ftypes/ftype-tvbuff.c @@ -260,6 +260,7 @@ ftype_register_tvbuff(void) { static ftype_t protocol_type = { + FT_PROTOCOL, /* ftype */ "FT_PROTOCOL", /* name */ "protocol", /* pretty_name */ 0, /* wire_size */ diff --git a/epan/ftypes/ftypes.c b/epan/ftypes/ftypes.c index e40dfc4f06..d48389496b 100644 --- a/epan/ftypes/ftypes.c +++ b/epan/ftypes/ftypes.c @@ -58,6 +58,7 @@ ftype_register(enum ftenum ftype, ftype_t *ft) { /* Check input */ g_assert(ftype < FT_NUM_TYPES); + g_assert(ftype == ft->ftype); /* Don't re-register. */ g_assert(type_list[ftype] == NULL); @@ -268,6 +269,12 @@ fvalue_from_string(ftenum_t ftype, char *s, LogFunc logfunc) return NULL; } +ftype_t* +fvalue_ftype(fvalue_t *fv) +{ + return fv->ftype; +} + const char* fvalue_type_name(fvalue_t *fv) { diff --git a/epan/ftypes/ftypes.h b/epan/ftypes/ftypes.h index 6a30a4b7b5..a21feb5291 100644 --- a/epan/ftypes/ftypes.h +++ b/epan/ftypes/ftypes.h @@ -196,6 +196,7 @@ typedef guint (*FvalueLen)(fvalue_t*); typedef void (*FvalueSlice)(fvalue_t*, GByteArray *, guint offset, guint length); struct _ftype_t { + ftenum_t ftype; const char *name; const char *pretty_name; int wire_size; @@ -288,6 +289,9 @@ fvalue_string_repr_len(fvalue_t *fv, ftrepr_t rtype); extern char * fvalue_to_string_repr(fvalue_t *fv, ftrepr_t rtype, char *buf); +ftype_t* +fvalue_ftype(fvalue_t *fv); + const char* fvalue_type_name(fvalue_t *fv); diff --git a/tools/dfilter-test.py b/tools/dfilter-test.py index 9dcae85dbc..f77319e3f2 100755 --- a/tools/dfilter-test.py +++ b/tools/dfilter-test.py @@ -1015,6 +1015,38 @@ class String(Test): return self.DFilterCount(pkt_http, 'http.request.method contains 48:45:41:44"', 1) # "HEAD" + def ck_contains_fail_0(self): + return self.DFilterCount(pkt_http, + 'http.user_agent contains "update"', 0) + + def ck_contains_fail_1(self): + return self.DFilterCount(pkt_http, + 'http.user_agent contains "UPDATE"', 0) + + def ck_contains_upper_0(self): + return self.DFilterCount(pkt_http, + 'upper(http.user_agent) contains "UPDATE"', 1) + + def ck_contains_upper_1(self): + return self.DFilterCount(pkt_http, + 'upper(http.user_agent) contains "update"', 0) + + def ck_contains_upper_2(self): + return self.DFilterCount(pkt_http, + 'upper(tcp.seq) == 4', None) + + def ck_contains_lower_0(self): + return self.DFilterCount(pkt_http, + 'lower(http.user_agent) contains "UPDATE"', 0) + + def ck_contains_lower_1(self): + return self.DFilterCount(pkt_http, + 'lower(http.user_agent) contains "update"', 1) + + def ck_contains_lower_2(self): + return self.DFilterCount(pkt_http, + 'lower(tcp.seq) == 4', None) + tests = [ ck_eq_1, @@ -1047,6 +1079,14 @@ class String(Test): ck_contains_3, ck_contains_4, ck_contains_5, + ck_contains_fail_0, + ck_contains_fail_1, + ck_contains_upper_0, + ck_contains_upper_1, + ck_contains_upper_2, + ck_contains_lower_0, + ck_contains_lower_1, + ck_contains_lower_2, ]