aboutsummaryrefslogtreecommitdiffstats
path: root/epan
diff options
context:
space:
mode:
authorGilbert Ramirez <gram@alumni.rice.edu>2000-09-27 04:55:05 +0000
committerGilbert Ramirez <gram@alumni.rice.edu>2000-09-27 04:55:05 +0000
commit83ec54675c19f25cfbb2e8a8863c6ee2191d8b0c (patch)
tree35d39e43d20aca160b96d8a85ddc2b80e4646f67 /epan
parentf881e13932e0ba390c26cce15d1334b2154a8062 (diff)
First step in moving core Ethereal routines to libepan.
svn path=/trunk/; revision=2458
Diffstat (limited to 'epan')
-rw-r--r--epan/.cvsignore8
-rw-r--r--epan/Makefile.am74
-rw-r--r--epan/conversation.c362
-rw-r--r--epan/conversation.h63
-rw-r--r--epan/dfilter-grammar.y1186
-rw-r--r--epan/dfilter-int.h145
-rw-r--r--epan/dfilter-scanner.l366
-rw-r--r--epan/dfilter.c1064
-rw-r--r--epan/dfilter.h70
-rw-r--r--epan/epan.c41
-rw-r--r--epan/epan.h49
-rw-r--r--epan/except.c415
-rw-r--r--epan/except.h149
-rw-r--r--epan/exceptions.h203
-rw-r--r--epan/pint.h115
-rw-r--r--epan/plugins.c586
-rw-r--r--epan/plugins.h74
-rw-r--r--epan/proto.c2480
-rw-r--r--epan/proto.h553
-rw-r--r--epan/strutil.c222
-rw-r--r--epan/strutil.h46
-rw-r--r--epan/tvbtest.c406
-rw-r--r--epan/tvbuff.c1144
-rw-r--r--epan/tvbuff.h290
24 files changed, 10111 insertions, 0 deletions
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 <gerald@zing.org>
+# Copyright 1998 Gerald Combs
+#
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 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 <gerald@zing.org>
+ * Copyright 1998 Gerald Combs
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdio.h>
+
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+
+#ifdef HAVE_NETINET_IN_H
+# include <netinet/in.h>
+#endif
+
+#include <string.h>
+#include <glib.h>
+#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 <gerald@zing.org>
+ * Copyright 1998 Gerald Combs
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 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 <gerald@zing.org>
+ * Copyright 1998 Gerald Combs
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+
+#ifdef HAVE_NETINET_IN_H
+# include <netinet/in.h>
+#endif
+
+#ifdef NEED_SNPRINTF_H
+# ifdef HAVE_STDARG_H
+# include <stdarg.h>
+# else
+# include <varargs.h>
+# endif
+# include "snprintf.h"
+#endif
+
+#ifndef __GLIB_H__
+#include <glib.h>
+#endif
+
+#include <string.h>
+#include <errno.h>
+#include <math.h>
+
+#ifndef _STDLIB_H
+#include <stdlib.h>
+#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 <node> statement expression relation
+%type <node> numeric_value numeric_variable
+%type <node> floating_value floating_variable
+%type <node> ether_value ether_variable
+%type <node> ipxnet_value ipxnet_variable
+%type <node> ipv4_value ipv4_variable
+%type <node> ipv6_value ipv6_variable
+%type <node> string_value string_variable
+%type <node> variable_name
+%type <node> bytes_value bytes_variable
+
+%type <operand> numeric_relation
+%type <operand> equality_relation
+%type <operand> bytes_relation
+
+%type <variable> any_variable_type
+
+%token <variable> T_FT_UINT8
+%token <variable> T_FT_UINT16
+%token <variable> T_FT_UINT24
+%token <variable> T_FT_UINT32
+%token <variable> T_FT_INT8
+%token <variable> T_FT_INT16
+%token <variable> T_FT_INT24
+%token <variable> T_FT_INT32
+%token <variable> T_FT_ETHER
+%token <variable> T_FT_IPv4
+%token <variable> T_FT_IPv6
+%token <variable> T_FT_NONE
+%token <variable> T_FT_BYTES
+%token <variable> T_FT_BOOLEAN
+%token <variable> T_FT_STRING
+%token <variable> T_FT_IPXNET
+%token <variable> T_FT_DOUBLE
+
+%token <string> T_VAL_UNQUOTED_STRING
+%token <string> T_VAL_QUOTED_STRING
+%token <string> T_VAL_BYTE_STRING
+%token <byte_range> T_VAL_BYTE_RANGE
+
+%token <operand> TOK_AND TOK_OR TOK_NOT TOK_XOR
+%token <operand> 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 <gerald@zing.org>
+ * Copyright 1998 Gerald Combs
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 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 <gerald@zing.org>
+ * Copyright 1998 Gerald Combs
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef HAVE_IO_H
+#include <io.h> /* for isatty() on win32 */
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+
+#ifndef _STDIO_H
+#include <stdio.h>
+#endif
+
+#ifndef _STRING_H
+#include <string.h>
+#endif
+
+#ifndef __G_LIB_H__
+#include <glib.h>
+#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 <gerald@zing.org>
+ * Copyright 1998 Gerald Combs
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <glib.h>
+
+#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 <gerald@zing.org>
+ * Copyright 1998 Gerald Combs
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 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 <glib.h>
+#include <epan.h>
+
+#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 <kaz@ashi.footprints.net>
+ *
+ * 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 <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <limits.h>
+#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 <pthread.h>
+
+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 <stdio.h>
+#include <ctype.h>
+
+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 <kaz@ashi.footprints.net>
+ *
+ * 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 <setjmp.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#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) {
+ * <TRY code>
+ * }
+ * else if (x == 1) {
+ * <CATCH(1) code>
+ * }
+ * else if (x == 2) {
+ * <CATCH(2) code>
+ * }
+ * else if (x == 3 || x == 4) {
+ * <CATCH2(3,4) code>
+ * }
+ * else {
+ * <CATCH_ALL code> {
+ * }
+ * <FINALLY code>
+ * }<ENDTRY tag>
+ *
+ * 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 <gerald@zing.org>
+ * Copyright 1998 Gerald Combs
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __PINT_H__
+#define __PINT_H__
+
+#include <glib.h>
+
+/* 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 <gerald@zing.org>
+ * 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 <time.h>
+
+#ifdef HAVE_DIRENT_H
+#include <dirent.h>
+#endif
+
+#ifdef HAVE_DIRECT_H
+#include <direct.h>
+#endif
+
+#include <string.h>
+#include <stdlib.h>
+
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#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 = &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 <gerald@zing.org>
+ * 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 <glib.h>
+#include <gmodule.h>
+
+#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 <winsock.h>
+#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 <gerald@zing.org>
+ * Copyright 1998 Gerald Combs
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <glib.h>
+
+#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: <MISSING>", 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 <gerald@zing.org>
+ * Copyright 1998 Gerald Combs
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+
+#ifndef __PROTO_H__
+#define __PROTO_H__
+
+#ifdef HAVE_SYS_TIME_H
+# include <sys/time.h>
+#endif
+
+#ifdef HAVE_STDARG_H
+# include <stdarg.h>
+#else
+# include <varargs.h>
+#endif
+
+#ifdef HAVE_WINSOCK_H
+# include <winsock.h>
+#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 <glib.h> */
+ 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 <gerald@zing.org>
+ * Copyright 1998 Gerald Combs
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <glib.h>
+#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 <non-LF><LF><CR>; 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 <gerald@zing.org>
+ * Copyright 1998 Gerald Combs
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 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 <sys/types.h> /* for u_char */
+#endif
+
+#ifdef HAVE_WINSOCK_H
+# include <winsock.h> /* 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 <gram@xiexie.org>
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#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 <gram@xiexie.org>
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@zing.org>
+ * Copyright 1998 Gerald Combs
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <string.h>
+
+#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 <gram@xiexie.org>
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@zing.org>
+ * Copyright 1998 Gerald Combs
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __TVBUFF_H__
+#define __TVBUFF_H__
+
+#include <glib.h>
+#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__ */