Ethereal-dev: [Ethereal-dev] RFC: display filter functions
Note: This archive is from the project's previous web site, ethereal.com. This list is no longer active.
From: "Gilbert Ramirez" <gram@xxxxxxxxxxxxxxx>
Date: Fri, 28 Apr 2006 13:43:32 -0500
Attached is my work on adding functions to the display filter language. Since I have not added new functionality to Ethereal in a long time, I'm posting it for review. Besides adding the infrastructure, this code adds 2 functions: upper() and lower(), which take a string field and return the uppercase or lowercase version for it. That's why this work was done in the first place --- to support case-insenstive string comparisons. Right now the code only works on ASCII strings; I have no idea what to do about non-ASCII. Similarly, it only works on the FT_STRING and related types... it does not work on an entire protocol (FT_PROTOCOL). The deficiency is due to the fact that I make a function return only one type. In this case, upper()/lower() return an FT_STRING; if they were to work on a protocol, they'd have to return an FT_PROTOCOL also. I'm not sure yet if it's worth changing this, as it will make the semantic check of the syntax tree more difficult. Also, to support any future functions where a display filter function takes more than one parameter, I had to change how the display filter scanner treats commas. Right now commas are allowed to be in field names (!) and in unquoted-strings. After my change, they won't be. I don't see this as a drawback, since it's unlikely that anyone would want a comman in a field name, and quoted strings would allow you to use commas. Thanks for any comments, --gilbert
Index: doc/README.developer =================================================================== --- doc/README.developer (revision 18025) +++ doc/README.developer (working copy) @@ -3209,7 +3209,71 @@ 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. Index: doc/ethereal-filter.pod.template =================================================================== --- doc/ethereal-filter.pod.template (revision 18025) +++ doc/ethereal-filter.pod.template (working copy) @@ -93,6 +93,19 @@ or selecting the "About Ethereal" item from the "Help" menu in B<Ethereal>. +=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: Index: epan/dfilter/dfvm.h =================================================================== --- epan/dfilter/dfvm.h (revision 18025) +++ epan/dfilter/dfvm.h (working copy) @@ -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 @@ INSN_NUMBER, REGISTER, INTEGER, - DRANGE + DRANGE, + FUNCTION_DEF } dfvm_value_type_t; typedef struct { @@ -47,6 +49,7 @@ guint32 numeric; drange *drange; header_field_info *hfinfo; + df_func_def_t *funcdef; } value; } dfvm_value_t; @@ -70,13 +73,13 @@ 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; Index: epan/dfilter/scanner.l =================================================================== --- epan/dfilter/scanner.l (revision 18025) +++ epan/dfilter/scanner.l (working copy) @@ -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 @@ "(" 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 @@ 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 @@ 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 @@ 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; } Index: epan/dfilter/syntax-tree.c =================================================================== --- epan/dfilter/syntax-tree.c (revision 18025) +++ epan/dfilter/syntax-tree.c (working copy) @@ -36,6 +36,7 @@ void sttype_init(void) { + sttype_register_function(); sttype_register_integer(); sttype_register_pointer(); sttype_register_range(); Index: epan/dfilter/Makefile.nmake =================================================================== --- epan/dfilter/Makefile.nmake (revision 18025) +++ epan/dfilter/Makefile.nmake (working copy) @@ -20,6 +20,7 @@ OBJECTS = \ dfilter.obj \ + dfunctions.obj \ dfvm.obj \ drange.obj \ gencode.obj \ @@ -27,6 +28,7 @@ grammar.obj \ scanner.obj \ semcheck.obj \ + sttype-function.obj \ sttype-integer.obj \ sttype-pointer.obj \ sttype-range.obj \ Index: epan/dfilter/syntax-tree.h =================================================================== --- epan/dfilter/syntax-tree.h (revision 18025) +++ epan/dfilter/syntax-tree.h (working copy) @@ -35,6 +35,7 @@ STTYPE_FVALUE, STTYPE_INTEGER, STTYPE_RANGE, + STTYPE_FUNCTION, STTYPE_NUM_TYPES } sttype_id_t; @@ -62,6 +63,7 @@ } 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); Index: epan/dfilter/gencode.c =================================================================== --- epan/dfilter/gencode.c (revision 18025) +++ epan/dfilter/gencode.c (working copy) @@ -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 @@ 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); + /* Create code for the LHS and RHS of the relation */ + reg1 = gen_entity(dfw, st_arg1, &jmp1); + reg2 = gen_entity(dfw, st_arg2, &jmp2); - 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(); - } - + /* 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 @@ 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,7 +271,46 @@ } } +/* 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) { Index: epan/dfilter/sttype-function.c =================================================================== --- epan/dfilter/sttype-function.c (revision 0) +++ epan/dfilter/sttype-function.c (revision 0) @@ -0,0 +1,125 @@ +/* + * $Id$ + * + * Copyright (c) 2006 by Gilbert Ramirez <gram@xxxxxxxxxxxxxxx> + * + * 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); +} + Index: epan/dfilter/dfunctions.c =================================================================== --- epan/dfilter/dfunctions.c (revision 0) +++ epan/dfilter/dfunctions.c (revision 0) @@ -0,0 +1,158 @@ +/* + * $Id$ + * + * Ethereal - Network traffic analyzer + * + * Copyright 2006 Gilbert Ramirez <gram@xxxxxxxxxxxxxxx> + * + * 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 <glib.h> + +#include "dfunctions.h" +#include "dfilter-int.h" + +#include <string.h> +#include <ctype.h> + +#include <ftypes/ftypes.h> +#include <epan/exceptions.h> + +/* 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; +} Index: epan/dfilter/grammar.lemon =================================================================== --- epan/dfilter/grammar.lemon (revision 18025) +++ epan/dfilter/grammar.lemon (working copy) @@ -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 @@ 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,12 +127,7 @@ 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_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; +} + Index: epan/dfilter/sttype-function.h =================================================================== --- epan/dfilter/sttype-function.h (revision 0) +++ epan/dfilter/sttype-function.h (revision 0) @@ -0,0 +1,42 @@ +/* + * $Id: sttype-test.h 11400 2004-07-18 00:24:25Z guy $ + * + * Ethereal - Network traffic analyzer + * By Gerald Combs <gerald@xxxxxxxx> + * 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 Index: epan/dfilter/dfunctions.h =================================================================== --- epan/dfilter/dfunctions.h (revision 0) +++ epan/dfilter/dfunctions.h (revision 0) @@ -0,0 +1,56 @@ +/* + * $Id$ + * + * Ethereal - Network traffic analyzer + * + * Copyright 2006 Gilbert Ramirez <gram@xxxxxxxxxxxxxxx> + * + * 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 <glib.h> +#include <ftypes/ftypes.h> +#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 Index: epan/dfilter/semcheck.c =================================================================== --- epan/dfilter/semcheck.c (revision 18025) +++ epan/dfilter/semcheck.c (working copy) @@ -31,6 +31,7 @@ #include "syntax-tree.h" #include "sttype-range.h" #include "sttype-test.h" +#include "sttype-function.h" #include <epan/exceptions.h> #include <epan/packet.h> @@ -307,6 +308,13 @@ 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,7 +759,162 @@ } } +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 check_relation(const char *relation_string, gboolean allow_partial_value, @@ -795,12 +958,16 @@ 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(); } } Index: epan/dfilter/Makefile.am =================================================================== --- epan/dfilter/Makefile.am (revision 18025) +++ epan/dfilter/Makefile.am (working copy) @@ -46,6 +46,8 @@ dfilter.c \ dfilter.h \ dfilter-int.h \ + dfunctions.c \ + dfunctions.h \ dfvm.c \ dfvm.h \ drange.c \ @@ -59,6 +61,8 @@ scanner.c \ semcheck.c \ semcheck.h \ + sttype-function.c \ + sttype-function.h \ sttype-integer.c \ sttype-pointer.c \ sttype-range.c \ Index: epan/dfilter/dfvm.c =================================================================== --- epan/dfilter/dfvm.c (revision 18025) +++ epan/dfilter/dfvm.c (working copy) @@ -118,6 +118,18 @@ 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_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 @@ 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); Index: epan/ftypes/ftype-double.c =================================================================== --- epan/ftypes/ftype-double.c (revision 18025) +++ epan/ftypes/ftype-double.c (working copy) @@ -157,6 +157,7 @@ { static ftype_t float_type = { + FT_FLOAT, /* ftype */ "FT_FLOAT", /* name */ "floating point (single-precision)", /* pretty_name */ 0, /* wire_size */ @@ -192,6 +193,7 @@ }; static ftype_t double_type = { + FT_DOUBLE, /* ftype */ "FT_DOUBLE", /* name */ "floating point (double-precision)", /* pretty_name */ 0, /* wire_size */ Index: epan/ftypes/ftype-ipv4.c =================================================================== --- epan/ftypes/ftype-ipv4.c (revision 18025) +++ epan/ftypes/ftype-ipv4.c (working copy) @@ -199,6 +199,7 @@ { static ftype_t ipv4_type = { + FT_IPv4, /* ftype */ "FT_IPv4", /* name */ "IPv4 address", /* pretty_name */ 4, /* wire_size */ Index: epan/ftypes/ftypes.c =================================================================== --- epan/ftypes/ftypes.c (revision 18025) +++ epan/ftypes/ftypes.c (working copy) @@ -58,6 +58,7 @@ { /* 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 @@ return NULL; } +ftype_t* +fvalue_ftype(fvalue_t *fv) +{ + return fv->ftype; +} + const char* fvalue_type_name(fvalue_t *fv) { Index: epan/ftypes/ftype-bytes.c =================================================================== --- epan/ftypes/ftype-bytes.c (revision 18025) +++ epan/ftypes/ftype-bytes.c (working copy) @@ -480,6 +480,7 @@ { static ftype_t bytes_type = { + FT_BYTES, /* ftype */ "FT_BYTES", /* name */ "sequence of bytes", /* pretty_name */ 0, /* wire_size */ @@ -515,6 +516,7 @@ }; 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 @@ }; 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 @@ }; static ftype_t ipv6_type = { + FT_IPv6, /* ftype */ "FT_IPv6", /* name */ "IPv6 address", /* pretty_name */ IPv6_LEN, /* wire_size */ @@ -620,6 +624,7 @@ }; static ftype_t oid_type = { + FT_OID, /* ftype */ "OID", /* name */ "OBJECT IDENTIFIER", /* pretty_name */ 0, /* wire_size */ Index: epan/ftypes/ftype-guid.c =================================================================== --- epan/ftypes/ftype-guid.c (revision 18025) +++ epan/ftypes/ftype-guid.c (working copy) @@ -129,6 +129,7 @@ { static ftype_t guid_type = { + FT_GUID, /* ftype */ "GUID", /* name */ "Globally Unique Identifier", /* pretty_name */ GUID_LEN, /* wire_size */ Index: epan/ftypes/ftype-pcre.c =================================================================== --- epan/ftypes/ftype-pcre.c (revision 18025) +++ epan/ftypes/ftype-pcre.c (working copy) @@ -177,6 +177,7 @@ 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 @@ 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 */ Index: epan/ftypes/ftypes.h =================================================================== --- epan/ftypes/ftypes.h (revision 18025) +++ epan/ftypes/ftypes.h (working copy) @@ -196,6 +196,7 @@ 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 @@ 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); Index: epan/ftypes/ftype-tvbuff.c =================================================================== --- epan/ftypes/ftype-tvbuff.c (revision 18025) +++ epan/ftypes/ftype-tvbuff.c (working copy) @@ -260,6 +260,7 @@ { static ftype_t protocol_type = { + FT_PROTOCOL, /* ftype */ "FT_PROTOCOL", /* name */ "protocol", /* pretty_name */ 0, /* wire_size */ Index: epan/ftypes/ftype-integer.c =================================================================== --- epan/ftypes/ftype-integer.c (revision 18025) +++ epan/ftypes/ftype-integer.c (working copy) @@ -444,6 +444,7 @@ { static ftype_t uint8_type = { + FT_UINT8, /* ftype */ "FT_UINT8", /* name */ "unsigned, 1 byte", /* pretty name */ 1, /* wire_size */ @@ -478,6 +479,7 @@ 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 @@ 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 @@ 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 @@ 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 @@ NULL, }; static ftype_t int8_type = { + FT_INT8, /* ftype */ "FT_INT8", /* name */ "signed, 1 byte", /* pretty_name */ 1, /* wire_size */ @@ -648,6 +654,7 @@ 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 @@ 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 @@ 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 @@ 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 @@ NULL, }; static ftype_t boolean_type = { + FT_BOOLEAN, /* ftype */ "FT_BOOLEAN", /* name */ "Boolean", /* pretty_name */ 0, /* wire_size */ @@ -819,6 +830,7 @@ }; static ftype_t ipxnet_type = { + FT_IPXNET, /* ftype */ "FT_IPXNET", /* name */ "IPX network number", /* pretty_name */ 4, /* wire_size */ @@ -854,6 +866,7 @@ }; static ftype_t framenum_type = { + FT_FRAMENUM, /* ftype */ "FT_FRAMENUM", /* name */ "frame number", /* pretty_name */ 4, /* wire_size */ Index: epan/ftypes/ftype-time.c =================================================================== --- epan/ftypes/ftype-time.c (revision 18025) +++ epan/ftypes/ftype-time.c (working copy) @@ -345,6 +345,7 @@ { static ftype_t abstime_type = { + FT_ABSOLUTE_TIME, /* ftype */ "FT_ABSOLUTE_TIME", /* name */ "date/time", /* pretty_name */ 0, /* wire_size */ @@ -379,6 +380,7 @@ NULL }; static ftype_t reltime_type = { + FT_RELATIVE_TIME, /* ftype */ "FT_RELATIVE_TIME", /* name */ "time offset", /* pretty_name */ 0, /* wire_size */ Index: epan/ftypes/ftype-none.c =================================================================== --- epan/ftypes/ftype-none.c (revision 18025) +++ epan/ftypes/ftype-none.c (working copy) @@ -32,6 +32,7 @@ { static ftype_t none_type = { + FT_NONE, /* ftype */ "FT_NONE", /* name */ "label", /* pretty_name */ 0, /* wire_size */ Index: epan/ftypes/ftype-string.c =================================================================== --- epan/ftypes/ftype-string.c (revision 18025) +++ epan/ftypes/ftype-string.c (working copy) @@ -301,6 +301,7 @@ { static ftype_t string_type = { + FT_STRING, /* ftype */ "FT_STRING", /* name */ "character string", /* pretty_name */ 0, /* wire_size */ @@ -335,6 +336,7 @@ slice, }; static ftype_t stringz_type = { + FT_STRINGZ, /* ftype */ "FT_STRINGZ", /* name */ "character string", /* pretty name */ 0, /* wire_size */ @@ -369,6 +371,7 @@ slice, }; static ftype_t uint_string_type = { + FT_UINT_STRING, /* ftype */ "FT_UINT_STRING", /* name */ "character string", /* pretty_name */ 0, /* wire_size */
- Prev by Date: [Ethereal-dev] RE: Capture Trigger!!!
- Next by Date: [Ethereal-dev] Re: Google's Summer of Code
- Previous by thread: [Ethereal-dev] RE: Capture Trigger!!!
- Next by thread: [Ethereal-dev] Re: Google's Summer of Code
- Index(es):