aboutsummaryrefslogtreecommitdiffstats
path: root/proto.c
diff options
context:
space:
mode:
authorGilbert Ramirez <gram@alumni.rice.edu>1999-07-07 22:52:57 +0000
committerGilbert Ramirez <gram@alumni.rice.edu>1999-07-07 22:52:57 +0000
commit07f42b5b31f9523deeb147226521f77a2c8dd797 (patch)
treec720d2a42f11c9d14ac1a161eb785b438c2c4b14 /proto.c
parentfba49cfe85d4b23ebbffa97fae126a379e913ecd (diff)
Created a new protocol tree implementation and a new display filter
mechanism that is built into ethereal. Wiretap is now used to read all file formats. Libpcap is used only for capturing. svn path=/trunk/; revision=342
Diffstat (limited to 'proto.c')
-rw-r--r--proto.c620
1 files changed, 620 insertions, 0 deletions
diff --git a/proto.c b/proto.c
new file mode 100644
index 0000000000..7bdd2f54b4
--- /dev/null
+++ b/proto.c
@@ -0,0 +1,620 @@
+/* proto.c
+ * Routines for protocol tree
+ *
+ * $Id: proto.c,v 1.1 1999/07/07 22:51:59 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
+
+#ifndef _STDIO_H
+#include <stdio.h>
+#endif
+
+#include <stdarg.h>
+
+#ifndef _STRING_H
+#include <string.h>
+#endif
+
+#ifdef NEED_SNPRINTF_H
+# include "snprintf.h"
+#endif
+
+#ifndef __G_LIB_H__
+#include <glib.h>
+#endif
+
+#ifndef __PROTO_H__
+#include "proto.h"
+#endif
+
+#ifndef __PACKET_H__
+#include "packet.h"
+#endif
+
+#ifndef __RESOLV_H__
+#include "resolv.h"
+#endif
+
+#define cVALS(x) (const value_string*)(x)
+
+static void
+proto_tree_free_node(GNode *node, gpointer data);
+
+static struct header_field_info*
+find_hfinfo_record(int hfindex);
+
+static proto_item *
+proto_tree_add_item_value(proto_tree *tree, int hfindex, gint start,
+ gint length, int include_format, int visible, va_list ap);
+
+static gboolean proto_check_id(GNode *node, gpointer data);
+
+void dfilter_yacc_init(void);
+
+/* centralization of registration functions */
+void proto_register_data(void);
+void proto_register_eth(void);
+void proto_register_fddi(void);
+void proto_register_frame(void);
+void proto_register_ip(void);
+void proto_register_llc(void);
+void proto_register_null(void);
+void proto_register_tr(void);
+
+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;
+
+
+/* initialize data structures and register protocols and fields */
+void
+proto_init(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); /* ever needs to be TRUE? */
+
+ 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();
+
+ /* Have each dissector register its protocols and fields. The
+ * order doesn't matter. Put the calls in alphabetical order
+ * just to make it easy. */
+ proto_register_data();
+ proto_register_eth();
+ proto_register_fddi();
+ proto_register_frame();
+ proto_register_ip();
+ proto_register_llc();
+ proto_register_null();
+ proto_register_tr();
+
+ /* 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 */
+ hf_text_only = proto_register_field (
+ /* name */ "Text",
+ /* abbrev */ "text",
+ /* ftype */ FT_TEXT_ONLY,
+ /* parent */ -1,
+ /* vals[] */ NULL );
+
+ dfilter_yacc_init();
+}
+
+/* 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,
+ (GNodeTraverseFunc)proto_tree_free_node, NULL);
+}
+
+static void
+proto_tree_free_node(GNode *node, gpointer data)
+{
+ field_info *fi = (field_info*) (node->data);
+ if (fi->representation)
+ g_mem_chunk_free(gmc_item_labels, fi->representation);
+ if (fi->hfinfo->type == FT_STRING)
+ g_free(fi->value.string);
+ g_mem_chunk_free(gmc_field_info, fi);
+}
+
+/* Finds a record in the hf_info_records array. */
+static struct header_field_info*
+find_hfinfo_record(int hfindex)
+{
+ g_assert(hfindex >= 0 && hfindex < gpa_hfinfo->len);
+ return g_ptr_array_index(gpa_hfinfo, hfindex);
+}
+
+proto_item *
+proto_tree_add_item(proto_tree *tree, int hfindex, gint start, gint length, ...)
+{
+ proto_item *pi;
+ va_list ap;
+
+ va_start(ap, length);
+ pi = proto_tree_add_item_value(tree, hfindex, start, length, 0, 1, ap);
+ va_end(ap);
+
+ return pi;
+}
+
+proto_item *
+proto_tree_add_item_hidden(proto_tree *tree, int hfindex, gint start, gint length, ...)
+{
+ proto_item *pi;
+ va_list ap;
+
+ va_start(ap, length);
+ pi = proto_tree_add_item_value(tree, hfindex, start, length, 0, 0, ap);
+ va_end(ap);
+
+ return pi;
+}
+
+proto_item *
+proto_tree_add_item_format(proto_tree *tree, int hfindex, gint start, gint length, ...)
+{
+ proto_item *pi;
+ va_list ap;
+
+ va_start(ap, length);
+ pi = proto_tree_add_item_value(tree, hfindex, start, length, 1, 1, ap);
+ va_end(ap);
+
+ return pi;
+}
+
+proto_item *
+proto_tree_add_text(proto_tree *tree, gint start, gint length, ...)
+{
+ proto_item *pi;
+ va_list ap;
+
+ va_start(ap, length);
+ pi = proto_tree_add_item_value(tree, hf_text_only, start, length, 1, 1, ap);
+ va_end(ap);
+
+ return pi;
+}
+
+static proto_item *
+proto_tree_add_item_value(proto_tree *tree, int hfindex, gint start,
+ gint length, int include_format, int visible, va_list ap)
+{
+ proto_item *pi;
+ field_info *fi;
+ char *junk, *format;
+
+ if (!tree)
+ return(NULL);
+
+ fi = g_mem_chunk_alloc(gmc_field_info);
+
+ fi->hfinfo = find_hfinfo_record(hfindex);
+ g_assert(fi->hfinfo != NULL);
+ fi->start = start;
+ fi->length = length;
+ fi->tree_type = ETT_NONE;
+ fi->visible = visible;
+
+/* from the stdarg man page on Solaris 2.6:
+NOTES
+ It is up to the calling routine to specify in some manner
+ how many arguments there are, since it is not always possi-
+ ble to determine the number of arguments from the stack
+ frame. For example, execl is passed a zero pointer to sig-
+ nal the end of the list. printf can tell how many arguments
+ there are by the format. It is non-portable to specify a
+ second argument of char, short, or float to va_arg, because
+ arguments seen by the called function are not char, short,
+ or float. C converts char and short arguments to int and
+ converts float arguments to double before passing them to a
+ function.
+*/
+ switch(fi->hfinfo->type) {
+ case FT_NONE:
+ junk = va_arg(ap, guint8*);
+ break;
+
+ case FT_BOOLEAN:
+ fi->value.boolean = va_arg(ap, unsigned int) ? TRUE : FALSE;
+ break;
+
+ case FT_UINT8:
+ case FT_VALS_UINT8:
+ fi->value.numeric = va_arg(ap, unsigned int);
+ break;
+
+ case FT_UINT16:
+ case FT_VALS_UINT16:
+ fi->value.numeric = va_arg(ap, unsigned int);
+ break;
+
+ case FT_UINT32:
+ case FT_VALS_UINT24:
+ case FT_VALS_UINT32:
+ case FT_RELATIVE_TIME:
+ case FT_IPv4:
+ case FT_IPXSERVER:
+ fi->value.numeric = va_arg(ap, guint32);
+ break;
+
+ case FT_ETHER:
+ case FT_ETHER_VENDOR:
+/* fi->value.ether = va_arg(ap, guint8*);*/
+ memcpy(fi->value.ether, va_arg(ap, guint8*), 6);
+ break;
+
+ case FT_ABSOLUTE_TIME:
+ memcpy(&fi->value.abs_time, va_arg(ap, struct timeval*),
+ sizeof(struct timeval));
+ break;
+
+ case FT_STRING:
+ fi->value.string = g_strdup(va_arg(ap, char*)); /* XXX */
+ break;
+
+ case FT_TEXT_ONLY:
+ ; /* nothing */
+ break;
+
+ default:
+ g_error("hfinfo->type %d not handled\n", fi->hfinfo->type);
+ break;
+ }
+
+ pi = (proto_item*) g_node_new(fi);
+ g_node_append((GNode*)tree, (GNode*)pi);
+
+ /* are there any formatting arguments? */
+ if (visible && include_format) {
+ fi->representation = g_mem_chunk_alloc(gmc_item_labels);
+ format = va_arg(ap, char*);
+ vsnprintf(fi->representation, ITEM_LABEL_LENGTH,
+ format, ap);
+ }
+ else {
+ fi->representation = NULL;
+ }
+
+ return pi;
+}
+
+void
+proto_item_set_len(proto_item *pi, gint length)
+{
+ field_info *fi = (field_info*) (((GNode*)pi)->data);
+ fi->length = 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);
+ fi->tree_type = idx;
+ return (proto_tree*) pi;
+}
+
+
+int
+proto_register_protocol(char *name, char *abbrev)
+{
+ return proto_register_field(name, abbrev, FT_NONE, -1, NULL);
+}
+
+void
+proto_register_field_array(int parent, const hf_register_info *hf, int num_records)
+{
+ int field_id, i;
+ const hf_register_info *ptr = hf;
+
+ for (i = 0; i < num_records; i++, ptr++) {
+ field_id = proto_register_field(ptr->name, ptr->abbrev,
+ ptr->type, parent, ptr->vals);
+ *ptr->p_id = field_id;
+ }
+}
+
+
+int
+proto_register_field(char *name, char *abbrev, enum ftenum type, int parent,
+ struct value_string* vals)
+{
+ struct header_field_info *hfinfo;
+
+ hfinfo = g_mem_chunk_alloc(gmc_hfinfo);
+ hfinfo->name = name; /* should I g_strdup? */
+ hfinfo->abbrev = abbrev; /* should I g_strdup? */
+ hfinfo->type = type;
+ hfinfo->parent = parent; /* this field differentiates protos and fields */
+ hfinfo->vals = vals;
+
+ g_assert((vals == NULL) || (type == FT_VALS_UINT8 || type == FT_VALS_UINT16 ||
+ type == FT_VALS_UINT24 || type == FT_VALS_UINT32));
+
+ /* 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_item_fill_label(field_info *fi, gchar *label_str)
+{
+ char *s;
+
+ switch(fi->hfinfo->type) {
+ case FT_NONE:
+ snprintf(label_str, ITEM_LABEL_LENGTH,
+ "%s", fi->hfinfo->name);
+ break;
+
+ case FT_BOOLEAN:
+ snprintf(label_str, ITEM_LABEL_LENGTH,
+ "%s: %s", fi->hfinfo->name,
+ fi->value.boolean == TRUE ? "True" : "False");
+ break;
+
+ case FT_UINT8:
+ case FT_UINT16:
+ case FT_UINT32:
+ snprintf(label_str, ITEM_LABEL_LENGTH,
+ "%s: %d", fi->hfinfo->name,
+ fi->value.numeric);
+ break;
+
+ case FT_ABSOLUTE_TIME:
+ snprintf(label_str, ITEM_LABEL_LENGTH,
+ "%s: %s", fi->hfinfo->name,
+ abs_time_to_str(&fi->value.abs_time));
+ break;
+
+ case FT_VALS_UINT8:
+ s = match_strval(fi->value.numeric, cVALS(fi->hfinfo->vals));
+ snprintf(label_str, ITEM_LABEL_LENGTH,
+ "%s: %s (0x%02x)", fi->hfinfo->name,
+ (s ? s : "Unknown"), fi->value.numeric);
+ break;
+
+ case FT_VALS_UINT16:
+ s = match_strval(fi->value.numeric, cVALS(fi->hfinfo->vals));
+ snprintf(label_str, ITEM_LABEL_LENGTH,
+ "%s: %s (0x%04x)", fi->hfinfo->name,
+ (s ? s : "Unknown"), fi->value.numeric);
+ break;
+
+ case FT_VALS_UINT24:
+ s = match_strval(fi->value.numeric, cVALS(fi->hfinfo->vals));
+ snprintf(label_str, ITEM_LABEL_LENGTH,
+ "%s: %s (0x%06x)", fi->hfinfo->name,
+ (s ? s : "Unknown"), fi->value.numeric);
+ break;
+
+
+ case FT_VALS_UINT32:
+ s = match_strval(fi->value.numeric, cVALS(fi->hfinfo->vals));
+ snprintf(label_str, ITEM_LABEL_LENGTH,
+ "%s: %s (0x%08x)", fi->hfinfo->name,
+ (s ? s : "Unknown"), fi->value.numeric);
+ break;
+
+ case FT_ETHER:
+ snprintf(label_str, ITEM_LABEL_LENGTH,
+ "%s: %s (%s)", fi->hfinfo->name,
+ ether_to_str(fi->value.ether),
+ get_ether_name(fi->value.ether));
+ break;
+
+ case FT_ETHER_VENDOR:
+ snprintf(label_str, ITEM_LABEL_LENGTH,
+ "%s: %02x:%02x:%02x (%s)", fi->hfinfo->name,
+ fi->value.ether[0],
+ fi->value.ether[1],
+ fi->value.ether[2],
+ get_manuf_name(fi->value.ether));
+ break;
+
+ case FT_IPv4:
+ snprintf(label_str, ITEM_LABEL_LENGTH,
+ "%s: %s (%s)", fi->hfinfo->name,
+ get_hostname(fi->value.numeric),
+ ip_to_str((guint8*)&fi->value.numeric));
+ break;
+
+ case FT_STRING:
+ snprintf(label_str, ITEM_LABEL_LENGTH,
+ "%s: %s", fi->hfinfo->name, fi->value.string);
+ break;
+
+ default:
+ g_error("hfinfo->type %d not handled\n", fi->hfinfo->type);
+ break;
+ }
+}
+
+int
+proto_registrar_n(void)
+{
+ return gpa_hfinfo->len;
+}
+
+char*
+proto_registrar_get_abbrev(int n)
+{
+ struct header_field_info *hfinfo;
+
+ hfinfo = find_hfinfo_record(n);
+ if (hfinfo)
+ return hfinfo->abbrev;
+ else
+ return NULL;
+}
+
+int
+proto_registrar_get_ftype(int n)
+{
+ struct header_field_info *hfinfo;
+
+ hfinfo = find_hfinfo_record(n);
+ if (hfinfo)
+ return hfinfo->type;
+ else
+ return -1;
+}
+
+int
+proto_registrar_get_parent(int n)
+{
+ struct header_field_info *hfinfo;
+
+ hfinfo = find_hfinfo_record(n);
+ if (hfinfo)
+ return hfinfo->parent;
+ else
+ return -2;
+}
+
+gboolean
+proto_registrar_is_protocol(int n)
+{
+ struct header_field_info *hfinfo;
+
+ hfinfo = find_hfinfo_record(n);
+ if (hfinfo)
+ return (hfinfo->parent == -1 ? TRUE : FALSE);
+ else
+ return FALSE;
+}
+
+typedef struct find_id_info {
+ int target;
+ GNode *result;
+} find_id_info;
+
+/* looks for a protocol or a header field in a proto_tree. Assumes that protocols
+ * are at the top level, and header fields only occur underneath their parent's
+ * subtree. Returns NULL if field not found
+ */
+proto_item*
+proto_find_field(proto_tree* tree, int id)
+{
+ find_id_info fiinfo;
+ int parent_protocol;
+ proto_tree *subtree;
+
+ fiinfo.target = id;
+ fiinfo.result = NULL;
+
+ /* do a quicker check if field is a protocol */
+ if (proto_registrar_is_protocol(id) == TRUE) {
+ return proto_find_protocol(tree, id);
+ }
+
+ /* find the field's parent protocol */
+ parent_protocol = proto_registrar_get_parent(id);
+ subtree = proto_find_protocol(tree, parent_protocol);
+
+ /* if there is a tree with that protocol, search it for the field */
+ if (subtree)
+ g_node_traverse((GNode*)subtree, G_IN_ORDER, G_TRAVERSE_ALL, -1, proto_check_id, &fiinfo);
+
+ return (proto_item*) fiinfo.result;
+}
+
+
+/* Looks for a protocol at the top layer of the tree.
+ * Assumption: a protocol can occur only once in a proto_tree.
+ */
+proto_item*
+proto_find_protocol(proto_tree* tree, int protocol_id)
+{
+ find_id_info fiinfo;
+
+ fiinfo.target = protocol_id;
+ fiinfo.result = NULL;
+
+ g_node_traverse((GNode*)tree, G_IN_ORDER, G_TRAVERSE_ALL, 2, proto_check_id, &fiinfo);
+ return (proto_item*) fiinfo.result;
+}
+
+
+static gboolean
+proto_check_id(GNode *node, gpointer data)
+{
+ field_info *fi = (field_info*) (node->data);
+ find_id_info *fiinfo = (find_id_info*) data;
+
+ if (fi) { /* !fi == the top most container node which holds nothing */
+ if (fi->hfinfo->id == fiinfo->target) {
+ fiinfo->result = node;
+ return TRUE; /* halt traversal */
+ }
+ }
+ return FALSE; /* keep traversing */
+}
+
+void
+proto_get_field_values(proto_tree* subtree, GNodeTraverseFunc fill_array_func, proto_tree_search_info *sinfo)
+{
+ g_node_traverse((GNode*)subtree, G_IN_ORDER, G_TRAVERSE_ALL, -1, fill_array_func, sinfo);
+}