Note that capture filters don't work on Linux loopback devices with the
[obnox/wireshark/wip.git] / proto.c
diff --git a/proto.c b/proto.c
index 9c68451a1463c491cbf2582359101b0eedb2dbd2..50d2b99a838f7401062fe9835181916112c204d2 100644 (file)
--- a/proto.c
+++ b/proto.c
@@ -1,7 +1,7 @@
 /* proto.c
  * Routines for protocol tree
  *
- * $Id: proto.c,v 1.6 1999/07/31 02:15:12 guy Exp $
+ * $Id: proto.c,v 1.52 2000/01/22 04:59:55 guy Exp $
  *
  * Ethereal - Network traffic analyzer
  * By Gerald Combs <gerald@zing.org>
 #include "resolv.h"
 #endif
 
+#ifndef __REGISTER_H__
+#include "register.h"
+#endif
+
+#include "packet-ipv6.h"
+
 #define cVALS(x) (const value_string*)(x)
 
-static void
+static gboolean
 proto_tree_free_node(GNode *node, gpointer data);
 
 static struct header_field_info*
@@ -73,59 +79,24 @@ 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);
+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 proto_register_field_init(header_field_info *hfinfo, int parent);
+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);
 
-void dfilter_yacc_init(void);
-
-/* centralization of registration functions */
-void proto_register_aarp(void);
-void proto_register_arp(void);
-void proto_register_atalk(void);
-void proto_register_bootp(void);
-void proto_register_cdp(void);
-void proto_register_data(void);
-void proto_register_dns(void);
-void proto_register_eth(void);
-void proto_register_fddi(void);
-void proto_register_frame(void);
-void proto_register_ftp(void);
-void proto_register_giop(void);
-void proto_register_gre(void);
-void proto_register_http(void);
-void proto_register_icmp(void);
-void proto_register_icmpv6(void);
-void proto_register_igmp(void);
-void proto_register_ip(void);
-void proto_register_ipsec(void);
-void proto_register_ipv6(void);
-void proto_register_ipx(void);
-void proto_register_isakmp(void);
-void proto_register_llc(void);
-void proto_register_nbipx(void);
-void proto_register_nbt(void);
-void proto_register_ncp(void);
-void proto_register_nntp(void);
-void proto_register_null(void);
-void proto_register_osi(void);
-void proto_register_ospf(void);
-void proto_register_pop(void);
-void proto_register_ppp(void);
-void proto_register_rip(void);
-void proto_register_rsvp(void);
-void proto_register_rtsp(void);
-void proto_register_sdp(void);
-void proto_register_smb(void);
-#if defined(WITH_SNMP_CMU) || defined(WITH_SNMP_UCD)
-void proto_register_snmp(void);
-#endif
-void proto_register_telnet(void);
-void proto_register_tftp(void);
-void proto_register_tcp(void);
-void proto_register_tr(void);
-void proto_register_trmac(void);
-void proto_register_udp(void);
+static gboolean check_for_protocol_or_field_id(GNode *node, gpointer data);
+static gboolean check_for_field_within_protocol(GNode *node, gpointer data);
+
+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;
@@ -144,11 +115,30 @@ 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 = TRUE;
 
 /* 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)
@@ -156,7 +146,9 @@ proto_init(void)
        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?  */
+               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 
@@ -169,67 +161,37 @@ proto_init(void)
                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_aarp();
-       proto_register_arp();
-       proto_register_atalk();
-       proto_register_bootp();
-       proto_register_cdp();
-       proto_register_data();
-       proto_register_dns();
-       proto_register_eth();
-       proto_register_fddi();
-       proto_register_frame();
-       proto_register_ftp();
-       proto_register_giop();
-       proto_register_gre();
-       proto_register_http();
-       proto_register_icmp();
-       proto_register_icmpv6();
-       proto_register_igmp();
-       proto_register_ip();
-       proto_register_ipsec();
-       proto_register_ipv6();
-       proto_register_ipx();
-       proto_register_isakmp();
-       proto_register_llc();
-       proto_register_nbipx();
-       proto_register_nbt();
-       proto_register_ncp();
-       proto_register_nntp();
-       proto_register_null();
-       proto_register_osi();
-       proto_register_ospf();
-       proto_register_pop();
-       proto_register_ppp();
-       proto_register_rip();
-       proto_register_rsvp();
-       proto_register_rtsp();
-       proto_register_sdp();
-       proto_register_smb();
-#if defined(WITH_SNMP_CMU) || defined(WITH_SNMP_UCD)
-       proto_register_snmp();
-#endif
-       proto_register_telnet();
-       proto_register_tftp();
-       proto_register_tcp();
-       proto_register_tr();
-       proto_register_trmac();
-       proto_register_udp();
+       /* 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. */
+       register_all_protocols();
 
        /* 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 );
+       proto_register_field_array(-1, hf, array_length(hf));
 
-       dfilter_yacc_init();
+       /* 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 */
@@ -237,18 +199,25 @@ 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);
+               proto_tree_free_node, NULL);
+       g_node_destroy((GNode*)tree);
 }
 
