Add infrastructure for display filter functions.

Add upper() and lower() display filter functions for string fields.

svn path=/trunk/; revision=18071
This commit is contained in:
Gilbert Ramirez 2006-05-02 14:26:17 +00:00
parent 7edd136c88
commit e3899ed4a4
29 changed files with 936 additions and 56 deletions

View File

@ -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.

View File

@ -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<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:

View File

@ -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 \

View File

@ -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 \

158
epan/dfilter/dfunctions.c Normal file
View File

@ -0,0 +1,158 @@
/*
* $Id$
*
* Ethereal - Network traffic analyzer
*
* Copyright 2006 Gilbert Ramirez <gram@alumni.rice.edu>
*
* 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;
}

56
epan/dfilter/dfunctions.h Normal file
View File

@ -0,0 +1,56 @@
/*
* $Id$
*
* Ethereal - Network traffic analyzer
*
* Copyright 2006 Gilbert Ramirez <gram@alumni.rice.edu>
*
* 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

View File

@ -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);

View File

@ -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;

View File

@ -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)

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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 @@ 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();
}
}

View File

@ -0,0 +1,125 @@
/*
* $Id$
*
* Copyright (c) 2006 by Gilbert Ramirez <gram@alumni.rice.edu>
*
* 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);
}

View File

@ -0,0 +1,42 @@
/*
* $Id: sttype-test.h 11400 2004-07-18 00:24:25Z guy $
*
* Ethereal - Network traffic analyzer
* By Gerald Combs <gerald@zing.org>
* 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

View File

@ -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();

View File

@ -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);

View File

@ -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 */

View File

@ -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 */

View File

@ -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 */

View File

@ -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 */

View File

@ -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 */

View File

@ -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 */

View File

@ -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 */

View File

@ -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 */

View File

@ -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 */

View File

@ -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 */

View File

@ -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)
{

View File

@ -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);

View File

@ -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,
]