From 83ec54675c19f25cfbb2e8a8863c6ee2191d8b0c Mon Sep 17 00:00:00 2001 From: Gilbert Ramirez Date: Wed, 27 Sep 2000 04:55:05 +0000 Subject: First step in moving core Ethereal routines to libepan. svn path=/trunk/; revision=2458 --- epan/.cvsignore | 8 + epan/Makefile.am | 74 ++ epan/conversation.c | 362 +++++++ epan/conversation.h | 63 ++ epan/dfilter-grammar.y | 1186 +++++++++++++++++++++++ epan/dfilter-int.h | 145 +++ epan/dfilter-scanner.l | 366 +++++++ epan/dfilter.c | 1064 +++++++++++++++++++++ epan/dfilter.h | 70 ++ epan/epan.c | 41 + epan/epan.h | 49 + epan/except.c | 415 ++++++++ epan/except.h | 149 +++ epan/exceptions.h | 203 ++++ epan/pint.h | 115 +++ epan/plugins.c | 586 ++++++++++++ epan/plugins.h | 74 ++ epan/proto.c | 2480 ++++++++++++++++++++++++++++++++++++++++++++++++ epan/proto.h | 553 +++++++++++ epan/strutil.c | 222 +++++ epan/strutil.h | 46 + epan/tvbtest.c | 406 ++++++++ epan/tvbuff.c | 1144 ++++++++++++++++++++++ epan/tvbuff.h | 290 ++++++ 24 files changed, 10111 insertions(+) create mode 100644 epan/.cvsignore create mode 100644 epan/Makefile.am create mode 100644 epan/conversation.c create mode 100644 epan/conversation.h create mode 100644 epan/dfilter-grammar.y create mode 100644 epan/dfilter-int.h create mode 100644 epan/dfilter-scanner.l create mode 100644 epan/dfilter.c create mode 100644 epan/dfilter.h create mode 100644 epan/epan.c create mode 100644 epan/epan.h create mode 100644 epan/except.c create mode 100644 epan/except.h create mode 100644 epan/exceptions.h create mode 100644 epan/pint.h create mode 100644 epan/plugins.c create mode 100644 epan/plugins.h create mode 100644 epan/proto.c create mode 100644 epan/proto.h create mode 100644 epan/strutil.c create mode 100644 epan/strutil.h create mode 100644 epan/tvbtest.c create mode 100644 epan/tvbuff.c create mode 100644 epan/tvbuff.h (limited to 'epan') diff --git a/epan/.cvsignore b/epan/.cvsignore new file mode 100644 index 0000000000..d438f6b552 --- /dev/null +++ b/epan/.cvsignore @@ -0,0 +1,8 @@ +.deps +.libs +Makefile +Makefile.in +dfilter-grammar.c +dfilter-grammar.h +dfilter-scanner.c +tvbtest diff --git a/epan/Makefile.am b/epan/Makefile.am new file mode 100644 index 0000000000..651a204ece --- /dev/null +++ b/epan/Makefile.am @@ -0,0 +1,74 @@ +# Makefile.am +# Automake file for the EPAN library +# (Ethereal Protocol ANalyzer Library) +# +# $Id: Makefile.am,v 1.1 2000/09/27 04:54:46 gram Exp $ +# +# Ethereal - Network traffic analyzer +# By Gerald Combs +# 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + +# Any POSIX-compatible YACC should honor the -p flag +YFLAGS=-d -p dfilter_ + +# EPAN will eventually be a shared library. While I move source code around, +# however, it is an archive library. + +noinst_LIBRARIES = libepan.a + + +libepan_a_SOURCES = \ + conversation.c \ + conversation.h \ + dfilter-int.h \ + dfilter-grammar.y \ + dfilter-scanner.l \ + dfilter.c \ + dfilter.h \ + epan.c \ + epan.h \ + except.c \ + except.h \ + exception.h \ + pint.h \ + plugins.c \ + plugins.h \ + proto.c \ + proto.h \ + strutil.c \ + strutil.h \ + tvbtest.c \ + tvbuff.c \ + tvbuff.h + +EXTRA_libepan_a_SOURCES = \ + dfilter-grammar.c \ + dfilter-grammar.h \ + dfilter-scanner.c + +CLEANFILES = \ + libepan.a \ + *~ + +dfilter-scanner.c : dfilter-scanner.l + $(LEX) -Pdfilter_ -odfilter-scanner.c $(srcdir)/epan/dfilter-scanner.l + +tvbtest: tvbtest.o tvbuff.o except.o strutil.o + $(LINK) -o tvbtest tvbtest.o tvbuff.o except.o strutil.o `glib-config --libs` + diff --git a/epan/conversation.c b/epan/conversation.c new file mode 100644 index 0000000000..144f27b993 --- /dev/null +++ b/epan/conversation.c @@ -0,0 +1,362 @@ +/* conversation.c + * Routines for building lists of packets that are part of a "conversation" + * + * $Id: conversation.c,v 1.1 2000/09/27 04:54:47 gram Exp $ + * + * Ethereal - Network traffic analyzer + * By Gerald Combs + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +#ifdef HAVE_SYS_TYPES_H +# include +#endif + +#ifdef HAVE_NETINET_IN_H +# include +#endif + +#include +#include +#include "packet.h" +#include "conversation.h" + +static GHashTable *conversation_hashtable = NULL; +static GMemChunk *conversation_key_chunk = NULL; +static GMemChunk *conversation_chunk = NULL; + +typedef struct conversation_key { + struct conversation_key *next; + address src; + address dst; + port_type ptype; + guint32 port_src; + guint32 port_dst; +} conversation_key; + +/* + * Linked list of conversation keys, so we can, before freeing them all, + * free the address data allocations associated with them. + */ +static conversation_key *conversation_keys; + +static guint32 new_index; + +static int conversation_init_count = 200; + +/* + * Compare two conversation keys. + */ +static gint +conversation_equal(gconstpointer v, gconstpointer w) +{ + conversation_key *v1 = (conversation_key *)v; + conversation_key *v2 = (conversation_key *)w; + + if (v1->ptype != v2->ptype) + return 0; /* different types of port */ + + /* + * Are the first and second source ports the same, the first and + * second destination ports the same, the first and second source + * addresses the same, and the first and second destination + * addresses the same? + */ + if (v1->port_src == v2->port_src && + v1->port_dst == v2->port_dst && + v1->src.type == v2->src.type && + v1->src.len == v2->src.len && + memcmp(v1->src.data, v2->src.data, v1->src.len) == 0 && + v1->dst.type == v2->dst.type && + v1->dst.len == v2->dst.len && + memcmp(v1->dst.data, v2->dst.data, v1->dst.len) == 0) { + /* + * Yes. It's the same conversation, and the two + * address/port pairs are going in the same direction. + */ + return 1; + } + + /* + * Is the first source port the same as the second destination + * port, the first destination port the same as the first + * source port, the first source address the same as the second + * destination address, and the first destination address the + * same as the second source address? + */ + if (v1->port_src == v2->port_dst && + v1->port_dst == v2->port_src && + v1->src.type == v2->dst.type && + v1->src.len == v2->dst.len && + memcmp(v1->src.data, v2->dst.data, v1->src.len) == 0 && + v1->dst.type == v2->src.type && + v1->dst.len == v2->src.len && + memcmp(v1->dst.data, v2->src.data, v1->dst.len) == 0) { + /* + * Yes. It's the same conversation, and the two + * address/port pairs are going in opposite directions. + */ + return 1; + } + + /* + * The addresses or the ports don't match. + */ + return 0; +} + +/* + * Compute the hash value for a given set of source and destination + * addresses and ports. + */ +static guint +conversation_hash(gconstpointer v) +{ + conversation_key *key = (conversation_key *)v; + guint hash_val; + int i; + + hash_val = 0; + for (i = 0; i < key->src.len; i++) + hash_val += key->src.data[i]; + for (i = 0; i < key->dst.len; i++) + hash_val += key->dst.data[i]; + hash_val += key->port_src + key->port_dst; + + return hash_val; +} + +/* + * Initialize some variables every time a file is loaded or re-loaded. + * Destroy all existing conversations, and create a new hash table + * for the conversations in the new file. + */ +void +conversation_init(void) +{ + conversation_key *key; + + /* + * Free the addresses associated with the conversation keys. + */ + for (key = conversation_keys; key != NULL; key = key->next) { + /* + * Grr. I guess the theory here is that freeing + * something sure as heck modifies it, so you + * want to ban attempts to free it, but, alas, + * if we make the "data" field of an "address" + * structure not a "const", the compiler whines if + * we try to make it point into the data for a packet, + * as that's a "const" array (and should be, as dissectors + * shouldn't trash it). + * + * So we cast the complaint into oblivion, and rely on + * the fact that these addresses are known to have had + * their data mallocated, i.e. they don't point into, + * say, the middle of the data for a packet. + */ + g_free((gpointer)key->src.data); + g_free((gpointer)key->dst.data); + } + conversation_keys = NULL; + if (conversation_hashtable != NULL) + g_hash_table_destroy(conversation_hashtable); + if (conversation_key_chunk != NULL) + g_mem_chunk_destroy(conversation_key_chunk); + if (conversation_chunk != NULL) + g_mem_chunk_destroy(conversation_chunk); + + conversation_hashtable = g_hash_table_new(conversation_hash, + conversation_equal); + conversation_key_chunk = g_mem_chunk_new("conversation_key_chunk", + sizeof(conversation_key), + conversation_init_count * sizeof(struct conversation_key), + G_ALLOC_AND_FREE); + conversation_chunk = g_mem_chunk_new("conversation_chunk", + sizeof(conversation_t), + conversation_init_count * sizeof(conversation_t), + G_ALLOC_AND_FREE); + + /* + * Start the conversation indices over at 0. + */ + new_index = 0; +} + +/* + * Copy an address, allocating a new buffer for the address data. + */ +static void +copy_address(address *to, address *from) +{ + guint8 *data; + + to->type = from->type; + to->len = from->len; + data = g_malloc(from->len); + memcpy(data, from->data, from->len); + to->data = data; +} + +/* + * Given source and destination addresses and ports for a packet, + * create a new conversation to contain packets between those address/port + * pairs. + */ +conversation_t * +conversation_new(address *src, address *dst, port_type ptype, + guint32 src_port, guint32 dst_port, void *data) +{ + conversation_t *conversation; + conversation_key *new_key; + + new_key = g_mem_chunk_alloc(conversation_key_chunk); + new_key->next = conversation_keys; + conversation_keys = new_key; + copy_address(&new_key->src, src); + copy_address(&new_key->dst, dst); + new_key->ptype = ptype; + new_key->port_src = src_port; + new_key->port_dst = dst_port; + + conversation = g_mem_chunk_alloc(conversation_chunk); + conversation->index = new_index; + conversation->data = data; + +/* clear dissector pointer */ + conversation->dissector.new_d = NULL; + + new_index++; + + g_hash_table_insert(conversation_hashtable, new_key, conversation); + return conversation; +} + +/* + * Given source and destination addresses and ports for a packet, + * search for a conversation containing packets between those address/port + * pairs. Returns NULL if not found. + */ +conversation_t * +find_conversation(address *src, address *dst, port_type ptype, + guint32 src_port, guint32 dst_port) +{ + conversation_key key; + + /* + * We don't make a copy of the address data, we just copy the + * pointer to it, as "key" disappears when we return. + */ + key.src = *src; + key.dst = *dst; + key.ptype = ptype; + key.port_src = src_port; + key.port_dst = dst_port; + return g_hash_table_lookup(conversation_hashtable, &key); +} + +/* + * Set the dissector for a conversation. + */ +void +old_conversation_set_dissector(conversation_t *conversation, + old_dissector_t dissector) +{ + conversation->is_old_dissector = TRUE; + conversation->dissector.old_d = dissector; +} + +void +conversation_set_dissector(conversation_t *conversation, + dissector_t dissector) +{ + conversation->is_old_dissector = FALSE; + conversation->dissector.new_d = dissector; +} + +/* + * Given source and destination addresses and ports for a packet, + * search for a conversational dissector. + * If found, call it and return TRUE, otherwise return FALSE. + */ +gboolean +old_try_conversation_dissector(address *src, address *dst, port_type ptype, + guint32 src_port, guint32 dst_port, const u_char *pd, int offset, + frame_data *fd, proto_tree *tree) +{ + conversation_t *conversation; + tvbuff_t *tvb; + + conversation = find_conversation(src, dst, ptype, src_port, dst_port); + if (conversation != NULL) { + if (conversation->is_old_dissector) { + if (conversation->dissector.old_d == NULL) + return FALSE; + (*conversation->dissector.old_d)(pd, offset, fd, tree); + } else { + if (conversation->dissector.new_d == NULL) + return FALSE; + + /* + * Old dissector calling new dissector; use + * "tvb_create_from_top()" to remap. + * + * XXX - what about the "pd" argument? Do + * any dissectors not just pass that along and + * let the "offset" argument handle stepping + * through the packet? + */ + tvb = tvb_create_from_top(offset); + (*conversation->dissector.new_d)(tvb, &pi, tree); + } + return TRUE; + } + return FALSE; +} + +gboolean +try_conversation_dissector(address *src, address *dst, port_type ptype, + guint32 src_port, guint32 dst_port, tvbuff_t *tvb, packet_info *pinfo, + proto_tree *tree) +{ + conversation_t *conversation; + const guint8 *pd; + int offset; + + conversation = find_conversation(src, dst, ptype, src_port, dst_port); + if (conversation != NULL) { + if (conversation->is_old_dissector) { + /* + * New dissector calling old dissector; use + * "tvb_compat()" to remap. + */ + tvb_compat(tvb, &pd, &offset); + (*conversation->dissector.old_d)(pd, offset, pinfo->fd, + tree); + } else + (*conversation->dissector.new_d)(tvb, pinfo, tree); + return TRUE; + } + return FALSE; +} diff --git a/epan/conversation.h b/epan/conversation.h new file mode 100644 index 0000000000..b8c066d0a5 --- /dev/null +++ b/epan/conversation.h @@ -0,0 +1,63 @@ +/* conversation.h + * Routines for building lists of packets that are part of a "conversation" + * + * $Id: conversation.h,v 1.1 2000/09/27 04:54:47 gram Exp $ + * + * Ethereal - Network traffic analyzer + * By Gerald Combs + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __CONVERSATION_H__ +#define __CONVERSATION_H__ + +#include "packet.h" /* for conversation dissector type */ +/* + * Data structure representing a conversation. + */ +typedef struct conversation { + struct conversation *next; /* pointer to next conversation on hash chain */ + guint32 index; /* unique ID for conversation */ + void *data; /* data our client can associate with a conversation */ + gboolean is_old_dissector; /* XXX - nuke when everybody tvbuffified */ + union { + old_dissector_t old_d; + dissector_t new_d; + } dissector; /* protocol dissector client can associate with conversation */ +} conversation_t; + +extern void conversation_init(void); +conversation_t *conversation_new(address *src, address *dst, port_type ptype, + guint32 src_port, guint32 dst_port, void *data); +conversation_t *find_conversation(address *src, address *dst, port_type ptype, + guint32 src_port, guint32 dst_port); + +void old_conversation_set_dissector(conversation_t *conversation, + old_dissector_t dissector); +void conversation_set_dissector(conversation_t *conversation, + dissector_t dissector); +gboolean +old_try_conversation_dissector(address *src, address *dst, port_type ptype, + guint32 src_port, guint32 dst_port, const u_char *pd, int offset, + frame_data *fd, proto_tree *tree); +gboolean +try_conversation_dissector(address *src, address *dst, port_type ptype, + guint32 src_port, guint32 dst_port, tvbuff_t *tvb, packet_info *pinfo, + proto_tree *tree); + +#endif /* conversation.h */ diff --git a/epan/dfilter-grammar.y b/epan/dfilter-grammar.y new file mode 100644 index 0000000000..61e1775f7e --- /dev/null +++ b/epan/dfilter-grammar.y @@ -0,0 +1,1186 @@ +%{ + +/* dfilter-grammar.y + * Parser for display filters + * + * $Id: dfilter-grammar.y,v 1.1 2000/09/27 04:54:47 gram Exp $ + * + * Ethereal - Network traffic analyzer + * By Gerald Combs + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_SYS_TYPES_H +# include +#endif + +#ifdef HAVE_NETINET_IN_H +# include +#endif + +#ifdef NEED_SNPRINTF_H +# ifdef HAVE_STDARG_H +# include +# else +# include +# endif +# include "snprintf.h" +#endif + +#ifndef __GLIB_H__ +#include +#endif + +#include +#include +#include + +#ifndef _STDLIB_H +#include +#endif + +#ifndef __PROTO_H__ +#include "proto.h" +#endif + +#ifndef __PACKET_H__ +#include "packet.h" +#endif + +#ifndef __DFILTER_H__ +#include "dfilter.h" +#endif + +#include "dfilter-int.h" + +#ifndef __RESOLV_H__ +#include "resolv.h" +#endif + +static GNode* dfilter_mknode_join(GNode *n1, enum node_type ntype, int operand, GNode *n2); +static GNode* dfilter_mknode_unary(int operand, GNode *n2); +static GNode* dfilter_mknode_numeric_variable(gint id); +static GNode* dfilter_mknode_numeric_value(guint32 val); +static GNode* dfilter_mknode_floating_variable(gint id); +static GNode* dfilter_mknode_floating_value(double val); +static GNode* dfilter_mknode_ether_value(gchar*); +static GNode* dfilter_mknode_ether_variable(gint id); +static GNode* dfilter_mknode_ipxnet_value(guint32); +static GNode* dfilter_mknode_ipxnet_variable(gint id); +static GNode* dfilter_mknode_ipv4_value(char *host, int nmask_bits); +static GNode* dfilter_mknode_ipv4_variable(gint id); +static GNode* dfilter_mknode_ipv6_value(char *host); +static GNode* dfilter_mknode_ipv6_variable(gint id); +static GNode* dfilter_mknode_existence(gint id); +static GNode* dfilter_mknode_bytes_value(GByteArray *barray); +static GNode* dfilter_mknode_bytes_variable(gint id, gint offset, guint length); +static GNode* dfilter_mknode_string_value(char *s); +static GNode* dfilter_mknode_string_variable(gint id); + +static guint32 string_to_guint32(char *s, gboolean *success); +static double string_to_double(char *s, gboolean *success); +static int ether_str_to_guint8_array(const char *s, guint8 *mac); +static guint dfilter_get_bytes_variable_offset(GNode *gnode); +static guint dfilter_get_bytes_value_length(GNode* gnode); +static void dfilter_set_bytes_variable_length(GNode *gnode, guint length); +static guint dfilter_get_bytes_variable_length(GNode *gnode); +static gint dfilter_get_bytes_variable_field_registered_length(GNode *gnode); +static char* dfilter_get_variable_abbrev(GNode *gnode); +static int check_bytes_variable_sanity(GNode *gnode); + +/* This is the dfilter we're currently processing. It's how + * dfilter_compile communicates with us. + */ +dfilter *global_df = NULL; + +%} + +%union { + gint operand; /* logical, relation, alternation */ + struct { + gint id; + gint type; /* using macros defined below, in this yacc grammar */ + } variable; + GNode* node; + gchar* string; + struct { + gint offset; + guint length; + } byte_range; +} + +%type statement expression relation +%type numeric_value numeric_variable +%type floating_value floating_variable +%type ether_value ether_variable +%type ipxnet_value ipxnet_variable +%type ipv4_value ipv4_variable +%type ipv6_value ipv6_variable +%type string_value string_variable +%type variable_name +%type bytes_value bytes_variable + +%type numeric_relation +%type equality_relation +%type bytes_relation + +%type any_variable_type + +%token T_FT_UINT8 +%token T_FT_UINT16 +%token T_FT_UINT24 +%token T_FT_UINT32 +%token T_FT_INT8 +%token T_FT_INT16 +%token T_FT_INT24 +%token T_FT_INT32 +%token T_FT_ETHER +%token T_FT_IPv4 +%token T_FT_IPv6 +%token T_FT_NONE +%token T_FT_BYTES +%token T_FT_BOOLEAN +%token T_FT_STRING +%token T_FT_IPXNET +%token T_FT_DOUBLE + +%token T_VAL_UNQUOTED_STRING +%token T_VAL_QUOTED_STRING +%token T_VAL_BYTE_STRING +%token T_VAL_BYTE_RANGE + +%token TOK_AND TOK_OR TOK_NOT TOK_XOR +%token TOK_EQ TOK_NE TOK_GT TOK_GE TOK_LT TOK_LE + +%expect 4 +%left TOK_AND +%left TOK_OR TOK_XOR +%nonassoc TOK_NOT + +%% + +statement: expression + { + global_df->dftree = $1; + } + | /* NULL */ { if (global_df != NULL) global_df->dftree = NULL; } + ; + +expression: '(' expression ')' { $$ = $2; } + | expression TOK_AND expression { $$ = dfilter_mknode_join($1, logical, $2, $3); } + | expression TOK_OR expression { $$ = dfilter_mknode_join($1, logical, $2, $3); } + | expression TOK_XOR expression { $$ = dfilter_mknode_join($1, logical, $2, $3); } + | TOK_NOT expression { $$ = dfilter_mknode_unary(TOK_NOT, $2); } + | relation { $$ = $1; } + | variable_name { $$ = $1; } + | expression error { YYABORT; } + ; + +relation: numeric_variable numeric_relation numeric_value + { + $$ = dfilter_mknode_join($1, relation, $2, $3); + } + | numeric_variable numeric_relation numeric_variable + { + $$ = dfilter_mknode_join($1, relation, $2, $3); + } + + | floating_variable numeric_relation floating_value + { + $$ = dfilter_mknode_join($1, relation, $2, $3); + } + | floating_variable numeric_relation floating_variable + { + $$ = dfilter_mknode_join($1, relation, $2, $3); + } + + | ether_variable equality_relation ether_value + { + $$ = dfilter_mknode_join($1, relation, $2, $3); + } + | ether_variable equality_relation ether_variable + { + $$ = dfilter_mknode_join($1, relation, $2, $3); + } + + | ipxnet_variable equality_relation ipxnet_value + { + $$ = dfilter_mknode_join($1, relation, $2, $3); + } + | ipxnet_variable equality_relation ipxnet_variable + { + $$ = dfilter_mknode_join($1, relation, $2, $3); + } + + | string_variable equality_relation string_value + { + $$ = dfilter_mknode_join($1, relation, $2, $3); + } + | string_variable equality_relation string_variable + { + $$ = dfilter_mknode_join($1, relation, $2, $3); + } + + + | ipv4_variable numeric_relation ipv4_value + { + $$ = dfilter_mknode_join($1, relation, $2, $3); + } + | ipv4_variable numeric_relation ipv4_variable + { + $$ = dfilter_mknode_join($1, relation, $2, $3); + } + + | ipv6_variable equality_relation ipv6_value + { + $$ = dfilter_mknode_join($1, relation, $2, $3); + } + | ipv6_variable equality_relation ipv6_variable + { + $$ = dfilter_mknode_join($1, relation, $2, $3); + } + + | bytes_variable bytes_relation bytes_value + { + int a_len, b_len; + + a_len = dfilter_get_bytes_variable_length($1); + b_len = dfilter_get_bytes_value_length($3); + + if (a_len == 0) { + dfilter_set_bytes_variable_length($1, b_len); + a_len = b_len; + } + + if (!check_bytes_variable_sanity($1)) { + YYERROR; + } + + if (a_len != b_len) { + dfilter_fail("Field \"%s\" has %u byte%s being compared, but %u byte%s " + "%s supplied.", + dfilter_get_variable_abbrev($1), + a_len, plurality(a_len, "", "s"), + b_len, plurality(b_len, "", "s"), + plurality(b_len, "was", "were")); + YYERROR; + } + + $$ = dfilter_mknode_join($1, relation, $2, $3); + } + | bytes_variable bytes_relation bytes_variable + { + int a_len, b_len; + + a_len = dfilter_get_bytes_variable_length($1); + b_len = dfilter_get_bytes_variable_length($3); + + if (!check_bytes_variable_sanity($1)) { + YYERROR; + } + + if (!check_bytes_variable_sanity($3)) { + YYERROR; + } + + if (a_len != b_len) { + dfilter_fail("Fields \"%s\" and \"%s\" are being compared with " + "disparate lengths of %u byte%s and %u byte%s.", + dfilter_get_variable_abbrev($1), + dfilter_get_variable_abbrev($3), + a_len, plurality(a_len, "", "s"), + b_len, plurality(b_len, "", "s")); + YYERROR; + } + + $$ = dfilter_mknode_join($1, relation, $2, $3); + } + + ; + + +numeric_value: T_VAL_UNQUOTED_STRING + { + gboolean success; + $$ = dfilter_mknode_numeric_value(string_to_guint32($1, &success)); + g_free($1); + if (!success) { + YYERROR; + } + } + ; + +ether_value: T_VAL_BYTE_STRING + { + $$ = dfilter_mknode_ether_value($1); + g_free($1); + if ($$ == NULL) { + YYERROR; + } + } + ; + +string_value: T_VAL_UNQUOTED_STRING + { + $$ = dfilter_mknode_string_value($1); + g_free($1); + if ($$ == NULL) { + YYERROR; + } + } + | T_VAL_QUOTED_STRING + { + $$ = dfilter_mknode_string_value($1); + g_free($1); + if ($$ == NULL) { + YYERROR; + } + } + ; + +ipxnet_value: T_VAL_UNQUOTED_STRING + { + gboolean success; + $$ = dfilter_mknode_ipxnet_value(string_to_guint32($1, &success)); + g_free($1); + if (!success) { + YYERROR; + } + } + ; + +floating_value: T_VAL_UNQUOTED_STRING + { + gboolean success; + $$ = dfilter_mknode_floating_value(string_to_double($1, &success)); + g_free($1); + if (!success) { + YYERROR; + } + } + + | T_VAL_BYTE_STRING + { + /* e.g., 0.0, 0.1, 0.01 ... */ + gboolean success; + $$ = dfilter_mknode_floating_value(string_to_double($1, &success)); + g_free($1); + if (!success) { + YYERROR; + } + } + ; + +ipv4_value: T_VAL_UNQUOTED_STRING + { + $$ = dfilter_mknode_ipv4_value($1, 32); + g_free($1); + if ($$ == NULL) { + YYERROR; + } + } + + | T_VAL_BYTE_STRING + { + $$ = dfilter_mknode_ipv4_value($1, 32); + g_free($1); + if ($$ == NULL) { + YYERROR; + } + } + + | T_VAL_UNQUOTED_STRING '/' T_VAL_UNQUOTED_STRING + { + gboolean success; + guint32 nmask_bits; + + nmask_bits = string_to_guint32($3, &success); + if (!success) { + g_free($1); + g_free($3); + YYERROR; + } + + if (nmask_bits > 32) { + dfilter_fail("The number of netmask bits in \"%s/%s\" should " + "be between 0 and 32.", $1, $3); + g_free($1); + g_free($3); + YYERROR; + } + + $$ = dfilter_mknode_ipv4_value($1, nmask_bits); + g_free($1); + g_free($3); + if ($$ == NULL) { + YYERROR; + } + } + + | T_VAL_BYTE_STRING '/' T_VAL_UNQUOTED_STRING + { + gboolean success; + guint32 nmask_bits; + + nmask_bits = string_to_guint32($3, &success); + if (!success) { + g_free($1); + g_free($3); + YYERROR; + } + + if (nmask_bits > 32) { + dfilter_fail("The number of netmask bits in \"%s/%s\" should " + "be between 0 and 32.", $1, $3); + g_free($1); + g_free($3); + YYERROR; + } + $$ = dfilter_mknode_ipv4_value($1, nmask_bits); + g_free($1); + g_free($3); + if ($$ == NULL) { + YYERROR; + } + } + ; + +ipv6_value: T_VAL_UNQUOTED_STRING + { + $$ = dfilter_mknode_ipv6_value($1); + g_free($1); + if ($$ == NULL) { + YYERROR; + } + } + + | T_VAL_BYTE_STRING + { + $$ = dfilter_mknode_ipv6_value($1); + g_free($1); + if ($$ == NULL) { + YYERROR; + } + } + ; + +bytes_value: T_VAL_BYTE_STRING + { + GByteArray *barray; + + /* the next function appends to list_of_byte_arrays for me */ + barray = byte_str_to_guint8_array($1); + $$ = dfilter_mknode_bytes_value(barray); + g_free($1); + } + + | T_VAL_UNQUOTED_STRING + { + gboolean success; + guint32 val32; + guint8 val8; + GByteArray *barray; + + val32 = string_to_guint32($1, &success); + if (!success) { + g_free($1); + YYERROR; + } + if (val32 > 0xff) { + dfilter_fail("The value \"%s\" cannot be stored in a single-byte byte-string. " + "Use the multi-byte \"xx:yy\" representation.", $1); + g_free($1); + YYERROR; + } + val8 = (guint8) val32; + barray = g_byte_array_new(); + global_df->list_of_byte_arrays = g_slist_append(global_df->list_of_byte_arrays, barray); + g_byte_array_append(barray, &val8, 1); + + $$ = dfilter_mknode_bytes_value(barray); + g_free($1); + } + ; + +numeric_variable: T_FT_UINT8 { $$ = dfilter_mknode_numeric_variable($1.id); } + | T_FT_UINT16 { $$ = dfilter_mknode_numeric_variable($1.id); } + | T_FT_UINT24 { $$ = dfilter_mknode_numeric_variable($1.id); } + | T_FT_UINT32 { $$ = dfilter_mknode_numeric_variable($1.id); } + | T_FT_INT8 { $$ = dfilter_mknode_numeric_variable($1.id); } + | T_FT_INT16 { $$ = dfilter_mknode_numeric_variable($1.id); } + | T_FT_INT24 { $$ = dfilter_mknode_numeric_variable($1.id); } + | T_FT_INT32 { $$ = dfilter_mknode_numeric_variable($1.id); } + ; + +ether_variable: T_FT_ETHER { $$ = dfilter_mknode_ether_variable($1.id); } + ; + +floating_variable: T_FT_DOUBLE { $$ = dfilter_mknode_floating_variable($1.id); } + ; + +ipxnet_variable: T_FT_IPXNET { $$ = dfilter_mknode_ipxnet_variable($1.id); } + ; + +ipv4_variable: T_FT_IPv4 { $$ = dfilter_mknode_ipv4_variable($1.id); } + ; + +ipv6_variable: T_FT_IPv6 { $$ = dfilter_mknode_ipv6_variable($1.id); } + ; + +string_variable: T_FT_STRING { $$ = dfilter_mknode_string_variable($1.id); } + ; + +variable_name: any_variable_type + { + GNode *variable; + GNode *value; + + if ($1.type == T_FT_BOOLEAN) { + /* Make "variable == TRUE" for BOOLEAN variable */ + variable = dfilter_mknode_numeric_variable($1.id); + value = dfilter_mknode_numeric_value(TRUE); + $$ = dfilter_mknode_join(variable, relation, TOK_EQ, value); + } + else { + $$ = dfilter_mknode_existence($1.id); + } + } + ; + +bytes_variable: any_variable_type T_VAL_BYTE_RANGE + { + $$ = dfilter_mknode_bytes_variable($1.id, $2.offset, $2.length); + } + ; + +any_variable_type: T_FT_UINT8 { $$ = $1; } + | T_FT_UINT16 { $$ = $1; } + | T_FT_UINT24 { $$ = $1; } + | T_FT_UINT32 { $$ = $1; } + | T_FT_INT8 { $$ = $1; } + | T_FT_INT16 { $$ = $1; } + | T_FT_INT24 { $$ = $1; } + | T_FT_INT32 { $$ = $1; } + | T_FT_DOUBLE { $$ = $1; } + | T_FT_ETHER { $$ = $1; } + | T_FT_IPv4 { $$ = $1; } + | T_FT_IPv6 { $$ = $1; } + | T_FT_IPXNET { $$ = $1; } + | T_FT_NONE { $$ = $1; } + | T_FT_BYTES { $$ = $1; } + | T_FT_BOOLEAN { $$ = $1; } + | T_FT_STRING { $$ = $1; } + ; + +numeric_relation: TOK_EQ { $$ = TOK_EQ; } + | TOK_NE { $$ = TOK_NE; } + | TOK_GT { $$ = TOK_GT; } + | TOK_GE { $$ = TOK_GE; } + | TOK_LT { $$ = TOK_LT; } + | TOK_LE { $$ = TOK_LE; } + ; + +equality_relation: TOK_EQ { $$ = TOK_EQ; } + | TOK_NE { $$ = TOK_NE; } + ; + +bytes_relation: TOK_EQ { $$ = TOK_EQ; } + | TOK_NE { $$ = TOK_NE; } + | TOK_GT { $$ = TOK_GT; } + | TOK_LT { $$ = TOK_LT; } + ; + +%% + +static GNode* +dfilter_mknode_join(GNode *n1, enum node_type ntype, int operand, GNode *n2) +{ + dfilter_node *node_root; + GNode *gnode_root; + + node_root = g_mem_chunk_alloc(global_df->node_memchunk); + node_root->ntype = ntype; + node_root->elem_size = 0; + node_root->fill_array_variable_func = NULL; + node_root->fill_array_value_func = NULL; + node_root->check_relation_func = NULL; + if (ntype == relation) { + node_root->value.relation = operand; + } + else if (ntype == logical) { + node_root->value.logical = operand; + } + else { + g_assert_not_reached(); + } + + gnode_root = g_node_new(node_root); + g_node_append(gnode_root, n1); + g_node_append(gnode_root, n2); + + return gnode_root; +} + +static GNode* +dfilter_mknode_unary(int operand, GNode *n2) +{ + dfilter_node *node_root; + GNode *gnode_root; + + node_root = g_mem_chunk_alloc(global_df->node_memchunk); + node_root->ntype = logical; + node_root->value.logical = operand; + node_root->elem_size = 0; + node_root->fill_array_variable_func = NULL; + node_root->fill_array_value_func = NULL; + node_root->check_relation_func = NULL; + + gnode_root = g_node_new(node_root); + g_node_append(gnode_root, n2); + + return gnode_root; +} + + +static GNode* +dfilter_mknode_numeric_variable(gint id) +{ + dfilter_node *node; + GNode *gnode; + + node = g_mem_chunk_alloc(global_df->node_memchunk); + node->ntype = variable; + node->elem_size = sizeof(guint32); + node->fill_array_variable_func = fill_array_numeric_variable; + node->fill_array_value_func = NULL; + node->check_relation_func = check_relation_numeric; + node->value.variable = id; + gnode = g_node_new(node); + + return gnode; +} + +static GNode* +dfilter_mknode_ether_variable(gint id) +{ + dfilter_node *node; + GNode *gnode; + + node = g_mem_chunk_alloc(global_df->node_memchunk); + node->ntype = variable; + node->elem_size = sizeof(guint8) * 6; + node->fill_array_variable_func = fill_array_ether_variable; + node->fill_array_value_func = NULL; + node->check_relation_func = check_relation_ether; + node->value.variable = id; + gnode = g_node_new(node); + + return gnode; +} + +static GNode* +dfilter_mknode_floating_variable(gint id) +{ + dfilter_node *node; + GNode *gnode; + + node = g_mem_chunk_alloc(global_df->node_memchunk); + node->ntype = variable; + node->elem_size = sizeof(double); + node->fill_array_variable_func = fill_array_floating_variable; + node->fill_array_value_func = NULL; + node->check_relation_func = check_relation_floating; + node->value.variable = id; + gnode = g_node_new(node); + + return gnode; +} + +static GNode* +dfilter_mknode_ipxnet_variable(gint id) +{ + dfilter_node *node; + GNode *gnode; + + node = g_mem_chunk_alloc(global_df->node_memchunk); + node->ntype = variable; + node->elem_size = sizeof(guint8) * 4; + node->fill_array_variable_func = fill_array_numeric_variable; /* cheating ! */ + node->fill_array_value_func = NULL; + node->check_relation_func = check_relation_numeric; /* cheating ! */ + node->value.variable = id; + gnode = g_node_new(node); + + return gnode; +} + +static GNode* +dfilter_mknode_ipv4_variable(gint id) +{ + dfilter_node *node; + GNode *gnode; + + node = g_mem_chunk_alloc(global_df->node_memchunk); + node->ntype = variable; + node->elem_size = sizeof(ipv4_addr); + node->fill_array_variable_func = fill_array_ipv4_variable; + node->fill_array_value_func = NULL; + node->check_relation_func = check_relation_ipv4; + node->value.variable = id; + gnode = g_node_new(node); + + return gnode; +} + +static GNode* +dfilter_mknode_ipv6_variable(gint id) +{ + dfilter_node *node; + GNode *gnode; + + node = g_mem_chunk_alloc(global_df->node_memchunk); + node->ntype = variable; + node->elem_size = 16; + node->fill_array_variable_func = fill_array_ipv6_variable; + node->fill_array_value_func = NULL; + node->check_relation_func = check_relation_ipv6; + node->value.variable = id; + gnode = g_node_new(node); + + return gnode; +} + +static GNode* +dfilter_mknode_string_variable(gint id) +{ + dfilter_node *node; + GNode *gnode; + + node = g_mem_chunk_alloc(global_df->node_memchunk); + node->ntype = variable; + node->elem_size = sizeof(char*); + node->fill_array_variable_func = fill_array_string_variable; + node->fill_array_value_func = NULL; + node->check_relation_func = check_relation_string; + node->value.variable = id; + gnode = g_node_new(node); + + return gnode; +} + +static GNode* +dfilter_mknode_bytes_variable(gint id, gint offset, guint length) +{ + dfilter_node *node; + GNode *gnode; + + node = g_mem_chunk_alloc(global_df->node_memchunk); + node->ntype = variable; + node->elem_size = sizeof(GByteArray*); + node->fill_array_variable_func = fill_array_bytes_variable; + node->fill_array_value_func = NULL; + node->check_relation_func = check_relation_bytes; + node->value.variable = id; + node->offset = offset; + node->length = length; + gnode = g_node_new(node); + + return gnode; +} + +/* Gets length of variable represented by node from proto_register */ +static gint +dfilter_get_bytes_variable_field_registered_length(GNode *gnode) +{ + dfilter_node *node = gnode->data; + + /* Is this really a bytes_variable? */ + g_assert(node->fill_array_variable_func == fill_array_bytes_variable); + + return proto_registrar_get_length(node->value.variable); +} + +/* Sets the length of a bytes_variable node */ +static void +dfilter_set_bytes_variable_length(GNode *gnode, guint length) +{ + dfilter_node *node = gnode->data; + + /* Is this really a bytes_variable? */ + g_assert(node->fill_array_variable_func == fill_array_bytes_variable); + + node->length = length; +} + +/* Gets the length of a bytes_variable node */ +static guint +dfilter_get_bytes_variable_length(GNode *gnode) +{ + dfilter_node *node = gnode->data; + + /* Is this really a bytes_variable? */ + g_assert(node->fill_array_variable_func == fill_array_bytes_variable); + + return node->length; +} + +/* Gets the offset of a bytes_variable node */ +static guint +dfilter_get_bytes_variable_offset(GNode *gnode) +{ + dfilter_node *node = gnode->data; + + /* Is this really a bytes_variable? */ + g_assert(node->fill_array_variable_func == fill_array_bytes_variable); + + return node->offset; +} + +static char* +dfilter_get_variable_abbrev(GNode *gnode) +{ + dfilter_node *node = gnode->data; + + return proto_registrar_get_abbrev(node->value.variable); +} + +static GNode* +dfilter_mknode_numeric_value(guint32 val) +{ + dfilter_node *node; + GNode *gnode; + + node = g_mem_chunk_alloc(global_df->node_memchunk); + node->ntype = numeric; + node->elem_size = sizeof(guint32); + node->fill_array_variable_func = NULL; + node->fill_array_value_func = fill_array_numeric_value; + node->check_relation_func = check_relation_numeric; + node->value.numeric = val; + gnode = g_node_new(node); + + return gnode; +} + +static GNode* +dfilter_mknode_floating_value(double val) +{ + dfilter_node *node; + GNode *gnode; + + node = g_mem_chunk_alloc(global_df->node_memchunk); + node->ntype = floating; + node->elem_size = sizeof(double); + node->fill_array_variable_func = NULL; + node->fill_array_value_func = fill_array_floating_value; + node->check_relation_func = check_relation_floating; + node->value.floating = val; + gnode = g_node_new(node); + + return gnode; +} + +/* Returns NULL on bad parse of ETHER value */ +static GNode* +dfilter_mknode_ether_value(gchar *byte_string) +{ + dfilter_node *node; + GNode *gnode; + + node = g_mem_chunk_alloc(global_df->node_memchunk); + node->ntype = ether; + node->elem_size = sizeof(guint8) * 6; + node->fill_array_variable_func = NULL; + node->fill_array_value_func = fill_array_ether_value; + node->check_relation_func = check_relation_ether; + + if (!ether_str_to_guint8_array(byte_string, &node->value.ether[0])) { + /* Rather than free the mem_chunk allocation, let it + * stay. It will be cleaned up when "dfilter_compile()" + * calls "dfilter_destroy()". */ + dfilter_fail("\"%s\" is not a valid hardware address.", + byte_string); + return NULL; + } + + gnode = g_node_new(node); + return gnode; +} + +static GNode* +dfilter_mknode_ipxnet_value(guint32 ipx_net_val) +{ + dfilter_node *node; + GNode *gnode; + + node = g_mem_chunk_alloc(global_df->node_memchunk); + node->ntype = ipxnet; + node->elem_size = sizeof(guint8) * 4; + node->fill_array_variable_func = NULL; + node->fill_array_value_func = fill_array_numeric_value; /* cheating ! */ + node->check_relation_func = check_relation_numeric; /* cheating ! */ + node->value.numeric = ipx_net_val; + gnode = g_node_new(node); + + return gnode; +} + +/* Returns NULL on bad parse of IP value */ +static GNode* +dfilter_mknode_ipv4_value(char *host, int nmask_bits) +{ + dfilter_node *node; + GNode *gnode; + guint32 addr; + + node = g_mem_chunk_alloc(global_df->node_memchunk); + node->ntype = numeric; + node->elem_size = sizeof(ipv4_addr); + node->fill_array_variable_func = NULL; + node->fill_array_value_func = fill_array_ipv4_value; + node->check_relation_func = check_relation_ipv4; + if (!get_host_ipaddr(host, &addr)) { + /* Rather than free the mem_chunk allocation, let it + * stay. It will be cleaned up when "dfilter_compile()" + * calls "dfilter_destroy()". */ + dfilter_fail("\"%s\" isn't a valid host name or IP address.", + host); + return NULL; + } + ipv4_addr_set_host_order_addr(&node->value.ipv4, addr); + ipv4_addr_set_netmask_bits(&node->value.ipv4, nmask_bits); + + gnode = g_node_new(node); + return gnode; +} + +/* Returns NULL on bad parse of IPv6 value */ +static GNode* +dfilter_mknode_ipv6_value(char *host) +{ + dfilter_node *node; + GNode *gnode; + + node = g_mem_chunk_alloc(global_df->node_memchunk); + node->ntype = ipv6; + node->elem_size = 16; + node->fill_array_variable_func = NULL; + node->fill_array_value_func = fill_array_ipv6_value; + node->check_relation_func = check_relation_ipv6; + + if (!get_host_ipaddr6(host, (struct e_in6_addr*)&node->value.ipv6[0])) { + /* Rather than free the mem_chunk allocation, let it + * stay. It will be cleaned up when "dfilter_compile()" + * calls "dfilter_destroy()". */ + dfilter_fail("\"%s\" isn't a valid IPv6 address.", + host); + return NULL; + } + + gnode = g_node_new(node); + return gnode; +} + + +static GNode* +dfilter_mknode_string_value(char *s) +{ + dfilter_node *node; + GNode *gnode; + + node = g_mem_chunk_alloc(global_df->node_memchunk); + node->ntype = string; + node->elem_size = sizeof(char*); + node->fill_array_variable_func = NULL; + node->fill_array_value_func = fill_array_string_value; + node->check_relation_func = check_relation_string; + node->value.string = g_strdup(s); + global_df->list_of_strings = g_slist_append(global_df->list_of_strings, + node->value.string); + gnode = g_node_new(node); + + return gnode; +} + + +static GNode* +dfilter_mknode_bytes_value(GByteArray *barray) +{ + dfilter_node *node; + GNode *gnode; + + node = g_mem_chunk_alloc(global_df->node_memchunk); + node->ntype = bytes; + node->elem_size = sizeof(GByteArray*); + node->fill_array_variable_func = NULL; + node->fill_array_value_func = fill_array_bytes_value; + node->check_relation_func = check_relation_bytes; + node->value.bytes = barray; + node->offset = G_MAXINT; + node->length = barray->len; + gnode = g_node_new(node); + + return gnode; +} + +/* Given a node representing a bytes_value, returns + * the length of the byte array */ +static guint +dfilter_get_bytes_value_length(GNode* gnode) +{ + dfilter_node *node = gnode->data; + + g_assert(node->ntype == bytes); + return node->length; +} + +static guint32 +string_to_guint32(char *s, gboolean *success) +{ + char *endptr; + guint32 val; + + val = strtoul(s, &endptr, 0); + *success = TRUE; + if (endptr == s || *endptr != '\0') { + /* This isn't a valid number. */ + dfilter_fail("\"%s\" is not a valid number.", s); + *success = FALSE; + } + if (errno == ERANGE) { + *success = FALSE; + if (val == ULONG_MAX) { + dfilter_fail("\"%s\" causes an integer overflow.", s); + } + else { + dfilter_fail("\"%s\" is not an integer.", s); + } + } + + return (guint32)val; +} + +static double +string_to_double(char *s, gboolean *success) +{ + char *endptr = NULL; + double retval; + + retval = strtod(s, &endptr); + *success = TRUE; + + if (endptr == s) { + dfilter_fail("\"%s\" is not a valid floating-point number.", s); + *success = FALSE; + } + + if (errno == ERANGE) { + *success = FALSE; + if (retval == 0) { + dfilter_fail("\"%s\" causes a floating-point underflow.", s); + } + else if (retval == HUGE_VAL) { + dfilter_fail("\"%s\" causes a floating-point overflow.", s); + } + else { + dfilter_fail("\"%s\" is not a valid floating-point.", s); + } + } + return retval; +} + +static GNode* +dfilter_mknode_existence(gint id) +{ + dfilter_node *node; + GNode *gnode; + + node = g_mem_chunk_alloc(global_df->node_memchunk); + node->ntype = existence; + node->elem_size = sizeof(guint32); + node->fill_array_variable_func = NULL; + node->fill_array_value_func = NULL; + node->check_relation_func = NULL; + node->value.variable = id; + gnode = g_node_new(node); + + return gnode; +} + + +/* converts a string representing an ether HW address + * to a guint8 array. + * + * Returns 0 on failure, 1 on success. + */ +static int +ether_str_to_guint8_array(const char *s, guint8 *mac) +{ + char ether_str[18]; /* 2+1+2+1+2+1+2+1+2+1+2 + 1 */ + char *p, *str; + int i = 0; + + if (strlen(s) > 17) { + return 0; + } + strcpy(ether_str, s); /* local copy of string */ + str = ether_str; + while ((p = strtok(str, "-:."))) { + /* catch short strings with too many hex bytes: 0.0.0.0.0.0.0 */ + if (i > 5) { + return 0; + } + mac[i] = (guint8) strtoul(p, NULL, 16); + i++; + /* subsequent calls to strtok() require NULL as arg 1 */ + str = NULL; + } + if (i != 6) + return 0; /* failed to read 6 hex pairs */ + else + return 1; /* read exactly 6 hex pairs */ +} + + +static int +check_bytes_variable_sanity(GNode *gnode) +{ + int a_off, a_len, reg_len, t_off; + + a_off = dfilter_get_bytes_variable_offset(gnode); + a_len = dfilter_get_bytes_variable_length(gnode); + reg_len = dfilter_get_bytes_variable_field_registered_length(gnode); + + if (reg_len > 0) { + t_off = a_off >= 0 ? a_off : reg_len + a_off; + if (t_off + a_len > reg_len) { + dfilter_fail("The \"%s\" field is only %u byte%s wide, but " + "%u byte%s %s supplied.", + dfilter_get_variable_abbrev(gnode), + reg_len, plurality(reg_len, "", "s"), + a_len, plurality(a_len, "", "s"), + plurality(a_len, "was", "were")); + return 0; + } + } + return 1; +} diff --git a/epan/dfilter-int.h b/epan/dfilter-int.h new file mode 100644 index 0000000000..207584e49c --- /dev/null +++ b/epan/dfilter-int.h @@ -0,0 +1,145 @@ +/* dfilter-int.h + * Definitions for routines common to multiple modules in the display + * filter code, but not used outside that code. + * + * $Id: dfilter-int.h,v 1.1 2000/09/27 04:54:48 gram Exp $ + * + * Ethereal - Network traffic analyzer + * By Gerald Combs + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __DFILTER_INT_H__ +#define __DFILTER_INT_H__ + +#ifndef __IPV4_H__ +#include "ipv4.h" +#endif + +/* in dfilter-scanner.l */ +GByteArray *byte_str_to_guint8_array(const char *s); +void dfilter_scanner_text(char*); +void dfilter_scanner_cleanup(void); + +/* in dfilter-grammar.y */ +extern dfilter *global_df; + +/* Here we provide interfaces to make our scanner act and look like lex */ +int dfilter_lex(void); +void dfilter_error(char *s); + +/* Report an error during compilation of a filter; this is called by code + * other than parser code, so all it does is record that an error occurred, + * so that even if the filter is nominally syntactically valid, we still + * fail. + */ +#if __GNUC__ == 2 +void dfilter_fail(char *fmt, ...) + __attribute__((format (printf, 1, 2))); +#else +void dfilter_fail(char *fmt, ...); +#endif + +/* functions that dfilter-grammar.y needs during parsing*/ +gboolean check_relation_numeric(gint operand, GArray *a, GArray *b); +gboolean check_relation_floating(gint operand, GArray *a, GArray *b); +gboolean check_relation_ether(gint operand, GArray *a, GArray *b); +gboolean check_relation_ipv4(gint operand, GArray *a, GArray *b); +gboolean check_relation_ipv6(gint operand, GArray *a, GArray *b); +gboolean check_relation_bytes(gint operand, GArray *a, GArray *b); +gboolean check_relation_string(gint operand, GArray *a, GArray *b); + +void fill_array_numeric_variable(field_info*, GArray*, const guint8*); +void fill_array_floating_variable(field_info*, GArray*, const guint8*); +void fill_array_ether_variable(field_info*, GArray*, const guint8*); +void fill_array_ipv4_variable(field_info*, GArray*, const guint8*); +void fill_array_ipv6_variable(field_info*, GArray*, const guint8*); +void fill_array_bytes_variable(field_info*, GArray*, const guint8*); +void fill_array_string_variable(field_info*, GArray*, const guint8*); + +gboolean fill_array_numeric_value(GNode *gnode, gpointer data); +gboolean fill_array_floating_value(GNode *gnode, gpointer data); +gboolean fill_array_ether_value(GNode *gnode, gpointer data); +gboolean fill_array_ipv4_value(GNode *gnode, gpointer data); +gboolean fill_array_ipv6_value(GNode *gnode, gpointer data); +gboolean fill_array_bytes_value(GNode *gnode, gpointer data); +gboolean fill_array_string_value(GNode *gnode, gpointer data); + +#ifdef WIN32 +#define boolean truth_value +#endif + +enum node_type { + relation, /* eq, ne, gt, ge, lt, le */ + logical, /* and, or, not, xor */ + variable, /* protocol or header field id */ + existence, /* existence of a variable (protocol or hf) */ + alternation, /* &, | */ + boolean, /* true, false */ + numeric, /* uint8, uint16, or uint32 value */ + floating, /* double */ + abs_time, + string, + ether, + bytes, + ipv4, + ipv6, + ipxnet +}; + +typedef gboolean(*CheckRelationFunc) (gint operand, GArray *a, GArray *b); +typedef void(*FillArrayFunc) (field_info*, GArray*, const guint8*); + +/* This struct is the parse tree node created by this grammary and used + * directly in the display filter routines to filter packets. + */ +typedef struct dfilter_node { + enum node_type ntype; /* from dfilter-grammar.h */ + int elem_size; /* computed at dfilter parse time rather than + when finding elements for each packet. Saves time + in get_values_from_ptree() */ + CheckRelationFunc check_relation_func; + FillArrayFunc fill_array_variable_func; + GNodeTraverseFunc fill_array_value_func; + + /* copied from proto.h */ + union { + gint relation; /* if type == relation (eq, ne, gt, ge, lt, le) */ + gint logical; /* if type == logical (and, or, not, xor) */ + gint variable; /* if type == variable (protocol or header field abbrev) */ + gint alternation; /* if type == alternation (& or |) */ + + guint32 numeric; + double floating; + struct timeval abs_time; /* the whole struct, not a pointer */ + gchar *string; + guint8 ether[6]; + ipv4_addr ipv4; /* the whole struct, not a pointer */ + guint8 ipv6[16]; + GByteArray *bytes; + } value; + + /* used for byte-ranges */ + gint offset; + guint length; +} dfilter_node; + +/* lookup an abbreviation in our token hash, returing the ID # */ +int dfilter_lookup_token(char *abbrev); + +#endif /* ! __DFILTER_INT_H__ */ diff --git a/epan/dfilter-scanner.l b/epan/dfilter-scanner.l new file mode 100644 index 0000000000..b78ddcb2c4 --- /dev/null +++ b/epan/dfilter-scanner.l @@ -0,0 +1,366 @@ +%{ + +/* dfilter-scanner.l + * Scanner for display filters + * + * $Id: dfilter-scanner.l,v 1.1 2000/09/27 04:54:48 gram Exp $ + * + * Ethereal - Network traffic analyzer + * By Gerald Combs + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_IO_H +#include /* for isatty() on win32 */ +#endif + +#ifdef HAVE_SYS_TYPES_H +# include +#endif + +#ifndef _STDIO_H +#include +#endif + +#ifndef _STRING_H +#include +#endif + +#ifndef __G_LIB_H__ +#include +#endif + +#ifndef __PROTO_H__ +#include "proto.h" +#endif + +#ifndef __DFILTER_H__ +#include "dfilter.h" +#endif + +#include "dfilter-int.h" + +#include "dfilter-grammar.h" + +/* Flex has a few routines which help us get the scanner to read + * from a string rather than from a file. POSIX lex only provides + * for reading from a file; any method of reading from a string + * is inherently non-portable. Besides reading from a string, + * we have to worry about resetting the scanner after a bad + * parse; this too is non-portable. Combine the reset with + * a string input, and you have major non-portability. I'll provide + * the routines for flex here. If you really want to modify the + * scanner and use a non-flex lex implementation, you may + * add more ifdef's below. + */ +#ifdef FLEX_SCANNER + +/* Flex has built-in support for using a string as an input source + * instead of using a file. Nice! + */ +YY_BUFFER_STATE string_input_buffer; + +/* We don't need yyunput, so use this macro to get it out of the + * generated C file, avoiding a compiler warning about its lack of use */ +#define YY_NO_UNPUT 1 + +#else + +static char *in_buffer; +#undef getc +#define getc(fp) (*in_buffer == 0 ? EOF : *in_buffer++) + +#endif + + +%} + +whitespace [\t ] +hex [A-Fa-f0-9]{1,2} +hexsep [-:\.] +minus [-] +plus [+] + +%% + +[\t\n ]+ /* ignore whitespace */ + + +and|\&\& { dfilter_lval.operand = TOK_AND; return TOK_AND; } +or|\|\| { dfilter_lval.operand = TOK_OR; return TOK_OR; } +not|\! { dfilter_lval.operand = TOK_NOT; return TOK_NOT; } +xor|\^\^ { dfilter_lval.operand = TOK_XOR; return TOK_XOR; } +eq|\=\= { dfilter_lval.operand = TOK_EQ; return TOK_EQ; } +ne|\!\= { dfilter_lval.operand = TOK_NE; return TOK_NE; } +gt|\> { dfilter_lval.operand = TOK_GT; return TOK_GT; } +ge|\>\= { dfilter_lval.operand = TOK_GE; return TOK_GE; } +lt|\< { dfilter_lval.operand = TOK_LT; return TOK_LT; } +le|\<\= { dfilter_lval.operand = TOK_LE; return TOK_LE; } + +\[{whitespace}*-?[0-9]+{whitespace}*:{whitespace}*[0-9]+{whitespace}*\] { /* range [ x : y ] */ + + char *byterange_string = g_strdup(yytext); + char *s = byterange_string + 1; /* I don't want the first '[' */ + char *p; + + /* Get the offset from the string */ + if ((p = strtok(s, ":"))) { + dfilter_lval.byte_range.offset = strtol(p, NULL, 10); + } + else { + g_free(byterange_string); + return 0; + } + + /* Get the Length from the string */ + if ((p = strtok(NULL, "]"))) { + dfilter_lval.byte_range.length = strtoul(p, NULL, 10); + } + else { + g_free(byterange_string); + return 0; + } + g_free(byterange_string); + return T_VAL_BYTE_RANGE; +} + +\[{whitespace}*-?[0-9]+{whitespace}*\] { /* range [ x ] */ + + char *byterange_string = g_strdup(yytext); + char *s = byterange_string + 1; /* I don't want the first '[' */ + char *p; + + /* Get the offset from the string */ + if ((p = strtok(s, "]"))) { + dfilter_lval.byte_range.offset = strtol(p, NULL, 10); + } + else { + g_free(byterange_string); + return 0; + } + + dfilter_lval.byte_range.length = 0; + g_free(byterange_string); + return T_VAL_BYTE_RANGE; +} + +{hex}({hexsep}{hex})+ { /* byte string, any length */ + dfilter_lval.string = g_strdup(yytext); + return T_VAL_BYTE_STRING; +} + + +0[xX][A-Fa-f0-9]+ { /* hex values */ + dfilter_lval.string = g_strdup(yytext); + return T_VAL_UNQUOTED_STRING; +} + +[A-Za-z0-9\:][A-Za-z0-9\.\_\-\:]+ { + /* looks like a protocol, field name, or hostname */ + + int retval = 0; + enum ftenum ftype; + dfilter_lval.variable.id = dfilter_lookup_token(yytext); + if (dfilter_lval.variable.id < 0) { + dfilter_lval.string = g_strdup(yytext); + return T_VAL_UNQUOTED_STRING; + } + + ftype = proto_registrar_get_ftype(dfilter_lval.variable.id); + switch (ftype) { + case FT_NONE: + retval = T_FT_NONE; + break; + case FT_BOOLEAN: + retval = T_FT_BOOLEAN; + break; + case FT_UINT8: + retval = T_FT_UINT8; + break; + case FT_UINT16: + retval = T_FT_UINT16; + break; + case FT_UINT24: + retval = T_FT_UINT24; + break; + case FT_UINT32: + retval = T_FT_UINT32; + break; + case FT_INT8: + retval = T_FT_INT8; + break; + case FT_INT16: + retval = T_FT_INT16; + break; + case FT_INT24: + retval = T_FT_INT24; + break; + case FT_INT32: + retval = T_FT_INT32; + break; + case FT_DOUBLE: + retval = T_FT_DOUBLE; + break; + case FT_ABSOLUTE_TIME: + dfilter_fail("Sorry, you can't filter on field \"%s\", as we don't yet support filtering on time-of-day values.", + yytext); + retval = 0; + break; + case FT_RELATIVE_TIME: + dfilter_fail("Sorry, you can't filter on field \"%s\", as we don't yet support filtering on time-delta values.", + yytext); + retval = 0; + break; + case FT_STRING: + case FT_STRINGZ: + case FT_UINT_STRING: + retval = T_FT_STRING; + break; + case FT_ETHER: + retval = T_FT_ETHER; + break; + case FT_BYTES: + retval = T_FT_BYTES; + break; + case FT_IPv4: + retval = T_FT_IPv4; + break; + case FT_IPv6: + retval = T_FT_IPv6; + break; + case FT_IPXNET: + retval = T_FT_IPXNET; + break; + default: + printf("ftype for %s is %d\n", yytext, ftype); + g_assert_not_reached(); + retval = 0; + break; + } + dfilter_lval.variable.type = retval; + return retval; +} + +({plus}|{minus})?[0-9]+ { /* decimal and octal integers */ + dfilter_lval.string = g_strdup(yytext); + return T_VAL_UNQUOTED_STRING; +} + +({plus}|{minus})?([0-9]+|[0-9]+\.[0-9]+|\.[0-9]+)([eE]({plus}|{minus})?[0-9]+)? { + /* I'm trying to capture all floating points here, and + * am using the strtod manpage as the description of + * valid formats */ + dfilter_lval.string = g_strdup(yytext); + return T_VAL_UNQUOTED_STRING; +} + +[0-9\:\.]+ { + dfilter_lval.string = g_strdup(yytext); + return T_VAL_UNQUOTED_STRING; +} + +\"[^"]+\" { + int length; + + /* Don't copy the first quote. */ + dfilter_lval.string = g_strdup(&yytext[1]); + + /* Chop of the final quote mark. */ + length = strlen(dfilter_lval.string); + g_assert(length > 0); + dfilter_lval.string[length - 1] = 0; + + return T_VAL_QUOTED_STRING; +} + +. return yytext[0]; +%% + +/* Resets scanner and assigns the char* argument + * as the text to scan + */ +void +dfilter_scanner_text(char *text) +{ +#ifdef FLEX_SCANNER + string_input_buffer = yy_scan_string(text); +#else + in_buffer = text; +#endif +} + +void +dfilter_scanner_cleanup(void) +{ +#ifdef FLEX_SCANNER + yy_delete_buffer(string_input_buffer); +#else + /* There is no standard way to reset a lex scanner. + * This is necessary after a failed parse on a syntactically + * incorrect display filter. You have to reset the scanner + * so that yy_lex() doesn't start scanning from the middle + * of the previous input string. + */ +#endif +} + +/* Flex has an option '%option noyywrap' so that I don't have to + * provide this yywrap function, but in order to maintain portability, + * I'll just use this yywrap() function. + */ +int +yywrap() +{ + return 1; /* stop at EOF, instead of looking for next file */ +} + +/* converts a string representing a byte array + * to a guint8 array. + * + * Returns a non-null GByteArray pointer on success, NULL on failure. + */ +GByteArray* +byte_str_to_guint8_array(const char *s) +{ + GByteArray *barray; + guint8 val; + char *byte_str; + char *p, *str; + + barray = g_byte_array_new(); + /* XXX - don't use global_df, but pass in pointer to GSList* */ + global_df->list_of_byte_arrays = g_slist_append(global_df->list_of_byte_arrays, barray); + + /* Local copy of string, since strtok will munge it */ + byte_str = g_strdup(s); + str = byte_str; + while ((p = strtok(str, "-:."))) { + val = (guint8) strtoul(p, NULL, 16); + g_byte_array_append(barray, &val, 1); + + /* subsequent calls to strtok() require NULL as arg 1 */ + str = NULL; + } + + g_free(byte_str); + return barray; +} diff --git a/epan/dfilter.c b/epan/dfilter.c new file mode 100644 index 0000000000..73fe5253e0 --- /dev/null +++ b/epan/dfilter.c @@ -0,0 +1,1064 @@ +/* dfilter.c + * Routines for display filters + * + * $Id: dfilter.c,v 1.1 2000/09/27 04:54:48 gram Exp $ + * + * Ethereal - Network traffic analyzer + * By Gerald Combs + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_SYS_TYPES_H +# include +#endif + +#include +#include +#include + +#ifdef NEED_SNPRINTF_H +# include "snprintf.h" +#endif + +#include "proto.h" +#include "dfilter.h" +#include "util.h" +#include "dfilter-int.h" +#include "dfilter-grammar.h" + +int dfilter_parse(void); /* yacc entry-point */ + +#define DFILTER_LEX_ABBREV_OFFSET 2000 + +/* Balanced tree of abbreviations and IDs */ +GTree *dfilter_tokens = NULL; + +/* Comparision function for tree insertion. A wrapper around strcmp() */ +static int g_strcmp(gconstpointer a, gconstpointer b); + +/* Silly global variables used to pass parameter to check_relation_bytes() */ +int bytes_offset = 0; +int bytes_length = 0; + +YYSTYPE yylval; + +/* Global error message space for dfilter_compile errors */ +gchar dfilter_error_msg_buf[1024]; +gchar *dfilter_error_msg; /* NULL when no error resulted */ + +static gboolean dfilter_apply_node(GNode *gnode, proto_tree *ptree, const guint8 *pd); +static gboolean check_relation(gint operand, GNode *a, GNode *b, proto_tree *ptree, const guint8 *pd); +static gboolean check_logical(gint operand, GNode *a, GNode *b, proto_tree *ptree, const guint8 *pd); +static GArray* get_values_from_ptree(dfilter_node *dnode, proto_tree *ptree, const guint8 *pd); +static GArray* get_values_from_dfilter(dfilter_node *dnode, GNode *gnode); +static gboolean check_existence_in_ptree(dfilter_node *dnode, proto_tree *ptree); +static void clear_byte_array(gpointer data, gpointer user_data); + +/* this is not so pretty. I need my own g_array "function" (macro) to + * retreive the pointer to the data stored in an array cell. I need this + * for type ether.. GArray makes it easy for me to store 6 bytes inside an array + * cell, but hard to retrieve it. + */ +#define g_array_index_ptr(a,s,i) (((guint8*) (a)->data) + (i*s)) + +extern int hf_text_only; /* in proto.c */ + +void +dfilter_init(void) +{ + int i, num_symbols, symbol; + char *s; + + dfilter_tokens = g_tree_new(g_strcmp); + + /* Add the header field and protocol abbrevs to the symbol table */ + num_symbols = proto_registrar_n(); + + for (i=0; i < num_symbols; i++) { + if (i == hf_text_only) { + continue; + } + s = proto_registrar_get_abbrev(i); + g_assert(s); /* Not Null */ + g_assert(s[0] != 0); /* Not empty string */ + /* Make sure we don't have duplicate abbreviation */ + if (g_tree_lookup(dfilter_tokens, s)) { + g_message("Already have abbreviation \"%s\"", s); + g_assert(0); + } + symbol = DFILTER_LEX_ABBREV_OFFSET + i; + g_tree_insert(dfilter_tokens, s, GINT_TO_POINTER(symbol)); + } +} + +void +dfilter_cleanup(void) +{ + if (dfilter_tokens) + g_tree_destroy(dfilter_tokens); +} + +/* Compiles the textual representation of the display filter into a tree + * of operations to perform. Can be called multiple times, compiling a new + * display filter each time, without having to clear any memory used, since + * dfilter_compile will take care of that automatically. + * + * Returns 0 on success, non-zero on failure. + * + * On success, sets the "dfilter *" pointed to by its second argument + * either to a null pointer (if the filter is a null filter, as + * generated by an all-blank string) or to a pointer to a newly-allocated + * dfilter structure (if the filter isn't null). + * + * On failure, "dfilter_error_msg" points to an appropriate error message. + * This error message is a global string, so another invocation of + * dfilter_compile will clear it. If the caller needs is stored, he + * needs to g_strdup it himself. + */ +int +dfilter_compile(gchar *dfilter_text, dfilter **dfp) +{ + dfilter *df; + int retval; + + g_assert(dfilter_text != NULL); + + df = dfilter_new(); + + /* tell the scanner to use the filter string as input */ + dfilter_scanner_text(dfilter_text); + + /* Assign global variable so dfilter_parse knows which dfilter we're + * talking about. Reset the global error message. + */ + global_df = df; + dfilter_error_msg = NULL; + + /* The magic happens right here. */ + retval = dfilter_parse(); + + /* clean up lex */ + dfilter_scanner_cleanup(); + + /* Errors not found by the parser may not cause the parse to + * fail; if "dfilter_error_msg" is set, it means somebody + * else called "dfilter_fail()", e.g. the lexical analyzer, + * so treat that as a parse error. */ + if (dfilter_error_msg != NULL) + retval = 1; + + if (retval != 0) { + if (dfilter_error_msg == NULL) { + snprintf(dfilter_error_msg_buf, sizeof(dfilter_error_msg_buf), + "Unable to parse filter string \"%s\".", + dfilter_text); + dfilter_error_msg = &dfilter_error_msg_buf[0]; + } + } + + /* Set global_df to NULL just to be tidy. */ + global_df = NULL; + + if (retval == 0) { + /* Success. Check if the filter is empty; if so, discard + * it and set "*dfp" to NULL, otherwise set "*dfp" to + * point to the filter. */ + if (df->dftree == NULL) { + /* The filter is empty. */ + dfilter_destroy(df); + df = NULL; + } + *dfp = df; + } else { + /* Failure. Destroy the filter. */ + dfilter_destroy(df); + df = NULL; + } + return retval; +} + +/* Allocates new dfilter, initializes values, and returns pointer to dfilter */ +dfilter* +dfilter_new(void) +{ + dfilter *df; + + df = g_malloc(sizeof(dfilter)); + + df->dftree = NULL; + df->node_memchunk = g_mem_chunk_new("df->node_memchunk", + sizeof(dfilter_node), 20 * sizeof(dfilter_node), G_ALLOC_ONLY); + df->list_of_byte_arrays = NULL; + df->list_of_strings = NULL; + + return df; +} + +static void +free_string(gpointer data, gpointer user_data) +{ + char *string = data; + if (string) + g_free(string); +} + +/* Frees all memory used by dfilter, and frees dfilter itself */ +void +dfilter_destroy(dfilter *df) +{ + if (!df) + return; + + if (df->dftree != NULL) + g_node_destroy(df->dftree); + + /* clear the memory that the tree was using for nodes */ + if (df->node_memchunk) + g_mem_chunk_reset(df->node_memchunk); + + /* clear the memory that the tree was using for byte arrays */ + if (df->list_of_byte_arrays) { + g_slist_foreach(df->list_of_byte_arrays, clear_byte_array, NULL); + g_slist_free(df->list_of_byte_arrays); + } + + /* clear the allocated strings */ + if (df->list_of_strings) { + g_slist_foreach(df->list_of_strings, free_string, NULL); + g_slist_free(df->list_of_strings); + } + + df->dftree = NULL; + df->list_of_byte_arrays = NULL; + df->list_of_strings = NULL; + + /* Git rid of memchunk */ + if (df->node_memchunk) + g_mem_chunk_destroy(df->node_memchunk); + + g_free(df); +} + + +static void +clear_byte_array(gpointer data, gpointer user_data) +{ + GByteArray *barray = data; + if (barray) + g_byte_array_free(barray, TRUE); +} + +/* Called when the yacc grammar finds a parsing error */ +void +dfilter_error(char *s) +{ +} + +/* Called when an error other than a parsing error occurs. */ +void +dfilter_fail(char *format, ...) +{ + va_list ap; + + /* If we've already reported one error, don't overwrite it with this + * one. */ + if (dfilter_error_msg != NULL) + return; + + va_start(ap, format); + vsnprintf(dfilter_error_msg_buf, sizeof dfilter_error_msg_buf, format, ap); + dfilter_error_msg = dfilter_error_msg_buf; + va_end(ap); +} + +/* lookup an abbreviation in our token tree, returing the ID # + * If the abbreviation doesn't exit, returns -1 */ +int dfilter_lookup_token(char *abbrev) +{ + int value; + + g_assert(abbrev != NULL); + value = GPOINTER_TO_INT(g_tree_lookup(dfilter_tokens, abbrev)); + + if (value < DFILTER_LEX_ABBREV_OFFSET) { + return -1; + } + return value - DFILTER_LEX_ABBREV_OFFSET; +} + +static int +g_strcmp(gconstpointer a, gconstpointer b) +{ + return strcmp((const char*)a, (const char*)b); +} + + +gboolean +dfilter_apply(dfilter *dfcode, proto_tree *ptree, const guint8* pd, guint pd_len) +{ + gboolean retval; + if (dfcode == NULL) + return FALSE; + retval = dfilter_apply_node(dfcode->dftree, ptree, pd); + return retval; +} + +static gboolean +dfilter_apply_node(GNode *gnode, proto_tree *ptree, const guint8* pd) +{ + GNode *gnode_a, *gnode_b; + dfilter_node *dnode = (dfilter_node*) (gnode->data); + + /* We'll get 2 NULLs if we don't have children */ + gnode_a = g_node_nth_child(gnode, 0); + gnode_b = g_node_nth_child(gnode, 1); + + switch(dnode->ntype) { + case variable: + /* We'll never see this case because if the parser finds the name of + * a variable, it will cause it to be an 'existence' operation. + */ + g_assert_not_reached(); + + case logical: + g_assert(gnode_a); + return check_logical(dnode->value.logical, gnode_a, gnode_b, ptree, pd); + + case relation: + g_assert(gnode_a && gnode_b); + return check_relation(dnode->value.relation, gnode_a, gnode_b, ptree, pd); + + case alternation: + g_assert_not_reached(); + /* not coded yet */ + + case numeric: + case floating: + case ipv4: + case ipv6: + case boolean: + case ether: + case string: + case abs_time: + case bytes: + case ipxnet: + /* the only time we'll see these at this point is if the display filter + * is really wacky. (like simply "192.168.1.1"). The parser as it stands + * now let these by. Just return TRUE */ + g_assert(!gnode_a && !gnode_b); + return TRUE; + + case existence: /* checking the existence of a protocol or hf*/ + g_assert(!gnode_a && !gnode_b); + return check_existence_in_ptree(dnode, ptree); + } + + g_assert_not_reached(); + return FALSE; +} + +static gboolean +check_logical(gint operand, GNode *a, GNode *b, proto_tree *ptree, const guint8 *pd) +{ + gboolean val_a = dfilter_apply_node(a, ptree, pd); + gboolean val_b; + + switch(operand) { + case TOK_AND: + g_assert(b); + return (val_a && dfilter_apply_node(b, ptree, pd)); + case TOK_OR: + g_assert(b); + return (val_a || dfilter_apply_node(b, ptree, pd)); + case TOK_XOR: + g_assert(b); + val_b = dfilter_apply_node(b, ptree, pd); + return ( ( val_a || val_b ) && ! ( val_a && val_b ) ); + case TOK_NOT: + return (!val_a); + default: + g_assert_not_reached(); + } + g_assert_not_reached(); + return FALSE; +} + + +static void +free_array_of_byte_arrays(GArray *array) +{ + int i, len; + GByteArray *ba_ptr; + + len = array->len; + + for (i = 0; i < len ; i++) { + ba_ptr = g_array_index(array, GByteArray*, i); + g_byte_array_free(ba_ptr, TRUE); + } +} + +/* this is inefficient. I get arrays for both a and b that represent all the values present. That is, + * if a is bootp.option, e.g., i'll get an array showing all the bootp.option values in the protocol + * tree. Then I'll get an array for b, which more than likely is a single int, and then I'll compare + * them all. It makes my coding easier in the beginning, but I should change this to make it run + * faster. + */ +static gboolean +check_relation(gint operand, GNode *a, GNode *b, proto_tree *ptree, const guint8* pd) +{ + dfilter_node *node_a = (dfilter_node*) (a->data); + dfilter_node *node_b = (dfilter_node*) (b->data); + GArray *vals_a, *vals_b; + gboolean retval; + + + bytes_length = MIN(node_a->length, node_b->length); + bytes_offset = MIN(node_a->offset, node_b->offset); + if (node_a->ntype == variable) + vals_a = get_values_from_ptree(node_a, ptree, pd); + else + vals_a = get_values_from_dfilter(node_a, a); + + if (node_b->ntype == variable) + vals_b = get_values_from_ptree(node_b, ptree, pd); + else + vals_b = get_values_from_dfilter(node_b, b); + + retval = node_a->check_relation_func(operand, vals_a, vals_b); + + /* Free GByteArrays alloated by fill_array_bytes_variable() */ + if (node_a->fill_array_variable_func == fill_array_bytes_variable) { + free_array_of_byte_arrays(vals_a); + } + + if (node_b->fill_array_variable_func == fill_array_bytes_variable) { + free_array_of_byte_arrays(vals_b); + } + + g_array_free(vals_a, FALSE); + g_array_free(vals_b, FALSE); + + return retval; +} + + +static gboolean +check_existence_in_ptree(dfilter_node *dnode, proto_tree *ptree) +{ + int target; + + target = dnode->value.variable; + return proto_check_for_protocol_or_field(ptree, target); +} + +static GArray* +get_values_from_ptree(dfilter_node *dnode, proto_tree *ptree, const guint8 *pd) +{ + GArray *result_array; + GPtrArray *finfo_array; + int i, len; + field_info *finfo; + + /* Prepare the array for results */ + g_assert(dnode->elem_size > 0); + result_array = g_array_new(FALSE, FALSE, dnode->elem_size); + + /* Cull the finfos from the proto_tree */ + finfo_array = proto_get_finfo_ptr_array(ptree, dnode->value.variable); + if (!finfo_array) { + return result_array; + } + + len = g_ptr_array_len(finfo_array); + + for (i = 0; i < len; i++) { + finfo = g_ptr_array_index(finfo_array, i); + dnode->fill_array_variable_func(finfo, result_array, pd); + } + + g_ptr_array_free(finfo_array, FALSE); + + return result_array; +} + + +void +fill_array_numeric_variable(field_info *finfo, GArray *array, const guint8 *pd) +{ + g_array_append_val(array, finfo->value.numeric); +} + +void +fill_array_floating_variable(field_info *finfo, GArray *array, const guint8 *pd) +{ + g_array_append_val(array, finfo->value.floating); +} + +void +fill_array_ether_variable(field_info *finfo, GArray *array, const guint8 *pd) +{ + /* hmmm, yes, I *can* copy a pointer instead of memcpy() */ + g_array_append_val(array, finfo->value.ether); +} + +void +fill_array_ipv4_variable(field_info *finfo, GArray *array, const guint8 *pd) +{ + /* hmmm, yes, I *can* copy a pointer instead of memcpy() */ + g_array_append_val(array, finfo->value.ipv4); +} + +void +fill_array_ipv6_variable(field_info *finfo, GArray *array, const guint8 *pd) +{ + /* hmmm, yes, I *can* copy a pointer instead of memcpy() */ + g_array_append_val(array, finfo->value.ipv6); +} + +void +fill_array_string_variable(field_info *finfo, GArray *array, const guint8 *pd) +{ + g_array_append_val(array, finfo->value.string); +} + +void +fill_array_bytes_variable(field_info *finfo, GArray *array, const guint8 *pd) +{ + GByteArray *barray; + guint read_start, pkt_end; + + if (bytes_offset < 0) { + /* Handle negative byte offsets */ + bytes_offset = finfo->length + bytes_offset; + if (bytes_offset < 0) { + return; + } + } + + /* Check to make sure offset exists for this field */ + if (bytes_offset >= finfo->length) { + return; + } + + pkt_end = finfo->start + finfo->length; + read_start = finfo->start + bytes_offset; + + /* Check to make sure entire length requested is inside field */ + if (pkt_end < read_start + bytes_length) { + return; + } + + barray = g_byte_array_new(); + g_byte_array_append(barray, pd + read_start, bytes_length); + g_array_append_val(array, barray); +} + +static GArray* +get_values_from_dfilter(dfilter_node *dnode, GNode *gnode) +{ + GArray *array; + + g_assert(dnode->elem_size > 0); + array = g_array_new(FALSE, FALSE, dnode->elem_size); + + g_node_traverse(gnode, G_IN_ORDER, G_TRAVERSE_ALL, -1, + dnode->fill_array_value_func, array); + return array; +} + +gboolean fill_array_numeric_value(GNode *gnode, gpointer data) +{ + GArray *array = (GArray*)data; + dfilter_node *dnode = (dfilter_node*) (gnode->data); + + g_array_append_val(array, dnode->value.numeric); + return FALSE; /* FALSE = do not end traversal of GNode tree */ +} + +gboolean fill_array_floating_value(GNode *gnode, gpointer data) +{ + GArray *array = (GArray*)data; + dfilter_node *dnode = (dfilter_node*) (gnode->data); + + g_array_append_val(array, dnode->value.floating); + return FALSE; /* FALSE = do not end traversal of GNode tree */ +} + +gboolean fill_array_ether_value(GNode *gnode, gpointer data) +{ + GArray *array = (GArray*)data; + dfilter_node *dnode = (dfilter_node*) (gnode->data); + + g_array_append_val(array, dnode->value.ether); + + return FALSE; /* FALSE = do not end traversal of GNode tree */ +} + +gboolean fill_array_ipv4_value(GNode *gnode, gpointer data) +{ + GArray *array = (GArray*)data; + dfilter_node *dnode = (dfilter_node*) (gnode->data); + + g_array_append_val(array, dnode->value.ipv4); + + return FALSE; /* FALSE = do not end traversal of GNode tree */ +} + +gboolean fill_array_ipv6_value(GNode *gnode, gpointer data) +{ + GArray *array = (GArray*)data; + dfilter_node *dnode = (dfilter_node*) (gnode->data); + + g_array_append_val(array, dnode->value.ipv6); + + return FALSE; /* FALSE = do not end traversal of GNode tree */ +} + +gboolean fill_array_bytes_value(GNode *gnode, gpointer data) +{ + GArray *array = (GArray*)data; + dfilter_node *dnode = (dfilter_node*) (gnode->data); + GByteArray *barray = dnode->value.bytes; + + g_array_append_val(array, barray); + + return FALSE; /* FALSE = do not end traversal of GNode tree */ +} + +gboolean fill_array_string_value(GNode *gnode, gpointer data) +{ + GArray *array = (GArray*)data; + dfilter_node *dnode = (dfilter_node*) (gnode->data); + + g_array_append_val(array, dnode->value.string); + + return FALSE; /* FALSE = do not end traversal of GNode tree */ +} + +gboolean check_relation_numeric(gint operand, GArray *a, GArray *b) +{ + int i, j, len_a, len_b; + guint32 val_a; + + len_a = a->len; + len_b = b->len; + + + switch(operand) { + case TOK_EQ: + for(i = 0; i < len_a; i++) { + val_a = g_array_index(a, guint32, i); + for (j = 0; j < len_b; j++) { + if (val_a == g_array_index(b, guint32, j)) + return TRUE; + } + } + return FALSE; + + case TOK_NE: + for(i = 0; i < len_a; i++) { + val_a = g_array_index(a, guint32, i); + for (j = 0; j < len_b; j++) { + if (val_a != g_array_index(b, guint32, j)) + return TRUE; + } + } + return FALSE; + + case TOK_GT: + for(i = 0; i < len_a; i++) { + val_a = g_array_index(a, guint32, i); + for (j = 0; j < len_b; j++) { + if (val_a > g_array_index(b, guint32, j)) + return TRUE; + } + } + return FALSE; + + case TOK_GE: + for(i = 0; i < len_a; i++) { + val_a = g_array_index(a, guint32, i); + for (j = 0; j < len_b; j++) { + if (val_a >= g_array_index(b, guint32, j)) + return TRUE; + } + } + return FALSE; + + case TOK_LT: + for(i = 0; i < len_a; i++) { + val_a = g_array_index(a, guint32, i); + for (j = 0; j < len_b; j++) { + if (val_a < g_array_index(b, guint32, j)) + return TRUE; + } + } + return FALSE; + + case TOK_LE: + for(i = 0; i < len_a; i++) { + val_a = g_array_index(a, guint32, i); + for (j = 0; j < len_b; j++) { + if (val_a <= g_array_index(b, guint32, j)) + return TRUE; + } + } + return FALSE; + + default: + g_assert_not_reached(); + } + + g_assert_not_reached(); + return FALSE; +} + +gboolean check_relation_floating(gint operand, GArray *a, GArray *b) +{ + int i, j, len_a, len_b; + double val_a; + + len_a = a->len; + len_b = b->len; + + + switch(operand) { + case TOK_EQ: + for(i = 0; i < len_a; i++) { + val_a = g_array_index(a, double, i); + for (j = 0; j < len_b; j++) { + if (val_a == g_array_index(b, double, j)) + return TRUE; + } + } + return FALSE; + + case TOK_NE: + for(i = 0; i < len_a; i++) { + val_a = g_array_index(a, double, i); + for (j = 0; j < len_b; j++) { + if (val_a != g_array_index(b, double, j)) + return TRUE; + } + } + return FALSE; + + case TOK_GT: + for(i = 0; i < len_a; i++) { + val_a = g_array_index(a, double, i); + for (j = 0; j < len_b; j++) { + if (val_a > g_array_index(b, double, j)) + return TRUE; + } + } + return FALSE; + + case TOK_GE: + for(i = 0; i < len_a; i++) { + val_a = g_array_index(a, double, i); + for (j = 0; j < len_b; j++) { + if (val_a >= g_array_index(b, double, j)) + return TRUE; + } + } + return FALSE; + + case TOK_LT: + for(i = 0; i < len_a; i++) { + val_a = g_array_index(a, double, i); + for (j = 0; j < len_b; j++) { + if (val_a < g_array_index(b, double, j)) + return TRUE; + } + } + return FALSE; + + case TOK_LE: + for(i = 0; i < len_a; i++) { + val_a = g_array_index(a, double, i); + for (j = 0; j < len_b; j++) { + if (val_a <= g_array_index(b, double, j)) + return TRUE; + } + } + return FALSE; + + default: + g_assert_not_reached(); + } + + g_assert_not_reached(); + return FALSE; +} + +gboolean check_relation_ipv4(gint operand, GArray *a, GArray *b) +{ + int i, j, len_a, len_b; + ipv4_addr *ptr_a, *ptr_b; + + len_a = a->len; + len_b = b->len; + + + switch(operand) { + case TOK_EQ: + for(i = 0; i < len_a; i++) { + ptr_a = (ipv4_addr*) g_array_index_ptr(a, sizeof(ipv4_addr), i); + for (j = 0; j < len_b; j++) { + ptr_b = (ipv4_addr*) g_array_index_ptr(b, sizeof(ipv4_addr), j); + if (ipv4_addr_eq(ptr_a, ptr_b)) + return TRUE; + } + } + return FALSE; + + case TOK_NE: + for(i = 0; i < len_a; i++) { + ptr_a = (ipv4_addr*) g_array_index_ptr(a, sizeof(ipv4_addr), i); + for (j = 0; j < len_b; j++) { + ptr_b = (ipv4_addr*) g_array_index_ptr(b, sizeof(ipv4_addr), j); + if (ipv4_addr_ne(ptr_a, ptr_b)) + return TRUE; + } + } + return FALSE; + + case TOK_GT: + for(i = 0; i < len_a; i++) { + ptr_a = (ipv4_addr*) g_array_index_ptr(a, sizeof(ipv4_addr), i); + for (j = 0; j < len_b; j++) { + ptr_b = (ipv4_addr*) g_array_index_ptr(b, sizeof(ipv4_addr), j); + if (ipv4_addr_gt(ptr_a, ptr_b)) + return TRUE; + } + } + return FALSE; + + case TOK_GE: + for(i = 0; i < len_a; i++) { + ptr_a = (ipv4_addr*) g_array_index_ptr(a, sizeof(ipv4_addr), i); + for (j = 0; j < len_b; j++) { + ptr_b = (ipv4_addr*) g_array_index_ptr(b, sizeof(ipv4_addr), j); + if (ipv4_addr_ge(ptr_a, ptr_b)) + return TRUE; + } + } + return FALSE; + + case TOK_LT: + for(i = 0; i < len_a; i++) { + ptr_a = (ipv4_addr*) g_array_index_ptr(a, sizeof(ipv4_addr), i); + for (j = 0; j < len_b; j++) { + ptr_b = (ipv4_addr*) g_array_index_ptr(b, sizeof(ipv4_addr), j); + if (ipv4_addr_lt(ptr_a, ptr_b)) + return TRUE; + } + } + return FALSE; + + case TOK_LE: + for(i = 0; i < len_a; i++) { + ptr_a = (ipv4_addr*) g_array_index_ptr(a, sizeof(ipv4_addr), i); + for (j = 0; j < len_b; j++) { + ptr_b = (ipv4_addr*) g_array_index_ptr(b, sizeof(ipv4_addr), j); + if (ipv4_addr_le(ptr_a, ptr_b)) + return TRUE; + } + } + return FALSE; + } + + g_assert_not_reached(); + return FALSE; +} + +gboolean check_relation_ipv6(gint operand, GArray *a, GArray *b) +{ + int i, j, len_a, len_b; + guint8 *ptr_a, *ptr_b; + + len_a = a->len; + len_b = b->len; + + + switch(operand) { + case TOK_EQ: + for(i = 0; i < len_a; i++) { + ptr_a = g_array_index_ptr(a, 16, i); + for (j = 0; j < len_b; j++) { + ptr_b = g_array_index_ptr(b, 16, j); + if (memcmp(ptr_a, ptr_b, 16) == 0) + return TRUE; + } + } + return FALSE; + + case TOK_NE: + for(i = 0; i < len_a; i++) { + ptr_a = g_array_index_ptr(a, 16, i); + for (j = 0; j < len_b; j++) { + ptr_b = g_array_index_ptr(b, 16, j); + if (memcmp(ptr_a, ptr_b, 16) != 0) + return TRUE; + } + } + return FALSE; + } + + g_assert_not_reached(); + return FALSE; +} + +gboolean check_relation_ether(gint operand, GArray *a, GArray *b) +{ + int i, j, len_a, len_b; + guint8 *ptr_a, *ptr_b; + + len_a = a->len; + len_b = b->len; + + + switch(operand) { + case TOK_EQ: + for(i = 0; i < len_a; i++) { + ptr_a = g_array_index_ptr(a, 6, i); + for (j = 0; j < len_b; j++) { + ptr_b = g_array_index_ptr(b, 6, j); + if (memcmp(ptr_a, ptr_b, 6) == 0) + return TRUE; + } + } + return FALSE; + + case TOK_NE: + for(i = 0; i < len_a; i++) { + ptr_a = g_array_index_ptr(a, 6, i); + for (j = 0; j < len_b; j++) { + ptr_b = g_array_index_ptr(b, 6, j); + if (memcmp(ptr_a, ptr_b, 6) != 0) + return TRUE; + } + } + return FALSE; + } + + g_assert_not_reached(); + return FALSE; +} + +gboolean check_relation_bytes(gint operand, GArray *a, GArray *b) +{ + int i, j, len_a, len_b; + GByteArray *ptr_a,*ptr_b; + + len_a = a->len; + len_b = b->len; + + + switch(operand) { + case TOK_EQ: + for(i = 0; i < len_a; i++) { + ptr_a = g_array_index(a, GByteArray*, i); + for (j = 0; j < len_b; j++) { + ptr_b = g_array_index(b, GByteArray*, j); + if (memcmp(ptr_a->data, ptr_b->data, bytes_length) == 0) + return TRUE; + } + } + return FALSE; + + case TOK_NE: + for(i = 0; i < len_a; i++) { + ptr_a = g_array_index(a, GByteArray*, i); + for (j = 0; j < len_b; j++) { + ptr_b = g_array_index(b, GByteArray*, j); + if (memcmp(ptr_a->data, ptr_b->data, bytes_length) != 0) + return TRUE; + } + } + return FALSE; + + case TOK_GT: + for(i = 0; i < len_a; i++) { + ptr_a = g_array_index(a, GByteArray*, i); + for (j = 0; j < len_b; j++) { + ptr_b = g_array_index(b, GByteArray*, j); + if (memcmp(ptr_a->data, ptr_b->data, bytes_length) > 0) + return TRUE; + } + } + return FALSE; + + case TOK_LT: + for(i = 0; i < len_a; i++) { + ptr_a = g_array_index(a, GByteArray*, i); + for (j = 0; j < len_b; j++) { + ptr_b = g_array_index(b, GByteArray*, j); + if (memcmp(ptr_a->data, ptr_b->data, bytes_length) < 0) + return TRUE; + } + } + return FALSE; + } + + g_assert_not_reached(); + return FALSE; +} + +gboolean check_relation_string(gint operand, GArray *a, GArray *b) +{ + int i, j, len_a, len_b; + char *ptr_a, *ptr_b; + + len_a = a->len; + len_b = b->len; + + + switch(operand) { + case TOK_EQ: + for(i = 0; i < len_a; i++) { + ptr_a = g_array_index(a, char*, i); + for (j = 0; j < len_b; j++) { + ptr_b = g_array_index(b, char*, j); + if (strcmp(ptr_a, ptr_b) == 0) + return TRUE; + } + } + return FALSE; + + case TOK_NE: + for(i = 0; i < len_a; i++) { + ptr_a = g_array_index(a, char*, i); + for (j = 0; j < len_b; j++) { + ptr_b = g_array_index(b, char*, j); + if (strcmp(ptr_a, ptr_b) != 0) + return TRUE; + } + } + return FALSE; + } + + g_assert_not_reached(); + return FALSE; +} + diff --git a/epan/dfilter.h b/epan/dfilter.h new file mode 100644 index 0000000000..200430001e --- /dev/null +++ b/epan/dfilter.h @@ -0,0 +1,70 @@ +/* dfilter.h + * Definitions for display filters + * + * $Id: dfilter.h,v 1.1 2000/09/27 04:54:49 gram Exp $ + * + * Ethereal - Network traffic analyzer + * By Gerald Combs + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __DFILTER_H__ +#define __DFILTER_H__ + +#include "proto.h" + +/* dfilter_error_msg is NULL if there was no error during dfilter_compile, + * otherwise it points to a displayable error message. */ +extern gchar *dfilter_error_msg; +extern gchar dfilter_error_msg_buf[1024]; + +typedef struct { + + GNode *dftree; + + /* space for dfilter_nodes */ + GMemChunk *node_memchunk; + + /* list of byte arrays we allocate during parse. We can traverse this list + * faster than the tree when we go back and free the byte arrays */ + GSList *list_of_byte_arrays; + + /* List of strings allocated during parse. */ + GSList *list_of_strings; + +} dfilter; + +/* Initialization of the symbol table. Called once during program startup */ +void dfilter_init(void); + +/* Free the memory used by the symbol table. Called at program shutdown */ +void dfilter_cleanup(void); + +/* Allocate and initialize new dfilter struct. Returns pointer to new dfilter */ +dfilter* dfilter_new(void); + +/* Frees all memory used by dfilter, and frees dfilter itself */ +void dfilter_destroy(dfilter *df); + +/* Compile display filter text */ +int dfilter_compile(gchar* dfilter_text, dfilter** dfp); + +/* Apply compiled dfilter to a proto_tree */ +gboolean dfilter_apply(dfilter *df, proto_tree *ptree, const guint8* pd, guint pd_len); + +#endif /* ! __DFILTER_H__ */ diff --git a/epan/epan.c b/epan/epan.c new file mode 100644 index 0000000000..d8ad05b6b6 --- /dev/null +++ b/epan/epan.c @@ -0,0 +1,41 @@ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#include "conversation.h" +#include "dfilter.h" +#include "except.h" +#include "proto.h" +#include "tvbuff.h" + +void +epan_init(void) +{ + except_init(); + tvbuff_init(); + proto_init(); + dfilter_init(); +#ifdef HAVE_PLUGINS + init_plugins(epan); +#endif +} + +void +epan_cleanup(void) +{ + dfilter_cleanup(); + proto_cleanup(); + tvbuff_cleanup(); + except_deinit(); +} + + +void +epan_conversation_init(void) +{ + conversation_init(); +} diff --git a/epan/epan.h b/epan/epan.h new file mode 100644 index 0000000000..87463449ea --- /dev/null +++ b/epan/epan.h @@ -0,0 +1,49 @@ +/* epan.h + * + * Ethereal Protocol Analyzer Library + * + */ + +#ifndef EPAN_H + + +void epan_init(void); +void epan_cleanup(void); +void epan_conversation_init(void); + +/* A client will create one epan_t for an entire dissection session. + * A single epan_t will be used to analyze the entire sequence of packets, + * sequentially, in a single session. A session corresponds to a single + * packet trace file. The reaons epan_t exists is that some packets in + * some protocols cannot be decoded without knowledge of previous packets. + * This inter-packet "state" is stored in the epan_t. + */ +typedef struct epan_session epan_t; + + +epan_t* +epan_new(void); + +void +epan_free(epan_t*); + + + + +/* Dissection of a single byte array. Holds tvbuff info as + * well as proto_tree info. As long as the epan_dissect_t for a byte + * array is in existence, you must not free or move that byte array, + * as the structures that the epan_dissect_t contains might have pointers + * to addresses in your byte array. + */ +typedef struct epan_dissect epan_dissect_t; + + +epan_dissect_t* +epan_dissect_new(epan_t*, guint8* data, guint len, guint32 wtap_encap, + void* pseudo_header); + +void +epan_dissect_free(epan_t*, epan_dissect_t*); + +#endif /* EPAN_H */ diff --git a/epan/except.c b/epan/except.c new file mode 100644 index 0000000000..a9c02d74ab --- /dev/null +++ b/epan/except.c @@ -0,0 +1,415 @@ +/* + * Portable Exception Handling for ANSI C. + * Copyright (C) 1999 Kaz Kylheku + * + * Free Software License: + * + * All rights are reserved by the author, with the following exceptions: + * Permission is granted to freely reproduce and distribute this software, + * possibly in exchange for a fee, provided that this copyright notice appears + * intact. Permission is also granted to adapt this software to produce + * derivative works, as long as the modified versions carry this copyright + * notice and additional notices stating that the work has been modified. + * This source code may be translated into executable form and incorporated + * into proprietary software; there is no requirement for such software to + * contain a copyright notice related to this source. + * $Id: except.c,v 1.1 2000/09/27 04:54:49 gram Exp $ + * $Name: $ + */ + +#include +#include +#include +#include +#include +#include "except.h" + +#define XCEPT_BUFFER_SIZE 1024 + +#ifdef KAZLIB_RCSID +static const char rcsid[] = "$Id: except.c,v 1.1 2000/09/27 04:54:49 gram Exp $"; +#endif + +#define group except_group +#define code except_code +#define id except_id +#define message except_message +#define dyndata except_dyndata +#define func except_func +#define context except_context +#define id except_id +#define size except_size +#define obj except_obj +#define jmp except_jmp +#define down except_down +#define type except_type +#define catcher except_catcher +#define cleanup except_cleanup +#define info except_info + +#ifdef KAZLIB_POSIX_THREADS + +#include + +static pthread_mutex_t init_mtx = PTHREAD_MUTEX_INITIALIZER; +static int init_counter; +static pthread_key_t top_key; +static pthread_key_t uh_key; +static pthread_key_t alloc_key; +static pthread_key_t dealloc_key; +static void unhandled_catcher(except_t *); + +#define get_top() ((struct except_stacknode *) pthread_getspecific(top_key)) +#define set_top(T) (pthread_setspecific(top_key, (T)), (void)((T) == (struct except_stacknode *) 0)) +#define set_catcher(C) (pthread_setspecific(uh_key, (void *) (C)), (void)((C) == (void (*)(except_t *)) 0)) +#define set_alloc(A) (pthread_setspecific(alloc_key, (void *) (A)), (void)((A) == (void *(*)(size_t)) 0)) +#define set_dealloc(D) (pthread_setspecific(dealloc_key, (void *) (D)), (void)((D) == (void (*)(void *)) 0)) + +static void (*get_catcher(void))(except_t *) +{ + void (*catcher)(except_t *) = (void (*)(except_t *)) pthread_getspecific(uh_key); + return (catcher == 0) ? unhandled_catcher : catcher; +} + +static void *(*get_alloc(void))(size_t) +{ + void *(*alloc)(size_t) = (void *(*)(size_t)) pthread_getspecific(alloc_key); + return (alloc == 0) ? malloc : alloc; +} + +static void (*get_dealloc(void))(void *) +{ + void (*dealloc)(void *) = (void (*)(void *)) pthread_getspecific(dealloc_key); + return (dealloc == 0) ? free : dealloc; +} + +int except_init(void) +{ + int retval = 1; + + pthread_mutex_lock(&init_mtx); + + assert (init_counter < INT_MAX); + + if (init_counter++ == 0) { + int top_ok = (pthread_key_create(&top_key, 0) == 0); + int uh_ok = (pthread_key_create(&uh_key, 0) == 0); + int alloc_ok = (pthread_key_create(&alloc_key, 0) == 0); + int dealloc_ok = (pthread_key_create(&dealloc_key, 0) == 0); + + if (!top_ok || !uh_ok || !alloc_ok || !dealloc_ok) { + retval = 0; + init_counter = 0; + if (top_ok) + pthread_key_delete(top_key); + if (uh_ok) + pthread_key_delete(uh_key); + if (alloc_ok) + pthread_key_delete(alloc_key); + if (dealloc_ok) + pthread_key_delete(dealloc_key); + } + } + + pthread_mutex_unlock(&init_mtx); + + return retval; +} + +void except_deinit(void) +{ + pthread_mutex_lock(&init_mtx); + + assert (init_counter > 0); + + if (--init_counter == 0) { + pthread_key_delete(top_key); + pthread_key_delete(uh_key); + pthread_key_delete(alloc_key); + pthread_key_delete(dealloc_key); + } + + pthread_mutex_unlock(&init_mtx); +} + +#else /* no thread support */ + +static int init_counter; +static void unhandled_catcher(except_t *); +static void (*uh_catcher_ptr)(except_t *) = unhandled_catcher; +static void *(*allocator)(size_t) = malloc; +static void (*deallocator)(void *) = free; +static struct except_stacknode *stack_top; + +#define get_top() (stack_top) +#define set_top(T) (stack_top = (T)) +#define get_catcher() (uh_catcher_ptr) +#define set_catcher(C) (uh_catcher_ptr = (C)) +#define get_alloc() (allocator) +#define set_alloc(A) (allocator = (A)) +#define get_dealloc() (deallocator) +#define set_dealloc(D) (deallocator = (D)) + +int except_init(void) +{ + assert (init_counter < INT_MAX); + init_counter++; + return 1; +} + +void except_deinit(void) +{ + assert (init_counter > 0); + init_counter--; +} + +#endif + + +static int match(const except_id_t *thrown, const except_id_t *caught) +{ + int group_match = (caught->group == XCEPT_GROUP_ANY || caught->group == thrown->group); + int code_match = (caught->code == XCEPT_CODE_ANY || caught->code == thrown->code); + + return group_match && code_match; +} + +static void do_throw(except_t *except) +{ + struct except_stacknode *top; + + assert (except->id.group != 0 && except->id.code != 0); + + for (top = get_top(); top != 0; top = top->down) { + if (top->type == XCEPT_CLEANUP) { + top->info.cleanup->func(top->info.cleanup->context); + } else { + struct except_catch *catcher = top->info.catcher; + const except_id_t *pi = catcher->id; + size_t i; + + assert (top->type == XCEPT_CATCHER); + except_free(catcher->obj.dyndata); + + for (i = 0; i < catcher->size; pi++, i++) { + if (match(&except->id, pi)) { + catcher->obj = *except; + set_top(top); + longjmp(catcher->jmp, 1); + } + } + } + } + + set_top(top); + get_catcher()(except); /* unhandled exception */ + abort(); +} + +static void unhandled_catcher(except_t *except) +{ + fprintf(stderr, "Unhandled exception (\"%s\", group=%ld, code=%ld)\n", + except->message, except->id.group, except->id.code); + abort(); +} + +static void stack_push(struct except_stacknode *node) +{ + node->down = get_top(); + set_top(node); +} + +void except_setup_clean(struct except_stacknode *esn, + struct except_cleanup *ecl, void (*cleanf)(void *), void *context) +{ + esn->type = XCEPT_CLEANUP; + ecl->func = cleanf; + ecl->context = context; + esn->info.cleanup = ecl; + stack_push(esn); +} + +void except_setup_try(struct except_stacknode *esn, + struct except_catch *ech, const except_id_t id[], size_t size) +{ + ech->id = id; + ech->size = size; + ech->obj.dyndata = 0; + esn->type = XCEPT_CATCHER; + esn->info.catcher = ech; + stack_push(esn); +} + +struct except_stacknode *except_pop(void) +{ + struct except_stacknode *top = get_top(); + set_top(top->down); + return top; +} + +void except_rethrow(except_t *except) +{ + struct except_stacknode *top = get_top(); + assert (top != 0); + assert (top->type == XCEPT_CATCHER); + assert (&top->info.catcher->obj == except); + set_top(top->down); + do_throw(except); +} + +void except_throw(long group, long code, const char *msg) +{ + except_t except; + + except.id.group = group; + except.id.code = code; + except.message = msg; + except.dyndata = 0; + + do_throw(&except); +} + +void except_throwd(long group, long code, const char *msg, void *data) +{ + except_t except; + + except.id.group = group; + except.id.code = code; + except.message = msg; + except.dyndata = data; + + do_throw(&except); +} + +void except_throwf(long group, long code, const char *fmt, ...) +{ + char *buf = except_alloc(XCEPT_BUFFER_SIZE); + va_list vl; + + va_start (vl, fmt); + vsprintf(buf, fmt, vl); + va_end (vl); + except_throwd(group, code, buf, buf); +} + +void (*except_unhandled_catcher(void (*new_catcher)(except_t *)))(except_t *) +{ + void (*old_catcher)(except_t *) = get_catcher(); + set_catcher(new_catcher); + return old_catcher; +} + +#undef except_code +#undef except_group +#undef except_message +#undef except_data + +unsigned long except_code(except_t *ex) +{ + return ex->id.code; +} + +unsigned long except_group(except_t *ex) +{ + return ex->id.group; +} + +const char *except_message(except_t *ex) +{ + return ex->message; +} + +void *except_data(except_t *ex) +{ + return ex->dyndata; +} + +void *except_take_data(except_t *ex) +{ + void *data = ex->dyndata; + ex->dyndata = 0; + return data; +} + +void except_set_allocator(void *(*alloc)(size_t), void (*dealloc)(void *)) +{ + set_alloc(alloc); + set_dealloc(dealloc); +} + +void *except_alloc(size_t size) +{ + void *ptr = get_alloc()(size); + + if (ptr == 0) + except_throw(XCEPT_BAD_ALLOC, 0, "out of memory"); + return ptr; +} + +void except_free(void *ptr) +{ + get_dealloc()(ptr); +} + +#ifdef KAZLIB_TEST_MAIN + +#include +#include + +static void cleanup(void *arg) +{ + printf("cleanup(\"%s\") called\n", (char *) arg); +} + +static void bottom_level(void) +{ + char buf[256]; + printf("throw exception? "); fflush(stdout); + fgets(buf, sizeof buf, stdin); + + if (toupper(buf[0]) == 'Y') + except_throw(1, 1, "nasty exception"); +} + +static void top_level(void) +{ + except_cleanup_push(cleanup, "argument"); + bottom_level(); + except_cleanup_pop(0); +} + +int main(int argc, char **argv) +{ + static const except_id_t catch[] = { { 1, 1 }, { 1, 2 } }; + except_t *ex; + + /* + * Nested exception ``try blocks'' + */ + + /* outer */ + except_try_push(catch, 2, &ex); + if (!ex) { + /* inner */ + except_try_push(catch, 2, &ex); + if (!ex) { + top_level(); + } else { + /* inner catch */ + printf("caught exception (inner): \"%s\", s=%ld, c=%ld\n", + except_message(ex), except_group(ex), except_code(ex)); + except_rethrow(ex); + } + except_try_pop(); + } else { + /* outer catch */ + printf("caught exception (outer): \"%s\", s=%ld, c=%ld\n", + except_message(ex), except_group(ex), except_code(ex)); + } + except_try_pop(); + except_throw(99, 99, "exception in main"); + return 0; +} + + +#endif diff --git a/epan/except.h b/epan/except.h new file mode 100644 index 0000000000..bb2e6edcf4 --- /dev/null +++ b/epan/except.h @@ -0,0 +1,149 @@ +/* + * Portable Exception Handling for ANSI C. + * Copyright (C) 1999 Kaz Kylheku + * + * Free Software License: + * + * All rights are reserved by the author, with the following exceptions: + * Permission is granted to freely reproduce and distribute this software, + * possibly in exchange for a fee, provided that this copyright notice appears + * intact. Permission is also granted to adapt this software to produce + * derivative works, as long as the modified versions carry this copyright + * notice and additional notices stating that the work has been modified. + * This source code may be translated into executable form and incorporated + * into proprietary software; there is no requirement for such software to + * contain a copyright notice related to this source. + * + * $Id: except.h,v 1.1 2000/09/27 04:54:50 gram Exp $ + * $Name: $ + */ + +#ifndef XCEPT_H +#define XCEPT_H + +#include +#include +#include + +#define XCEPT_GROUP_ANY 0 +#define XCEPT_CODE_ANY 0 +#define XCEPT_BAD_ALLOC 1 + +#ifdef __cplusplus +extern "C" { +#endif + +enum { except_no_call, except_call }; + +typedef struct { + unsigned long except_group; + unsigned long except_code; +} except_id_t; + +typedef struct { + except_id_t except_id; + const char *except_message; + void *except_dyndata; +} except_t; + +struct except_cleanup { + void (*except_func)(void *); + void *except_context; +}; + +struct except_catch { + const except_id_t *except_id; + size_t except_size; + except_t except_obj; + jmp_buf except_jmp; +}; + +enum except_stacktype { + XCEPT_CLEANUP, XCEPT_CATCHER +}; + +struct except_stacknode { + struct except_stacknode *except_down; + enum except_stacktype except_type; + union { + struct except_catch *except_catcher; + struct except_cleanup *except_cleanup; + } except_info; +}; + +/* private functions made external so they can be used in macros */ +void except_setup_clean(struct except_stacknode *, + struct except_cleanup *, void (*)(void *), void *); +void except_setup_try(struct except_stacknode *, + struct except_catch *, const except_id_t [], size_t); +struct except_stacknode *except_pop(void); + +/* public interface functions */ +int except_init(void); +void except_deinit(void); +void except_rethrow(except_t *); +void except_throw(long, long, const char *); +void except_throwd(long, long, const char *, void *); +void except_throwf(long, long, const char *, ...); +void (*except_unhandled_catcher(void (*)(except_t *)))(except_t *); +unsigned long except_code(except_t *); +unsigned long except_group(except_t *); +const char *except_message(except_t *); +void *except_data(except_t *); +void *except_take_data(except_t *); +void except_set_allocator(void *(*)(size_t), void (*)(void *)); +void *except_alloc(size_t); +void except_free(void *); + +#define except_code(E) ((E)->except_id.except_code) +#define except_group(E) ((E)->except_id.except_group) +#define except_message(E) ((E)->except_message) +#define except_data(E) ((E)->except_dyndata) + +#ifdef __cplusplus +} +#endif + +/* + * void except_cleanup_push(void (*)(void *), void *); + * void except_cleanup_pop(int); + * void except_checked_cleanup_pop(void (*)(void *), int); + * void except_try_push(const except_id_t [], size_t, except_t **); + * void except_try_pop(void); + */ + +#define except_cleanup_push(F, C) \ + { \ + struct except_stacknode except_sn; \ + struct except_cleanup except_cl; \ + except_setup_clean(&except_sn, &except_cl, F, C) + +#define except_cleanup_pop(E) \ + except_pop(); \ + if (E) \ + except_cl.except_func(except_cl.except_context); \ + } + +#define except_checked_cleanup_pop(F, E) \ + except_pop(); \ + assert (except_cl.except_func == (F)); \ + if (E) \ + except_cl.except_func(except_cl.except_context); \ + } + +#define except_try_push(ID, NUM, PPE) \ + { \ + struct except_stacknode except_sn; \ + struct except_catch except_ch; \ + except_setup_try(&except_sn, &except_ch, ID, NUM); \ + if (setjmp(except_ch.except_jmp)) \ + *(PPE) = &except_ch.except_obj; \ + else \ + *(PPE) = 0 + +#define except_try_pop() \ + except_free(except_ch.except_obj.except_dyndata); \ + except_pop(); \ + } + +#endif diff --git a/epan/exceptions.h b/epan/exceptions.h new file mode 100644 index 0000000000..19eda9fe2b --- /dev/null +++ b/epan/exceptions.h @@ -0,0 +1,203 @@ +#ifndef __EXCEPTIONS_H__ +#define __EXCEPTIONS_H__ + +#ifndef XCEPT_H +#include "except.h" +#endif + +/* Ethereal has only one exception group, to make these macros simple */ +#define XCEPT_GROUP_ETHEREAL 1 + +/* Ethereal's exceptions */ +#define BoundsError 1 /* Index is out of range */ +#define ReportedBoundsError 2 /* Index is beyond reported length (not cap_len) */ + +/* Usage: + * + * TRY { + * code; + * } + * + * CATCH(exception) { + * code; + * } + * + * CATCH2(exception1, exception2) { + * code; + * } + * + * CATCH_ALL { + * code; + * } + * + * FINALLY { + * code; + * } + * + * ENDTRY; + * + * ********* Never use 'goto' or 'return' inside the TRY, CATCH, CATCH_ALL, + * ********* or FINALLY blocks. Execution must proceed through ENDTRY before + * ********* branching out. + * + * This is really something like: + * + * { + * x = setjmp() + * if (x == 0) { + * + * } + * else if (x == 1) { + * + * } + * else if (x == 2) { + * + * } + * else if (x == 3 || x == 4) { + * + * } + * else { + * { + * } + * + * } + * + * All CATCH's must precede a CATCH_ALL. + * FINALLY must occur after any CATCH or CATCH_ALL. + * ENDTRY marks the end of the TRY code. + * TRY and ENDTRY are the mandatory parts of a TRY block. + * CATCH, CATCH_ALL, and FINALLY are all optional (although + * you'll probably use at least one, otherwise why "TRY"?) + * + * GET_MESSAGE returns string ptr to exception message + * when exception is thrown via THROW_MESSAGE() + * + * To throw/raise an exception. + * + * THROW(exception) + * RETHROW rethrow the caught exception + * + * A cleanup callback is a function called in case an exception occurs + * and is not caught. It should be used to free any dynamically-allocated data. + * A pop or call_and_pop should occur at the same statement-nesting level + * as the push. + * + * CLEANUP_CB_PUSH(func, data) + * CLEANUP_CB_POP + * CLEANUP_CB_CALL_AND_POP + */ + + + +#define TRY \ +{\ + except_t *exc; \ + static const except_id_t catch_spec[] = { \ + { XCEPT_GROUP_ETHEREAL, XCEPT_CODE_ANY } }; \ + except_try_push(catch_spec, 1, &exc); \ + if (exc == 0) { \ + /* user's code goes here */ + +#define ENDTRY \ + } \ + except_try_pop();\ +} + +#define CATCH(x) \ + } \ + else if (exc->except_id.except_code == (x)) { \ + /* user's code goes here */ + +#define CATCH2(x,y) \ + } \ + else if (exc->except_id.except_code == (x) || exc->except_id.except_code == (y)) { \ + /* user's code goes here */ + +#define CATCH_ALL \ + } \ + else { \ + /* user's code goes here */ + +#define FINALLY \ + } \ + { \ + /* user's code goes here */ + +#define THROW(x) \ + except_throw(XCEPT_GROUP_ETHEREAL, (x), "XCEPT_GROUP_ETHEREAL") + +#define THROW_MESSAGE(x, y) \ + except_throw(XCEPT_GROUP_ETHEREAL, (x), (y)) + +#define GET_MESSAGE except_message(exc) + +#define RETHROW except_rethrow(exc) + +/* Register cleanup functions in case an exception is thrown and not caught. + * From the Kazlib documentation, with modifications for use with the + * Ethereal-specific macros: + * + * CLEANUP_PUSH(func, arg) + * + * The call to CLEANUP_PUSH shall be matched with a call to + * CLEANUP_CALL_AND_POP or CLEANUP_POP which must occur in the same + * statement block at the same level of nesting. This requirement allows + * an implementation to provide a CLEANUP_PUSH macro which opens up a + * statement block and a CLEANUP_POP which closes the statement block. + * The space for the registered pointers can then be efficiently + * allocated from automatic storage. + * + * The CLEANUP_PUSH macro registers a cleanup handler that will be + * called if an exception subsequently occurs before the matching + * CLEANUP_[CALL_AND_]POP is executed, and is not intercepted and + * handled by a try-catch region that is nested between the two. + * + * The first argument to CLEANUP_PUSH is a pointer to the cleanup + * handler, a function that returns nothing and takes a single + * argument of type void*. The second argument is a void* value that + * is registered along with the handler. This value is what is passed + * to the registered handler, should it be called. + * + * Cleanup handlers are called in the reverse order of their nesting: + * inner handlers are called before outer handlers. + * + * The program shall not leave the cleanup region between + * the call to the macro CLEANUP_PUSH and the matching call to + * CLEANUP_[CALL_AND_]POP by means other than throwing an exception, + * or calling CLEANUP_[CALL_AND_]POP. + * + * Within the call to the cleanup handler, it is possible that new + * exceptions may happen. Such exceptions must be handled before the + * cleanup handler terminates. If the call to the cleanup handler is + * terminated by an exception, the behavior is undefined. The exception + * which triggered the cleanup is not yet caught; thus the program + * would be effectively trying to replace an exception with one that + * isn't in a well-defined state. + * + * + * CLEANUP_POP and CLEANUP_CALL_AND_POP + * + * A call to the CLEANUP_POP or CLEANUP_CALL_AND_POP macro shall match + * each call to CLEANUP_PUSH which shall be in the same statement block + * at the same nesting level. It shall match the most recent such a + * call that is not matched by a previous CLEANUP_[CALL_AND_]POP at + * the same level. + * + * These macros causes the registered cleanup handler to be removed. If + * CLEANUP_CALL_AND_POP is called, the cleanup handler is called. + * In that case, the registered context pointer is passed to the cleanup + * handler. If CLEANUP_POP is called, the cleanup handler is not called. + * + * The program shall not leave the region between the call to the + * macro CLEANUP_PUSH and the matching call to CLEANUP_[CALL_AND_]POP + * other than by throwing an exception, or by executing the + * CLEANUP_CALL_AND_POP. + * + */ + + +#define CLEANUP_PUSH(f,a) except_cleanup_push((f),(a)) +#define CLEANUP_POP except_cleanup_pop(0) +#define CLEANUP_CALL_AND_POP except_cleanup_pop(1) + +#endif /* __EXCEPTIONS_H__ */ diff --git a/epan/pint.h b/epan/pint.h new file mode 100644 index 0000000000..38f9201ffc --- /dev/null +++ b/epan/pint.h @@ -0,0 +1,115 @@ +/* pint.h + * Definitions for extracting and translating integers safely and portably + * via pointers. + * + * $Id: pint.h,v 1.1 2000/09/27 04:54:50 gram Exp $ + * + * Ethereal - Network traffic analyzer + * By Gerald Combs + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __PINT_H__ +#define __PINT_H__ + +#include + +/* Pointer versions of ntohs and ntohl. Given a pointer to a member of a + * byte array, returns the value of the two or four bytes at the pointer. + * The pletoh[sl] versions return the little-endian representation. + * + * If G_HAVE_GINT64 is defined, so we can use "gint64" and "guint64" to + * refer to 64-bit integral quantities, we also provide pntohll and + * phtolell, which extract 64-bit integral quantities. + */ + +#define pntohs(p) ((guint16) \ + ((guint16)*((guint8 *)(p)+0)<<8| \ + (guint16)*((guint8 *)(p)+1)<<0)) + +#define pntoh24(p) ((guint32)*((guint8 *)(p)+0)<<16| \ + (guint32)*((guint8 *)(p)+1)<<8| \ + (guint32)*((guint8 *)(p)+2)<<0) + +#define pntohl(p) ((guint32)*((guint8 *)(p)+0)<<24| \ + (guint32)*((guint8 *)(p)+1)<<16| \ + (guint32)*((guint8 *)(p)+2)<<8| \ + (guint32)*((guint8 *)(p)+3)<<0) + +#ifdef G_HAVE_GINT64 +#define pntohll(p) ((guint64)*((guint8 *)(p)+0)<<56| \ + (guint64)*((guint8 *)(p)+1)<<48| \ + (guint64)*((guint8 *)(p)+2)<<40| \ + (guint64)*((guint8 *)(p)+3)<<32| \ + (guint64)*((guint8 *)(p)+4)<<24| \ + (guint64)*((guint8 *)(p)+5)<<16| \ + (guint64)*((guint8 *)(p)+6)<<8| \ + (guint64)*((guint8 *)(p)+7)<<0) +#endif + +#define pletohs(p) ((guint16) \ + ((guint16)*((guint8 *)(p)+1)<<8| \ + (guint16)*((guint8 *)(p)+0)<<0)) + +#define pletoh24(p) ((guint32)*((guint8 *)(p)+2)<<16| \ + (guint32)*((guint8 *)(p)+1)<<8| \ + (guint32)*((guint8 *)(p)+0)<<0) + +#define pletohl(p) ((guint32)*((guint8 *)(p)+3)<<24| \ + (guint32)*((guint8 *)(p)+2)<<16| \ + (guint32)*((guint8 *)(p)+1)<<8| \ + (guint32)*((guint8 *)(p)+0)<<0) + +#ifdef G_HAVE_GINT64 +#define pletohll(p) ((guint64)*((guint8 *)(p)+7)<<56| \ + (guint64)*((guint8 *)(p)+6)<<48| \ + (guint64)*((guint8 *)(p)+5)<<40| \ + (guint64)*((guint8 *)(p)+4)<<32| \ + (guint64)*((guint8 *)(p)+3)<<24| \ + (guint64)*((guint8 *)(p)+2)<<16| \ + (guint64)*((guint8 *)(p)+1)<<8| \ + (guint64)*((guint8 *)(p)+0)<<0) +#endif + + +/* Macros to byte-swap 32-bit and 16-bit quantities. */ +#define BSWAP32(x) \ + ((((x)&0xFF000000)>>24) | \ + (((x)&0x00FF0000)>>8) | \ + (((x)&0x0000FF00)<<8) | \ + (((x)&0x000000FF)<<24)) +#define BSWAP16(x) \ + ((((x)&0xFF00)>>8) | \ + (((x)&0x00FF)<<8)) + +/* Turn host-byte-order values into little-endian values. */ +#ifdef WORDS_BIGENDIAN +#define htoles(s) ((guint16) \ + ((guint16)((s) & 0x00FF)<<8| \ + (guint16)((s) & 0xFF00)>>8)) + +#define htolel(l) ((guint32)((l) & 0x000000FF)<<24| \ + (guint32)((l) & 0x0000FF00)<<8| \ + (guint32)((l) & 0x00FF0000)>>8| \ + (guint32)((l) & 0xFF000000)>>24) +#else +#define htoles(s) (s) +#define htolel(l) (l) +#endif + +#endif /* PINT_H */ diff --git a/epan/plugins.c b/epan/plugins.c new file mode 100644 index 0000000000..8ef8800844 --- /dev/null +++ b/epan/plugins.c @@ -0,0 +1,586 @@ +/* plugins.c + * plugin routines + * + * $Id: plugins.c,v 1.1 2000/09/27 04:54:50 gram Exp $ + * + * Ethereal - Network traffic analyzer + * By Gerald Combs + * Copyright 1999 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 "plugins.h" + +#ifdef HAVE_PLUGINS + +#include + +#ifdef HAVE_DIRENT_H +#include +#endif + +#ifdef HAVE_DIRECT_H +#include +#endif + +#include +#include + +#ifdef HAVE_SYS_STAT_H +#include +#endif + +#ifdef HAVE_SYS_TYPES_H +#include +#endif + +#ifdef HAVE_FCNTL_H +#include +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif + +#include "globals.h" +#include "util.h" + +#ifdef PLUGINS_NEED_ADDRESS_TABLE +#include "plugins/plugin_table.h" +plugin_address_table_t patable; +#endif + +/* linked list of all plugins */ +plugin *plugin_list; +guint32 enabled_plugins_number; + +#ifdef WIN32 +static gchar std_plug_dir[] = "c:/program files/ethereal/plugins/0.8.12"; +static gchar local_plug_dir[] = "c:/ethereal/plugins/0.8.12"; +#else +static gchar std_plug_dir[] = "/usr/lib/ethereal/plugins/0.8.12"; +static gchar local_plug_dir[] = "/usr/local/lib/ethereal/plugins/0.8.12"; +#endif +static gchar *user_plug_dir = NULL; +static gchar *plugin_status_file = NULL; + +#define PLUGINS_STATUS "plugins.status" +#define PLUGINS_DIR_NAME "plugins" + +/* + * add a new plugin to the list + * returns : + * - 0 : OK + * - ENOMEM : memory allocation problem + * - EEXIST : the same plugin (i.e. name/version) was already registered. + */ +int +add_plugin(void *handle, gchar *name, gchar *version, gchar *protocol, + gchar *filter_string, dfilter *filter, + void (*dissector) (const u_char *, + int, + frame_data *, + proto_tree *)) +{ + plugin *new_plug, *pt_plug; + + pt_plug = plugin_list; + if (!pt_plug) /* the list is empty */ + { + new_plug = (plugin *)g_malloc(sizeof(plugin)); + if (new_plug == NULL) return ENOMEM; + plugin_list = new_plug; + } + else + { + while (1) + { + /* check if the same name/version is already registered */ + if (!strcmp(pt_plug->name, name) && + !strcmp(pt_plug->version, version)) + { + return EEXIST; + } + + /* we found the last plugin in the list */ + if (pt_plug->next == NULL) break; + + pt_plug = pt_plug->next; + } + new_plug = (plugin *)g_malloc(sizeof(plugin)); + if (new_plug == NULL) return ENOMEM; + pt_plug->next = new_plug; + } + + new_plug->handle = handle; + new_plug->name = name; + new_plug->version = version; + new_plug->enabled = FALSE; + new_plug->protocol = protocol; + new_plug->filter_string = g_strdup(filter_string); + new_plug->filter = filter; + new_plug->dissector = dissector; + new_plug->next = NULL; + return 0; +} + +/* + * enable a plugin + * returns a pointer to the enabled plugin, or NULL if the plugin wasn't found + * in the list + */ +void * +enable_plugin(const gchar *name, const gchar *version) +{ + plugin *pt_plug; + + pt_plug = plugin_list; + while (pt_plug) + { + if (!strcmp(pt_plug->name, name) && !strcmp(pt_plug->version, version)) + { + pt_plug->enabled = TRUE; + enabled_plugins_number++; + return pt_plug; + } + pt_plug = pt_plug->next; + } + return NULL; +} + +/* + * disable a plugin + * returns a pointer to the disabled plugin, or NULL if the plugin wasn't found + * in the list + */ +void * +disable_plugin(const gchar *name, const gchar *version) +{ + plugin *pt_plug; + + pt_plug = plugin_list; + while (pt_plug) + { + if (!strcmp(pt_plug->name, name) && !strcmp(pt_plug->version, version)) + { + pt_plug->enabled = FALSE; + enabled_plugins_number--; + return pt_plug; + } + pt_plug = pt_plug->next; + } + return NULL; +} + +/* + * find a plugin using its name/version + */ +void * +find_plugin(const gchar *name, const gchar *version) +{ + plugin *pt_plug; + + pt_plug = plugin_list; + while (pt_plug) + { + if (!strcmp(pt_plug->name, name) && !strcmp(pt_plug->version, version)) + { + return pt_plug; + } + pt_plug = pt_plug->next; + } + return NULL; +} + +/* + * check if a plugin is enabled + */ +gboolean +is_enabled(const gchar *name, const gchar *version) +{ + plugin *pt_plug; + + pt_plug = plugin_list; + while (pt_plug) + { + if (!strcmp(pt_plug->name, name) && !strcmp(pt_plug->version, version)) + return pt_plug->enabled; + pt_plug = pt_plug->next; + } + return FALSE; +} + +/* + * replace the filter used by a plugin (filter string and dfilter) + */ +void +plugin_replace_filter(const gchar *name, const gchar *version, + const gchar *filter_string, dfilter *filter) +{ + plugin *pt_plug; + + pt_plug = plugin_list; + while (pt_plug) + { + if (!strcmp(pt_plug->name, name) && !strcmp(pt_plug->version, version)) + { + g_free(pt_plug->filter_string); + pt_plug->filter_string = g_strdup(filter_string); + dfilter_destroy(pt_plug->filter); + pt_plug->filter = filter; + return; + } + pt_plug = pt_plug->next; + } +} + +/* + * save plugin status, returns 0 on success, -1 on failure: + * file format : + * for each plugin, two lines are saved : + * plugin_name plugin_version [0|1] (0: disabled, 1: enabled) + * filter_string + * + * Ex : + * gryphon.so 0.8.0 1 + * tcp.port == 7000 + */ + +int +save_plugin_status() +{ + gchar *pf_path; + FILE *statusfile; + plugin *pt_plug; + + if (!plugin_status_file) { + plugin_status_file = (gchar *)g_malloc(strlen(get_home_dir()) + + strlen(PF_DIR) + + strlen(PLUGINS_STATUS) + 3); + sprintf(plugin_status_file, "%s/%s/%s", + get_home_dir(), PF_DIR, PLUGINS_STATUS); + } + statusfile=fopen(plugin_status_file, "w"); + if (!statusfile) { + pf_path = g_malloc(strlen(get_home_dir()) + strlen(PF_DIR) + 2); + sprintf(pf_path, "%s/%s", get_home_dir(), PF_DIR); +#ifdef WIN32 + mkdir(pf_path); +#else + mkdir(pf_path, 0755); +#endif + g_free(pf_path); + statusfile=fopen(plugin_status_file, "w"); + if (!statusfile) return -1; + } + + pt_plug = plugin_list; + while (pt_plug) + { + fprintf(statusfile,"%s %s %s\n%s\n", pt_plug->name, pt_plug->version, + (pt_plug->enabled ? "1" : "0"), pt_plug->filter_string); + pt_plug = pt_plug->next; + } + fclose(statusfile); + return 0; +} + +/* + * Check if the status of this plugin has been saved. + * If necessary, enable the plugin, and change the filter. + */ +static void +check_plugin_status(gchar *name, gchar *version, GModule *handle, + gchar *filter_string, FILE *statusfile) +{ + gchar *ref_string; + guint16 ref_string_len; + gchar line[512]; + void (*plugin_init)(void*); + dfilter *filter; + + if (!statusfile) return; + + ref_string = (gchar *)g_malloc(strlen(name) + strlen(version) + 2); + ref_string_len = sprintf(ref_string, "%s %s", name, version); + + while (!feof(statusfile)) + { + if (fgets(line, 512, statusfile) == NULL) return; + if (strncmp(line, ref_string, ref_string_len) != 0) { /* not the right plugin */ + if (fgets(line, 512, statusfile) == NULL) return; + } + else { /* found the plugin */ + if (line[ref_string_len+1] == '1') { + enable_plugin(name, version); + if (g_module_symbol(handle, "plugin_init", (gpointer*)&plugin_init) == TRUE) { +#ifdef PLUGINS_NEED_ADDRESS_TABLE + plugin_init(&patable); +#else + plugin_init(NULL); +#endif + } +#ifdef PLUGINS_NEED_ADDRESS_TABLE + else { + return; + } +#endif + } + + if (fgets(line, 512, statusfile) == NULL) return; + if (line[strlen(line)-1] == '\n') line[strlen(line)-1] = '\0'; + /* only compile the new filter if it is different from the default */ + if (strcmp(line, filter_string) && dfilter_compile(line, &filter) == 0) + plugin_replace_filter(name, version, line, filter); + return; + } + } + g_free(ref_string); +} + +static void +plugins_scan_dir(const char *dirname) +{ +#define FILENAME_LEN 1024 + DIR *dir; /* scanned directory */ + struct dirent *file; /* current file */ + gchar filename[FILENAME_LEN]; /* current file name */ + GModule *handle; /* handle returned by dlopen */ + gchar *name; + gchar *version; + gchar *protocol; + gchar *filter_string; + gchar *dot; + dfilter *filter = NULL; + void (*dissector) (const u_char *, int, frame_data *, proto_tree *); + int cr; + FILE *statusfile; + +#ifdef WIN32 +#define LT_LIB_EXT ".dll" +#else +#define LT_LIB_EXT ".so" +#endif + + if (!plugin_status_file) + { + plugin_status_file = (gchar *)g_malloc(strlen(get_home_dir()) + + strlen(PF_DIR) + + strlen(PLUGINS_STATUS) + 3); + sprintf(plugin_status_file, "%s/%s/%s", + get_home_dir(), PF_DIR, PLUGINS_STATUS); + } + statusfile = fopen(plugin_status_file, "r"); + + if ((dir = opendir(dirname)) != NULL) + { + while ((file = readdir(dir)) != NULL) + { + /* don't try to open "." and ".." */ + if (!(strcmp(file->d_name, "..") && + strcmp(file->d_name, "."))) continue; + + /* skip anything but files with LT_LIB_EXT */ + dot = strrchr(file->d_name, '.'); + if (dot == NULL || strcmp(dot, LT_LIB_EXT) != 0) continue; + + snprintf(filename, FILENAME_LEN, "%s/%s", dirname, file->d_name); + if ((handle = g_module_open(filename, 0)) == NULL) continue; + name = (gchar *)file->d_name; + if (g_module_symbol(handle, "version", (gpointer*)&version) == FALSE) + { + g_warning("The plugin %s has no version symbol", name); + g_module_close(handle); + continue; + } + if (g_module_symbol(handle, "protocol", (gpointer*)&protocol) == FALSE) + { + g_warning("The plugin %s has no protocol symbol", name); + g_module_close(handle); + continue; + } + if (g_module_symbol(handle, "filter_string", (gpointer*)&filter_string) == FALSE) + { + g_warning("The plugin %s has no filter_string symbol", name); + g_module_close(handle); + continue; + } + if (dfilter_compile(filter_string, &filter) != 0) { + g_warning("The plugin %s has a non compilable filter", name); + g_module_close(handle); + continue; + } + if (g_module_symbol(handle, "dissector", (gpointer*)&dissector) == FALSE) { + if (filter != NULL) + dfilter_destroy(filter); + g_warning("The plugin %s has no dissector symbol", name); + g_module_close(handle); + continue; + } + + if ((cr = add_plugin(handle, g_strdup(file->d_name), version, + protocol, filter_string, filter, dissector))) + { + if (cr == EEXIST) + fprintf(stderr, "The plugin : %s, version %s\n" + "was found in multiple directories\n", name, version); + else + fprintf(stderr, "Memory allocation problem\n" + "when processing plugin %s, version %sn", + name, version); + if (filter != NULL) + dfilter_destroy(filter); + g_module_close(handle); + continue; + } + if (statusfile) { + check_plugin_status(file->d_name, version, handle, + filter_string, statusfile); + rewind(statusfile); + } + } + closedir(dir); + } + if (statusfile) fclose(statusfile); +} + +/* + * init plugins + */ +void +init_plugins() +{ + struct stat std_dir_stat, local_dir_stat, plugin_dir_stat; + + if (plugin_list == NULL) /* ensure init_plugins is only run once */ + { + enabled_plugins_number = 0; + +#ifdef PLUGINS_NEED_ADDRESS_TABLE + /* Intialize address table */ + patable.p_check_col = check_col; + patable.p_col_add_fstr = col_add_fstr; + patable.p_col_append_fstr = col_append_str; + patable.p_col_add_str = col_add_str; + patable.p_col_append_str = col_append_str; + + patable.p_dfilter_init = dfilter_init; + patable.p_dfilter_cleanup = dfilter_cleanup; + + patable.p_pi = π + + patable.p_proto_register_protocol = proto_register_protocol; + patable.p_proto_register_field_array = proto_register_field_array; + patable.p_proto_register_subtree_array = proto_register_subtree_array; + + patable.p_dissector_add = dissector_add; + + patable.p_heur_dissector_add = heur_dissector_add; + + patable.p_proto_item_add_subtree = proto_item_add_subtree; + patable.p_proto_tree_add_item = proto_tree_add_item; + patable.p_proto_tree_add_item_hidden = proto_tree_add_item_hidden; + patable.p_proto_tree_add_protocol_format = proto_tree_add_protocol_format; + patable.p_proto_tree_add_bytes = proto_tree_add_bytes; + patable.p_proto_tree_add_bytes_hidden = proto_tree_add_bytes_hidden; + patable.p_proto_tree_add_bytes_format = proto_tree_add_bytes_format; + patable.p_proto_tree_add_time = proto_tree_add_time; + patable.p_proto_tree_add_time_hidden = proto_tree_add_time_hidden; + patable.p_proto_tree_add_time_format = proto_tree_add_time_format; + patable.p_proto_tree_add_ipxnet = proto_tree_add_ipxnet; + patable.p_proto_tree_add_ipxnet_hidden = proto_tree_add_ipxnet_hidden; + patable.p_proto_tree_add_ipxnet_format = proto_tree_add_ipxnet_format; + patable.p_proto_tree_add_ipv4 = proto_tree_add_ipv4; + patable.p_proto_tree_add_ipv4_hidden = proto_tree_add_ipv4_hidden; + patable.p_proto_tree_add_ipv4_format = proto_tree_add_ipv4_format; + patable.p_proto_tree_add_ipv6 = proto_tree_add_ipv6; + patable.p_proto_tree_add_ipv6_hidden = proto_tree_add_ipv6_hidden; + patable.p_proto_tree_add_ipv6_format = proto_tree_add_ipv6_format; + patable.p_proto_tree_add_ether = proto_tree_add_ether; + patable.p_proto_tree_add_ether_hidden = proto_tree_add_ether_hidden; + patable.p_proto_tree_add_ether_format = proto_tree_add_ether_format; + patable.p_proto_tree_add_string = proto_tree_add_string; + patable.p_proto_tree_add_string_hidden = proto_tree_add_string_hidden; + patable.p_proto_tree_add_string_format = proto_tree_add_string_format; + patable.p_proto_tree_add_boolean = proto_tree_add_boolean; + patable.p_proto_tree_add_boolean_hidden = proto_tree_add_boolean_hidden; + patable.p_proto_tree_add_boolean_format = proto_tree_add_boolean_format; + patable.p_proto_tree_add_double = proto_tree_add_double; + patable.p_proto_tree_add_double_hidden = proto_tree_add_double_hidden; + patable.p_proto_tree_add_double_format = proto_tree_add_double_format; + patable.p_proto_tree_add_uint = proto_tree_add_uint; + patable.p_proto_tree_add_uint_hidden = proto_tree_add_uint_hidden; + patable.p_proto_tree_add_uint_format = proto_tree_add_uint_format; + patable.p_proto_tree_add_int = proto_tree_add_int; + patable.p_proto_tree_add_int_hidden = proto_tree_add_int_hidden; + patable.p_proto_tree_add_int_format = proto_tree_add_int_format; + patable.p_proto_tree_add_text = proto_tree_add_text; + patable.p_proto_tree_add_notext = proto_tree_add_notext; +#endif + + plugins_scan_dir(std_plug_dir); + plugins_scan_dir(local_plug_dir); + if ((strcmp(std_plug_dir, PLUGIN_DIR) != 0) && + (strcmp(local_plug_dir, PLUGIN_DIR) != 0)) + { + if (stat(PLUGIN_DIR, &plugin_dir_stat) == 0) + { + /* check if PLUGIN_DIR is really different from std_dir and + * local_dir if they exist ! */ + if (stat(std_plug_dir, &std_dir_stat) == 0) + { + if (stat(local_plug_dir, &local_dir_stat) == 0) + { + if ((plugin_dir_stat.st_dev != std_dir_stat.st_dev || + plugin_dir_stat.st_ino != std_dir_stat.st_ino) && + (plugin_dir_stat.st_dev != local_dir_stat.st_dev || + plugin_dir_stat.st_ino != local_dir_stat.st_ino)) + plugins_scan_dir(PLUGIN_DIR); + } + else + { + if ((plugin_dir_stat.st_dev != std_dir_stat.st_dev || + plugin_dir_stat.st_ino != std_dir_stat.st_ino)) + plugins_scan_dir(PLUGIN_DIR); + } + } + else if (stat(local_plug_dir, &local_dir_stat) == 0) + { + if ((plugin_dir_stat.st_dev != local_dir_stat.st_dev || + plugin_dir_stat.st_ino != local_dir_stat.st_ino)) + plugins_scan_dir(PLUGIN_DIR); + } + else plugins_scan_dir(PLUGIN_DIR); + } + } + if (!user_plug_dir) + { + user_plug_dir = (gchar *)g_malloc(strlen(get_home_dir()) + + strlen(PF_DIR) + + strlen(PLUGINS_DIR_NAME) + 3); + sprintf(user_plug_dir, "%s/%s/%s", get_home_dir(), + PF_DIR, PLUGINS_DIR_NAME); + } + plugins_scan_dir(user_plug_dir); + } +} + +#endif diff --git a/epan/plugins.h b/epan/plugins.h new file mode 100644 index 0000000000..f66674aaeb --- /dev/null +++ b/epan/plugins.h @@ -0,0 +1,74 @@ +/* plugins.h + * definitions for plugins structures + * + * $Id: plugins.h,v 1.1 2000/09/27 04:54:51 gram Exp $ + * + * Ethereal - Network traffic analyzer + * By Gerald Combs + * Copyright 1999 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 __PLUGINS_H__ +#define __PLUGINS_H__ + +#include +#include + +#ifdef HAVE_CONFIG_H +#include "config.h" +#ifdef HAVE_DLFCN_H +#define HAVE_PLUGINS 1 +#endif +#endif /* HAVE_CONFIG_H */ + +#include "dfilter.h" +#include "packet.h" + +#ifdef HAVE_WINSOCK_H +#include +#endif + +typedef struct _plugin { + GModule *handle; /* handle returned by dlopen */ + gchar *name; /* plugin name */ + gchar *version; /* plugin version */ + gboolean enabled; /* is it active ? */ + gchar *protocol; /* protocol which should call the dissector + * for this plugin eg "tcp" */ + gchar *filter_string; /* display filter string matching frames for + * which the dissector should be used */ + dfilter *filter; /* compiled display filter */ + /* the dissector */ + void (*dissector) (const u_char *, int, frame_data *, proto_tree *); + struct _plugin *next; /* forward link */ +} plugin; + +extern plugin *plugin_list; +extern guint32 enabled_plugins_number; + +int add_plugin(void *, gchar *, gchar *, gchar *, gchar *, dfilter *, + void (*) (const u_char *, int, frame_data *, proto_tree *)); +void *enable_plugin(const gchar *, const gchar *); +void *disable_plugin(const gchar *, const gchar *); +void *find_plugin(const gchar *, const gchar *); +gboolean is_enabled(const gchar *, const gchar *); +void plugin_replace_filter(const gchar *, const gchar *, const gchar *, dfilter *); +int save_plugin_status(); +void init_plugins(); + +#endif /* __PLUGINS_H__ */ diff --git a/epan/proto.c b/epan/proto.c new file mode 100644 index 0000000000..99c5c16867 --- /dev/null +++ b/epan/proto.c @@ -0,0 +1,2480 @@ +/* proto.c + * Routines for protocol tree + * + * $Id: proto.c,v 1.1 2000/09/27 04:54:51 gram Exp $ + * + * Ethereal - Network traffic analyzer + * By Gerald Combs + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_SYS_TYPES_H +# include +#endif + +#include +#include +#include + +#ifdef NEED_SNPRINTF_H +# include "snprintf.h" +#endif + +#include "packet.h" +#include "resolv.h" +#include "register.h" +#include "packet-ipv6.h" +#include "proto.h" + +#define cVALS(x) (const value_string*)(x) + +static gboolean +proto_tree_free_node(GNode *node, gpointer data); + +static void fill_label_boolean(field_info *fi, gchar *label_str); +static void fill_label_uint(field_info *fi, gchar *label_str); +static void fill_label_enumerated_uint(field_info *fi, gchar *label_str); +static void fill_label_enumerated_bitfield(field_info *fi, gchar *label_str); +static void fill_label_numeric_bitfield(field_info *fi, gchar *label_str); +static void fill_label_int(field_info *fi, gchar *label_str); +static void fill_label_enumerated_int(field_info *fi, gchar *label_str); + +static int hfinfo_bitwidth(header_field_info *hfinfo); +static char* hfinfo_uint_vals_format(header_field_info *hfinfo); +static char* hfinfo_uint_format(header_field_info *hfinfo); +static char* hfinfo_int_vals_format(header_field_info *hfinfo); +static char* hfinfo_int_format(header_field_info *hfinfo); + +static gboolean check_for_protocol_or_field_id(GNode *node, gpointer data); + +static proto_item* +proto_tree_add_node(proto_tree *tree, field_info *fi); + +static field_info * +alloc_field_info(int hfindex, tvbuff_t *tvb, gint start, gint length); + +static proto_item * +proto_tree_add_pi(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, gint length, + field_info **pfi); +static void +proto_tree_set_representation(proto_item *pi, const char *format, va_list ap); + +static void +proto_tree_set_bytes(field_info *fi, const guint8* start_ptr, gint length); +static void +proto_tree_set_bytes_tvb(field_info *fi, tvbuff_t *tvb, gint offset, gint length); +static void +proto_tree_set_time(field_info *fi, struct timeval *value_ptr); +static void +proto_tree_set_string(field_info *fi, const char* value); +static void +proto_tree_set_string_tvb(field_info *fi, tvbuff_t *tvb, gint start, gint length); +static void +proto_tree_set_ether(field_info *fi, const guint8* value); +static void +proto_tree_set_ether_tvb(field_info *fi, tvbuff_t *tvb, gint start); +static void +proto_tree_set_ipxnet(field_info *fi, guint32 value); +static void +proto_tree_set_ipv4(field_info *fi, guint32 value); +static void +proto_tree_set_ipv6(field_info *fi, const guint8* value_ptr); +static void +proto_tree_set_ipv6_tvb(field_info *fi, tvbuff_t *tvb, gint start); +static void +proto_tree_set_boolean(field_info *fi, guint32 value); +static void +proto_tree_set_double(field_info *fi, double value); +static void +proto_tree_set_uint(field_info *fi, guint32 value); +static void +proto_tree_set_int(field_info *fi, gint32 value); + +static int proto_register_field_init(header_field_info *hfinfo, int parent); + +/* special-case header field used within proto.c */ +int hf_text_only = 1; + +/* Contains information about protocols and header fields. Used when + * dissectors register their data */ +GMemChunk *gmc_hfinfo = NULL; + +/* Contains information about a field when a dissector calls + * proto_tree_add_item. */ +GMemChunk *gmc_field_info = NULL; + +/* String space for protocol and field items for the GUI */ +GMemChunk *gmc_item_labels = NULL; + +/* List which stores protocols and fields that have been registered */ +GPtrArray *gpa_hfinfo = NULL; + +/* Points to the first element of an array of Booleans, indexed by + a subtree item type; that array element is TRUE if subtrees of + an item of that type are to be expanded. */ +gboolean *tree_is_expanded; + +/* Number of elements in that array. */ +int num_tree_types; + +/* Is the parsing being done for a visible proto_tree or an invisible one? + * By setting this correctly, the proto_tree creation is sped up by not + * having to call vsnprintf and copy strings around. + */ +gboolean proto_tree_is_visible = FALSE; + +/* initialize data structures and register protocols and fields */ +void +proto_init(void) +{ + static hf_register_info hf[] = { + { &hf_text_only, + { "Text", "text", FT_TEXT_ONLY, BASE_NONE, NULL, 0x0, + "" }}, + }; + + if (gmc_hfinfo) + g_mem_chunk_destroy(gmc_hfinfo); + if (gmc_field_info) + g_mem_chunk_destroy(gmc_field_info); + if (gmc_item_labels) + g_mem_chunk_destroy(gmc_item_labels); + if (gpa_hfinfo) + g_ptr_array_free(gpa_hfinfo, FALSE); + if (tree_is_expanded != NULL) + g_free(tree_is_expanded); + + gmc_hfinfo = g_mem_chunk_new("gmc_hfinfo", + sizeof(struct header_field_info), 50 * sizeof(struct + header_field_info), G_ALLOC_ONLY); + gmc_field_info = g_mem_chunk_new("gmc_field_info", + sizeof(struct field_info), 200 * sizeof(struct field_info), + G_ALLOC_AND_FREE); + gmc_item_labels = g_mem_chunk_new("gmc_item_labels", + ITEM_LABEL_LENGTH, 20 * ITEM_LABEL_LENGTH, + G_ALLOC_AND_FREE); + gpa_hfinfo = g_ptr_array_new(); + + /* Allocate "tree_is_expanded", with one element for ETT_NONE, + and initialize that element to FALSE. */ + tree_is_expanded = g_malloc(sizeof (gint)); + tree_is_expanded[0] = FALSE; + num_tree_types = 1; + + /* Have each dissector register its protocols and fields, and + do whatever one-time initialization it needs to do. */ + register_all_protocols(); + + /* Now have the ones that register a "handoff", i.e. that + specify that another dissector for a protocol under which + this dissector's protocol lives call it. */ + register_all_protocol_handoffs(); + + /* Register one special-case FT_TEXT_ONLY field for use when + converting ethereal to new-style proto_tree. These fields + are merely strings on the GUI tree; they are not filterable */ + proto_register_field_array(-1, hf, array_length(hf)); + + /* We've assigned all the subtree type values; allocate the array + for them, and zero it out. */ + tree_is_expanded = g_malloc(num_tree_types*sizeof (gint *)); + memset(tree_is_expanded, '\0', num_tree_types*sizeof (gint *)); +} + +void +proto_cleanup(void) +{ + if (gmc_hfinfo) + g_mem_chunk_destroy(gmc_hfinfo); + if (gmc_field_info) + g_mem_chunk_destroy(gmc_field_info); + if (gmc_item_labels) + g_mem_chunk_destroy(gmc_item_labels); + if (gpa_hfinfo) + g_ptr_array_free(gpa_hfinfo, FALSE); +} + +/* frees the resources that the dissection a proto_tree uses */ +void +proto_tree_free(proto_tree *tree) +{ + g_node_traverse((GNode*)tree, G_IN_ORDER, G_TRAVERSE_ALL, -1, + proto_tree_free_node, NULL); + g_node_destroy((GNode*)tree); +} + +/* We accept a void* instead of a field_info* to satisfy CLEANUP_POP */ +static void +free_field_info(void *fi) +{ + g_mem_chunk_free(gmc_field_info, (field_info*)fi); +} + +static gboolean +proto_tree_free_node(GNode *node, gpointer data) +{ + field_info *fi = (field_info*) (node->data); + + if (fi != NULL) { + if (fi->representation) + g_mem_chunk_free(gmc_item_labels, fi->representation); + if (fi->hfinfo->type == FT_STRING) + g_free(fi->value.string); + else if (fi->hfinfo->type == FT_STRINGZ) + g_free(fi->value.string); + else if (fi->hfinfo->type == FT_UINT_STRING) + g_free(fi->value.string); + else if (fi->hfinfo->type == FT_BYTES) + g_free(fi->value.bytes); + free_field_info(fi); + } + return FALSE; /* FALSE = do not end traversal of GNode tree */ +} + +/* Finds a record in the hf_info_records array by id. */ +struct header_field_info* +proto_registrar_get_nth(int hfindex) +{ + g_assert(hfindex >= 0 && hfindex < gpa_hfinfo->len); + return g_ptr_array_index(gpa_hfinfo, hfindex); +} + + +/* Add a node with no text */ +proto_item * +proto_tree_add_notext(proto_tree *tree, tvbuff_t *tvb, gint start, gint length) +{ + proto_item *pi; + + pi = proto_tree_add_pi(tree, hf_text_only, tvb, start, length, NULL); + if (pi == NULL) + return(NULL); + + return pi; +} + +/* Add a text-only node to the proto_tree */ +proto_item * +proto_tree_add_text(proto_tree *tree, tvbuff_t *tvb, gint start, gint length, + const char *format, ...) +{ + proto_item *pi; + va_list ap; + + pi = proto_tree_add_notext(tree, tvb, start, length); + if (pi == NULL) + return(NULL); + + va_start(ap, format); + proto_tree_set_representation(pi, format, ap); + va_end(ap); + + return pi; +} + +/* Add a text-only node to the proto_tree (va_list version) */ +proto_item * +proto_tree_add_text_valist(proto_tree *tree, tvbuff_t *tvb, gint start, + gint length, const char *format, va_list ap) +{ + proto_item *pi; + + pi = proto_tree_add_notext(tree, tvb, start, length); + if (pi == NULL) + return(NULL); + + proto_tree_set_representation(pi, format, ap); + + return pi; +} + +/* Add a text-only node for debugging purposes. The caller doesn't need + * to worry about tvbuff, start, or length. Debug message gets sent to + * STDOUT, too */ +proto_item * +proto_tree_add_debug_text(proto_tree *tree, const char *format, ...) +{ + proto_item *pi; + va_list ap; + + pi = proto_tree_add_notext(tree, NULL, 0, 0); + if (pi == NULL) + return(NULL); + + va_start(ap, format); + proto_tree_set_representation(pi, format, ap); + vprintf(format, ap); + va_end(ap); + printf("\n"); + + return pi; +} + + +static guint32 +get_uint_value(tvbuff_t *tvb, gint offset, gint length, gboolean little_endian) +{ + guint32 value; + + switch (length) { + + case 1: + value = tvb_get_guint8(tvb, offset); + break; + + case 2: + value = little_endian ? tvb_get_letohs(tvb, offset) + : tvb_get_ntohs(tvb, offset); + break; + + case 3: + value = little_endian ? tvb_get_letoh24(tvb, offset) + : tvb_get_ntoh24(tvb, offset); + break; + + case 4: + value = little_endian ? tvb_get_letohl(tvb, offset) + : tvb_get_ntohl(tvb, offset); + break; + + default: + g_assert_not_reached(); + value = 0; + break; + } + return value; +} + +static gint32 +get_int_value(tvbuff_t *tvb, gint offset, gint length, gboolean little_endian) +{ + gint32 value; + + switch (length) { + + case 1: + value = (gint8)tvb_get_guint8(tvb, offset); + break; + + case 2: + value = (gint16) (little_endian ? tvb_get_letohs(tvb, offset) + : tvb_get_ntohs(tvb, offset)); + break; + + case 3: + value = little_endian ? tvb_get_letoh24(tvb, offset) + : tvb_get_ntoh24(tvb, offset); + if (value & 0x00800000) { + /* Sign bit is set; sign-extend it. */ + value |= 0xFF000000; + } + break; + + case 4: + value = little_endian ? tvb_get_letohl(tvb, offset) + : tvb_get_ntohl(tvb, offset); + break; + + default: + g_assert_not_reached(); + value = 0; + break; + } + return value; +} + +/* Add an item to a proto_tree, using the text label registered to that item; + the item is extracted from the tvbuff handed to it. */ +proto_item * +proto_tree_add_item(proto_tree *tree, int hfindex, tvbuff_t *tvb, + gint start, gint length, gboolean little_endian) +{ + field_info *new_fi; + proto_item *pi; + guint32 value, n; + char *string; + int found_length; + + new_fi = alloc_field_info(hfindex, tvb, start, length); + + if (new_fi == NULL) + return(NULL); + + /* Register a cleanup function in case on of our tvbuff accesses + * throws an exception. We need to clean up new_fi. */ + CLEANUP_PUSH(free_field_info, new_fi); + + switch(new_fi->hfinfo->type) { + case FT_NONE: + /* no value to set for FT_NONE */ + break; + + case FT_BYTES: + proto_tree_set_bytes_tvb(new_fi, tvb, start, length); + break; + + case FT_BOOLEAN: + proto_tree_set_boolean(new_fi, + get_uint_value(tvb, start, length, little_endian)); + break; + + /* XXX - make these just FT_UINT? */ + case FT_UINT8: + case FT_UINT16: + case FT_UINT24: + case FT_UINT32: + proto_tree_set_uint(new_fi, + get_uint_value(tvb, start, length, little_endian)); + break; + + /* XXX - make these just FT_INT? */ + case FT_INT8: + case FT_INT16: + case FT_INT24: + case FT_INT32: + proto_tree_set_int(new_fi, + get_int_value(tvb, start, length, little_endian)); + break; + + case FT_IPv4: + g_assert(length == 4); + tvb_memcpy(tvb, (guint8 *)&value, start, 4); + proto_tree_set_ipv4(new_fi, value); + break; + + case FT_IPXNET: + g_assert(length == 4); + proto_tree_set_ipxnet(new_fi, + get_uint_value(tvb, start, 4, FALSE)); + break; + + case FT_IPv6: + g_assert(length == 16); + proto_tree_set_ipv6_tvb(new_fi, tvb, start); + break; + + case FT_ETHER: + g_assert(length == 6); + proto_tree_set_ether_tvb(new_fi, tvb, start); + break; + + case FT_STRING: + /* This g_strdup'ed memory is freed in proto_tree_free_node() */ + proto_tree_set_string_tvb(new_fi, tvb, start, length); + break; + + case FT_STRINGZ: + /* This g_strdup'ed memory is freed in proto_tree_free_node() */ + string = g_malloc(length); + + CLEANUP_PUSH(g_free, string); + + found_length = tvb_get_nstringz(tvb, start, length, string); + if (found_length < 1) { + found_length = tvb_get_nstringz0(tvb, start, length, string); + } + + CLEANUP_POP; + + proto_tree_set_string(new_fi, string); + new_fi->length = found_length + 1; + + break; + + case FT_UINT_STRING: + /* This g_strdup'ed memory is freed in proto_tree_free_node() */ + n = get_uint_value(tvb, start, length, little_endian); + proto_tree_set_string_tvb(new_fi, tvb, start + 1, n); + + /* Instead of calling proto_item_set_len(), since we don't yet + * have a proto_item, we set the field_info's length ourselves. */ + new_fi->length = n + 1; + break; + default: + g_error("new_fi->hfinfo->type %d (%s) not handled\n", + new_fi->hfinfo->type, + proto_registrar_ftype_name(new_fi->hfinfo->type)); + g_assert_not_reached(); + break; + + } + CLEANUP_POP; + + /* Don't add to proto_item to proto_tree until now so that any exceptions + * raised by a tvbuff access method doesn't leave junk in the proto_tree. */ + pi = proto_tree_add_node(tree, new_fi); + + return pi; +} + +proto_item * +proto_tree_add_item_hidden(proto_tree *tree, int hfindex, tvbuff_t *tvb, + gint start, gint length, gboolean little_endian) +{ + proto_item *pi; + field_info *fi; + + pi = proto_tree_add_item(tree, hfindex, tvb, start, length, little_endian); + if (pi == NULL) + return(NULL); + + fi = (field_info*) (((GNode*)pi)->data); + fi->visible = FALSE; + + return pi; +} + + +/* Add a FT_NONE to a proto_tree */ +proto_item * +proto_tree_add_protocol_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, + gint length, const char *format, ...) +{ + proto_item *pi; + va_list ap; + header_field_info *hfinfo; + + if (!tree) + return (NULL); + + hfinfo = proto_registrar_get_nth(hfindex); + g_assert(hfinfo->type == FT_NONE); + + pi = proto_tree_add_pi(tree, hfindex, tvb, start, length, NULL); + + va_start(ap, format); + proto_tree_set_representation(pi, format, ap); + va_end(ap); + + /* no value to set for FT_NONE */ + + return pi; +} + +/* Add a FT_BYTES to a proto_tree */ +proto_item * +proto_tree_add_bytes(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, + gint length, const guint8 *start_ptr) +{ + proto_item *pi; + field_info *new_fi; + header_field_info *hfinfo; + + if (!tree) + return (NULL); + + hfinfo = proto_registrar_get_nth(hfindex); + g_assert(hfinfo->type == FT_BYTES); + + pi = proto_tree_add_pi(tree, hfindex, tvb, start, length, &new_fi); + proto_tree_set_bytes(new_fi, start_ptr, length); + + return pi; +} + +proto_item * +proto_tree_add_bytes_hidden(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, + gint length, const guint8 *start_ptr) +{ + proto_item *pi; + field_info *fi; + + pi = proto_tree_add_bytes(tree, hfindex, tvb, start, length, start_ptr); + if (pi == NULL) + return (NULL); + + fi = (field_info*) (((GNode*)pi)->data); + fi->visible = FALSE; + + return pi; +} + +proto_item * +proto_tree_add_bytes_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, + gint length, const guint8 *start_ptr, const char *format, ...) +{ + proto_item *pi; + va_list ap; + + pi = proto_tree_add_bytes(tree, hfindex, tvb, start, length, start_ptr); + if (pi == NULL) + return (NULL); + + va_start(ap, format); + proto_tree_set_representation(pi, format, ap); + va_end(ap); + + return pi; +} + +/* Set the FT_BYTES value */ +static void +proto_tree_set_bytes(field_info *fi, const guint8* start_ptr, gint length) +{ + g_assert(start_ptr != NULL); + + if (length > 0) { + /* This g_malloc'ed memory is freed in + proto_tree_free_node() */ + fi->value.bytes = g_malloc(length); + memcpy(fi->value.bytes, start_ptr, length); + } + else { + fi->value.bytes = NULL; + } +} + +static void +proto_tree_set_bytes_tvb(field_info *fi, tvbuff_t *tvb, gint offset, gint length) +{ + if (length > 0) { + /* This g_malloc'ed memory is freed in + proto_tree_free_node() */ + fi->value.bytes = tvb_memdup(tvb, offset, length); + } + else { + fi->value.bytes = NULL; + } +} + +/* Add a FT_*TIME to a proto_tree */ +proto_item * +proto_tree_add_time(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, gint length, + struct timeval *value_ptr) +{ + proto_item *pi; + field_info *new_fi; + header_field_info *hfinfo; + + if (!tree) + return (NULL); + + hfinfo = proto_registrar_get_nth(hfindex); + g_assert(hfinfo->type == FT_ABSOLUTE_TIME || + hfinfo->type == FT_RELATIVE_TIME); + + pi = proto_tree_add_pi(tree, hfindex, tvb, start, length, &new_fi); + proto_tree_set_time(new_fi, value_ptr); + + return pi; +} + +proto_item * +proto_tree_add_time_hidden(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, gint length, + struct timeval *value_ptr) +{ + proto_item *pi; + field_info *fi; + + pi = proto_tree_add_time(tree, hfindex, tvb, start, length, value_ptr); + if (pi == NULL) + return (NULL); + + fi = (field_info*) (((GNode*)pi)->data); + fi->visible = FALSE; + + return pi; +} + +proto_item * +proto_tree_add_time_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, gint length, + struct timeval *value_ptr, const char *format, ...) +{ + proto_item *pi; + va_list ap; + + pi = proto_tree_add_time(tree, hfindex, tvb, start, length, value_ptr); + if (pi == NULL) + return (NULL); + + va_start(ap, format); + proto_tree_set_representation(pi, format, ap); + va_end(ap); + + return pi; +} + +/* Set the FT_*TIME value */ +static void +proto_tree_set_time(field_info *fi, struct timeval *value_ptr) +{ + memcpy(&fi->value.time, value_ptr, sizeof(struct timeval)); +} + +/* Add a FT_IPXNET to a proto_tree */ +proto_item * +proto_tree_add_ipxnet(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, gint length, + guint32 value) +{ + proto_item *pi; + field_info *new_fi; + header_field_info *hfinfo; + + if (!tree) + return (NULL); + + hfinfo = proto_registrar_get_nth(hfindex); + g_assert(hfinfo->type == FT_IPXNET); + + pi = proto_tree_add_pi(tree, hfindex, tvb, start, length, &new_fi); + proto_tree_set_ipxnet(new_fi, value); + + return pi; +} + +proto_item * +proto_tree_add_ipxnet_hidden(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, gint length, + guint32 value) +{ + proto_item *pi; + field_info *fi; + + pi = proto_tree_add_ipxnet(tree, hfindex, tvb, start, length, value); + if (pi == NULL) + return (NULL); + + fi = (field_info*) (((GNode*)pi)->data); + fi->visible = FALSE; + + return pi; +} + +proto_item * +proto_tree_add_ipxnet_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, gint length, + guint32 value, const char *format, ...) +{ + proto_item *pi; + va_list ap; + + pi = proto_tree_add_ipxnet(tree, hfindex, tvb, start, length, value); + if (pi == NULL) + return (NULL); + + va_start(ap, format); + proto_tree_set_representation(pi, format, ap); + va_end(ap); + + return pi; +} + +/* Set the FT_IPXNET value */ +static void +proto_tree_set_ipxnet(field_info *fi, guint32 value) +{ + fi->value.numeric = value; +} + +/* Add a FT_IPv4 to a proto_tree */ +proto_item * +proto_tree_add_ipv4(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, gint length, + guint32 value) +{ + proto_item *pi; + field_info *new_fi; + header_field_info *hfinfo; + + if (!tree) + return (NULL); + + hfinfo = proto_registrar_get_nth(hfindex); + g_assert(hfinfo->type == FT_IPv4); + + pi = proto_tree_add_pi(tree, hfindex, tvb, start, length, &new_fi); + proto_tree_set_ipv4(new_fi, value); + + return pi; +} + +proto_item * +proto_tree_add_ipv4_hidden(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, gint length, + guint32 value) +{ + proto_item *pi; + field_info *fi; + + pi = proto_tree_add_ipv4(tree, hfindex, tvb, start, length, value); + if (pi == NULL) + return (NULL); + + fi = (field_info*) (((GNode*)pi)->data); + fi->visible = FALSE; + + return pi; +} + +proto_item * +proto_tree_add_ipv4_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, gint length, + guint32 value, const char *format, ...) +{ + proto_item *pi; + va_list ap; + + pi = proto_tree_add_ipv4(tree, hfindex, tvb, start, length, value); + if (pi == NULL) + return (NULL); + + va_start(ap, format); + proto_tree_set_representation(pi, format, ap); + va_end(ap); + + return pi; +} + +/* Set the FT_IPv4 value */ +static void +proto_tree_set_ipv4(field_info *fi, guint32 value) +{ + ipv4_addr_set_net_order_addr(&(fi->value.ipv4), value); + ipv4_addr_set_netmask_bits(&(fi->value.ipv4), 32); +} + +/* Add a FT_IPv6 to a proto_tree */ +proto_item * +proto_tree_add_ipv6(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, gint length, + const guint8* value_ptr) +{ + proto_item *pi; + field_info *new_fi; + header_field_info *hfinfo; + + if (!tree) + return (NULL); + + hfinfo = proto_registrar_get_nth(hfindex); + g_assert(hfinfo->type == FT_IPv6); + + pi = proto_tree_add_pi(tree, hfindex, tvb, start, length, &new_fi); + proto_tree_set_ipv6(new_fi, value_ptr); + + return pi; +} + +proto_item * +proto_tree_add_ipv6_hidden(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, gint length, + const guint8* value_ptr) +{ + proto_item *pi; + field_info *fi; + + pi = proto_tree_add_ipv6(tree, hfindex, tvb, start, length, value_ptr); + if (pi == NULL) + return (NULL); + + fi = (field_info*) (((GNode*)pi)->data); + fi->visible = FALSE; + + return pi; +} + +proto_item * +proto_tree_add_ipv6_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, gint length, + const guint8* value_ptr, const char *format, ...) +{ + proto_item *pi; + va_list ap; + + pi = proto_tree_add_ipv6(tree, hfindex, tvb, start, length, value_ptr); + if (pi == NULL) + return (NULL); + + va_start(ap, format); + proto_tree_set_representation(pi, format, ap); + va_end(ap); + + return pi; +} + +/* Set the FT_IPv6 value */ +static void +proto_tree_set_ipv6(field_info *fi, const guint8* value_ptr) +{ + memcpy(fi->value.ipv6, value_ptr, 16); +} + +static void +proto_tree_set_ipv6_tvb(field_info *fi, tvbuff_t *tvb, gint start) +{ + tvb_memcpy(tvb, fi->value.ipv6, start, 16); +} + +/* Add a FT_STRING to a proto_tree */ +proto_item * +proto_tree_add_string(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, + gint length, const char* value) +{ + proto_item *pi; + field_info *new_fi; + header_field_info *hfinfo; + + if (!tree) + return (NULL); + + hfinfo = proto_registrar_get_nth(hfindex); + g_assert(hfinfo->type == FT_STRING); + + pi = proto_tree_add_pi(tree, hfindex, tvb, start, length, &new_fi); + proto_tree_set_string(new_fi, value); + + return pi; +} + +proto_item * +proto_tree_add_string_hidden(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, + gint length, const char* value) +{ + proto_item *pi; + field_info *fi; + + pi = proto_tree_add_string(tree, hfindex, tvb, start, length, value); + if (pi == NULL) + return (NULL); + + fi = (field_info*) (((GNode*)pi)->data); + fi->visible = FALSE; + + return pi; +} + +proto_item * +proto_tree_add_string_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, + gint length, const char* value, const char *format, ...) +{ + proto_item *pi; + va_list ap; + + pi = proto_tree_add_string(tree, hfindex, tvb, start, length, value); + if (pi == NULL) + return (NULL); + + va_start(ap, format); + proto_tree_set_representation(pi, format, ap); + va_end(ap); + + return pi; +} + +/* Set the FT_STRING value */ +static void +proto_tree_set_string(field_info *fi, const char* value) +{ + /* This g_strdup'ed memory is freed in proto_tree_free_node() */ + fi->value.string = g_strdup(value); +} + +static void +proto_tree_set_string_tvb(field_info *fi, tvbuff_t *tvb, gint start, gint length) +{ + /* This memory is freed in proto_tree_free_node() */ + fi->value.string = g_malloc(length + 1); + tvb_memcpy(tvb, fi->value.string, start, length); + fi->value.string[length] = '\0'; +} + +/* Add a FT_ETHER to a proto_tree */ +proto_item * +proto_tree_add_ether(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, gint length, + const guint8* value) +{ + proto_item *pi; + field_info *new_fi; + header_field_info *hfinfo; + + if (!tree) + return (NULL); + + hfinfo = proto_registrar_get_nth(hfindex); + g_assert(hfinfo->type == FT_ETHER); + + pi = proto_tree_add_pi(tree, hfindex, tvb, start, length, &new_fi); + proto_tree_set_ether(new_fi, value); + + return pi; +} + +proto_item * +proto_tree_add_ether_hidden(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, gint length, + const guint8* value) +{ + proto_item *pi; + field_info *fi; + + pi = proto_tree_add_ether(tree, hfindex, tvb, start, length, value); + if (pi == NULL) + return (NULL); + + fi = (field_info*) (((GNode*)pi)->data); + fi->visible = FALSE; + + return pi; +} + +proto_item * +proto_tree_add_ether_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, gint length, + const guint8* value, const char *format, ...) +{ + proto_item *pi; + va_list ap; + + pi = proto_tree_add_ether(tree, hfindex, tvb, start, length, value); + if (pi == NULL) + return (NULL); + + va_start(ap, format); + proto_tree_set_representation(pi, format, ap); + va_end(ap); + + return pi; +} + +/* Set the FT_ETHER value */ +static void +proto_tree_set_ether(field_info *fi, const guint8* value) +{ + memcpy(fi->value.ether, value, 6); +} + +static void +proto_tree_set_ether_tvb(field_info *fi, tvbuff_t *tvb, gint start) +{ + tvb_memcpy(tvb, fi->value.ether, start, 6); +} + +/* Add a FT_BOOLEAN to a proto_tree */ +proto_item * +proto_tree_add_boolean(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, gint length, + guint32 value) +{ + proto_item *pi; + field_info *new_fi; + header_field_info *hfinfo; + + if (!tree) + return (NULL); + + hfinfo = proto_registrar_get_nth(hfindex); + g_assert(hfinfo->type == FT_BOOLEAN); + + pi = proto_tree_add_pi(tree, hfindex, tvb, start, length, &new_fi); + proto_tree_set_boolean(new_fi, value); + + return pi; +} + +proto_item * +proto_tree_add_boolean_hidden(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, gint length, + guint32 value) +{ + proto_item *pi; + field_info *fi; + + pi = proto_tree_add_boolean(tree, hfindex, tvb, start, length, value); + if (pi == NULL) + return (NULL); + + fi = (field_info*) (((GNode*)pi)->data); + fi->visible = FALSE; + + return pi; +} + +proto_item * +proto_tree_add_boolean_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, gint length, + guint32 value, const char *format, ...) +{ + proto_item *pi; + va_list ap; + + pi = proto_tree_add_boolean(tree, hfindex, tvb, start, length, value); + if (pi == NULL) + return (NULL); + + va_start(ap, format); + proto_tree_set_representation(pi, format, ap); + va_end(ap); + + return pi; +} + +/* Set the FT_BOOLEAN value */ +static void +proto_tree_set_boolean(field_info *fi, guint32 value) +{ + proto_tree_set_uint(fi, value); +} + +/* Add a FT_DOUBLE to a proto_tree */ +proto_item * +proto_tree_add_double(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, gint length, + double value) +{ + proto_item *pi; + field_info *new_fi; + header_field_info *hfinfo; + + if (!tree) + return (NULL); + + hfinfo = proto_registrar_get_nth(hfindex); + g_assert(hfinfo->type == FT_DOUBLE); + + pi = proto_tree_add_pi(tree, hfindex, tvb, start, length, &new_fi); + proto_tree_set_double(new_fi, value); + + return pi; +} + +proto_item * +proto_tree_add_double_hidden(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, gint length, + double value) +{ + proto_item *pi; + field_info *fi; + + pi = proto_tree_add_double(tree, hfindex, tvb, start, length, value); + if (pi == NULL) + return (NULL); + + fi = (field_info*) (((GNode*)pi)->data); + fi->visible = FALSE; + + return pi; +} + +proto_item * +proto_tree_add_double_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, gint length, + double value, const char *format, ...) +{ + proto_item *pi; + va_list ap; + + pi = proto_tree_add_double(tree, hfindex, tvb, start, length, value); + if (pi == NULL) + return (NULL); + + va_start(ap, format); + proto_tree_set_representation(pi, format, ap); + va_end(ap); + + return pi; +} + +/* Set the FT_DOUBLE value */ +static void +proto_tree_set_double(field_info *fi, double value) +{ + fi->value.floating = value; +} + +/* Add any FT_UINT* to a proto_tree */ +proto_item * +proto_tree_add_uint(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, gint length, + guint32 value) +{ + proto_item *pi = NULL; + field_info *new_fi; + header_field_info *hfinfo; + + if (!tree) + return (NULL); + + hfinfo = proto_registrar_get_nth(hfindex); + switch(hfinfo->type) { + case FT_UINT8: + case FT_UINT16: + case FT_UINT24: + case FT_UINT32: + pi = proto_tree_add_pi(tree, hfindex, tvb, start, length, + &new_fi); + proto_tree_set_uint(new_fi, value); + break; + + default: + g_assert_not_reached(); + } + + return pi; +} + +proto_item * +proto_tree_add_uint_hidden(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, gint length, + guint32 value) +{ + proto_item *pi; + field_info *fi; + + pi = proto_tree_add_uint(tree, hfindex, tvb, start, length, value); + if (pi == NULL) + return (NULL); + + fi = (field_info*) (((GNode*)pi)->data); + fi->visible = FALSE; + + return pi; +} + +proto_item * +proto_tree_add_uint_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, gint length, + guint32 value, const char *format, ...) +{ + proto_item *pi; + va_list ap; + + pi = proto_tree_add_uint(tree, hfindex, tvb, start, length, value); + if (pi == NULL) + return (NULL); + + va_start(ap, format); + proto_tree_set_representation(pi, format, ap); + va_end(ap); + + return pi; +} + +/* Set the FT_UINT* value */ +static void +proto_tree_set_uint(field_info *fi, guint32 value) +{ + header_field_info *hfinfo; + + hfinfo = fi->hfinfo; + fi->value.numeric = value; + if (hfinfo->bitmask) { + /* Mask out irrelevant portions */ + fi->value.numeric &= hfinfo->bitmask; + + /* Shift bits */ + if (hfinfo->bitshift > 0) { + fi->value.numeric >>= hfinfo->bitshift; + } + } +} + +/* Add any FT_INT* to a proto_tree */ +proto_item * +proto_tree_add_int(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, gint length, + gint32 value) +{ + proto_item *pi = NULL; + field_info *new_fi; + header_field_info *hfinfo; + + if (!tree) + return (NULL); + + hfinfo = proto_registrar_get_nth(hfindex); + switch(hfinfo->type) { + case FT_INT8: + case FT_INT16: + case FT_INT24: + case FT_INT32: + pi = proto_tree_add_pi(tree, hfindex, tvb, start, length, + &new_fi); + proto_tree_set_int(new_fi, value); + break; + + default: + g_assert_not_reached(); + } + + return pi; +} + +proto_item * +proto_tree_add_int_hidden(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, gint length, + gint32 value) +{ + proto_item *pi; + field_info *fi; + + pi = proto_tree_add_int(tree, hfindex, tvb, start, length, value); + if (pi == NULL) + return (NULL); + + fi = (field_info*) (((GNode*)pi)->data); + fi->visible = FALSE; + + return pi; +} + +proto_item * +proto_tree_add_int_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, gint length, + gint32 value, const char *format, ...) +{ + proto_item *pi = NULL; + va_list ap; + + pi = proto_tree_add_int(tree, hfindex, tvb, start, length, value); + if (pi == NULL) + return (NULL); + + va_start(ap, format); + proto_tree_set_representation(pi, format, ap); + va_end(ap); + + return pi; +} + +/* Set the FT_INT* value */ +static void +proto_tree_set_int(field_info *fi, gint32 value) +{ + header_field_info *hfinfo; + + hfinfo = fi->hfinfo; + fi->value.numeric = (guint32) value; + if (hfinfo->bitmask) { + /* Mask out irrelevant portions */ + fi->value.numeric &= hfinfo->bitmask; + + /* Shift bits */ + if (hfinfo->bitshift > 0) { + fi->value.numeric >>= hfinfo->bitshift; + } + } +} + + +/* Add a field_info struct to the proto_tree, encapsulating it in a GNode (proto_item) */ +static proto_item * +proto_tree_add_node(proto_tree *tree, field_info *fi) +{ + proto_item *pi; + + pi = (proto_item*) g_node_new(fi); + g_node_append((GNode*)tree, (GNode*)pi); + + return pi; +} + + +/* Generic way to allocate field_info and add to proto_tree. + * Sets *pfi to address of newly-allocated field_info struct, if pfi is non-NULL. */ +static proto_item * +proto_tree_add_pi(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, gint length, + field_info **pfi) +{ + proto_item *pi; + field_info *fi; + + if (!tree) + return(NULL); + + fi = alloc_field_info(hfindex, tvb, start, length); + pi = proto_tree_add_node(tree, fi); + + if (pfi) { + *pfi = fi; + } + + return pi; +} + +static field_info * +alloc_field_info(int hfindex, tvbuff_t *tvb, gint start, gint length) +{ + field_info *fi; + + fi = g_mem_chunk_alloc(gmc_field_info); + + g_assert(hfindex >= 0 && hfindex < gpa_hfinfo->len); + fi->hfinfo = proto_registrar_get_nth(hfindex); + g_assert(fi->hfinfo != NULL); + fi->start = start; + if (tvb) { + fi->start += tvb_raw_offset(tvb); + } + fi->length = length; + fi->tree_type = ETT_NONE; + fi->visible = proto_tree_is_visible; + fi->representation = NULL; + + return fi; +} + +/* Set representation of a proto_tree entry, if the protocol tree is to + be visible. */ +static void +proto_tree_set_representation(proto_item *pi, const char *format, va_list ap) +{ + field_info *fi = (field_info*) (((GNode*)pi)->data); + + if (fi->visible) { + fi->representation = g_mem_chunk_alloc(gmc_item_labels); + vsnprintf(fi->representation, ITEM_LABEL_LENGTH, format, ap); + } +} + +void +proto_item_set_text(proto_item *pi, const char *format, ...) +{ + field_info *fi = (field_info*) (((GNode*)pi)->data); + va_list ap; + + if (fi->representation) + g_mem_chunk_free(gmc_item_labels, fi->representation); + + va_start(ap, format); + proto_tree_set_representation(pi, format, ap); + va_end(ap); +} + +void +proto_item_set_len(proto_item *pi, gint length) +{ + field_info *fi = (field_info*) (((GNode*)pi)->data); + fi->length = length; +} + +int +proto_item_get_len(proto_item *pi) +{ + field_info *fi = (field_info*) (((GNode*)pi)->data); + return fi->length; +} + +proto_tree* +proto_tree_create_root(void) +{ + return (proto_tree*) g_node_new(NULL); +} + +proto_tree* +proto_item_add_subtree(proto_item *pi, gint idx) { + field_info *fi = (field_info*) (((GNode*)pi)->data); + g_assert(idx >= 0 && idx < num_tree_types); + fi->tree_type = idx; + return (proto_tree*) pi; +} + + +int +proto_register_protocol(char *name, char *abbrev) +{ + struct header_field_info *hfinfo; + + /* Here we do allocate a new header_field_info struct */ + hfinfo = g_mem_chunk_alloc(gmc_hfinfo); + hfinfo->name = name; + hfinfo->abbrev = abbrev; + hfinfo->type = FT_NONE; + hfinfo->strings = NULL; + hfinfo->bitmask = 0; + hfinfo->bitshift = 0; + hfinfo->blurb = ""; + hfinfo->parent = -1; /* this field differentiates protos and fields */ + hfinfo->display = TRUE; /* XXX protocol is enabled by default */ + + return proto_register_field_init(hfinfo, hfinfo->parent); +} + + +/* + * XXX - In the future, we might need a hash table or list of procotol + * characteristics that will be fill in each time proto_register_protocol is + * called. + * A protocol entry could contain the display flag among others (such as the + * address of the dissector function for intance). The access to an entry + * by protocol abbrev (which shall be unique) would be faster than the actual + * way. + */ + +gboolean +proto_is_protocol_enabled(int n) +{ + struct header_field_info *hfinfo; + + hfinfo = proto_registrar_get_nth(n); + if (hfinfo) + return (hfinfo->display); + else + return FALSE; + +} + +void +proto_set_decoding(int n, gboolean enabled) +{ + struct header_field_info *hfinfo; + + hfinfo = proto_registrar_get_nth(n); + if (hfinfo) + hfinfo->display = enabled; +} + +/* for use with static arrays only, since we don't allocate our own copies +of the header_field_info struct contained withing the hf_register_info struct */ +void +proto_register_field_array(int parent, hf_register_info *hf, int num_records) +{ + int field_id, i; + hf_register_info *ptr = hf; + + for (i = 0; i < num_records; i++, ptr++) { + field_id = proto_register_field_init(&ptr->hfinfo, parent); + *ptr->p_id = field_id; + } +} + +static int +proto_register_field_init(header_field_info *hfinfo, int parent) +{ + /* These types of fields are allowed to have value_strings or true_false_strings */ + g_assert((hfinfo->strings == NULL) || ( + (hfinfo->type == FT_UINT8) || + (hfinfo->type == FT_UINT16) || + (hfinfo->type == FT_UINT24) || + (hfinfo->type == FT_UINT32) || + (hfinfo->type == FT_INT8) || + (hfinfo->type == FT_INT16) || + (hfinfo->type == FT_INT24) || + (hfinfo->type == FT_INT32) || + (hfinfo->type == FT_BOOLEAN) )); + + /* if this is a bitfield, compure bitshift */ + if (hfinfo->bitmask) { + while ((hfinfo->bitmask & (1 << hfinfo->bitshift)) == 0) + hfinfo->bitshift++; + } + + hfinfo->parent = parent; + + /* if we always add and never delete, then id == len - 1 is correct */ + g_ptr_array_add(gpa_hfinfo, hfinfo); + hfinfo->id = gpa_hfinfo->len - 1; + return hfinfo->id; +} + +void +proto_register_subtree_array(gint **indices, int num_indices) +{ + int i; + gint **ptr = indices; + + /* + * Add "num_indices" elements to "tree_is_expanded". + */ + tree_is_expanded = g_realloc(tree_is_expanded, + (num_tree_types + num_indices)*sizeof (gint)); + + /* + * Assign "num_indices" subtree numbers starting at "num_tree_types", + * returning the indices through the pointers in the array whose + * first element is pointed to by "indices", set to FALSE the + * elements to which those subtree numbers refer, and update + * "num_tree_types" appropriately. + */ + for (i = 0; i < num_indices; i++, ptr++, num_tree_types++) { + tree_is_expanded[num_tree_types] = FALSE; + **ptr = num_tree_types; + } +} + +void +proto_item_fill_label(field_info *fi, gchar *label_str) +{ + struct header_field_info *hfinfo = fi->hfinfo; + guint32 n_addr; /* network-order IPv4 address */ + + switch(hfinfo->type) { + case FT_NONE: + snprintf(label_str, ITEM_LABEL_LENGTH, + "%s", hfinfo->name); + break; + + case FT_BOOLEAN: + fill_label_boolean(fi, label_str); + break; + + case FT_BYTES: + if (fi->value.bytes) { + snprintf(label_str, ITEM_LABEL_LENGTH, + "%s: %s", hfinfo->name, + bytes_to_str(fi->value.bytes, fi->length)); + } + else { + snprintf(label_str, ITEM_LABEL_LENGTH, + "%s: ", hfinfo->name); + } + break; + + /* Four types of integers to take care of: + * Bitfield, with val_string + * Bitfield, w/o val_string + * Non-bitfield, with val_string + * Non-bitfield, w/o val_string + */ + case FT_UINT8: + case FT_UINT16: + case FT_UINT24: + case FT_UINT32: + if (hfinfo->bitmask) { + if (hfinfo->strings) { + fill_label_enumerated_bitfield(fi, label_str); + } + else { + fill_label_numeric_bitfield(fi, label_str); + } + } + else { + if (hfinfo->strings) { + fill_label_enumerated_uint(fi, label_str); + } + else { + fill_label_uint(fi, label_str); + } + } + break; + + case FT_INT8: + case FT_INT16: + case FT_INT24: + case FT_INT32: + g_assert(!hfinfo->bitmask); + if (hfinfo->strings) { + fill_label_enumerated_int(fi, label_str); + } + else { + fill_label_int(fi, label_str); + } + break; + + case FT_DOUBLE: + snprintf(label_str, ITEM_LABEL_LENGTH, + "%s: %g", hfinfo->name, + fi->value.floating); + break; + + case FT_ABSOLUTE_TIME: + snprintf(label_str, ITEM_LABEL_LENGTH, + "%s: %s", hfinfo->name, + abs_time_to_str(&fi->value.time)); + break; + + case FT_RELATIVE_TIME: + snprintf(label_str, ITEM_LABEL_LENGTH, + "%s: %s seconds", hfinfo->name, + rel_time_to_str(&fi->value.time)); + break; + + case FT_IPXNET: + snprintf(label_str, ITEM_LABEL_LENGTH, + "%s: 0x%08X (%s)", hfinfo->name, + fi->value.numeric, get_ipxnet_name(fi->value.numeric)); + break; + + case FT_ETHER: + snprintf(label_str, ITEM_LABEL_LENGTH, + "%s: %s (%s)", hfinfo->name, + ether_to_str(fi->value.ether), + get_ether_name(fi->value.ether)); + break; + + case FT_IPv4: + n_addr = ipv4_get_net_order_addr(&fi->value.ipv4); + snprintf(label_str, ITEM_LABEL_LENGTH, + "%s: %s (%s)", hfinfo->name, + get_hostname(n_addr), + ip_to_str((guint8*)&n_addr)); + break; + + case FT_IPv6: + snprintf(label_str, ITEM_LABEL_LENGTH, + "%s: %s (%s)", hfinfo->name, + get_hostname6((struct e_in6_addr *)fi->value.ipv6), + ip6_to_str((struct e_in6_addr*)fi->value.ipv6)); + break; + + case FT_STRING: + case FT_STRINGZ: + case FT_UINT_STRING: + snprintf(label_str, ITEM_LABEL_LENGTH, + "%s: %s", hfinfo->name, fi->value.string); + break; + + default: + g_error("hfinfo->type %d (%s) not handled\n", + hfinfo->type, + proto_registrar_ftype_name(hfinfo->type)); + g_assert_not_reached(); + break; + } +} + +static void +fill_label_boolean(field_info *fi, gchar *label_str) +{ + char *p = label_str; + int bitfield_byte_length = 0, bitwidth; + guint32 unshifted_value; + + struct header_field_info *hfinfo = fi->hfinfo; + struct true_false_string default_tf = { "True", "False" }; + struct true_false_string *tfstring = &default_tf; + + if (hfinfo->strings) { + tfstring = (struct true_false_string*) hfinfo->strings; + } + + if (hfinfo->bitmask) { + /* Figure out the bit width */ + bitwidth = hfinfo_bitwidth(hfinfo); + + /* Un-shift bits */ + unshifted_value = fi->value.numeric; + if (hfinfo->bitshift > 0) { + unshifted_value <<= hfinfo->bitshift; + } + + /* Create the bitfield first */ + p = decode_bitfield_value(label_str, unshifted_value, hfinfo->bitmask, bitwidth); + bitfield_byte_length = p - label_str; + } + + /* Fill in the textual info */ + snprintf(p, ITEM_LABEL_LENGTH - bitfield_byte_length, + "%s: %s", hfinfo->name, + fi->value.numeric ? tfstring->true_string : tfstring->false_string); +} + + +/* Fills data for bitfield ints with val_strings */ +static void +fill_label_enumerated_bitfield(field_info *fi, gchar *label_str) +{ + char *format = NULL, *p; + int bitfield_byte_length, bitwidth; + guint32 unshifted_value; + + struct header_field_info *hfinfo = fi->hfinfo; + + /* Figure out the bit width */ + bitwidth = hfinfo_bitwidth(hfinfo); + + /* Pick the proper format string */ + format = hfinfo_uint_vals_format(hfinfo); + + /* Un-shift bits */ + unshifted_value = fi->value.numeric; + if (hfinfo->bitshift > 0) { + unshifted_value <<= hfinfo->bitshift; + } + + /* Create the bitfield first */ + p = decode_bitfield_value(label_str, unshifted_value, hfinfo->bitmask, bitwidth); + bitfield_byte_length = p - label_str; + + /* Fill in the textual info using stored (shifted) value */ + snprintf(p, ITEM_LABEL_LENGTH - bitfield_byte_length, + format, hfinfo->name, + val_to_str(fi->value.numeric, cVALS(hfinfo->strings), "Unknown"), + fi->value.numeric); +} + +static void +fill_label_numeric_bitfield(field_info *fi, gchar *label_str) +{ + char *format = NULL, *p; + int bitfield_byte_length, bitwidth; + guint32 unshifted_value; + + struct header_field_info *hfinfo = fi->hfinfo; + + /* Figure out the bit width */ + bitwidth = hfinfo_bitwidth(hfinfo); + + /* Pick the proper format string */ + format = hfinfo_uint_format(hfinfo); + + /* Un-shift bits */ + unshifted_value = fi->value.numeric; + if (hfinfo->bitshift > 0) { + unshifted_value <<= hfinfo->bitshift; + } + + /* Create the bitfield using */ + p = decode_bitfield_value(label_str, unshifted_value, hfinfo->bitmask, bitwidth); + bitfield_byte_length = p - label_str; + + /* Fill in the textual info using stored (shifted) value */ + snprintf(p, ITEM_LABEL_LENGTH - bitfield_byte_length, + format, hfinfo->name, fi->value.numeric); +} + +static void +fill_label_enumerated_uint(field_info *fi, gchar *label_str) +{ + char *format = NULL; + struct header_field_info *hfinfo = fi->hfinfo; + + /* Pick the proper format string */ + format = hfinfo_uint_vals_format(hfinfo); + + /* Fill in the textual info */ + snprintf(label_str, ITEM_LABEL_LENGTH, + format, hfinfo->name, + val_to_str(fi->value.numeric, cVALS(hfinfo->strings), "Unknown"), + fi->value.numeric); +} + +static void +fill_label_uint(field_info *fi, gchar *label_str) +{ + char *format = NULL; + struct header_field_info *hfinfo = fi->hfinfo; + + /* Pick the proper format string */ + format = hfinfo_uint_format(hfinfo); + + /* Fill in the textual info */ + snprintf(label_str, ITEM_LABEL_LENGTH, + format, hfinfo->name, fi->value.numeric); +} + +static void +fill_label_enumerated_int(field_info *fi, gchar *label_str) +{ + char *format = NULL; + struct header_field_info *hfinfo = fi->hfinfo; + + /* Pick the proper format string */ + format = hfinfo_int_vals_format(hfinfo); + + /* Fill in the textual info */ + snprintf(label_str, ITEM_LABEL_LENGTH, + format, hfinfo->name, + val_to_str(fi->value.numeric, cVALS(hfinfo->strings), "Unknown"), + fi->value.numeric); +} + +static void +fill_label_int(field_info *fi, gchar *label_str) +{ + char *format = NULL; + struct header_field_info *hfinfo = fi->hfinfo; + + /* Pick the proper format string */ + format = hfinfo_int_format(hfinfo); + + /* Fill in the textual info */ + snprintf(label_str, ITEM_LABEL_LENGTH, + format, hfinfo->name, fi->value.numeric); +} + +static int +hfinfo_bitwidth(header_field_info *hfinfo) +{ + int bitwidth = 0; + + if (!hfinfo->bitmask) { + return 0; + } + + switch(hfinfo->type) { + case FT_UINT8: + case FT_INT8: + bitwidth = 8; + break; + case FT_UINT16: + case FT_INT16: + bitwidth = 16; + break; + case FT_UINT24: + case FT_INT24: + bitwidth = 24; + break; + case FT_UINT32: + case FT_INT32: + bitwidth = 32; + break; + case FT_BOOLEAN: + bitwidth = hfinfo->display; /* hacky? :) */ + break; + default: + g_assert_not_reached(); + ; + } + return bitwidth; +} + +static char* +hfinfo_uint_vals_format(header_field_info *hfinfo) +{ + char *format = NULL; + + switch(hfinfo->display) { + case BASE_DEC: + case BASE_NONE: + case BASE_OCT: /* I'm lazy */ + case BASE_BIN: /* I'm lazy */ + format = "%s: %s (%u)"; + break; + case BASE_HEX: + switch(hfinfo->type) { + case FT_UINT8: + format = "%s: %s (0x%02x)"; + break; + case FT_UINT16: + format = "%s: %s (0x%04x)"; + break; + case FT_UINT24: + format = "%s: %s (0x%06x)"; + break; + case FT_UINT32: + format = "%s: %s (0x%08x)"; + break; + default: + g_assert_not_reached(); + ; + } + break; + default: + g_assert_not_reached(); + ; + } + return format; +} + +static char* +hfinfo_uint_format(header_field_info *hfinfo) +{ + char *format = NULL; + + /* Pick the proper format string */ + switch(hfinfo->display) { + case BASE_DEC: + case BASE_NONE: + case BASE_OCT: /* I'm lazy */ + case BASE_BIN: /* I'm lazy */ + format = "%s: %u"; + break; + case BASE_HEX: + switch(hfinfo->type) { + case FT_UINT8: + format = "%s: 0x%02x"; + break; + case FT_UINT16: + format = "%s: 0x%04x"; + break; + case FT_UINT24: + format = "%s: 0x%06x"; + break; + case FT_UINT32: + format = "%s: 0x%08x"; + break; + default: + g_assert_not_reached(); + ; + } + break; + default: + g_assert_not_reached(); + ; + } + return format; +} + +static char* +hfinfo_int_vals_format(header_field_info *hfinfo) +{ + char *format = NULL; + + switch(hfinfo->display) { + case BASE_DEC: + case BASE_NONE: + case BASE_OCT: /* I'm lazy */ + case BASE_BIN: /* I'm lazy */ + format = "%s: %s (%d)"; + break; + case BASE_HEX: + switch(hfinfo->type) { + case FT_INT8: + format = "%s: %s (0x%02x)"; + break; + case FT_INT16: + format = "%s: %s (0x%04x)"; + break; + case FT_INT24: + format = "%s: %s (0x%06x)"; + break; + case FT_INT32: + format = "%s: %s (0x%08x)"; + break; + default: + g_assert_not_reached(); + ; + } + break; + default: + g_assert_not_reached(); + ; + } + return format; +} + +static char* +hfinfo_int_format(header_field_info *hfinfo) +{ + char *format = NULL; + + /* Pick the proper format string */ + switch(hfinfo->display) { + case BASE_DEC: + case BASE_NONE: + case BASE_OCT: /* I'm lazy */ + case BASE_BIN: /* I'm lazy */ + format = "%s: %d"; + break; + case BASE_HEX: + switch(hfinfo->type) { + case FT_INT8: + format = "%s: 0x%02x"; + break; + case FT_INT16: + format = "%s: 0x%04x"; + break; + case FT_INT24: + format = "%s: 0x%06x"; + break; + case FT_INT32: + format = "%s: 0x%08x"; + break; + default: + g_assert_not_reached(); + ; + } + break; + default: + g_assert_not_reached(); + ; + } + return format; +} + + + +int +proto_registrar_n(void) +{ + return gpa_hfinfo->len; +} + +char* +proto_registrar_get_name(int n) +{ + struct header_field_info *hfinfo; + hfinfo = proto_registrar_get_nth(n); + if (hfinfo) + return hfinfo->name; + else return NULL; +} + +char* +proto_registrar_get_abbrev(int n) +{ + struct header_field_info *hfinfo; + + hfinfo = proto_registrar_get_nth(n); + if (hfinfo) + return hfinfo->abbrev; + else + return NULL; +} + +int +proto_registrar_get_ftype(int n) +{ + struct header_field_info *hfinfo; + + hfinfo = proto_registrar_get_nth(n); + if (hfinfo) + return hfinfo->type; + else + return -1; +} + +int +proto_registrar_get_parent(int n) +{ + struct header_field_info *hfinfo; + + hfinfo = proto_registrar_get_nth(n); + if (hfinfo) + return hfinfo->parent; + else + return -2; +} + +gboolean +proto_registrar_is_protocol(int n) +{ + struct header_field_info *hfinfo; + + hfinfo = proto_registrar_get_nth(n); + if (hfinfo) + return (hfinfo->parent == -1 ? TRUE : FALSE); + else + return FALSE; +} + +/* Returns length of field in packet (not necessarily the length + * in our internal representation, as in the case of IPv4). + * 0 means undeterminable at time of registration + * -1 means the field is not registered. */ +gint +proto_registrar_get_length(int n) +{ + struct header_field_info *hfinfo; + + hfinfo = proto_registrar_get_nth(n); + if (!hfinfo) + return -1; + + switch (hfinfo->type) { + case FT_TEXT_ONLY: /* not filterable */ + case NUM_FIELD_TYPES: /* satisfy picky compilers */ + return -1; + + case FT_NONE: + case FT_BYTES: + case FT_BOOLEAN: + case FT_STRING: + case FT_STRINGZ: + case FT_UINT_STRING: + case FT_DOUBLE: + case FT_ABSOLUTE_TIME: + case FT_RELATIVE_TIME: + return 0; + + case FT_UINT8: + case FT_INT8: + return 1; + + case FT_UINT16: + case FT_INT16: + return 2; + + case FT_UINT24: + case FT_INT24: + return 3; + + case FT_UINT32: + case FT_INT32: + case FT_IPXNET: + case FT_IPv4: + return 4; + + case FT_ETHER: + return 6; + + case FT_IPv6: + return 16; + } + g_assert_not_reached(); + return -1; +} + + +/* =================================================================== */ +/* used when calling proto search functions */ +typedef struct { + int target; + int parent; + const guint8 *packet_data; + guint packet_len; + gboolean halt_on_first_hit; + GNodeTraverseFunc traverse_func; /* for traverse_subtree_for_field() */ + union { + GPtrArray *ptr_array; + GNode *node; + } result; +} proto_tree_search_info; + +/* Looks for a protocol at the top layer of the tree. The protocol can occur + * more than once, for those encapsulated protocols. For each protocol subtree + * that is found, the callback function is called. + */ +static void +proto_find_protocol_multi(proto_tree* tree, int target, GNodeTraverseFunc callback, + proto_tree_search_info *sinfo) +{ + g_assert(callback != NULL); + g_node_traverse((GNode*)tree, G_IN_ORDER, G_TRAVERSE_ALL, 2, callback, (gpointer*)sinfo); +} + +/* Calls a traversal function for all subtrees where: + * 1. Subtree is immediate child of root node. That is, subtree is a "protocol" + * 2. Subtree has finfo such that finfo->hfinfo->id == sinfo->parent + */ +static gboolean +traverse_subtree_for_field(GNode *node, gpointer data) +{ + field_info *fi = (field_info*) (node->data); + proto_tree_search_info *sinfo = (proto_tree_search_info*) data; + + if (fi) { /* !fi == the top most container node which holds nothing */ + if (fi->hfinfo->id == sinfo->parent) { + g_node_traverse(node, G_IN_ORDER, G_TRAVERSE_ALL, -1, + sinfo->traverse_func, sinfo); + if (sinfo->result.node) + return sinfo->halt_on_first_hit; /* halt? continue? */ + } + } + return FALSE; /* keep traversing */ +} + +static gboolean +check_for_protocol_or_field_id(GNode *node, gpointer data) +{ + field_info *fi = (field_info*) (node->data); + proto_tree_search_info *sinfo = (proto_tree_search_info*) data; + + if (fi) { /* !fi == the top most container node which holds nothing */ + if (fi->hfinfo->id == sinfo->target) { + sinfo->result.node = node; + return TRUE; /* halt traversal */ + } + } + return FALSE; /* keep traversing */ +} + +/* Looks for a protocol or a field in a proto_tree. Returns TRUE if + * it exists anywhere, or FALSE if it exists nowhere. */ +gboolean +proto_check_for_protocol_or_field(proto_tree* tree, int id) +{ + proto_tree_search_info sinfo; + + sinfo.target = id; + sinfo.result.node = NULL; + sinfo.parent = -1; + sinfo.traverse_func = check_for_protocol_or_field_id; + sinfo.halt_on_first_hit = TRUE; + + /* do a quicker check if target is a protocol */ + if (proto_registrar_is_protocol(id) == TRUE) { + proto_find_protocol_multi(tree, id, check_for_protocol_or_field_id, &sinfo); + } + else { + /* find the field's parent protocol */ + sinfo.parent = proto_registrar_get_parent(id); + + /* Go through each protocol subtree, checking if the protocols + * is the parent protocol of the field that we're looking for. + * We may have protocols that occur more than once (e.g., IP in IP), + * so we do indeed have to check all protocol subtrees, looking + * for the parent protocol. That's why proto_find_protocol() + * is not used --- it assumes a protocol occurs only once. */ + g_node_traverse((GNode*)tree, G_IN_ORDER, G_TRAVERSE_ALL, 2, + traverse_subtree_for_field, &sinfo); + } + + if (sinfo.result.node) + return TRUE; + else + return FALSE; +} + + + +static gboolean +get_finfo_ptr_array(GNode *node, gpointer data) +{ + field_info *fi = (field_info*) (node->data); + proto_tree_search_info *sinfo = (proto_tree_search_info*) data; + + if (fi) { /* !fi == the top most container node which holds nothing */ + if (fi->hfinfo->id == sinfo->target) { + if (!sinfo->result.ptr_array) { + sinfo->result.ptr_array = g_ptr_array_new(); + } + g_ptr_array_add(sinfo->result.ptr_array, + (gpointer)fi); + return FALSE; /* keep traversing */ + } + } + return FALSE; /* keep traversing */ +} + +/* Return GPtrArray* of field_info pointers for all hfindex that appear in tree + * (we assume that a field will only appear under its registered parent's subtree) */ +GPtrArray* +proto_get_finfo_ptr_array(proto_tree *tree, int id) +{ + proto_tree_search_info sinfo; + + sinfo.target = id; + sinfo.result.ptr_array = NULL; + sinfo.parent = -1; + sinfo.traverse_func = get_finfo_ptr_array; + sinfo.halt_on_first_hit = FALSE; + + /* do a quicker check if target is a protocol */ + if (proto_registrar_is_protocol(id) == TRUE) { + proto_find_protocol_multi(tree, id, get_finfo_ptr_array, &sinfo); + } + else { + /* find the field's parent protocol */ + sinfo.parent = proto_registrar_get_parent(id); + + /* Go through each protocol subtree, checking if the protocols + * is the parent protocol of the field that we're looking for. + * We may have protocols that occur more than once (e.g., IP in IP), + * so we do indeed have to check all protocol subtrees, looking + * for the parent protocol. That's why proto_find_protocol() + * is not used --- it assumes a protocol occurs only once. */ + sinfo.traverse_func = get_finfo_ptr_array; + g_node_traverse((GNode*)tree, G_IN_ORDER, G_TRAVERSE_ALL, 2, + traverse_subtree_for_field, &sinfo); + } + + return sinfo.result.ptr_array; +} + + +/* Dumps the contents of the registration database to stdout. An indepedent program can take + * this output and format it into nice tables or HTML or whatever. + * + * There is one record per line. Each record is either a protocol or a header + * field, differentiated by the first field. The fields are tab-delimited. + * + * Protocols + * --------- + * Field 1 = 'P' + * Field 2 = protocol name + * Field 3 = protocol abbreviation + * + * Header Fields + * ------------- + * Field 1 = 'F' + * Field 2 = field name + * Field 3 = field abbreviation + * Field 4 = type ( textual representation of the the ftenum type ) + * Field 5 = parent protocol abbreviation + */ +void +proto_registrar_dump(void) +{ + header_field_info *hfinfo, *parent_hfinfo; + int i, len; + const char *enum_name; + + len = gpa_hfinfo->len; + for (i = 0; i < len ; i++) { + hfinfo = proto_registrar_get_nth(i); + + /* format for protocols */ + if (proto_registrar_is_protocol(i)) { + printf("P\t%s\t%s\n", hfinfo->name, hfinfo->abbrev); + } + /* format for header fields */ + else { + parent_hfinfo = proto_registrar_get_nth(hfinfo->parent); + g_assert(parent_hfinfo); + + enum_name = proto_registrar_ftype_name(hfinfo->type); + printf("F\t%s\t%s\t%s\t%s\n", hfinfo->name, hfinfo->abbrev, + enum_name,parent_hfinfo->abbrev); + } + } +} + + +/* Returns a string representing the field type */ +const char* +proto_registrar_ftype_name(enum ftenum ftype) +{ + const char *enum_name = NULL; + + switch(ftype) { + case FT_NONE: + enum_name = "FT_NONE"; + break; + case FT_BOOLEAN: + enum_name = "FT_BOOLEAN"; + break; + case FT_UINT8: + enum_name = "FT_UINT8"; + break; + case FT_UINT16: + enum_name = "FT_UINT16"; + break; + case FT_UINT24: + enum_name = "FT_UINT24"; + break; + case FT_UINT32: + enum_name = "FT_UINT32"; + break; + case FT_INT8: + enum_name = "FT_INT8"; + break; + case FT_INT16: + enum_name = "FT_INT16"; + break; + case FT_INT24: + enum_name = "FT_INT24"; + break; + case FT_INT32: + enum_name = "FT_INT32"; + break; + case FT_DOUBLE: + enum_name = "FT_DOUBLE"; + break; + case FT_ABSOLUTE_TIME: + enum_name = "FT_ABSOLUTE_TIME"; + break; + case FT_RELATIVE_TIME: + enum_name = "FT_RELATIVE_TIME"; + break; + case FT_UINT_STRING: + enum_name = "FT_UINT_STRING"; + break; + case FT_STRING: + enum_name = "FT_STRING"; + break; + case FT_STRINGZ: + enum_name = "FT_STRINGZ"; + break; + case FT_ETHER: + enum_name = "FT_ETHER"; + break; + case FT_BYTES: + enum_name = "FT_BYTES"; + break; + case FT_IPv4: + enum_name = "FT_IPv4"; + break; + case FT_IPv6: + enum_name = "FT_IPv6"; + break; + case FT_IPXNET: + enum_name = "FT_IPXNET"; + break; + case FT_TEXT_ONLY: + enum_name = "FT_TEXT_ONLY"; + break; + case NUM_FIELD_TYPES: + g_assert_not_reached(); + break; + } + g_assert(enum_name); + return enum_name; +} diff --git a/epan/proto.h b/epan/proto.h new file mode 100644 index 0000000000..2c388db283 --- /dev/null +++ b/epan/proto.h @@ -0,0 +1,553 @@ +/* proto.h + * Definitions for protocol display + * + * $Id: proto.h,v 1.1 2000/09/27 04:54:52 gram Exp $ + * + * Ethereal - Network traffic analyzer + * By Gerald Combs + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + + +#ifndef __PROTO_H__ +#define __PROTO_H__ + +#ifdef HAVE_SYS_TIME_H +# include +#endif + +#ifdef HAVE_STDARG_H +# include +#else +# include +#endif + +#ifdef HAVE_WINSOCK_H +# include +#endif + +#include "ipv4.h" +#include "tvbuff.h" + +/* needs glib.h */ +typedef GNode proto_tree; +typedef GNode proto_item; +struct value_string; + +#define ITEM_LABEL_LENGTH 240 + +/* In order to make a const value_string[] look like a value_string*, I + * need this macro */ +#define VALS(x) (struct value_string*)(x) + +/* ... and similarly, */ +#define TFS(x) (struct true_false_string*)(x) + +/* check protocol activation */ +#define OLD_CHECK_DISPLAY_AS_DATA(index, pd, offset, fd, tree) {\ + if (!proto_is_protocol_enabled(index)) { \ + old_dissect_data(pd, offset, fd, tree); \ + return; \ + } \ + } + +#define CHECK_DISPLAY_AS_DATA(index, tvb, pinfo, tree) { \ + if (!proto_is_protocol_enabled(index)) { \ + dissect_data(tvb, pinfo, tree); \ + return; \ + } \ + } + +/* field types */ +enum ftenum { + FT_NONE, /* used for protocol labels (thus no field type) */ + FT_BOOLEAN, /* TRUE and FALSE come from */ + FT_UINT8, + FT_UINT16, + FT_UINT24, /* really a UINT32, but displayed as 3 hex-digits if FD_HEX*/ + FT_UINT32, + FT_INT8, + FT_INT16, + FT_INT24, + FT_INT32, + FT_DOUBLE, + FT_ABSOLUTE_TIME, + FT_RELATIVE_TIME, + FT_STRING, + FT_STRINGZ, /* for use with proto_tree_add_item() */ + FT_UINT_STRING, /* for use with proto_tree_add_item() */ + FT_ETHER, + FT_BYTES, + FT_IPv4, + FT_IPv6, + FT_IPXNET, + FT_TEXT_ONLY, /* non-filterable, used when converting ethereal + from old-style proto_tree to new-style proto_tree */ + NUM_FIELD_TYPES /* last item number plus one */ +}; + +enum { + BASE_NONE, + BASE_DEC, + BASE_HEX, + BASE_OCT, + BASE_BIN +}; + + +/* information describing a header field */ +typedef struct header_field_info { + char *name; + char *abbrev; + enum ftenum type; + int display; /* for integers only, so far. Base */ + void *strings; /* val_string or true_false_string */ + guint32 bitmask; + char *blurb; /* Brief description of field. */ + + int id; /* assigned by registration function, not programmer */ + int parent; /* parent protocol */ + int bitshift; /* bits to shift */ +} header_field_info; + + + +/* Used when registering many fields at once */ +typedef struct hf_register_info { + int *p_id; /* pointer to int; written to by register() function */ + header_field_info hfinfo; +} hf_register_info; + + +/* Info stored in each proto_item GNode */ +typedef struct field_info { + struct header_field_info *hfinfo; + gint start; + gint length; + gint tree_type; /* ETT_* */ + char *representation; /* for GUI tree */ + int visible; + union { + guint32 numeric; + struct timeval time; /* the whole struct, not a pointer */ + gdouble floating; + gchar *string; + guint8 *bytes; + guint8 ether[6]; + ipv4_addr ipv4; + guint8 ipv6[16]; + } value; +} field_info; + + +/* For use while converting dissectors to use tvbuff's */ +#define NullTVB NULL + +/* Sets up memory used by proto routines. Called at program startup */ +void proto_init(void); + +/* Frees memory used by proto routines. Called at program shutdown */ +void proto_cleanup(void); + +/* Set text of proto_item after having already been created. */ +#if __GNUC__ == 2 +void proto_item_set_text(proto_item *ti, const char *format, ...) + __attribute__((format (printf, 2, 3))); +#else +void proto_item_set_text(proto_item *ti, const char *format, ...); +#endif + +/* Set length of proto_item after having already been created. */ +void proto_item_set_len(proto_item *ti, gint length); + +/* Get length of proto_item. Useful after using proto_tree_add_item() + * to add a variable-length field (e.g., FT_NSTRING_UINT8) */ +int proto_item_get_len(proto_item *ti); + +/* Creates new proto_tree root */ +proto_tree* proto_tree_create_root(void); + +/* Clear memory for entry proto_tree. Clears proto_tree struct also. */ +void proto_tree_free(proto_tree *tree); + +/* Create a subtree under an existing item; returns tree pointer */ +proto_tree* proto_item_add_subtree(proto_item *ti, gint idx); + +int +proto_register_field(char *name, char *abbrev, enum ftenum type, int parent, + struct value_string* vals); + +int +proto_register_protocol(char *name, char *abbrev); + +void +proto_register_field_array(int parent, hf_register_info *hf, int num_records); + +void +proto_register_subtree_array(gint **indices, int num_indices); + +/* Add an item to a proto_tree, using the text label registered to that item; + the item is extracted from the tvbuff handed to it. */ +proto_item * +proto_tree_add_item(proto_tree *tree, int hfindex, tvbuff_t *tvb, + gint start, gint length, gboolean little_endian); + +proto_item * +proto_tree_add_item_hidden(proto_tree *tree, int hfindex, tvbuff_t *tvb, + gint start, gint length, gboolean little_endian); + +/* Add a FT_NONE to a proto_tree */ +#if __GNUC__ == 2 +proto_item * +proto_tree_add_protocol_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, + gint length, const char *format, ...) + __attribute__((format (printf, 6, 7))); +#else +proto_item * +proto_tree_add_protocol_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, + gint length, const char *format, ...); +#endif + +/* Add a FT_BYTES to a proto_tree */ +proto_item * +proto_tree_add_bytes(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, + gint length, const guint8* start_ptr); + +proto_item * +proto_tree_add_bytes_hidden(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, + gint length, const guint8* start_ptr); + +#if __GNUC__ == 2 +proto_item * +proto_tree_add_bytes_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, + gint length, const guint8* start_ptr, const char *format, ...) + __attribute__((format (printf, 7, 8))); +#else +proto_item * +proto_tree_add_bytes_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, + gint length, const guint8* start_ptr, const char *format, ...); +#endif + +/* Add a FT_*TIME to a proto_tree */ +proto_item * +proto_tree_add_time(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, + gint length, struct timeval* value_ptr); + +proto_item * +proto_tree_add_time_hidden(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, + gint length, struct timeval* value_ptr); + +#if __GNUC__ == 2 +proto_item * +proto_tree_add_time_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, + gint length, struct timeval* value_ptr, const char *format, ...) + __attribute__((format (printf, 7, 8))); +#else +proto_item * +proto_tree_add_time_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, + gint length, struct timeval* value_ptr, const char *format, ...); +#endif + +/* Add a FT_IPXNET to a proto_tree */ +proto_item * +proto_tree_add_ipxnet(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, + gint length, guint32 value); + +proto_item * +proto_tree_add_ipxnet_hidden(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, + gint length, guint32 value); + +#if __GNUC__ == 2 +proto_item * +proto_tree_add_ipxnet_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, + gint length, guint32 value, const char *format, ...) + __attribute__((format (printf, 7, 8))); +#else +proto_item * +proto_tree_add_ipxnet_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, + gint length, guint32 value, const char *format, ...); +#endif + +/* Add a FT_IPv4 to a proto_tree */ +proto_item * +proto_tree_add_ipv4(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, + gint length, guint32 value); + +proto_item * +proto_tree_add_ipv4_hidden(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, + gint length, guint32 value); + +#if __GNUC__ == 2 +proto_item * +proto_tree_add_ipv4_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, + gint length, guint32 value, const char *format, ...) + __attribute__((format (printf, 7, 8))); +#else +proto_item * +proto_tree_add_ipv4_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, + gint length, guint32 value, const char *format, ...); +#endif + +/* Add a FT_IPv6 to a proto_tree */ +proto_item * +proto_tree_add_ipv6(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, + gint length, const guint8* value_ptr); + +proto_item * +proto_tree_add_ipv6_hidden(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, + gint length, const guint8* value_ptr); + +#if __GNUC__ == 2 +proto_item * +proto_tree_add_ipv6_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, + gint length, const guint8* value_ptr, const char *format, ...) + __attribute__((format (printf, 7, 8))); +#else +proto_item * +proto_tree_add_ipv6_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, + gint length, const guint8* value_ptr, const char *format, ...); +#endif + +/* Add a FT_ETHER to a proto_tree */ +proto_item * +proto_tree_add_ether(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, + gint length, const guint8* value); + +proto_item * +proto_tree_add_ether_hidden(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, + gint length, const guint8* value); + +#if __GNUC__ == 2 +proto_item * +proto_tree_add_ether_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, + gint length, const guint8* value, const char *format, ...) + __attribute__((format (printf, 7, 8))); +#else +proto_item * +proto_tree_add_ether_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, + gint length, const guint8* value, const char *format, ...); +#endif + +/* Add a FT_STRING to a proto_tree */ +proto_item * +proto_tree_add_string(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, + gint length, const char* value); + +proto_item * +proto_tree_add_string_hidden(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, + gint length, const char* value); + +#if __GNUC__ == 2 +proto_item * +proto_tree_add_string_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, + gint length, const char* value, const char *format, ...) + __attribute__((format (printf, 7, 8))); +#else +proto_item * +proto_tree_add_string_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, + gint length, const char* value, const char *format, ...); +#endif + +/* Add a FT_BOOLEAN to a proto_tree */ +proto_item * +proto_tree_add_boolean(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, + gint length, guint32 value); + +proto_item * +proto_tree_add_boolean_hidden(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, + gint length, guint32 value); + +#if __GNUC__ == 2 +proto_item * +proto_tree_add_boolean_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, + gint length, guint32 value, const char *format, ...) + __attribute__((format (printf, 7, 8))); +#else +proto_item * +proto_tree_add_boolean_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, + gint length, guint32 value, const char *format, ...); +#endif + +/* Add a FT_DOUBLE to a proto_tree */ +proto_item * +proto_tree_add_double(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, + gint length, double value); + +proto_item * +proto_tree_add_double_hidden(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, + gint length, double value); + +#if __GNUC__ == 2 +proto_item * +proto_tree_add_double_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, + gint length, double value, const char *format, ...) + __attribute__((format (printf, 7, 8))); +#else +proto_item * +proto_tree_add_double_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, + gint length, double value, const char *format, ...); +#endif + +/* Add any FT_UINT* to a proto_tree */ +proto_item * +proto_tree_add_uint(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, + gint length, guint32 value); + +proto_item * +proto_tree_add_uint_hidden(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, + gint length, guint32 value); + +#if __GNUC__ == 2 +proto_item * +proto_tree_add_uint_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, + gint length, guint32 value, const char *format, ...) + __attribute__((format (printf, 7, 8))); +#else +proto_item * +proto_tree_add_uint_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, + gint length, guint32 value, const char *format, ...); +#endif + +/* Add any FT_INT* to a proto_tree */ +proto_item * +proto_tree_add_int(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, + gint length, gint32 value); + +proto_item * +proto_tree_add_int_hidden(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, + gint length, gint32 value); + +#if __GNUC__ == 2 +proto_item * +proto_tree_add_int_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, + gint length, gint32 value, const char *format, ...) + __attribute__((format (printf, 7, 8))); +#else +proto_item * +proto_tree_add_int_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, + gint length, gint32 value, const char *format, ...); +#endif + + +/* Add a text-only node to the proto_tree */ +#if __GNUC__ == 2 +proto_item * +proto_tree_add_text(proto_tree *tree, tvbuff_t *tvb, gint start, gint length, const char *, + ...) __attribute__((format (printf, 5, 6))); +#else +proto_item * +proto_tree_add_text(proto_tree *tree, tvbuff_t *tvb, gint start, gint length, const char *, + ...); +#endif + +proto_item * +proto_tree_add_text_valist(proto_tree *tree, tvbuff_t *tvb, gint start, + gint length, const char *format, va_list ap); + +/* Add a node with no text */ +proto_item * +proto_tree_add_notext(proto_tree *tree, tvbuff_t *tvb, gint start, gint length); + + +/* Useful for quick debugging. Also sends string to STDOUT, so don't + * leave call to this function in production code. */ +#if __GNUC__ == 2 +proto_item * +proto_tree_add_debug_text(proto_tree *tree, const char *format, ...) + __attribute__((format (printf, 2, 3))); +#else +proto_item * +proto_tree_add_debug_text(proto_tree *tree, const char *format, ...); +#endif + +void +proto_item_fill_label(field_info *fi, gchar *label_str); + +/* Returns number of items (protocols or header fields) registered. */ +int proto_registrar_n(void); + +/* Returns char* to name for item # n (0-indexed) */ +char* proto_registrar_get_name(int n); + +/* Returns char* to abbrev for item # n (0-indexed) */ +char* proto_registrar_get_abbrev(int n); + +/* get the header field information based upon a field or protocol id */ +struct header_field_info* proto_registrar_get_nth(int hfindex); + +/* Returns enum ftenum for item # n */ +int proto_registrar_get_ftype(int n); + +/* Returns parent protocol for item # n. + * Returns -1 if item _is_ a protocol */ +int proto_registrar_get_parent(int n); + +/* Is item #n a protocol? */ +gboolean proto_registrar_is_protocol(int n); + +/* Is item #n decoding enabled ? */ +gboolean proto_is_protocol_enabled(int n); + +/* Enable / Disable protocol */ +void proto_set_decoding(int n, gboolean enabled); + +/* Get length of registered field according to field type. + * 0 means undeterminable at registration time. + * -1 means unknown field */ +gint proto_registrar_get_length(int n); + +/* Checks for existence any protocol or field within a tree. + * "Protocols" are assumed to be a child of the [empty] root node. + * TRUE = found, FALSE = not found */ +gboolean proto_check_for_protocol_or_field(proto_tree* tree, int id); + +/* Return GPtrArray* of field_info pointers for all hfindex that appear in + * tree. Assume that a field will only appear under its registered parent's + * subtree, and that the parent's subtree is a child of the + * [empty] root node. */ +GPtrArray* proto_get_finfo_ptr_array(proto_tree *tree, int hfindex); + +/* Dumps a glossary of the protocol and field registrations to STDOUT */ +void proto_registrar_dump(void); + +/* Is the parsing being done for a visible proto_tree or an invisible one? + * By setting this correctly, the proto_tree creation is sped up by not + * having to call vsnprintf and copy strings around. + */ +extern gboolean proto_tree_is_visible; + +/* Points to the first element of an array of Booleans, indexed by + a subtree item type; that array element is TRUE if subtrees of + an item of that type are to be expanded. + + ETT_NONE is reserved for unregistered subtree types. */ +#define ETT_NONE 0 +extern gboolean *tree_is_expanded; + +/* Number of elements in that array. */ +extern int num_tree_types; + +/* glib doesn't have g_ptr_array_len of all things!*/ +#ifndef g_ptr_array_len +#define g_ptr_array_len(a) ((a)->len) +#endif + +/* Returns a string representing the field type */ +const char* proto_registrar_ftype_name(enum ftenum ftype); + +#endif /* proto.h */ diff --git a/epan/strutil.c b/epan/strutil.c new file mode 100644 index 0000000000..bfdfd8ef43 --- /dev/null +++ b/epan/strutil.c @@ -0,0 +1,222 @@ +/* strutil.c + * String utility routines + * + * $Id: strutil.c,v 1.1 2000/09/27 04:54:52 gram Exp $ + * + * Ethereal - Network traffic analyzer + * By Gerald Combs + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include +#include "strutil.h" + + +/* + * Given a pointer into a data buffer, and to the end of the buffer, + * find the end of the (putative) line at that position in the data + * buffer. + * Return a pointer to the EOL character(s) in "*eol". + */ +const u_char * +find_line_end(const u_char *data, const u_char *dataend, const u_char **eol) +{ + const u_char *lineend; + + lineend = memchr(data, '\n', dataend - data); + if (lineend == NULL) { + /* + * No LF - line is probably continued in next TCP segment. + */ + lineend = dataend; + *eol = dataend; + } else { + /* + * Is the LF at the beginning of the line? + */ + if (lineend > data) { + /* + * No - is it preceded by a carriage return? + * (Perhaps it's supposed to be, but that's not guaranteed....) + */ + if (*(lineend - 1) == '\r') { + /* + * Yes. The EOL starts with the CR. + */ + *eol = lineend - 1; + } else { + /* + * No. The EOL starts with the LF. + */ + *eol = lineend; + + /* + * I seem to remember that we once saw lines ending with LF-CR + * in an HTTP request or response, so check if it's *followed* + * by a carriage return. + */ + if (lineend < (dataend - 1) && *(lineend + 1) == '\r') { + /* + * It's ; say it ends with the CR. + */ + lineend++; + } + } + } + + /* + * Point to the character after the last character. + */ + lineend++; + } + return lineend; +} + +/* + * Get the length of the next token in a line, and the beginning of the + * next token after that (if any). + * Return 0 if there is no next token. + */ +int +get_token_len(const u_char *linep, const u_char *lineend, + const u_char **next_token) +{ + const u_char *tokenp; + int token_len; + + tokenp = linep; + + /* + * Search for a blank, a CR or an LF, or the end of the buffer. + */ + while (linep < lineend && *linep != ' ' && *linep != '\r' && *linep != '\n') + linep++; + token_len = linep - tokenp; + + /* + * Skip trailing blanks. + */ + while (linep < lineend && *linep == ' ') + linep++; + + *next_token = linep; + + return token_len; +} + + +#define MAX_COLUMNS_LINE_DETAIL 62 + +/* + * Given a string, generate a string from it that shows non-printable + * characters as C-style escapes, and return a pointer to it. + */ +gchar * +format_text(const u_char *string, int len) +{ + static gchar fmtbuf[MAX_COLUMNS_LINE_DETAIL + 3 + 4 + 1]; + gchar *fmtbufp; + int column; + const u_char *stringend = string + len; + u_char c; + int i; + + column = 0; + fmtbufp = &fmtbuf[0]; + while (string < stringend) { + if (column >= MAX_COLUMNS_LINE_DETAIL) { + /* + * Put "..." and quit. + */ + strcpy(fmtbufp, " ..."); + fmtbufp += 4; + break; + } + c = *string++; + if (isprint(c)) { + *fmtbufp++ = c; + column++; + } else { + *fmtbufp++ = '\\'; + column++; + switch (c) { + + case '\\': + *fmtbufp++ = '\\'; + column++; + break; + + case '\a': + *fmtbufp++ = 'a'; + column++; + break; + + case '\b': + *fmtbufp++ = 'b'; + column++; + break; + + case '\f': + *fmtbufp++ = 'f'; + column++; + break; + + case '\n': + *fmtbufp++ = 'n'; + column++; + break; + + case '\r': + *fmtbufp++ = 'r'; + column++; + break; + + case '\t': + *fmtbufp++ = 't'; + column++; + break; + + case '\v': + *fmtbufp++ = 'v'; + column++; + break; + + default: + i = (c>>6)&03; + *fmtbufp++ = i + '0'; + column++; + i = (c>>3)&07; + *fmtbufp++ = i + '0'; + column++; + i = (c>>0)&07; + *fmtbufp++ = i + '0'; + column++; + break; + } + } + } + *fmtbufp = '\0'; + return fmtbuf; +} diff --git a/epan/strutil.h b/epan/strutil.h new file mode 100644 index 0000000000..fdd95ca891 --- /dev/null +++ b/epan/strutil.h @@ -0,0 +1,46 @@ +/* util.h + * Utility definitions + * + * $Id: strutil.h,v 1.1 2000/09/27 04:54:53 gram Exp $ + * + * Ethereal - Network traffic analyzer + * By Gerald Combs + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __STRUTIL_H__ +#define __STRUTIL_H__ + +/* ... thus, config.h needs to be #included */ + +#ifdef HAVE_SYS_TYPES_H +#include /* for u_char */ +#endif + +#ifdef HAVE_WINSOCK_H +# include /* for u_char */ +#endif + +const u_char *find_line_end(const u_char *data, const u_char *dataend, + const u_char **eol); +int get_token_len(const u_char *linep, const u_char *lineend, + const u_char **next_token); +gchar* format_text(const u_char *line, int len); + + +#endif /* __STRUTIL_H__ */ diff --git a/epan/tvbtest.c b/epan/tvbtest.c new file mode 100644 index 0000000000..c528f7c837 --- /dev/null +++ b/epan/tvbtest.c @@ -0,0 +1,406 @@ +/* Standalone program to test functionality of tvbuffs. + * + * tvbtest : tvbtest.o tvbuff.o except.o + * + * $Id: tvbtest.c,v 1.1 2000/09/27 04:54:53 gram Exp $ + * + * Copyright (c) 2000 by Gilbert Ramirez + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#include +#include +#include + +#include "tvbuff.h" +#include "pint.h" + +/* Tests a tvbuff against the expected pattern/length. + * Returns TRUE if all tests succeeed, FALSE if any test fails */ +gboolean +test(tvbuff_t *tvb, gchar* name, + guint8* expected_data, guint expected_length) +{ + guint length; + guint8 *ptr; + volatile gboolean ex_thrown; + volatile guint32 val32; + guint32 expected32; + int incr, i; + + length = tvb_length(tvb); + + if (length != expected_length) { + printf("01: Failed TVB=%s Length of tvb=%u while expected length=%u\n", + name, length, expected_length); + return FALSE; + } + + /* Test boundary case. A BoundsError exception should be thrown. */ + ex_thrown = FALSE; + TRY { + ptr = tvb_get_ptr(tvb, 0, length + 1); + } + CATCH(BoundsError) { + ex_thrown = TRUE; + } + CATCH(ReportedBoundsError) { + printf("02: Caught wrong exception: ReportedBoundsError\n"); + } + ENDTRY; + + if (!ex_thrown) { + printf("02: Failed TVB=%s No BoundsError when retrieving %u bytes\n", + name, length + 1); + return FALSE; + } + + /* Test boundary case with one more byte. A ReportedBoundsError + exception should be thrown. */ + ex_thrown = FALSE; + TRY { + ptr = tvb_get_ptr(tvb, 0, length + 2); + } + CATCH(BoundsError) { + printf("03: Caught wrong exception: BoundsError\n"); + } + CATCH(ReportedBoundsError) { + ex_thrown = TRUE; + } + ENDTRY; + + if (!ex_thrown) { + printf("03: Failed TVB=%s No ReportedBoundsError when retrieving %u bytes\n", + name, length + 2); + return FALSE; + } + + /* Test boundary case. A BoundsError exception should be thrown. */ + ex_thrown = FALSE; + TRY { + ptr = tvb_get_ptr(tvb, -1, 2); + } + CATCH(BoundsError) { + ex_thrown = TRUE; + } + CATCH(ReportedBoundsError) { + printf("04: Caught wrong exception: ReportedBoundsError\n"); + } + ENDTRY; + + if (!ex_thrown) { + printf("04: Failed TVB=%s No BoundsError when retrieving 2 bytes from" + " offset -1\n", name); + return FALSE; + } + + /* Test boundary case. A BoundsError exception should not be thrown. */ + ex_thrown = FALSE; + TRY { + ptr = tvb_get_ptr(tvb, 0, 1); + } + CATCH(BoundsError) { + ex_thrown = TRUE; + } + CATCH(ReportedBoundsError) { + printf("05: Caught wrong exception: ReportedBoundsError\n"); + } + ENDTRY; + + if (ex_thrown) { + printf("05: Failed TVB=%s BoundsError when retrieving 1 bytes from" + " offset 0\n", name); + return FALSE; + } + + /* Test boundary case. A BoundsError exception should not be thrown. */ + ex_thrown = FALSE; + TRY { + ptr = tvb_get_ptr(tvb, -1, 1); + } + CATCH(BoundsError) { + ex_thrown = TRUE; + } + CATCH(ReportedBoundsError) { + printf("06: Caught wrong exception: ReportedBoundsError\n"); + } + ENDTRY; + + if (ex_thrown) { + printf("06: Failed TVB=%s BoundsError when retrieving 1 bytes from" + " offset -1\n", name); + return FALSE; + } + + + /* Check data at boundary. An exception should not be thrown. */ + if (length >= 4) { + ex_thrown = FALSE; + TRY { + val32 = tvb_get_ntohl(tvb, 0); + } + CATCH_ALL { + ex_thrown = TRUE; + } + ENDTRY; + + if (ex_thrown) { + printf("07: Failed TVB=%s Exception when retrieving " + "guint32 from offset 0\n", name); + return FALSE; + } + + expected32 = pntohl(expected_data); + if (val32 != expected32) { + printf("08: Failed TVB=%s guint32 @ 0 %u != expected %u\n", + name, val32, expected32); + return FALSE; + } + } + + /* Check data at boundary. An exception should not be thrown. */ + if (length >= 4) { + ex_thrown = FALSE; + TRY { + val32 = tvb_get_ntohl(tvb, -4); + } + CATCH_ALL { + ex_thrown = TRUE; + } + ENDTRY; + + if (ex_thrown) { + printf("09: Failed TVB=%s Exception when retrieving " + "guint32 from offset 0\n", name); + return FALSE; + } + + expected32 = pntohl(&expected_data[length-4]); + if (val32 != expected32) { + printf("10: Failed TVB=%s guint32 @ -4 %u != expected %u\n", + name, val32, expected32); + return FALSE; + } + } + + /* Sweep across data in various sized increments checking + * tvb_memdup() */ + for (incr = 1; incr < length; incr++) { + for (i = 0; i < length - incr; i += incr) { + ptr = tvb_memdup(tvb, i, incr); + if (memcmp(ptr, &expected_data[i], incr) != 0) { + printf("11: Failed TVB=%s Offset=%d Length=%d " + "Bad memdup\n", + name, i, incr); + g_free(ptr); + return FALSE; + } + g_free(ptr); + } + } + + /* One big memdup */ + ptr = tvb_memdup(tvb, 0, -1); + if (memcmp(ptr, expected_data, length) != 0) { + printf("12: Failed TVB=%s Offset=0 Length=-1 " + "Bad memdup\n", name); + g_free(ptr); + return FALSE; + } + g_free(ptr); + + + printf("Passed TVB=%s\n", name); + + return TRUE; +} + + + +void +run_tests(void) +{ + int i, j; + + tvbuff_t *tvb_small[3]; + tvbuff_t *tvb_large[3]; + tvbuff_t *tvb_subset[6]; + tvbuff_t *tvb_comp[6]; + guint8 *small[3]; + guint8 *large[3]; + guint8 *subset[6]; + guint subset_length[6]; + guint8 *comp[6]; + guint comp_length[6]; + guint8 temp; + int len; + + for (i = 0; i < 3; i++) { + small[i] = g_new(guint8, 16); + + temp = 16 * i; + for (j = 0; j < 16; j++) { + small[i][j] = temp + j; + } + + tvb_small[i] = tvb_new_real_data(small[i], 16, 17); + } + + for (i = 0; i < 3; i++) { + large[i] = g_new(guint8, 19); + + temp = 19 * i; + for (j = 0; j < 19; j++) { + large[i][j] = temp + j; + } + + tvb_large[i] = tvb_new_real_data(large[i], 19, 20); + } + + /* Test the TVBUFF_REAL_DATA objects. */ + test(tvb_small[0], "Small 0", small[0], 16); + test(tvb_small[1], "Small 1", small[1], 16); + test(tvb_small[2], "Small 2", small[2], 16); + + test(tvb_large[0], "Large 0", large[0], 19); + test(tvb_large[1], "Large 1", large[1], 19); + test(tvb_large[2], "Large 2", large[2], 19); + + tvb_subset[0] = tvb_new_subset(tvb_small[0], 0, 8, 9); + subset[0] = &small[0][0]; + subset_length[0] = 8; + + tvb_subset[1] = tvb_new_subset(tvb_large[0], -10, 10, 11); + subset[1] = &large[0][9]; + subset_length[1] = 10; + + tvb_subset[2] = tvb_new_subset(tvb_small[1], -16, -1, 17); + subset[2] = &small[1][0]; + subset_length[2] = 16; + + tvb_subset[3] = tvb_new_subset(tvb_subset[0], 0, 3, 4); + subset[3] = &small[0][0]; + subset_length[3] = 3; + + tvb_subset[4] = tvb_new_subset(tvb_subset[1], -5, 5, 6); + subset[4] = &large[0][14]; + subset_length[4] = 5; + + tvb_subset[5] = tvb_new_subset(tvb_subset[2], 4, 8, 9); + subset[5] = &small[1][4]; + subset_length[5] = 8; + + /* Test the TVBUFF_SUBSET objects. */ + test(tvb_subset[0], "Subset 0", subset[0], subset_length[0]); + test(tvb_subset[1], "Subset 1", subset[1], subset_length[1]); + test(tvb_subset[2], "Subset 2", subset[2], subset_length[2]); + test(tvb_subset[3], "Subset 3", subset[3], subset_length[3]); + test(tvb_subset[4], "Subset 4", subset[4], subset_length[4]); + test(tvb_subset[5], "Subset 5", subset[5], subset_length[5]); + + /* One Real */ + printf("Making Composite 0\n"); + tvb_comp[0] = tvb_new_composite(); + comp[0] = small[0]; + comp_length[0] = 16; + tvb_composite_append(tvb_comp[0], tvb_small[0]); + tvb_composite_finalize(tvb_comp[0]); + + /* Two Reals */ + printf("Making Composite 1\n"); + tvb_comp[1] = tvb_new_composite(); + comp[1] = g_malloc(32); + comp_length[1] = 32; + memcpy(comp[1], small[0], 16); + memcpy(&comp[1][16], small[1], 16); + tvb_composite_append(tvb_comp[1], tvb_small[0]); + tvb_composite_append(tvb_comp[1], tvb_small[1]); + tvb_composite_finalize(tvb_comp[1]); + + /* One subset */ + printf("Making Composite 2\n"); + tvb_comp[2] = tvb_new_composite(); + comp[2] = subset[1]; + comp_length[2] = subset_length[1]; + tvb_composite_append(tvb_comp[2], tvb_subset[1]); + tvb_composite_finalize(tvb_comp[2]); + + /* Two subsets */ + printf("Making Composite 3\n"); + tvb_comp[3] = tvb_new_composite(); + comp[3] = g_malloc(13); + comp_length[3] = 13; + memcpy(comp[3], &large[0][14], 5); + memcpy(&comp[3][5], &small[1][4], 8); + tvb_composite_append(tvb_comp[3], tvb_subset[4]); + tvb_composite_append(tvb_comp[3], tvb_subset[5]); + tvb_composite_finalize(tvb_comp[3]); + + /* One real, one subset */ + printf("Making Composite 4\n"); + tvb_comp[4] = tvb_new_composite(); + comp[4] = g_malloc(16 + subset_length[1]); + comp_length[4] = 16 + subset_length[1]; + memcpy(comp[4], small[0], 16); + memcpy(&comp[4][16], subset[1], subset_length[1]); + tvb_composite_append(tvb_comp[4], tvb_small[0]); + tvb_composite_append(tvb_comp[4], tvb_subset[1]); + tvb_composite_finalize(tvb_comp[4]); + + /* 4 composites */ + printf("Making Composite 5\n"); + tvb_comp[5] = tvb_new_composite(); + comp_length[5] = comp_length[0] + + comp_length[1] + + comp_length[2] + + comp_length[3]; + comp[5] = g_malloc(comp_length[5]); + + len = 0; + memcpy(&comp[5][len], comp[0], comp_length[0]); + len += comp_length[0]; + memcpy(&comp[5][len], comp[1], comp_length[1]); + len += comp_length[1]; + memcpy(&comp[5][len], comp[2], comp_length[2]); + len += comp_length[2]; + memcpy(&comp[5][len], comp[3], comp_length[3]); + + tvb_composite_append(tvb_comp[5], tvb_comp[0]); + tvb_composite_append(tvb_comp[5], tvb_comp[1]); + tvb_composite_append(tvb_comp[5], tvb_comp[2]); + tvb_composite_append(tvb_comp[5], tvb_comp[3]); + tvb_composite_finalize(tvb_comp[5]); + + /* Test the TVBUFF_COMPOSITE objects. */ + test(tvb_comp[0], "Composite 0", comp[0], comp_length[0]); + test(tvb_comp[1], "Composite 1", comp[1], comp_length[1]); + test(tvb_comp[2], "Composite 2", comp[2], comp_length[2]); + test(tvb_comp[3], "Composite 3", comp[3], comp_length[3]); + test(tvb_comp[4], "Composite 4", comp[4], comp_length[4]); + test(tvb_comp[5], "Composite 5", comp[5], comp_length[5]); +} + +int +main(void) +{ + except_init(); + tvbuff_init(); + run_tests(); + tvbuff_cleanup(); + except_deinit(); + exit(0); +} diff --git a/epan/tvbuff.c b/epan/tvbuff.c new file mode 100644 index 0000000000..74071276ad --- /dev/null +++ b/epan/tvbuff.c @@ -0,0 +1,1144 @@ +/* tvbuff.c + * + * Testy, Virtual(-izable) Buffer of guint8*'s + * + * "Testy" -- the buffer gets mad when an attempt to access data + * beyond the bounds of the buffer. An exception is thrown. + * + * "Virtual" -- the buffer can have its own data, can use a subset of + * the data of a backing tvbuff, or can be a composite of + * other tvbuffs. + * + * $Id: tvbuff.c,v 1.1 2000/09/27 04:54:53 gram Exp $ + * + * Copyright (c) 2000 by Gilbert Ramirez + * + * Ethereal - Network traffic analyzer + * By Gerald Combs + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +#include "pint.h" +#include "tvbuff.h" +#include "strutil.h" + +typedef struct { + /* The backing tvbuff_t */ + tvbuff_t *tvb; + + /* The offset/length of 'tvb' to which I'm privy */ + guint offset; + guint length; + +} tvb_backing_t; + +typedef struct { + GSList *tvbs; + + /* Used for quick testing to see if this + * is the tvbuff that a COMPOSITE is + * interested in. */ + guint *start_offsets; + guint *end_offsets; + +} tvb_comp_t; + +struct tvbuff { + /* Record-keeping */ + tvbuff_type type; + gboolean initialized; + guint usage_count; + + /* The tvbuffs in which this tvbuff is a member + * (that is, a backing tvbuff for a TVBUFF_SUBSET + * or a member for a TVB_COMPOSITE) */ + GSList *used_in; + + /* TVBUFF_SUBSET and TVBUFF_COMPOSITE keep track + * of the other tvbuff's they use */ + union { + tvb_backing_t subset; + tvb_comp_t composite; + } tvbuffs; + + /* We're either a TVBUFF_REAL_DATA or a + * TVBUFF_SUBSET that has a backing buffer that + * has real_data != NULL, or a TVBUFF_COMPOSITE + * which has flattened its data due to a call + * to tvb_get_ptr(). + */ + guint8 *real_data; + + /* Length of virtual buffer (and/or real_data). */ + guint length; + + /* Reported length. */ + guint reported_length; + + /* Offset from beginning of first TVBUFF_REAL. */ + gint raw_offset; + + /* Func to call when actually freed */ + tvbuff_free_cb_t free_cb; +}; + +static guint8* +ensure_contiguous(tvbuff_t *tvb, gint offset, gint length); + +/* We dole out tvbuff's from this memchunk. */ +GMemChunk *tvbuff_mem_chunk = NULL; + +void +tvbuff_init(void) +{ + if (!tvbuff_mem_chunk) + tvbuff_mem_chunk = g_mem_chunk_create(tvbuff_t, 20, G_ALLOC_AND_FREE); +} + +void +tvbuff_cleanup(void) +{ + if (tvbuff_mem_chunk) + g_mem_chunk_destroy(tvbuff_mem_chunk); + + tvbuff_mem_chunk = NULL; +} + + + + +static void +tvb_init(tvbuff_t *tvb, tvbuff_type type) +{ + tvb_backing_t *backing; + tvb_comp_t *composite; + + tvb->type = type; + tvb->initialized = FALSE; + tvb->usage_count = 1; + tvb->length = 0; + tvb->reported_length = 0; + tvb->free_cb = NULL; + tvb->real_data = NULL; + tvb->raw_offset = -1; + tvb->used_in = NULL; + + switch(type) { + case TVBUFF_REAL_DATA: + /* Nothing */ + break; + + case TVBUFF_SUBSET: + backing = &tvb->tvbuffs.subset; + backing->tvb = NULL; + backing->offset = 0; + backing->length = 0; + break; + + case TVBUFF_COMPOSITE: + composite = &tvb->tvbuffs.composite; + composite->tvbs = NULL; + composite->start_offsets = NULL; + composite->end_offsets = NULL; + break; + } +} + + +tvbuff_t* +tvb_new(tvbuff_type type) +{ + tvbuff_t *tvb; + + tvb = g_chunk_new(tvbuff_t, tvbuff_mem_chunk); + g_assert(tvb); + + tvb_init(tvb, type); + + return tvb; +} + +/* We accept a void* instead of a field_info* to satisfy CLEANUP_POP */ +static void +tvb_free_void(void *tvb) +{ + tvb_free((tvbuff_t*)tvb); +} + + + +void +tvb_free(tvbuff_t* tvb) +{ + tvbuff_t *member_tvb; + tvb_comp_t *composite; + GSList *slist; + + tvb->usage_count--; + + if (tvb->usage_count == 0) { + switch (tvb->type) { + case TVBUFF_REAL_DATA: + if (tvb->free_cb) { + tvb->free_cb(tvb->real_data); + } + break; + + case TVBUFF_SUBSET: + /* This will be NULL if tvb_new_subset() fails because + * reported_length < -1 */ + if (tvb->tvbuffs.subset.tvb) { + tvb_decrement_usage_count(tvb->tvbuffs.subset.tvb, 1); + } + break; + + case TVBUFF_COMPOSITE: + composite = &tvb->tvbuffs.composite; + for (slist = composite->tvbs; slist != NULL ; slist = slist->next) { + member_tvb = slist->data; + tvb_decrement_usage_count(member_tvb, 1); + } + + g_slist_free(composite->tvbs); + + if (composite->start_offsets) + g_free(composite->start_offsets); + if (composite->end_offsets) + g_free(composite->end_offsets); + if (tvb->real_data) + g_free(tvb->real_data); + + break; + } + + if (tvb->used_in) { + g_slist_free(tvb->used_in); + } + + g_chunk_free(tvb, tvbuff_mem_chunk); + } +} + +guint +tvb_increment_usage_count(tvbuff_t* tvb, guint count) +{ + tvb->usage_count += count; + + return tvb->usage_count; +} + +guint +tvb_decrement_usage_count(tvbuff_t* tvb, guint count) +{ + if (tvb->usage_count <= count) { + tvb->usage_count = 1; + tvb_free(tvb); + return 0; + } + else { + tvb->usage_count -= count; + return tvb->usage_count; + } + +} + +void +tvb_free_chain(tvbuff_t* tvb) +{ + GSList *slist; + + /* Recursively call tvb_free_chain() */ + for (slist = tvb->used_in; slist != NULL ; slist = slist->next) { + tvb_free_chain( (tvbuff_t*)slist->data ); + } + + /* Stop the recursion */ + tvb_free(tvb); +} + + + +void +tvb_set_free_cb(tvbuff_t* tvb, tvbuff_free_cb_t func) +{ + g_assert(tvb->type == TVBUFF_REAL_DATA); + tvb->free_cb = func; +} + +void +tvb_set_real_data(tvbuff_t* tvb, const guint8* data, guint length, gint reported_length) +{ + g_assert(tvb->type == TVBUFF_REAL_DATA); + g_assert(!tvb->initialized); + + if (reported_length < -1) { + THROW(ReportedBoundsError); + } + + tvb->real_data = (gpointer) data; + tvb->length = length; + tvb->reported_length = reported_length; + tvb->initialized = TRUE; +} + +tvbuff_t* +tvb_new_real_data(const guint8* data, guint length, gint reported_length) +{ + tvbuff_t *tvb; + + tvb = tvb_new(TVBUFF_REAL_DATA); + + CLEANUP_PUSH(tvb_free_void, tvb); + + tvb_set_real_data(tvb, data, length, reported_length); + + CLEANUP_POP; + + return tvb; +} + +/* Computes the absolute offset and length based on a possibly-negative offset + * and a length that is possible -1 (which means "to the end of the data"). + * Returns TRUE/FALSE indicating whether the offset is in bounds or + * not. The integer ptrs are modified with the new offset and length. + * No exception is thrown. + * + * XXX - we return TRUE, not FALSE, if the offset is positive and right + * after the end of the tvbuff (i.e., equal to the length). We do this + * so that a dissector constructing a subset tvbuff for the next protocol + * will get a zero-length tvbuff, not an exception, if there's no data + * left for the next protocol - we want the next protocol to be the one + * that gets an exception, so the error is reported as an error in that + * protocol rather than the containing protocol. */ +static gboolean +compute_offset_length(tvbuff_t *tvb, gint offset, gint length, + guint *offset_ptr, guint *length_ptr, int *exception) +{ + g_assert(offset_ptr); + g_assert(length_ptr); + + /* Compute the offset */ + if (offset >= 0) { + /* Positive offset - relative to the beginning of the packet. */ + if (offset > tvb->reported_length) { + if (exception) { + *exception = ReportedBoundsError; + } + return FALSE; + } + else if (offset > tvb->length) { + if (exception) { + *exception = BoundsError; + } + return FALSE; + } + else { + *offset_ptr = offset; + } + } + else { + /* Negative offset - relative to the end of the packet. */ + if (-offset > tvb->reported_length) { + if (exception) { + *exception = ReportedBoundsError; + } + return FALSE; + } + else if (-offset > tvb->length) { + if (exception) { + *exception = BoundsError; + } + return FALSE; + } + else { + *offset_ptr = tvb->length + offset; + } + } + + /* Compute the length */ + g_assert(length >= -1); + if (length == -1) { + *length_ptr = tvb->length - *offset_ptr; + } + else { + *length_ptr = length; + } + + return TRUE; +} + + +static gboolean +check_offset_length_no_exception(tvbuff_t *tvb, gint offset, gint length, + guint *offset_ptr, guint *length_ptr, int *exception) +{ + g_assert(tvb->initialized); + + if (!compute_offset_length(tvb, offset, length, offset_ptr, length_ptr, exception)) { + return FALSE; + } + + if (*offset_ptr + *length_ptr <= tvb->length) { + return TRUE; + } + else if (*offset_ptr + *length_ptr <= tvb->reported_length) { + if (exception) { + *exception = BoundsError; + } + return FALSE; + } + else { + if (exception) { + *exception = ReportedBoundsError; + } + return FALSE; + } + + g_assert_not_reached(); +} + +/* Checks (+/-) offset and length and throws BoundsError if + * either is out of bounds. Sets integer ptrs to the new offset + * and length. */ +static void +check_offset_length(tvbuff_t *tvb, gint offset, gint length, + guint *offset_ptr, guint *length_ptr) +{ + int exception = 0; + + if (!check_offset_length_no_exception(tvb, offset, length, offset_ptr, length_ptr, &exception)) { + g_assert(exception > 0); + THROW(exception); + } + return; +} + +static void +add_to_used_in_list(tvbuff_t *tvb, tvbuff_t *used_in) +{ + tvb->used_in = g_slist_prepend(tvb->used_in, used_in); +} + +void +tvb_set_subset(tvbuff_t *tvb, tvbuff_t *backing, + gint backing_offset, gint backing_length, gint reported_length) +{ + g_assert(tvb->type == TVBUFF_SUBSET); + g_assert(!tvb->initialized); + + if (reported_length < -1) { + THROW(ReportedBoundsError); + } + + check_offset_length(backing, backing_offset, backing_length, + &tvb->tvbuffs.subset.offset, + &tvb->tvbuffs.subset.length); + + tvb_increment_usage_count(backing, 1); + tvb->tvbuffs.subset.tvb = backing; + tvb->length = tvb->tvbuffs.subset.length; + + if (reported_length == -1) { + tvb->reported_length = backing->reported_length - tvb->tvbuffs.subset.offset; + } + else { + tvb->reported_length = reported_length; + } + tvb->initialized = TRUE; + add_to_used_in_list(backing, tvb); + + /* Optimization. If the backing buffer has a pointer to contiguous, real data, + * then we can point directly to our starting offset in that buffer */ + if (backing->real_data != NULL) { + tvb->real_data = backing->real_data + tvb->tvbuffs.subset.offset; + } +} + + +tvbuff_t* +tvb_new_subset(tvbuff_t *backing, gint backing_offset, gint backing_length, gint reported_length) +{ + tvbuff_t *tvb; + + tvb = tvb_new(TVBUFF_SUBSET); + + CLEANUP_PUSH(tvb_free_void, tvb); + + tvb_set_subset(tvb, backing, backing_offset, backing_length, reported_length); + + CLEANUP_POP; + + return tvb; +} + +void +tvb_composite_append(tvbuff_t* tvb, tvbuff_t* member) +{ + tvb_comp_t *composite; + + g_assert(!tvb->initialized); + composite = &tvb->tvbuffs.composite; + composite->tvbs = g_slist_append( composite->tvbs, member ); +} + +void +tvb_composite_prepend(tvbuff_t* tvb, tvbuff_t* member) +{ + tvb_comp_t *composite; + + g_assert(!tvb->initialized); + composite = &tvb->tvbuffs.composite; + composite->tvbs = g_slist_prepend( composite->tvbs, member ); +} + +tvbuff_t* +tvb_new_composite(void) +{ + return tvb_new(TVBUFF_COMPOSITE); +} + +void +tvb_composite_finalize(tvbuff_t* tvb) +{ + GSList *slist; + guint num_members; + tvbuff_t *member_tvb; + tvb_comp_t *composite; + int i = 0; + + g_assert(!tvb->initialized); + g_assert(tvb->length == 0); + + composite = &tvb->tvbuffs.composite; + num_members = g_slist_length(composite->tvbs); + + composite->start_offsets = g_new(guint, num_members); + composite->end_offsets = g_new(guint, num_members); + + for (slist = composite->tvbs; slist != NULL; slist = slist->next) { + g_assert(i < num_members); + member_tvb = slist->data; + composite->start_offsets[i] = tvb->length; + tvb->length += member_tvb->length; + composite->end_offsets[i] = tvb->length - 1; + i++; + } + + tvb->initialized = TRUE; +} + + + +guint +tvb_length(tvbuff_t* tvb) +{ + g_assert(tvb->initialized); + + return tvb->length; +} + +guint +tvb_length_remaining(tvbuff_t *tvb, gint offset) +{ + guint abs_offset, abs_length; + + g_assert(tvb->initialized); + + if (compute_offset_length(tvb, offset, -1, &abs_offset, &abs_length, NULL)) { + return abs_length; + } + else { + return -1; + } +} + + + +/* Validates that 'length' bytes are available starting from + * offset (pos/neg). Does not throw BoundsError exception. */ +gboolean +tvb_bytes_exist(tvbuff_t *tvb, gint offset, gint length) +{ + guint abs_offset, abs_length; + + g_assert(tvb->initialized); + + if (!compute_offset_length(tvb, offset, length, &abs_offset, &abs_length, NULL)) + return FALSE; + + if (abs_offset + abs_length <= tvb->length) { + return TRUE; + } + else { + return FALSE; + } +} + +gboolean +tvb_offset_exists(tvbuff_t *tvb, gint offset) +{ + guint abs_offset, abs_length; + + g_assert(tvb->initialized); + if (!compute_offset_length(tvb, offset, -1, &abs_offset, &abs_length, NULL)) + return FALSE; + + if (abs_offset < tvb->length) { + return TRUE; + } + else { + return FALSE; + } +} + +guint +tvb_reported_length(tvbuff_t* tvb) +{ + g_assert(tvb->initialized); + + return tvb->reported_length; +} + + + + +static guint8* +first_real_data_ptr(tvbuff_t *tvb) +{ + tvbuff_t *member; + + switch(tvb->type) { + case TVBUFF_REAL_DATA: + return tvb->real_data; + case TVBUFF_SUBSET: + member = tvb->tvbuffs.subset.tvb; + return first_real_data_ptr(member); + case TVBUFF_COMPOSITE: + member = tvb->tvbuffs.composite.tvbs->data; + return first_real_data_ptr(member); + } + + g_assert_not_reached(); + return NULL; +} + +static int +offset_from_real_beginning(tvbuff_t *tvb, int counter) +{ + tvbuff_t *member; + + switch(tvb->type) { + case TVBUFF_REAL_DATA: + return counter; + case TVBUFF_SUBSET: + member = tvb->tvbuffs.subset.tvb; + return offset_from_real_beginning(member, counter + tvb->tvbuffs.subset.offset); + case TVBUFF_COMPOSITE: + member = tvb->tvbuffs.composite.tvbs->data; + return offset_from_real_beginning(member, counter); + } + + g_assert_not_reached(); + return 0; +} + +gint +tvb_raw_offset(tvbuff_t *tvb) +{ + if (tvb->raw_offset == -1) { + tvb->raw_offset = offset_from_real_beginning(tvb, 0); + } + return tvb->raw_offset; +} + +void +tvb_compat(tvbuff_t *tvb, const guint8 **pd, int *offset) +{ + g_assert(tvb->initialized); + *pd = first_real_data_ptr(tvb); + *offset = tvb_raw_offset(tvb); +} + + +static guint8* +composite_ensure_contiguous(tvbuff_t *tvb, guint abs_offset, guint abs_length) +{ + guint i, num_members; + tvb_comp_t *composite; + tvbuff_t *member_tvb = NULL; + guint member_offset, member_length; + GSList *slist; + + g_assert(tvb->type == TVBUFF_COMPOSITE); + + /* Maybe the range specified by offset/length + * is contiguous inside one of the member tvbuffs */ + composite = &tvb->tvbuffs.composite; + num_members = g_slist_length(composite->tvbs); + + for (i = 0; i < num_members; i++) { + if (abs_offset <= composite->end_offsets[i]) { + slist = g_slist_nth(composite->tvbs, i); + member_tvb = slist->data; + break; + } + } + g_assert(member_tvb); + + if (check_offset_length_no_exception(member_tvb, abs_offset - composite->start_offsets[i], + abs_length, &member_offset, &member_length, NULL)) { + + g_assert(!tvb->real_data); + return ensure_contiguous(member_tvb, member_offset, member_length); + } + else { + tvb->real_data = tvb_memdup(tvb, 0, -1); + return tvb->real_data + abs_offset; + } + + g_assert_not_reached(); + return NULL; +} + +static guint8* +ensure_contiguous(tvbuff_t *tvb, gint offset, gint length) +{ + guint abs_offset, abs_length; + + check_offset_length(tvb, offset, length, &abs_offset, &abs_length); + + if (tvb->real_data) { + return tvb->real_data + abs_offset; + } + else { + switch(tvb->type) { + case TVBUFF_REAL_DATA: + g_assert_not_reached(); + case TVBUFF_SUBSET: + return ensure_contiguous(tvb->tvbuffs.subset.tvb, + abs_offset - tvb->tvbuffs.subset.offset, + abs_length); + case TVBUFF_COMPOSITE: + return composite_ensure_contiguous(tvb, abs_offset, abs_length); + } + } + + g_assert_not_reached(); + return NULL; +} + +static const guint8* +guint8_find(const guint8* haystack, size_t haystacklen, guint8 needle) +{ + const guint8 *b; + int i; + + for (b = haystack, i = 0; i < haystacklen; i++, b++) { + if (*b == needle) { + return b; + } + } + + return NULL; +} + + + +/************** ACCESSORS **************/ + +static guint8* +composite_memcpy(tvbuff_t *tvb, guint8* target, guint abs_offset, guint abs_length) +{ + guint i, num_members; + tvb_comp_t *composite; + tvbuff_t *member_tvb = NULL; + guint member_offset, member_length; + gboolean retval; + GSList *slist; + + g_assert(tvb->type == TVBUFF_COMPOSITE); + + /* Maybe the range specified by offset/length + * is contiguous inside one of the member tvbuffs */ + composite = &tvb->tvbuffs.composite; + num_members = g_slist_length(composite->tvbs); + + for (i = 0; i < num_members; i++) { + if (abs_offset <= composite->end_offsets[i]) { + slist = g_slist_nth(composite->tvbs, i); + member_tvb = slist->data; + break; + } + } + g_assert(member_tvb); + + if (check_offset_length_no_exception(member_tvb, abs_offset - composite->start_offsets[i], + abs_length, &member_offset, &member_length, NULL)) { + + g_assert(!tvb->real_data); + return tvb_memcpy(member_tvb, target, member_offset, member_length); + } + else { + /* The requested data is non-contiguous inside + * the member tvb. We have to memcpy() the part that's in the member tvb, + * then iterate across the other member tvb's, copying their portions + * until we have copied all data. + */ + retval = compute_offset_length(member_tvb, abs_offset - composite->start_offsets[i], -1, + &member_offset, &member_length, NULL); + g_assert(retval); + + tvb_memcpy(member_tvb, target, member_offset, member_length); + abs_offset += member_length; + abs_length -= member_length; + + /* Recurse */ + if (abs_length > 0) { + composite_memcpy(tvb, target + member_length, abs_offset, abs_length); + } + + return target; + } + + g_assert_not_reached(); + return NULL; +} + +guint8* +tvb_memcpy(tvbuff_t *tvb, guint8* target, gint offset, gint length) +{ + guint abs_offset, abs_length; + + g_assert(length >= -1); + check_offset_length(tvb, offset, length, &abs_offset, &abs_length); + + if (tvb->real_data) { + return (guint8*) memcpy(target, tvb->real_data + abs_offset, abs_length); + } + + switch(tvb->type) { + case TVBUFF_REAL_DATA: + g_assert_not_reached(); + + case TVBUFF_SUBSET: + return tvb_memcpy(tvb->tvbuffs.subset.tvb, target, + abs_offset - tvb->tvbuffs.subset.offset, + abs_length); + + case TVBUFF_COMPOSITE: + return composite_memcpy(tvb, target, offset, length); + } + + g_assert_not_reached(); + return NULL; +} + + +guint8* +tvb_memdup(tvbuff_t *tvb, gint offset, gint length) +{ + guint abs_offset, abs_length; + guint8 *duped; + + check_offset_length(tvb, offset, length, &abs_offset, &abs_length); + + duped = g_malloc(abs_length); + return tvb_memcpy(tvb, duped, abs_offset, abs_length); +} + + + +guint8* +tvb_get_ptr(tvbuff_t *tvb, gint offset, gint length) +{ + return ensure_contiguous(tvb, offset, length); +} + +guint8 +tvb_get_guint8(tvbuff_t *tvb, gint offset) +{ + guint8* ptr; + + ptr = ensure_contiguous(tvb, offset, sizeof(guint8)); + return *ptr; +} + +guint16 +tvb_get_ntohs(tvbuff_t *tvb, gint offset) +{ + guint8* ptr; + + ptr = ensure_contiguous(tvb, offset, sizeof(guint16)); + return pntohs(ptr); +} + +guint32 +tvb_get_ntoh24(tvbuff_t *tvb, gint offset) +{ + guint8* ptr; + + ptr = ensure_contiguous(tvb, offset, 3); + return pntoh24(ptr); +} + +guint32 +tvb_get_ntohl(tvbuff_t *tvb, gint offset) +{ + guint8* ptr; + + ptr = ensure_contiguous(tvb, offset, sizeof(guint32)); + return pntohl(ptr); +} + +#ifdef G_HAVE_GINT64 +guint64 +tvb_get_ntohll(tvbuff_t *tvb, gint offset) +{ + guint8* ptr; + + ptr = ensure_contiguous(tvb, offset, sizeof(guint64)); + return pntohll(ptr); +} +#endif + +guint16 +tvb_get_letohs(tvbuff_t *tvb, gint offset) +{ + guint8* ptr; + + ptr = ensure_contiguous(tvb, offset, sizeof(guint16)); + return pletohs(ptr); +} + +guint32 +tvb_get_letoh24(tvbuff_t *tvb, gint offset) +{ + guint8* ptr; + + ptr = ensure_contiguous(tvb, offset, 3); + return pletoh24(ptr); +} + +guint32 +tvb_get_letohl(tvbuff_t *tvb, gint offset) +{ + guint8* ptr; + + ptr = ensure_contiguous(tvb, offset, sizeof(guint32)); + return pletohl(ptr); +} + +#ifdef G_HAVE_GINT64 +guint64 +tvb_get_letohll(tvbuff_t *tvb, gint offset) +{ + guint8* ptr; + + ptr = ensure_contiguous(tvb, offset, sizeof(guint64)); + return pletohll(ptr); +} +#endif + + +/* Find first occurence of needle in tvbuff, starting at offset. Searches + * at most maxlength number of bytes. Returns the offset of the found needle, + * or -1 if not found. Will not throw an exception, even if maxlength exceeds + * boundary of tvbuff; in that case, -1 will be returned if the boundary is + * reached before finding needle. */ +gint +tvb_find_guint8(tvbuff_t *tvb, gint offset, guint maxlength, guint8 needle) +{ + guint abs_offset, junk_length; + const guint8 *result; + guint limit; + + check_offset_length(tvb, offset, 0, &abs_offset, &junk_length); + + /* Only search to end of tvbuff, w/o throwing exception. */ + if (tvb_length_remaining(tvb, abs_offset) < maxlength) { + limit = maxlength - (tvb_length(tvb) - abs_offset); + } + else { + limit = maxlength; + } + + /* If we have real data, perform our search now. */ + if (tvb->real_data) { + result = guint8_find(tvb->real_data + abs_offset, limit, needle); + if (result == NULL) { + return -1; + } + else { + return result - tvb->real_data; + } + } + + switch(tvb->type) { + case TVBUFF_REAL_DATA: + g_assert_not_reached(); + + case TVBUFF_SUBSET: + return tvb_find_guint8(tvb->tvbuffs.subset.tvb, + abs_offset - tvb->tvbuffs.subset.offset, + limit, needle); + + case TVBUFF_COMPOSITE: + g_assert_not_reached(); + /* XXX - return composite_find_guint8(tvb, offset, limit, needle); */ + } + + g_assert_not_reached(); + return -1; +} + +/* Find length of string by looking for end of string ('\0'), up to + * 'max_length' characters'. Returns -1 if 'max_length' reached + * before finding EOS. */ +gint +tvb_strnlen(tvbuff_t *tvb, gint offset, guint maxlength) +{ + gint result_offset; + guint abs_offset, junk_length; + + check_offset_length(tvb, offset, 0, &abs_offset, &junk_length); + + result_offset = tvb_find_guint8(tvb, abs_offset, maxlength, 0); + + if (result_offset == -1) { + return -1; + } + else { + return result_offset; + } +} + +/* + * Implement strneql etc + */ + +/* Call strncmp after checking if enough chars left, otherwise return -1 */ +gint +tvb_strneql(tvbuff_t *tvb, gint offset, guint8 *str, gint size) +{ + guint8 *ptr; + + ptr = ensure_contiguous(tvb, offset, size); + + if (ptr) { + + int cmp = strncmp(ptr, str, size); + + return (cmp == 0 ? 0 : -1); /* Make it -1 if comparison failed */ + + } + else { + + return -1; /* Not enough chars in the TVB */ + + } + +} + +/* + * Format the data in the tvb from offset for length ... + */ + +guint8 * +tvb_format_text(tvbuff_t *tvb, gint offset, gint size) +{ + guint8 *ptr; + gint len = size; + + if ((ptr = ensure_contiguous(tvb, offset, size)) == NULL) { + + len = tvb_length_remaining(tvb, offset); + ptr = ensure_contiguous(tvb, offset, len); + + } + + return format_text(ptr, len); + +} + +/* Looks for a stringz (NUL-terminated string) in tvbuff and copies + * no more than maxlength number of bytes, including terminating NUL, to buffer. + * Returns length of string (not including terminating NUL), or -1 if the string was + * truncated in the buffer due to not having reached the terminating NUL. + * In this way, it acts like snprintf(). + */ +gint +tvb_get_nstringz(tvbuff_t *tvb, gint offset, guint maxlength, guint8* buffer) +{ + gint stringlen, NUL_offset; + guint abs_offset, junk_length; + gint limit; + + check_offset_length(tvb, offset, 0, &abs_offset, &junk_length); + + if (maxlength == 0) { + buffer[0] = 0; + return 0; + } + + /* Only copy to end of tvbuff, w/o throwing exception. */ + if (tvb_length_remaining(tvb, abs_offset) < maxlength) { + limit = maxlength - (tvb_length(tvb) - abs_offset); + } + else { + limit = maxlength; + } + + NUL_offset = tvb_strnlen(tvb, abs_offset, limit); + + /* If NUL wasn't found, copy the data and return -1 */ + if (NUL_offset == -1) { + tvb_memcpy(tvb, buffer, abs_offset, limit); + return -1; + } + + /* Copy the string to buffer */ + stringlen = NUL_offset - abs_offset; + tvb_memcpy(tvb, buffer, abs_offset, stringlen + 1); + return stringlen; +} + +/* Like tvb_get_nstringz(), but never returns -1. The string is guaranteed to + * have a terminating NUL. If the string was truncated when copied into buffer, + * a NUL is placed at the end of buffer to terminate it. + */ +gint +tvb_get_nstringz0(tvbuff_t *tvb, gint offset, guint maxlength, guint8* buffer) +{ + gint len; + + len = tvb_get_nstringz(tvb, offset, maxlength, buffer); + + if (len == -1) { + buffer[maxlength] = 0; + return maxlength - 1; + } + else { + return len; + } +} diff --git a/epan/tvbuff.h b/epan/tvbuff.h new file mode 100644 index 0000000000..2b69584f04 --- /dev/null +++ b/epan/tvbuff.h @@ -0,0 +1,290 @@ +/* tvbuff.h + * + * Testy, Virtual(-izable) Buffer of guint8*'s + * + * "Testy" -- the buffer gets mad when an attempt is made to access data + * beyond the bounds of the buffer. An exception is thrown. + * + * "Virtual" -- the buffer can have its own data, can use a subset of + * the data of a backing tvbuff, or can be a composite of + * other tvbuffs. + * + * $Id: tvbuff.h,v 1.1 2000/09/27 04:54:54 gram Exp $ + * + * Copyright (c) 2000 by Gilbert Ramirez + * + * Ethereal - Network traffic analyzer + * By Gerald Combs + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __TVBUFF_H__ +#define __TVBUFF_H__ + +#include +#include "exceptions.h" + +typedef struct tvbuff tvbuff_t; + +typedef void (*tvbuff_free_cb_t)(void*); + +/* The different types of tvbuff's */ +typedef enum { + TVBUFF_REAL_DATA, + TVBUFF_SUBSET, + TVBUFF_COMPOSITE +} tvbuff_type; + +/* TVBUFF_REAL_DATA contains a guint8* that points to real data. + * The data is allocated and contiguous. + * + * TVBUFF_SUBSET has a backing tvbuff. The TVBUFF_SUBSET is a "window" + * through which the program sees only a portion of the backing tvbuff. + * + * TVBUFF_COMPOSITE combines multiple tvbuffs sequentually to produce + * a larger byte array. + * + * tvbuff's of any type can be used as the backing-tvbuff of a + * TVBUFF_SUBSET or as the member of a TVBUFF_COMPOSITE. + * TVBUFF_COMPOSITEs can have member-tvbuffs of different types. + * + * Once a tvbuff is create/initialized/finalized, the tvbuff is read-only. + * That is, it cannot point to any other data. A new tvbuff must be created if + * you want a tvbuff that points to other data. + */ + + +/* "class" initialization. Called once during execution of program + * so that tvbuff.c can initialize its data. */ +void tvbuff_init(void); + +/* "class" cleanup. Called once during execution of program + * so that tvbuff.c can clean up its data. */ +void tvbuff_cleanup(void); + + +/* Returns a pointer to a newly initialized tvbuff. Note that + * tvbuff's of types TVBUFF_SUBSET and TVBUFF_COMPOSITE + * require further initialization via the appropriate functions */ +tvbuff_t* tvb_new(tvbuff_type); + +/* Marks a tvbuff for freeing. The guint8* data of a TVBUFF_REAL_DATA + * is *never* freed by the tvbuff routines. The tvbuff itself is actually freed + * once its usage count drops to 0. + * + * Usage counts increment for any time the tvbuff is + * used as a member of another tvbuff, i.e., as the backing buffer for + * a TVBUFF_SUBSET or as a member of a TVBUFF_COMPOSITE. + * + * Although you may call tvb_free(), the tvbuff may still be in use + * by other tvbuff's (TVBUFF_SUBSET or TVBUFF_COMPOSITE), so it is not + * safe, unless you know otherwise, to free your guint8* data. If you + * cannot be sure that your TVBUFF_REAL_DATA is not in use by another + * tvbuff, register a callback with tvb_set_free_cb(); when your tvbuff + * is _really_ freed, then your callback will be called, and at that time + * you can free your original data. + * + * The caller can artificially increment/decrement the usage count + * with tvbuff_increment_usage_count()/tvbuff_decrement_usage_count(). + */ +void tvb_free(tvbuff_t*); + +/* Free the tvbuff_t and all tvbuff's created from it. */ +void tvb_free_chain(tvbuff_t*); + +/* Both return the new usage count, after the increment or decrement */ +guint tvb_increment_usage_count(tvbuff_t*, guint count); + +/* If a decrement causes the usage count to drop to 0, a the tvbuff + * is immediately freed. Be sure you know exactly what you're doing + * if you decide to use this function, as another tvbuff could + * still have a pointer to the just-freed tvbuff, causing corrupted data + * or a segfault in the future */ +guint tvb_decrement_usage_count(tvbuff_t*, guint count); + +/* Set a callback function to call when a tvbuff is actually freed + * (once the usage count drops to 0). One argument is passed to + * that callback --- the guint* that points to the real data. + * Obviously, this only applies to a TVBUFF_REAL_DATA tvbuff. */ +void tvb_set_free_cb(tvbuff_t*, tvbuff_free_cb_t); + + +/* Sets parameters for TVBUFF_REAL_DATA. Can throw ReportedBoundsError. */ +void tvb_set_real_data(tvbuff_t*, const guint8* data, guint length, gint reported_length); + +/* Combination of tvb_new() and tvb_set_real_data(). Can throw ReportedBoundsError. */ +tvbuff_t* tvb_new_real_data(const guint8* data, guint length, gint reported_length); + + +/* Define the subset of the backing buffer to use. + * + * 'backing_offset' can be negative, to indicate bytes from + * the end of the backing buffer. + * + * 'backing_length' can be 0, although the usefulness of the buffer would + * be rather limited. + * + * 'backing_length' of -1 means "to the end of the backing buffer" + * + * Will throw BoundsError if 'backing_offset'/'length' + * is beyond the bounds of the backing tvbuff. + * Can throw ReportedBoundsError. */ +void tvb_set_subset(tvbuff_t* tvb, tvbuff_t* backing, + gint backing_offset, gint backing_length, gint reported_length); + +/* Combination of tvb_new() and tvb_set_subset() + * Can throw ReportedBoundsError. */ +tvbuff_t* tvb_new_subset(tvbuff_t* backing, + gint backing_offset, gint backing_length, gint reported_length); + + +/* Both tvb_composite_append and tvb_composite_prepend can throw + * BoundsError if member_offset/member_length goes beyond bounds of + * the 'member' tvbuff. */ + +/* Append to the list of tvbuffs that make up this composite tvbuff */ +void tvb_composite_append(tvbuff_t* tvb, tvbuff_t* member); + +/* Prepend to the list of tvbuffs that make up this composite tvbuff */ +void tvb_composite_prepend(tvbuff_t* tvb, tvbuff_t* member); + +/* Helper function that calls tvb_new(TVBUFF_COMPOSITE). + * Provided only to maintain symmetry with other constructors */ +tvbuff_t* tvb_new_composite(void); + +/* Mark a composite tvbuff as initialized. No further appends or prepends + * occur, data access can finally happen after this finalization. */ +void tvb_composite_finalize(tvbuff_t* tvb); + + +/* Get total length of buffer */ +guint tvb_length(tvbuff_t*); + +/* Computes bytes to end of buffer, from offset (which can be negative, + * to indicate bytes from end of buffer). Function returns -1 to + * indicate that offset is out of bounds. No exception is thrown. */ +guint tvb_length_remaining(tvbuff_t*, gint offset); + +/* Checks (w/o throwing exception) that the bytes referred to by 'offset'/'length' + * actualy exist in the buffer */ +gboolean tvb_bytes_exist(tvbuff_t*, gint offset, gint length); + +/* Checks (w/o throwing exception) that offset exists in buffer */ +gboolean tvb_offset_exists(tvbuff_t*, gint offset); + +/* Get reported length of buffer */ +guint tvb_reported_length(tvbuff_t*); + +/* Returns the offset from the first byte of real data. This is + * the same value as 'offset' in tvb_compat() */ +gint tvb_raw_offset(tvbuff_t*); + +/************** START OF ACCESSORS ****************/ +/* All accessors will throw BoundsError or ReportedBoundsError if appropriate */ + +guint8 tvb_get_guint8(tvbuff_t*, gint offset); + +guint16 tvb_get_ntohs(tvbuff_t*, gint offset); +guint32 tvb_get_ntoh24(tvbuff_t*, gint offset); +guint32 tvb_get_ntohl(tvbuff_t*, gint offset); +#ifdef G_HAVE_GINT64 +guint64 tvb_get_ntohll(tvbuff_t*, gint offset); +#endif + +guint16 tvb_get_letohs(tvbuff_t*, gint offset); +guint32 tvb_get_letoh24(tvbuff_t*, gint offset); +guint32 tvb_get_letohl(tvbuff_t*, gint offset); +#ifdef G_HAVE_GINT64 +guint64 tvb_get_letohll(tvbuff_t*, gint offset); +#endif + +/* Returns target for convenience. Does not suffer from possible + * expense of tvb_get_ptr(), since this routine is smart enough + * to copy data in chunks if the request range actually exists in + * different TVBUFF_REAL_DATA tvbuffs. This function assumes that the + * target memory is already allocated; it does not allocate or free the + * target memory. */ +guint8* tvb_memcpy(tvbuff_t*, guint8* target, gint offset, gint length); + +/* It is the user's responsibility to g_free() the memory allocated by + * tvb_memdup(). Calls tvb_memcpy() */ +guint8* tvb_memdup(tvbuff_t*, gint offset, gint length); + +/* WARNING! This function is possibly expensive, temporarily allocating + * another copy of the packet data. Furthermore, it's dangerous because once + * this pointer is given to the user, there's no guarantee that the user will + * honor the 'length' and not overstep the boundaries of the buffer. + * + * The returned pointer is data that is internal to the tvbuff, so do not + * attempt to free it. Don't modify the data, either, because another tvbuff + * that might be using this tvbuff may have already copied that portion of + * the data (sometimes tvbuff's need to make copies of data, but that's the + * internal implementation that you need not worry about). Assume that the + * guint8* points to read-only data that the tvbuff manages. + * + * Return a pointer into our buffer if the data asked for via 'offset'/'length' + * is contiguous (which might not be the case for TVBUFF_COMPOSITE). If the + * data is not contiguous, a tvb_memdup() is called for the entire buffer + * and the pointer to the newly-contiguous data is returned. This dynamically- + * allocated memory will be freed when the tvbuff is freed, after the + * tvbuff_free_cb_t() is called, if any. */ +guint8* tvb_get_ptr(tvbuff_t*, gint offset, gint length); + +/* Find first occurence of needle in tvbuff, starting at offset. Searches + * at most maxlength number of bytes. Returns the offset of the found needle, + * or -1 if not found. Will not throw an exception, even if maxlength exceeds + * boundary of tvbuff; in that case, -1 will be returned if the boundary is + * reached before finding needle. */ +gint tvb_find_guint8(tvbuff_t*, gint offset, guint maxlength, guint8 needle); + +/* Find length of string by looking for end of string ('\0'), up to + * 'max_length' characters'. Returns -1 if 'max_length' reached + * before finding EOS. */ +gint tvb_strnlen(tvbuff_t*, gint offset, guint maxlength); +guint8 * tvb_format_text(tvbuff_t *tvb, gint offset, gint size); + +/* Looks for a stringz (NUL-terminated string) in tvbuff and copies + * no more than maxlength number of bytes, including terminating NUL, to buffer. + * Returns length of string (not including terminating NUL), or -1 if the string was + * truncated in the buffer due to not having reached the terminating NUL. + * In this way, it acts like snprintf(). + */ +gint tvb_get_nstringz(tvbuff_t *tvb, gint offset, guint maxlength, guint8* buffer); + +/* Like tvb_get_nstringz(), but never returns -1. The string is guaranteed to + * have a terminating NUL. If the string was truncated when copied into buffer, + * a NUL is placed at the end of buffer to terminate it. + */ +gint tvb_get_nstringz0(tvbuff_t *tvb, gint offset, guint maxlength, guint8* buffer); + + +/* Call strncmp after checking if enough chars left, otherwise return -1 */ +gint tvb_strneql(tvbuff_t *tvb, gint offset, guint8 *str, gint size); + +/************** END OF ACCESSORS ****************/ + +/* Sets pd and offset so that tvbuff's can be used with code + * that only understands pd/offset and not tvbuffs. + * This is the "compatibility" function */ +void tvb_compat(tvbuff_t*, const guint8 **pd, int *offset); + +#define tvb_create_from_top(offset) \ + tvb_new_subset(pi.compat_top_tvb, (offset), \ + pi.captured_len - (offset), pi.len - (offset)) + +#endif /* __TVBUFF_H__ */ -- cgit v1.2.3