-static void
+static gboolean
 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);
+
+       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_BYTES) 
+                       g_free(fi->value.bytes);
+               g_mem_chunk_free(gmc_field_info, fi);
+       }
+       return FALSE; /* FALSE = do not end traversal of GNode tree */
 }      
 
 /* Finds a record in the hf_info_records array. */
@@ -298,6 +267,19 @@ proto_tree_add_item_format(proto_tree *tree, int hfindex, gint start, gint lengt
        return pi;
 }
 
+proto_item *
+proto_tree_add_notext(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, 0, 1, ap);
+       va_end(ap);
+
+       return pi;
+}
+
 proto_item *
 proto_tree_add_text(proto_tree *tree, gint start, gint length, ...)
 {
@@ -318,10 +300,14 @@ proto_tree_add_item_value(proto_tree *tree, int hfindex, gint start,
        proto_item      *pi;
        field_info      *fi;
        char            *junk, *format;
+       header_field_info *hfinfo;
 
        if (!tree)
                return(NULL);
-  
+
+       /* either visibility flag can nullify the other */
+       visible = proto_tree_is_visible && visible;
+
        fi = g_mem_chunk_alloc(gmc_field_info);
 
        fi->hfinfo = find_hfinfo_record(hfindex);
@@ -331,6 +317,9 @@ proto_tree_add_item_value(proto_tree *tree, int hfindex, gint start,
        fi->tree_type = ETT_NONE;
        fi->visible = visible;
 
+       /* for convenience */
+
+       hfinfo = fi->hfinfo;
 /* from the stdarg man page on Solaris 2.6:
 NOTES
      It is up to the calling routine to specify  in  some  manner
@@ -345,47 +334,69 @@ NOTES
      converts  float arguments to double before passing them to a
      function.
 */
-       switch(fi->hfinfo->type) {
+       switch(hfinfo->type) {
                case FT_NONE:
                        junk = va_arg(ap, guint8*);
                        break;
 
-               case FT_BOOLEAN:
-                       fi->value.boolean = va_arg(ap, unsigned int) ? TRUE : FALSE;
+               case FT_BYTES:
+                       /* This g_malloc'ed memory is freed in
+                          proto_tree_free_node() */
+                       fi->value.bytes = (guint8 *)g_malloc(length);
+                       memcpy(fi->value.bytes, va_arg(ap, guint8*), length);
                        break;
 
+               case FT_BOOLEAN:
                case FT_UINT8:
-               case FT_VALS_UINT8:
+               case FT_UINT16:
+               case FT_UINT24:
+               case FT_UINT32:
+               case FT_INT8:
+               case FT_INT16:
+               case FT_INT24:
+               case FT_INT32:
                        fi->value.numeric = va_arg(ap, unsigned int);
+                       if (hfinfo->bitmask) {
+                               /* Mask out irrelevant portions */
+                               fi->value.numeric &= hfinfo->bitmask;
+
+                               /* Shift bits */
+                               if (hfinfo->bitshift > 0) {
+                                       fi->value.numeric >>= hfinfo->bitshift;
+                               }
+                       }
                        break;
 
-               case FT_UINT16:
-               case FT_VALS_UINT16:
+               case FT_IPv4:
+                       ipv4_addr_set_net_order_addr(&(fi->value.ipv4), va_arg(ap, unsigned int));
+                       ipv4_addr_set_netmask_bits(&(fi->value.ipv4), 32);
+                       break;
+
+               case FT_IPXNET:
                        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);
+               case FT_IPv6:
+                       memcpy(fi->value.ipv6, va_arg(ap, guint8*), 16);
+                       break;
+
+               case FT_DOUBLE:
+                       fi->value.floating = va_arg(ap, double);
                        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*),
+               case FT_RELATIVE_TIME:
+                       memcpy(&fi->value.time, va_arg(ap, struct timeval*),
                                sizeof(struct timeval));
                        break;
 
                case FT_STRING:
-                       fi->value.string = g_strdup(va_arg(ap, char*)); /* XXX */
+                       /* This g_strdup'ed memory is freed in proto_tree_free_node() */
+                       fi->value.string = g_strdup(va_arg(ap, char*));
                        break;
 
                case FT_TEXT_ONLY:
@@ -393,7 +404,7 @@ NOTES
                        break;
 
                default:
-                       g_error("hfinfo->type %d not handled\n", fi->hfinfo->type);
+                       g_error("hfinfo->type %d not handled\n", hfinfo->type);
                        break;
        }
 
@@ -402,8 +413,8 @@ NOTES
 
        /* are there any formatting arguments? */
        if (visible && include_format) {
-               fi->representation = g_mem_chunk_alloc(gmc_item_labels);
                format = va_arg(ap, char*);
+               fi->representation = g_mem_chunk_alloc(gmc_item_labels);
                vsnprintf(fi->representation, ITEM_LABEL_LENGTH,
                                format, ap);
        }
@@ -414,6 +425,24 @@ NOTES
        return pi;
 }
 
+void
+proto_item_set_text(proto_item *pi, ...)
+{
+       field_info *fi = (field_info*) (((GNode*)pi)->data);
+       va_list ap;
+       char *format;
+
+       if (fi->representation)
+               g_mem_chunk_free(gmc_item_labels, fi->representation);
+
+       fi->representation = g_mem_chunk_alloc(gmc_item_labels);
+       va_start(ap, pi);
+       format = va_arg(ap, char*);
+       vsnprintf(fi->representation, ITEM_LABEL_LENGTH,
+                               format, ap);
+       va_end(ap);
+}
+
 void
 proto_item_set_len(proto_item *pi, gint length)
 {
@@ -430,6 +459,7 @@ proto_tree_create_root(void)
 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;
 }
@@ -438,7 +468,20 @@ proto_item_add_subtree(proto_item *pi,  gint idx) {
 int
 proto_register_protocol(char *name, char *abbrev)
 {
-       return proto_register_field(name, abbrev, FT_NONE, -1, NULL);
+       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 */
+
+       return proto_register_field_init(hfinfo, hfinfo->parent);
 }
 
 /* for use with static arrays only, since we don't allocate our own copies
@@ -455,32 +498,28 @@ proto_register_field_array(int parent, hf_register_info *hf, int num_records)
        }
 }
 
-
-/* Here we do allocate a new header_field_info struct */
-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->vals = vals;
-       hfinfo->parent = parent; /* this field differentiates protos and fields */
-
-       return proto_register_field_init(hfinfo, parent);
-}
-
 static int
 proto_register_field_init(header_field_info *hfinfo, int parent)
 {
-       g_assert((hfinfo->vals == NULL) || (hfinfo->type == FT_VALS_UINT8 || hfinfo->type == FT_VALS_UINT16 ||
-               hfinfo->type == FT_VALS_UINT24 || hfinfo->type == FT_VALS_UINT32));
+       /* 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;
-       hfinfo->color = 0;
 
        /* if we always add and never delete, then id == len - 1 is correct */
        g_ptr_array_add(gpa_hfinfo, hfinfo);
@@ -488,64 +527,116 @@ proto_register_field_init(header_field_info *hfinfo, int parent)
        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)
 {
-       char *s;
+       struct header_field_info        *hfinfo = fi->hfinfo;
+       guint32                         n_addr; /* network-order IPv4 address */
 
-       switch(fi->hfinfo->type) {
+       switch(hfinfo->type) {
                case FT_NONE:
                        snprintf(label_str, ITEM_LABEL_LENGTH,
-                               "%s", fi->hfinfo->name);
+                               "%s", hfinfo->name);
                        break;
 
                case FT_BOOLEAN:
+                       fill_label_boolean(fi, label_str);
+                       break;
+
+               case FT_BYTES:
                        snprintf(label_str, ITEM_LABEL_LENGTH,
-                               "%s: %s", fi->hfinfo->name,
-                               fi->value.boolean == TRUE ? "True" : "False");
+                               "%s: %s", hfinfo->name, 
+                                bytes_to_str(fi->value.bytes, fi->length));
                        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:
-                       snprintf(label_str, ITEM_LABEL_LENGTH,
-                               "%s: %u", fi->hfinfo->name,
-                               fi->value.numeric);
+                       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_ABSOLUTE_TIME:
-                       snprintf(label_str, ITEM_LABEL_LENGTH,
-                               "%s: %s", fi->hfinfo->name,
-                               abs_time_to_str(&fi->value.abs_time));
+               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_VALS_UINT8:
-                       s = match_strval(fi->value.numeric, cVALS(fi->hfinfo->vals));
+               case FT_DOUBLE:
                        snprintf(label_str, ITEM_LABEL_LENGTH,
-                               "%s: %s (0x%02x)", fi->hfinfo->name,
-                               (s ? s : "Unknown"), fi->value.numeric);
+                               "%s: %g", fi->hfinfo->name,
+                               fi->value.floating);
                        break;
 
-               case FT_VALS_UINT16:
-                       s = match_strval(fi->value.numeric, cVALS(fi->hfinfo->vals));
+               case FT_ABSOLUTE_TIME:
                        snprintf(label_str, ITEM_LABEL_LENGTH,
-                               "%s: %s (0x%04x)", fi->hfinfo->name,
-                               (s ? s : "Unknown"), fi->value.numeric);
+                               "%s: %s", fi->hfinfo->name,
+                               abs_time_to_str(&fi->value.time));
                        break;
 
