wireshark/epan/dfilter/dfilter-macro.c

717 lines
16 KiB
C

/* dfilter-macro.c
*
* $Id$
*
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@wireshark.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.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdio.h>
#include <errno.h>
#include <ctype.h>
#include <string.h>
#include "dfilter-int.h"
#include "dfilter.h"
#include "dfilter-macro.h"
#include <epan/emem.h>
#include <epan/uat.h>
#include <epan/report_err.h>
#include <epan/proto.h>
#include <wsutil/file_util.h>
typedef struct {
const char* name;
gboolean usable;
char* repr;
} fvt_cache_entry_t;
static uat_t* dfilter_macro_uat = NULL;
static dfilter_macro_t* macros = NULL;
static guint num_macros;
static GHashTable* fvt_cache = NULL;
/* #define DUMP_DFILTER_MACRO */
#ifdef DUMP_DFILTER_MACRO
void dump_dfilter_macro_t(const dfilter_macro_t *m, const char *function, const char *file, int line);
#define DUMP_MACRO(m) dump_dfilter_macro_t(m, __func__, __FILE__, __LINE__)
#else
#define DUMP_MACRO(m)
#endif
static gboolean free_value(gpointer k _U_, gpointer v, gpointer u _U_) {
fvt_cache_entry_t* e = v;
if (e->repr) g_free(e->repr);
g_free(e);
return TRUE;
}
static gboolean fvt_cache_cb(proto_node * node, gpointer data _U_) {
field_info* finfo = node->finfo;
fvt_cache_entry_t* e;
if (!finfo) return FALSE;
if ((e = g_hash_table_lookup(fvt_cache,finfo->hfinfo->abbrev))) {
e->usable = FALSE;
} else if (finfo->value.ftype->val_to_string_repr) {
switch (finfo->hfinfo->type) {
case FT_NONE:
case FT_PROTOCOL:
return FALSE;
default:
break;
}
e = g_malloc(sizeof(fvt_cache_entry_t));
e->name = finfo->hfinfo->abbrev,
e->repr = fvalue_to_string_repr(&(finfo->value), FTREPR_DFILTER, NULL);
e->usable = TRUE;
g_hash_table_insert(fvt_cache,(void*)finfo->hfinfo->abbrev,e);
}
return FALSE;
}
void dfilter_macro_build_ftv_cache(void* tree_root) {
g_hash_table_foreach_remove(fvt_cache,free_value,NULL);
proto_tree_traverse_in_order(tree_root, fvt_cache_cb, NULL);
}
void dfilter_macro_foreach(dfilter_macro_cb_t cb, void* data) {
guint i;
for (i = 0; i < num_macros; i++) {
cb(&(macros[i]),data);
}
return;
}
static void macro_fprint(dfilter_macro_t* m, void* ud) {
FILE* f = ud;
fprintf(f,"%s\t%s\n",m->name,m->text);
}
void dfilter_macro_save(const gchar* filename, gchar** error) {
FILE* f = ws_fopen(filename,"w");
if (!f) {
*error = ep_strdup_printf("Could not open file: '%s', error: %s\n", filename, strerror(errno) );
return;
}
dfilter_macro_foreach(macro_fprint, f);
fclose(f);
return;
}
#ifdef DUMP_MACROS
static void macro_dump(dfilter_macro_t* m _U_, void* ud _U_) {
gchar** part = m->parts;
int* args_pos = m->args_pos;
printf("\n->%s\t%s\t%d [%d]\n\t'%s'\n",
m->name, m->text, m->argc, m->usable, *(part++));
while (*part) {
printf("\t$%d '%s'\n",*args_pos,*part);
args_pos++;
part++;
}
}
#else
#define macro_dump(a,b)
#endif
void dfilter_macro_dump(void) {
#ifdef DUMP_MACROS
dfilter_macro_foreach(macro_dump, NULL);
#endif
}
static gchar* dfilter_macro_resolve(gchar* name, gchar** args, const gchar** error) {
GString* text;
int argc = 0;
dfilter_macro_t* m = NULL;
fvt_cache_entry_t* e;
int* arg_pos_p;
gchar** parts;
gchar* ret;
guint i;
for (i = 0; i < num_macros; i++) {
dfilter_macro_t* c = &(macros[i]);
if ( c->usable && g_str_equal(c->name,name) ) {
m = c;
break;
}
}
if (!m) {
if (fvt_cache && (e = g_hash_table_lookup(fvt_cache,name) )) {
if(e->usable) {
return e->repr;
} else {
*error = ep_strdup_printf("macro '%s' is unusable", name);
return NULL;
}
} else {
*error = ep_strdup_printf("macro '%s' does not exist", name);
return NULL;
}
}
DUMP_MACRO(m);
if (args) {
while(args[argc]) argc++;
}
if (argc != m->argc) {
*error = ep_strdup_printf("wrong number of arguments for macro '%s', expecting %d instead of %d",
name, m->argc, argc);
return NULL;
}
arg_pos_p = m->args_pos;
parts = m->parts;
text = g_string_new(*(parts++));
if (args) {
while (*parts) {
g_string_append_printf(text,"%s%s",
args[*(arg_pos_p++)],
*(parts++));
}
}
ret = ep_strdup(text->str);
g_string_free(text,TRUE);
return ret;
}
gchar* dfilter_macro_apply(const gchar* text, guint depth, const gchar** error) {
enum { OUTSIDE, STARTING, NAME, ARGS } state = OUTSIDE;
GString* out;
GString* name = NULL;
GString* arg = NULL;
GPtrArray* args = NULL;
gchar c;
const gchar* r = text;
gboolean changed = FALSE;
if ( depth > 31) {
*error = "too much nesting in macros";
return NULL;
}
#define FGS(n) if (n) g_string_free(n,TRUE); n = NULL
#define FREE_ALL() \
do { \
FGS(name); \
FGS(arg); \
if (args) { \
while(args->len) { void* p = g_ptr_array_remove_index_fast(args,0); if (p) g_free(p); } \
g_ptr_array_free(args,TRUE); \
args = NULL; } } while(0)
*error = NULL;
out = g_string_sized_new(64);
while(1) {
c = *r++;
switch(state) {
case OUTSIDE: {
switch(c) {
case '\0': {
goto finish;
} case '$': {
state = STARTING;
break;
} default: {
g_string_append_c(out,c);
break;
}
}
break;
} case STARTING: {
switch (c) {
case '{': {
args = g_ptr_array_new();
arg = g_string_sized_new(32);
name = g_string_sized_new(32);
state = NAME;
break;
} case '\0': {
g_string_append_c(out,'$');
goto finish;
} default: {
g_string_append_c(out,'$');
g_string_append_c(out,c);
state = OUTSIDE;
break;
}
}
break;
} case NAME: {
if ( isalnum((int)c) || c == '_' || c == '-' || c == '.' ) {
g_string_append_c(name,c);
} else if ( c == ':') {
state = ARGS;
} else if ( c == '}') {
gchar* resolved;
g_ptr_array_add(args,NULL);
resolved = dfilter_macro_resolve(name->str, (gchar**)args->pdata, error);
if (*error) goto on_error;
changed = TRUE;
g_string_append(out,resolved);
FREE_ALL();
state = OUTSIDE;
} else if ( c == '\0') {
*error = "end of filter in the middle of a macro expression";
goto on_error;
} else {
*error = "invalid char in macro name";
goto on_error;
}
break;
} case ARGS: {
switch(c) {
case '\0': {
*error = "end of filter in the middle of a macro expression";
goto on_error;
} case ';': {
g_ptr_array_add(args,arg->str);
g_string_free(arg,FALSE);
arg = g_string_sized_new(32);
break;
} case '\\': {
c = *r++;
if (c) {
g_string_append_c(arg,c);
break;
} else {
*error = "end of filter in the middle of a macro expression";
goto on_error;
}
} default: {
g_string_append_c(arg,c);
break;
} case '}': {
gchar* resolved;
g_ptr_array_add(args,arg->str);
g_ptr_array_add(args,NULL);
g_string_free(arg,FALSE);
arg = NULL;
resolved = dfilter_macro_resolve(name->str, (gchar**)args->pdata, error);
if (*error) goto on_error;
changed = TRUE;
g_string_append(out,resolved);
FREE_ALL();
state = OUTSIDE;
break;
}
}
break;
}
}
}
finish:
{
FREE_ALL();
if (changed) {
gchar* resolved = dfilter_macro_apply(out->str, depth++, error);
g_string_free(out,TRUE);
return (*error) ? NULL : resolved;
} else {
gchar* out_str = ep_strdup(out->str);
g_string_free(out,TRUE);
return out_str;
}
}
on_error:
{
FREE_ALL();
if (! *error) *error = "unknown error in macro expression";
g_string_free(out,TRUE);
return NULL;
}
}
static void macro_update(void* mp, const gchar** error) {
dfilter_macro_t* m = mp;
GPtrArray* parts;
GArray* args_pos;
const gchar* r;
gchar* w;
gchar* part;
int argc = 0;
guint i;
DUMP_MACRO(m);
*error = NULL;
for (i = 0; i < num_macros; i++) {
if (m == &(macros[i])) continue;
if ( g_str_equal(m->name,macros[i].name) ) {
*error = ep_strdup_printf("macro '%s' exists already", m->name);
m->usable = FALSE;
return;
}
}
parts = g_ptr_array_new();
args_pos = g_array_new(FALSE,FALSE,sizeof(int));
m->priv = part = w = g_strdup(m->text);
r = m->text;
g_ptr_array_add(parts,part);
while (r && *r) {
switch (*r) {
default:
*(w++) = *(r++);
break;
case '\0':
*(w++) = *(r++);
goto done;
case '\\':
*(w++) = *(++r);
r++;
break;
case '$': {
int cnt = 0;
int arg_pos = 0;
do {
char c = *(r+1);
if (c >= '0' && c <= '9') {
cnt++;
r++;
*(w++) = '\0';
arg_pos *= 10;
arg_pos += c - '0';
} else {
break;
}
} while(*r);
if (cnt) {
*(w++) = '\0';
r++;
argc = argc < arg_pos ? arg_pos : argc;
arg_pos--;
g_array_append_val(args_pos,arg_pos);
g_ptr_array_add(parts,w);
} else {
*(w++) = *(r++);
}
break;
}
}
}
done:
g_ptr_array_add(parts,NULL);
if (m->parts) g_free(m->parts);
m->parts = (gchar**)parts->pdata;
if (m->args_pos) g_free(m->args_pos);
m->args_pos = (int*)(void *)args_pos->data;
g_ptr_array_free(parts,FALSE);
g_array_free(args_pos,FALSE);
m->argc = argc;
m->usable = TRUE;
macro_dump(m,NULL);
DUMP_MACRO(m);
return;
}
static void macro_free(void* r) {
dfilter_macro_t* m = r;
DUMP_MACRO(r);
g_free(m->name);
g_free(m->text);
g_free(m->priv);
g_free(m->parts);
g_free(m->args_pos);
}
static void* macro_copy(void* dest, const void* orig, unsigned len _U_) {
dfilter_macro_t* d = dest;
const dfilter_macro_t* m = orig;
DUMP_MACRO(m);
d->name = g_strdup(m->name);
d->text = g_strdup(m->text);
d->usable = m->usable;
if (m->parts) {
guint nparts = 0;
/*
* Copy the contents of m->priv (a "cooked" version
* of m->text) into d->priv.
*
* First we clone m->text into d->priv, this gets
* us a NUL terminated string of the proper length.
*
* Then we loop copying bytes from m->priv into
* d-priv. Since m->priv contains internal ACSII NULs
* we use the length of m->text to stop the copy.
*/
d->priv = g_strdup(m->text);
{
const gchar* oldText = m->text;
const gchar* oldPriv = m->priv;
gchar* newPriv = d->priv;
while(oldText && *oldText) {
*(newPriv++) = *(oldPriv++);
oldText++;
}
}
/*
* The contents of the m->parts array contains pointers
* into various sections of m->priv. Since it's
* an argv style array of ponters, this array is
* actually one larger than the number of parts
* to hold the final NULL terminator.
*
* The following copy clones the original m->parts
* array into d->parts but then fixes-up the pointers
* so that they point into the appropriate sections
* of the d->priv.
*/
do nparts++; while (m->parts[nparts]);
d->parts = g_memdup(m->parts,(nparts+1)*sizeof(void*));
nparts = 0;
while(m->parts[nparts]) {
if(nparts) {
d->parts[nparts] = d->parts[nparts - 1] + (m->parts[nparts] - m->parts[nparts - 1]);
} else {
d->parts[nparts] = d->priv;
}
nparts++;
}
/*
* Clone the contents of m->args_pos into d->args_pos.
*/
d->args_pos = g_memdup(m->args_pos,(--nparts)*sizeof(int));
}
DUMP_MACRO(d);
return d;
}
static gboolean macro_name_chk(void* r _U_, const char* in_name, unsigned name_len, void* u1 _U_, void* u2 _U_, const char** error) {
guint i;
if (name_len == 0) {
*error = "invalid name";
return FALSE;
}
for (i=0; i < name_len; i++) {
if (!(in_name[i] == '_' || isalnum((guchar)in_name[i]) ) ) {
*error = "invalid char in name";
return FALSE;
}
}
return TRUE;
}
UAT_CSTRING_CB_DEF(macro,name,dfilter_macro_t)
UAT_CSTRING_CB_DEF(macro,text,dfilter_macro_t)
void dfilter_macro_init(void) {
static uat_field_t uat_fields[] = {
UAT_FLD_CSTRING_OTHER(macro,name,macro_name_chk,"The name of the macro."),
UAT_FLD_CSTRING_ISPRINT(macro,text,"The text this macro resolves to."),
UAT_END_FIELDS
};
dfilter_macro_uat = uat_new("Display Filter Macros",
sizeof(dfilter_macro_t),
DFILTER_MACRO_FILENAME,
TRUE,
(void*) &macros,
&num_macros,
NULL,
"ChDisplayFilterMacrosSection",
macro_copy,
macro_update,
macro_free,
uat_fields);
fvt_cache = g_hash_table_new(g_str_hash,g_str_equal);
}
void dfilter_macro_get_uat(void** p) {
*p = dfilter_macro_uat;
}
#ifdef DUMP_DFILTER_MACRO
/*
* The dfilter_macro_t has several characteristics that are
* not immediattly obvious. The dump_dfilter_filter_macro_t()
* function can be used to help "visualize" the contents of
* a dfilter_macro_t.
*
* Some non-obvious components of this struct include:
*
* m->parts is an argv style array of pointers into the
* m->priv string.
*
* The last pointer of an m->parts array should contain
* NULL to indicate the end of the parts pointer array.
*
* m->priv is a "cooked" copy of the m->text string.
* Any variable substitution indicators within m->text
* ("$1", "$2", ...) will have been replaced with ASCII
* NUL characters within m->priv.
*
* The first element of m->parts array (m-parts[0]) will
* usually have the same pointer value as m->priv (unless
* the dfilter-macro starts off with a variable
* substitution indicator (e.g. "$1").
*/
void dump_dfilter_macro_t(const dfilter_macro_t *m, const char *function, const char *file, int line)
{
printf("\n<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
if(m == NULL) {
printf(" dfilter_macro_t * == NULL! (via: %s(): %s:%d)\n", function, file, line);
printf("\n>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
}
printf("DUMP of dfilter_macro_t: %p (via: %s(): %s:%d)\n", m, function, file, line);
printf(" &dfilter_macro->name == %p\n", &m->name);
if(m->name == NULL) {
printf(" ->name == NULL\n");
} else {
printf(" ->name == %p\n", m->name);
printf(" ->name == <%s>\n", m->name);
}
printf(" &dfilter_macro->text == %p\n", &m->text);
if(m->text == NULL) {
printf(" ->text == NULL\n");
} else {
printf(" ->text == %p\n", m->text);
printf(" ->text == <%s>\n", m->text);
}
printf(" &dfilter_macro->usable == %p\n", &m->usable);
printf(" ->usable == %u\n", m->usable);
printf(" &dfilter_macro->parts == %p\n", &m->parts);
if(m->parts == NULL) {
printf(" ->parts == NULL\n");
} else {
int i = 0;
while (m->parts[i]) {
printf(" ->parts[%d] == %p\n", i, m->parts[i]);
printf(" ->parts[%d] == <%s>\n", i, m->parts[i]);
i++;
}
printf(" ->parts[%d] == NULL\n", i);
}
printf(" &dfilter_macro->args_pos == %p\n", &m->args_pos);
if(m->args_pos == NULL) {
printf(" ->args_pos == NULL\n");
} else {
printf(" ->args_pos == %p\n", m->args_pos);
/*printf(" ->args_pos == <%?>\n", m->args_pos);*/
}
printf(" &dfilter_macro->argc == %p\n", &m->argc);
printf(" ->argc == %d\n", m->argc);
printf(" &dfilter_macro->priv == %p\n", &m->priv);
if(m->priv == NULL) {
printf(" ->priv == NULL\n");
} else {
printf(" ->priv == %p\n", m->priv);
printf(" ->priv == <%s>\n", (char *)m->priv);
}
printf("\n>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
}
#endif