wireshark/epan/stats_tree.c
Michael Mann 2657071e88 Don't allow invalid ranges to be specified for the stats tree. Bug 9130 (https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=9130)
Not sure which memory allocation should be used here (using wmem caused crash), but this revision can at least be easily backported to 1.10 where the bug was reported.

Also allow a single number to be used in the stats range since it's considered a valid "range" by the UAT.

svn path=/trunk/; revision=52679
2013-10-18 21:17:01 +00:00

797 lines
19 KiB
C

/* stats_tree.c
* API for a counter tree for Wireshark
* 2004, Luis E. G. Ontanon
*
* $Id$
*
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@wireshark.org>
* Copyright 1998 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config.h"
#include <glib.h>
#include <epan/stats_tree_priv.h>
#include <string.h>
#include "stats_tree.h"
/*
TODO:
- sort out the sorting issue
*/
/* used to contain the registered stat trees */
static GHashTable *registry = NULL;
/* writes into the buffers pointed by value, rate and percent
the string representations of a node*/
extern void
stats_tree_get_strs_from_node(const stat_node *node, gchar *value, gchar *rate, gchar *percent)
{
float f;
if (value) g_snprintf(value,NUM_BUF_SIZE,"%u",node->counter);
if (rate) {
*rate = '\0';
if (node->st->elapsed > 0.0) {
f = ((float)node->counter) / (float)node->st->elapsed;
g_snprintf(rate,NUM_BUF_SIZE,"%f",f);
}
}
if (percent) {
*percent = '\0';
if (node->parent->counter > 0) {
f = (float)(((float)node->counter * 100.0) / node->parent->counter);
g_snprintf(percent,NUM_BUF_SIZE,"%.2f%%",f);
}
}
}
/* a text representation of a node
if buffer is NULL returns a newly allocated string */
extern gchar*
stats_tree_node_to_str(const stat_node *node, gchar *buffer, guint len)
{
if (buffer) {
g_snprintf(buffer,len,"%s: %i",node->name, node->counter);
return buffer;
} else {
return g_strdup_printf("%s: %i",node->name, node->counter);
}
}
extern guint
stats_tree_branch_max_namelen(const stat_node *node, guint indent)
{
stat_node *child;
guint maxlen = 0;
guint len;
indent = indent > INDENT_MAX ? INDENT_MAX : indent;
if (node->children) {
for (child = node->children; child; child = child->next ) {
len = stats_tree_branch_max_namelen(child,indent+1);
maxlen = len > maxlen ? len : maxlen;
}
}
len = (guint) strlen(node->name) + indent;
maxlen = len > maxlen ? len : maxlen;
return maxlen;
}
static gchar *format;
/* populates the given GString with a tree representation of a branch given by node,
using indent spaces as initial indentation */
extern void
stats_tree_branch_to_str(const stat_node *node, GString *s, guint indent)
{
stat_node *child;
static gchar indentation[INDENT_MAX+1];
static gchar value[NUM_BUF_SIZE];
static gchar rate[NUM_BUF_SIZE];
static gchar percent[NUM_BUF_SIZE];
guint i = 0;
if (indent == 0) {
format = g_strdup_printf(" %%s%%-%us%%12s %%12s %%12s\n",stats_tree_branch_max_namelen(node,0));
}
stats_tree_get_strs_from_node(node, value, rate, percent);
indent = indent > INDENT_MAX ? INDENT_MAX : indent;
/* fill indentation with indent spaces */
if (indent > 0) {
while(i<indent)
indentation[i++] = ' ';
}
indentation[i] = '\0';
g_string_append_printf(s,format,
indentation,node->name,value,rate,percent);
if (node->children) {
for (child = node->children; child; child = child->next ) {
stats_tree_branch_to_str(child,s,indent+1);
}
}
if (indent == 0) {
g_free(format);
}
}
/* frees the resources allocated by a stat_tree node */
static void
free_stat_node(stat_node *node)
{
stat_node *child;
stat_node *next;
if (node->children) {
for (child = node->children; child; child = next ) {
/* child->next will be gone after free_stat_node, so cache it here */
next = child->next;
free_stat_node(child);
}
}
if(node->st->cfg->free_node_pr) node->st->cfg->free_node_pr(node);
if (node->hash) g_hash_table_destroy(node->hash);
g_free(node->rng);
g_free(node->name);
g_free(node);
}
/* destroys the whole tree instance */
extern void
stats_tree_free(stats_tree *st)
{
stat_node *child;
stat_node *next;
g_free(st->filter);
g_hash_table_destroy(st->names);
g_ptr_array_free(st->parents,TRUE);
for (child = st->root.children; child; child = next ) {
/* child->next will be gone after free_stat_node, so cache it here */
next = child->next;
free_stat_node(child);
}
if (st->cfg->free_tree_pr)
st->cfg->free_tree_pr(st);
if (st->cfg->cleanup)
st->cfg->cleanup(st);
g_free(st);
}
/* reset a node to its original state */
static void
reset_stat_node(stat_node *node)
{
stat_node *child;
if (node->children) {
for (child = node->children; child; child = child->next )
reset_stat_node(child);
}
node->counter = 0;
if(node->st->cfg->reset_node) {
node->st->cfg->reset_node(node);
}
}
/* reset the whole stats_tree */
extern void
stats_tree_reset(void *p)
{
stats_tree *st = (stats_tree *)p;
st->start = -1.0;
st->elapsed = 0.0;
reset_stat_node(&st->root);
if (st->cfg->reset_tree) {
st->cfg->reset_tree(st);
}
}
extern void
stats_tree_reinit(void *p)
{
stats_tree *st = (stats_tree *)p;
stat_node *child;
stat_node *next;
for (child = st->root.children; child; child = next) {
/* child->next will be gone after free_stat_node, so cache it here */
next = child->next;
free_stat_node(child);
}
st->root.children = NULL;
st->root.counter = 0;
if (st->cfg->init) {
st->cfg->init(st);
}
}
/* register a new stats_tree */
extern void
stats_tree_register_with_group(const char *tapname, const char *abbr, const char *name,
guint flags,
stat_tree_packet_cb packet, stat_tree_init_cb init,
stat_tree_cleanup_cb cleanup, register_stat_group_t stat_group)
{
stats_tree_cfg *cfg = (stats_tree_cfg *)g_malloc( sizeof(stats_tree_cfg) );
/* at the very least the abbrev and the packet function should be given */
g_assert( tapname && abbr && packet );
cfg->plugin = FALSE;
cfg->tapname = g_strdup(tapname);
cfg->abbr = g_strdup(abbr);
cfg->name = name ? g_strdup(name) : g_strdup(abbr);
cfg->stat_group = stat_group;
cfg->packet = packet;
cfg->init = init;
cfg->cleanup = cleanup;
cfg->flags = flags;
/* these have to be filled in by implementations */
cfg->setup_node_pr = NULL;
cfg->new_tree_pr = NULL;
cfg->free_node_pr = NULL;
cfg->free_tree_pr = NULL;
cfg->draw_node = NULL;
cfg->draw_tree = NULL;
cfg->reset_node = NULL;
cfg->reset_tree = NULL;
if (!registry) registry = g_hash_table_new(g_str_hash,g_str_equal);
g_hash_table_insert(registry,cfg->abbr,cfg);
}
/* register a new stats_tree with default group REGISTER_STAT_GROUP_UNSORTED */
extern void
stats_tree_register(const char *tapname, const char *abbr, const char *name,
guint flags,
stat_tree_packet_cb packet, stat_tree_init_cb init,
stat_tree_cleanup_cb cleanup)
{
stats_tree_register_with_group(tapname, abbr, name,
flags,
packet, init,
cleanup, REGISTER_STAT_GROUP_UNSORTED);
}
/* register a new stat_tree with default group REGISTER_STAT_GROUP_UNSORTED from a plugin */
extern void
stats_tree_register_plugin(const char *tapname, const char *abbr, const char *name,
guint flags,
stat_tree_packet_cb packet, stat_tree_init_cb init,
stat_tree_cleanup_cb cleanup)
{
stats_tree_cfg *cfg;
stats_tree_register(tapname, abbr, name,
flags,
packet, init,
cleanup);
cfg = stats_tree_get_cfg_by_abbr((char*)abbr);
cfg->plugin = TRUE;
}
extern stats_tree*
stats_tree_new(stats_tree_cfg *cfg, tree_pres *pr, const char *filter)
{
stats_tree *st = (stats_tree *)g_malloc(sizeof(stats_tree));
st->cfg = cfg;
st->pr = pr;
st->names = g_hash_table_new(g_str_hash,g_str_equal);
st->parents = g_ptr_array_new();
st->filter = g_strdup(filter);
st->start = -1.0;
st->elapsed = 0.0;
st->root.counter = 0;
st->root.name = g_strdup(cfg->name);
st->root.st = st;
st->root.parent = NULL;
st->root.children = NULL;
st->root.next = NULL;
st->root.hash = NULL;
st->root.pr = NULL;
g_ptr_array_add(st->parents,&st->root);
return st;
}
/* will be the tap packet cb */
extern int
stats_tree_packet(void *p, packet_info *pinfo, epan_dissect_t *edt, const void *pri)
{
stats_tree *st = (stats_tree *)p;
double now = nstime_to_msec(&pinfo->rel_ts);
if (st->start < 0.0) st->start = now;
st->elapsed = now - st->start;
if (st->cfg->packet)
return st->cfg->packet(st,pinfo,edt,pri);
else
return 0;
}
extern stats_tree_cfg*
stats_tree_get_cfg_by_abbr(char *abbr)
{
return (stats_tree_cfg *)g_hash_table_lookup(registry,abbr);
}
extern GList*
stats_tree_get_cfg_list(void)
{
return g_hash_table_get_values(registry);
}
struct _stats_tree_pres_cbs {
void (*setup_node_pr)(stat_node*);
void (*free_node_pr)(stat_node*);
void (*draw_node)(stat_node*);
void (*reset_node)(stat_node*);
tree_pres *(*new_tree_pr)(stats_tree*);
void (*free_tree_pr)(stats_tree*);
void (*draw_tree)(stats_tree*);
void (*reset_tree)(stats_tree*);
};
static void
setup_tree_presentation(gpointer k _U_, gpointer v, gpointer p)
{
stats_tree_cfg *cfg = (stats_tree_cfg *)v;
struct _stats_tree_pres_cbs *d = (struct _stats_tree_pres_cbs *)p;
cfg->in_use = FALSE;
cfg->setup_node_pr = d->setup_node_pr;
cfg->new_tree_pr = d->new_tree_pr;
cfg->free_node_pr = d->free_node_pr;
cfg->free_tree_pr = d->free_tree_pr;
cfg->draw_node = d->draw_node;
cfg->draw_tree = d->draw_tree;
cfg->reset_node = d->reset_node;
cfg->reset_tree = d->reset_tree;
}
extern void
stats_tree_presentation(void (*registry_iterator)(gpointer,gpointer,gpointer),
void (*setup_node_pr)(stat_node*),
void (*free_node_pr)(stat_node*),
void (*draw_node)(stat_node*),
void (*reset_node)(stat_node*),
tree_pres *(*new_tree_pr)(stats_tree*),
void (*free_tree_pr)(stats_tree*),
void (*draw_tree)(stats_tree*),
void (*reset_tree)(stats_tree*),
void *data)
{
static struct _stats_tree_pres_cbs d;
d.setup_node_pr = setup_node_pr;
d.new_tree_pr = new_tree_pr;
d.free_node_pr = free_node_pr;
d.free_tree_pr = free_tree_pr;
d.draw_node = draw_node;
d.draw_tree = draw_tree;
d.reset_node = reset_node;
d.reset_tree = reset_tree;
if (registry) g_hash_table_foreach(registry,setup_tree_presentation,&d);
if (registry_iterator && registry)
g_hash_table_foreach(registry,registry_iterator,data);
}
/* creates a stat_tree node
* name: the name of the stats_tree node
* parent_name: the name of the ALREADY REGISTERED parent
* with_hash: whether or not it should keep a hash with its children names
* as_named_node: whether or not it has to be registered in the root namespace
*/
static stat_node*
new_stat_node(stats_tree *st, const gchar *name, int parent_id,
gboolean with_hash, gboolean as_parent_node)
{
stat_node *node = (stat_node *)g_malloc (sizeof(stat_node));
stat_node *last_chld = NULL;
node->counter = 0;
node->name = g_strdup(name);
node->children = NULL;
node->next = NULL;
node->st = (stats_tree*) st;
node->hash = with_hash ? g_hash_table_new(g_str_hash,g_str_equal) : NULL;
node->parent = NULL;
node->rng = NULL;
if (as_parent_node) {
g_hash_table_insert(st->names,
node->name,
node);
g_ptr_array_add(st->parents,node);
node->id = st->parents->len - 1;
} else {
node->id = -1;
}
if (parent_id >= 0 && parent_id < (int) st->parents->len ) {
node->parent = (stat_node *)g_ptr_array_index(st->parents,parent_id);
} else {
/* ??? should we set the parent to be root ??? */
g_assert_not_reached();
}
if (node->parent->children) {
/* insert as last child */
for (last_chld = node->parent->children;
last_chld->next;
last_chld = last_chld->next ) ;
last_chld->next = node;
} else {
/* insert as first child */
node->parent->children = node;
}
if(node->parent->hash) {
g_hash_table_insert(node->parent->hash,node->name,node);
}
if (st->cfg->setup_node_pr) {
st->cfg->setup_node_pr(node);
} else {
node->pr = NULL;
}
return node;
}
/***/
extern int
stats_tree_create_node(stats_tree *st, const gchar *name, int parent_id, gboolean with_hash)
{
stat_node *node = new_stat_node(st,name,parent_id,with_hash,TRUE);
if (node)
return node->id;
else
return 0;
}
/* XXX: should this be a macro? */
extern int
stats_tree_create_node_by_pname(stats_tree *st, const gchar *name,
const gchar *parent_name, gboolean with_children)
{
return stats_tree_create_node(st,name,stats_tree_parent_id_by_name(st,parent_name),with_children);
}
/*
* Increases by delta the counter of the node whose name is given
* if the node does not exist yet it's created (with counter=1)
* using parent_name as parent node.
* with_hash=TRUE to indicate that the created node will have a parent
*/
extern int
stats_tree_manip_node(manip_node_mode mode, stats_tree *st, const char *name,
int parent_id, gboolean with_hash, gint value)
{
stat_node *node = NULL;
stat_node *parent = NULL;
g_assert( parent_id >= 0 && parent_id < (int) st->parents->len );
parent = (stat_node *)g_ptr_array_index(st->parents,parent_id);
if( parent->hash ) {
node = (stat_node *)g_hash_table_lookup(parent->hash,name);
} else {
node = (stat_node *)g_hash_table_lookup(st->names,name);
}
if ( node == NULL )
node = new_stat_node(st,name,parent_id,with_hash,with_hash);
switch (mode) {
case MN_INCREASE: node->counter += value; break;
case MN_SET: node->counter = value; break;
}
if (node)
return node->id;
else
return -1;
}
extern char*
stats_tree_get_abbr(const char *opt_arg)
{
guint i;
/* XXX: this fails when tshark is given any options
after the -z */
g_assert(opt_arg != NULL);
for (i=0; opt_arg[i] && opt_arg[i] != ','; i++);
if (opt_arg[i] == ',') {
return g_strndup(opt_arg,i);
} else {
return NULL;
}
}
/*
* This function accepts an input string which should define a long integer range.
* The normal result is a struct containing the floor and ceil value of this
* range.
*
* It is allowed to define a range string in the following ways :
*
* "0-10" -> { 0, 10 }
* "-0" -> { G_MININT, 0 }
* "0-" -> { 0, G_MAXINT }
* "-" -> { G_MININT, G_MAXINT }
*
* Note that this function is robust to buggy input string. If in some cases it
* returns NULL, it but may also return a pair with undefined values.
*
*/
static range_pair_t*
get_range(char *rngstr)
{
gchar **split;
range_pair_t *rng;
split = g_strsplit((gchar*)rngstr,"-",2);
/* empty string */
if (split[0] == NULL) {
g_strfreev(split);
return NULL;
}
rng = (range_pair_t *)g_malloc(sizeof(range_pair_t));
if (split[1] == NULL) {
/* means we have a non empty string with no delimiter
* so it must be a single number */
rng->floor = (gint)strtol(split[0],NULL,10);
rng->ceil = rng->floor;
} else {
/* string == "X-?" */
if (*(split[0]) != '\0') {
rng->floor = (gint)strtol(split[0],NULL,10);
} else
/* string == "-?" */
rng->floor = G_MININT;
/* string != "?-" */
if (*(split[1]) != '\0') {
rng->ceil = (gint)strtol(split[1],NULL,10);
} else
/* string == "?-" */
rng->ceil = G_MAXINT;
}
g_strfreev(split);
return rng;
}
extern int
stats_tree_create_range_node(stats_tree *st, const gchar *name, int parent_id, ...)
{
va_list list;
gchar *curr_range;
stat_node *rng_root = new_stat_node(st, name, parent_id, FALSE, TRUE);
stat_node *range_node = NULL;
va_start( list, parent_id );
while (( curr_range = va_arg(list, gchar*) )) {
range_node = new_stat_node(st, curr_range, rng_root->id, FALSE, FALSE);
range_node->rng = get_range(curr_range);
}
va_end( list );
return rng_root->id;
}
extern int
stats_tree_create_range_node_string(stats_tree *st, const gchar *name,
int parent_id, int num_str_ranges,
gchar** str_ranges)
{
int i;
stat_node *rng_root = new_stat_node(st, name, parent_id, FALSE, TRUE);
stat_node *range_node = NULL;
for (i = 0; i < num_str_ranges; i++) {
range_node = new_stat_node(st, str_ranges[i], rng_root->id, FALSE, FALSE);
range_node->rng = get_range(str_ranges[i]);
}
return rng_root->id;
}
/****/
extern int
stats_tree_parent_id_by_name(stats_tree *st, const gchar *parent_name)
{
stat_node *node = (stat_node *)g_hash_table_lookup(st->names,parent_name);
if (node)
return node->id;
else
return 0; /* XXX: this is the root shoud we return -1 instead?*/
}
extern int
stats_tree_range_node_with_pname(stats_tree *st, const gchar *name,
const gchar *parent_name, ...)
{
va_list list;
gchar *curr_range;
stat_node *range_node = NULL;
int parent_id = stats_tree_parent_id_by_name(st,parent_name);
stat_node *rng_root = new_stat_node(st, name, parent_id, FALSE, TRUE);
va_start( list, parent_name );
while (( curr_range = va_arg(list, gchar*) )) {
range_node = new_stat_node(st, curr_range, rng_root->id, FALSE, FALSE);
range_node->rng = get_range(curr_range);
}
va_end( list );
return rng_root->id;
}
extern int
stats_tree_tick_range(stats_tree *st, const gchar *name, int parent_id,
int value_in_range)
{
stat_node *node = NULL;
stat_node *parent = NULL;
stat_node *child = NULL;
gint floor, ceil;
if (parent_id >= 0 && parent_id < (int) st->parents->len) {
parent = (stat_node *)g_ptr_array_index(st->parents,parent_id);
} else {
g_assert_not_reached();
}
if( parent->hash ) {
node = (stat_node *)g_hash_table_lookup(parent->hash,name);
} else {
node = (stat_node *)g_hash_table_lookup(st->names,name);
}
if ( node == NULL )
g_assert_not_reached();
for ( child = node->children; child; child = child->next) {
floor = child->rng->floor;
ceil = child->rng->ceil;
if ( value_in_range >= floor && value_in_range <= ceil ) {
child->counter++;
return node->id;
}
}
return node->id;
}
extern int
stats_tree_create_pivot(stats_tree *st, const gchar *name, int parent_id)
{
stat_node *node = new_stat_node(st,name,parent_id,TRUE,TRUE);
if (node)
return node->id;
else
return 0;
}
extern int
stats_tree_create_pivot_by_pname(stats_tree *st, const gchar *name,
const gchar *parent_name)
{
int parent_id = stats_tree_parent_id_by_name(st,parent_name);
stat_node *node;
node = new_stat_node(st,name,parent_id,TRUE,TRUE);
if (node)
return node->id;
else
return 0;
}
extern int
stats_tree_tick_pivot(stats_tree *st, int pivot_id, const gchar *pivot_value)
{
stat_node *parent = (stat_node *)g_ptr_array_index(st->parents,pivot_id);
parent->counter++;
stats_tree_manip_node( MN_INCREASE, st, pivot_value, pivot_id, FALSE, 1);
return pivot_id;
}