-               case FT_VALS_UINT24:
-                       s = match_strval(fi->value.numeric, cVALS(fi->hfinfo->vals));
+               case FT_RELATIVE_TIME:
                        snprintf(label_str, ITEM_LABEL_LENGTH,
-                               "%s: %s (0x%06x)", fi->hfinfo->name,
-                               (s ? s : "Unknown"), fi->value.numeric);
+                               "%s: %s seconds", fi->hfinfo->name,
+                               rel_time_to_str(&fi->value.time));
                        break;
 
-
-               case FT_VALS_UINT32:
-                       s = match_strval(fi->value.numeric, cVALS(fi->hfinfo->vals));
+               case FT_IPXNET:
                        snprintf(label_str, ITEM_LABEL_LENGTH,
-                               "%s: %s (0x%08x)", fi->hfinfo->name,
-                               (s ? s : "Unknown"), fi->value.numeric);
+                               "%s: 0x%08X (%s)", fi->hfinfo->name,
+                               fi->value.numeric, get_ipxnet_name(fi->value.numeric));
                        break;
 
                case FT_ETHER:
@@ -555,20 +646,19 @@ proto_item_fill_label(field_info *fi, gchar *label_str)
                                get_ether_name(fi->value.ether));
                        break;
 
-               case FT_ETHER_VENDOR:
+               case FT_IPv4:
+                       n_addr = ipv4_get_net_order_addr(&fi->value.ipv4);
                        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));
+                               "%s: %s (%s)", fi->hfinfo->name,
+                               get_hostname(n_addr),
+                               ip_to_str((guint8*)&n_addr));
                        break;
 
-               case FT_IPv4:
+               case FT_IPv6:
                        snprintf(label_str, ITEM_LABEL_LENGTH,
                                "%s: %s (%s)", fi->hfinfo->name,
-                               get_hostname(fi->value.numeric),
-                               ip_to_str((guint8*)&fi->value.numeric));
+                               get_hostname6((struct e_in6_addr *)fi->value.ipv6),
+                               ip6_to_str((struct e_in6_addr*)fi->value.ipv6));
                        break;
        
                case FT_STRING:
@@ -582,12 +672,374 @@ proto_item_fill_label(field_info *fi, gchar *label_str)
        }
 }
 
+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 = find_hfinfo_record(n);
+    if (hfinfo)
+        return hfinfo->name;
+    else        return NULL;
+}
+
 char*
 proto_registrar_get_abbrev(int n)
 {
@@ -636,77 +1088,151 @@ proto_registrar_is_protocol(int n)
                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)
+/* 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)
 {
-       find_id_info    fiinfo;
-       int             parent_protocol;
-       proto_tree      *subtree;
+       struct header_field_info *hfinfo;
 
-       fiinfo.target = id;
-       fiinfo.result = NULL;
+       hfinfo = find_hfinfo_record(n);
+       if (!hfinfo)
+               return -1;
 
-       /* do a quicker check if field is a protocol */
-       if (proto_registrar_is_protocol(id) == TRUE) {
-               return proto_find_protocol(tree, id);
-       }
+       switch (hfinfo->type) {
+               case FT_TEXT_ONLY: /* not filterable */
+               case NUM_FIELD_TYPES: /* satisfy picky compilers */
+                       return -1;
 
-       /* find the field's parent protocol */
-       parent_protocol = proto_registrar_get_parent(id);
-       subtree = proto_find_protocol(tree, parent_protocol);
+               case FT_NONE:
+               case FT_BYTES:
+               case FT_BOOLEAN:
+               case FT_STRING:
+               case FT_DOUBLE:
+               case FT_ABSOLUTE_TIME:
+               case FT_RELATIVE_TIME:
+                       return 0;
 
-       /* 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);
+               case FT_UINT8:
+               case FT_INT8:
+                       return 1;
 
-       return (proto_item*) fiinfo.result;
-}
+               case FT_UINT16:
+               case FT_INT16:
+                       return 2;
 
+               case FT_UINT24:
+               case FT_INT24:
+                       return 3;
 
-/* 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;
+               case FT_UINT32:
+               case FT_INT32:
+               case FT_IPXNET:
+               case FT_IPv4:
+                       return 4;
 
-       fiinfo.target = protocol_id;
-       fiinfo.result = NULL;
+               case FT_ETHER:
+                       return 6;
 
-       g_node_traverse((GNode*)tree, G_IN_ORDER, G_TRAVERSE_ALL, 2, proto_check_id, &fiinfo);
-       return (proto_item*) fiinfo.result;
+               case FT_IPv6:
+                       return 16;
+       }
+       g_assert_not_reached();
+       return -1;
 }
 
+/* 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 = NULL;
+
+       /* 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,
+                                               check_for_field_within_protocol, &sinfo);
+       }
+
+       if (sinfo.result.node)
+               return TRUE;
+       else
+               return FALSE;
+}
 
 static gboolean
-proto_check_id(GNode *node, gpointer data)
+check_for_protocol_or_field_id(GNode *node, gpointer data)
 {
-       field_info      *fi = (field_info*) (node->data);
-       find_id_info    *fiinfo = (find_id_info*) 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 == fiinfo->target) {
-                       fiinfo->result = node;
+               if (fi->hfinfo->id == sinfo->target) {
+                       sinfo->result.node = node;
                        return TRUE; /* halt traversal */
                }
        }
        return FALSE; /* keep traversing */
 }
 
+static gboolean
+check_for_field_within_protocol(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,
+                                       check_for_protocol_or_field_id, sinfo);
+                       if (sinfo->result.node)
+                               return TRUE; /* halt traversal */
+               }
+       }
+       return FALSE; /* keep traversing */
+}
+
+/* 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.
+ */
 void
-proto_get_field_values(proto_tree* subtree, GNodeTraverseFunc fill_array_func, proto_tree_search_info *sinfo)
+proto_find_protocol_multi(proto_tree* tree, int target, GNodeTraverseFunc callback,
+                       proto_tree_search_info *sinfo)
 {
-       g_node_traverse((GNode*)subtree, G_IN_ORDER, G_TRAVERSE_ALL, -1, fill_array_func, sinfo);
+       g_assert(callback != NULL);
+       g_node_traverse((GNode*)tree, G_IN_ORDER, G_TRAVERSE_ALL, 2, callback, (gpointer*)sinfo);
+}
+
+/* Simple wrappter to traverse all nodes, calling the sinfo traverse function with sinfo as an arg */
+gboolean
+proto_get_field_values(proto_tree* subtree, proto_tree_search_info *sinfo)
+{
+       /* Don't try to check value of top-level NULL GNode */
+       if (!((GNode*)subtree)->data) {
+               return FALSE; /* don't halt */
+       }
+       g_node_traverse((GNode*)subtree, G_IN_ORDER, G_TRAVERSE_ALL, -1, sinfo->traverse_func, (gpointer*)sinfo);
+       return FALSE; /* don't halt */
 }
 
 /* Dumps the contents of the registration database to stdout. An indepedent program can take
@@ -762,9 +1288,27 @@ proto_registrar_dump(void)
                        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;
@@ -777,9 +1321,6 @@ proto_registrar_dump(void)
                        case FT_ETHER:
                                enum_name = "FT_ETHER";
                                break;
-                       case FT_ETHER_VENDOR:
-                               enum_name = "FT_ETHER_VENDOR";
-                               break;
                        case FT_BYTES:
                                enum_name = "FT_BYTES";
                                break;
@@ -789,20 +1330,8 @@ proto_registrar_dump(void)
                        case FT_IPv6:
                                enum_name = "FT_IPv6";
                                break;
-                       case FT_IPXSERVER:
-                               enum_name = "FT_IPXSERVER";
-                               break;
-                       case FT_VALS_UINT8:
-                               enum_name = "FT_VALS_UINT8";
-                               break;
-                       case FT_VALS_UINT16:
-                               enum_name = "FT_VALS_UINT16";
-                               break;
-                       case FT_VALS_UINT24:
-                               enum_name = "FT_VALS_UINT24";
-                               break;
-                       case FT_VALS_UINT32:
-                               enum_name = "FT_VALS_UINT32";
+                       case FT_IPXNET:
+                               enum_name = "FT_IPXNET";
                                break;
                        case FT_TEXT_ONLY:
                                enum_name = "FT_TEXT_ONLY";