/* proto.c * Routines for protocol tree * * $Id$ * * Ethereal - Network traffic analyzer * By Gerald Combs * Copyright 1998 Gerald Combs * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include #include #include #include #ifdef NEED_SNPRINTF_H # include "snprintf.h" #endif #include "packet.h" #include "strutil.h" #include "addr_resolv.h" #include "plugins.h" #include "ipv6-utils.h" #include "proto.h" #include "epan_dissect.h" #include "slab.h" #include "tvbuff.h" #define cVALS(x) (const value_string*)(x) static gboolean proto_tree_free_node(proto_node *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_uint64(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_int64(field_info *fi, gchar *label_str); static void fill_label_enumerated_int(field_info *fi, gchar *label_str); 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_uint64_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 char* hfinfo_int64_format(header_field_info *hfinfo); static proto_item* proto_tree_add_node(proto_tree *tree, field_info *fi); static field_info * alloc_field_info(proto_tree *tree, 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_protocol_tvb(field_info *fi, tvbuff_t *tvb); 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, nstime_t *value_ptr); static void proto_tree_set_string(field_info *fi, const char* value, gboolean); 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_float(field_info *fi, float 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 void proto_tree_set_uint64(field_info *fi, guint64 value); static void proto_tree_set_uint64_tvb(field_info *fi, tvbuff_t *tvb, gint start, gboolean little_endian); static int proto_register_field_init(header_field_info *hfinfo, int parent); /* Comparision function for tree insertion. A wrapper around strcmp() */ static int g_strcmp(gconstpointer a, gconstpointer b); /* special-case header field used within proto.c */ int hf_text_only = -1; /* Structure for information about a protocol */ struct _protocol { char *name; /* long description */ char *short_name; /* short description */ char *filter_name; /* name of this protocol in filters */ int proto_id; /* field ID for this protocol */ GList *fields; /* fields for this protocol */ GList *last_field; /* pointer to end of list of fields */ gboolean is_enabled; /* TRUE if protocol is enabled */ gboolean can_toggle; /* TRUE if is_enabled can be changed */ }; /* List of all protocols */ static GList *protocols; #define INITIAL_NUM_PROTOCOL_HFINFO 200 /* Contains information about protocols and header fields. Used when * dissectors register their data */ static GMemChunk *gmc_hfinfo = NULL; /* Contains information about a field when a dissector calls * proto_tree_add_item. */ SLAB_ITEM_TYPE_DEFINE(field_info) static SLAB_FREE_LIST_DEFINE(field_info) static field_info *field_info_tmp=NULL; #define FIELD_INFO_NEW(fi) \ SLAB_ALLOC(fi, field_info) #define FIELD_INFO_FREE(fi) \ SLAB_FREE(fi, field_info) /* Contains the space for proto_nodes. */ SLAB_ITEM_TYPE_DEFINE(proto_node) static SLAB_FREE_LIST_DEFINE(proto_node) #define PROTO_NODE_NEW(node) \ SLAB_ALLOC(node, proto_node) \ node->first_child = NULL; \ node->last_child = NULL; \ node->next = NULL; #define PROTO_NODE_FREE(node) \ SLAB_FREE(node, proto_node) /* String space for protocol and field items for the GUI */ SLAB_ITEM_TYPE_DEFINE(item_label_t) static SLAB_FREE_LIST_DEFINE(item_label_t) #define ITEM_LABEL_NEW(il) \ SLAB_ALLOC(il, item_label_t) #define ITEM_LABEL_FREE(il) \ SLAB_FREE(il, item_label_t) /* List which stores protocols and fields that have been registered */ typedef struct _gpa_hfinfo_t { guint32 len; guint32 allocated_len; header_field_info **hfi; } gpa_hfinfo_t; gpa_hfinfo_t gpa_hfinfo; /* Balanced tree of abbreviations and IDs */ static GTree *gpa_name_tree = 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; /* initialize data structures and register protocols and fields */ void proto_init(const char *plugin_dir #ifndef HAVE_PLUGINS _U_ #endif , void (register_all_protocols)(void), void (register_all_protocol_handoffs)(void)) { static hf_register_info hf[] = { { &hf_text_only, { "", "", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }}, }; proto_cleanup(); gmc_hfinfo = g_mem_chunk_new("gmc_hfinfo", sizeof(header_field_info), INITIAL_NUM_PROTOCOL_HFINFO * sizeof(header_field_info), G_ALLOC_ONLY); gpa_hfinfo.len=0; gpa_hfinfo.allocated_len=0; gpa_hfinfo.hfi=NULL; gpa_name_tree = g_tree_new(g_strcmp); /* Initialize the ftype subsystem */ ftypes_initialize(); /* 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)); /* Have each built-in dissector register its protocols, fields, dissector tables, and dissectors to be called through a handle, and do whatever one-time initialization it needs to do. */ register_all_protocols(); #ifdef HAVE_PLUGINS /* Now scan for plugins and load all the ones we find, calling their register routines to do the stuff described above. */ init_plugins(plugin_dir); #endif /* Now call the "handoff registration" routines of all built-in dissectors; those routines register the dissector in other dissectors' handoff tables, and fetch any dissector handles they need. */ register_all_protocol_handoffs(); #ifdef HAVE_PLUGINS /* Now do the same with plugins. */ register_all_plugin_handoffs(); #endif /* 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 *)); } /* String comparison func for dfilter_token GTree */ static int g_strcmp(gconstpointer a, gconstpointer b) { return strcmp((const char*)a, (const char*)b); } void proto_cleanup(void) { /* Free the abbrev/ID GTree */ if (gpa_name_tree) { g_tree_destroy(gpa_name_tree); gpa_name_tree = NULL; } if (gmc_hfinfo) g_mem_chunk_destroy(gmc_hfinfo); if(gpa_hfinfo.allocated_len){ gpa_hfinfo.len=0; gpa_hfinfo.allocated_len=0; g_free(gpa_hfinfo.hfi); gpa_hfinfo.hfi=NULL; } if (tree_is_expanded != NULL) g_free(tree_is_expanded); } typedef gboolean (*proto_tree_traverse_func)(proto_node *, gpointer); static gboolean proto_tree_traverse_pre_order(proto_tree *tree, proto_tree_traverse_func func, gpointer data) { proto_node *pnode = tree; proto_node *child; proto_node *current; if (func(pnode, data)) return TRUE; child = pnode->first_child; while (child != NULL) { /* * The routine we call might modify the child, e.g. by * freeing it, so we get the child's successor before * calling that routine. */ current = child; child = current->next; if (proto_tree_traverse_pre_order((proto_tree *)current, func, data)) return TRUE; } return FALSE; } static gboolean proto_tree_traverse_in_order(proto_tree *tree, proto_tree_traverse_func func, gpointer data) { proto_node *pnode = tree; proto_node *child; proto_node *current; child = pnode->first_child; if (child != NULL) { /* * The routine we call might modify the child, e.g. by * freeing it, so we get the child's successor before * calling that routine. */ current = child; child = current->next; if (proto_tree_traverse_in_order((proto_tree *)current, func, data)) return TRUE; if (func(pnode, data)) return TRUE; while (child != NULL) { /* * The routine we call might modify the child, e.g. by * freeing it, so we get the child's successor before * calling that routine. */ current = child; child = current->next; if (proto_tree_traverse_in_order((proto_tree *)current, func, data)) return TRUE; } } else { if (func(pnode, data)) return TRUE; } return FALSE; } void proto_tree_children_foreach(proto_tree *tree, proto_tree_foreach_func func, gpointer data) { proto_node *node = tree; proto_node *current; node = node->first_child; while (node != NULL) { current = node; node = current->next; func((proto_tree *)current, data); } } /* frees the resources that the dissection a proto_tree uses */ void proto_tree_free(proto_tree *tree) { proto_tree_traverse_in_order(tree, proto_tree_free_node, NULL); } static void free_GPtrArray_value(gpointer key _U_, gpointer value, gpointer user_data _U_) { GPtrArray *ptrs = value; g_ptr_array_free(ptrs, TRUE); } static void free_node_tree_data(tree_data_t *tree_data) { /* Free all the GPtrArray's in the interesting_hfids hash. */ g_hash_table_foreach(tree_data->interesting_hfids, free_GPtrArray_value, NULL); /* And then destroy the hash. */ g_hash_table_destroy(tree_data->interesting_hfids); /* And finally the tree_data_t itself. */ g_free(tree_data); } #define FREE_NODE_FIELD_INFO(finfo) \ if(finfo->rep){ \ ITEM_LABEL_FREE(finfo->rep); \ } \ FVALUE_CLEANUP(&finfo->value); \ FIELD_INFO_FREE(finfo); static gboolean proto_tree_free_node(proto_node *node, gpointer data _U_) { field_info *finfo = PITEM_FINFO(node); if (finfo == NULL) { /* This is the root node. Destroy the per-tree data. * There is no field_info to destroy. */ free_node_tree_data(PTREE_DATA(node)); } else { /* This is a child node. Don't free the per-tree data, but * do free the field_info data. */ FREE_NODE_FIELD_INFO(finfo); } /* Free the proto_node. */ PROTO_NODE_FREE(node); return FALSE; /* FALSE = do not end traversal of protocol tree */ } /* 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. */ void proto_tree_set_visible(proto_tree *tree, gboolean visible) { PTREE_DATA(tree)->visible = visible; } #define PROTO_REGISTRAR_GET_NTH(hfindex, hfinfo) \ g_assert((guint)hfindex < gpa_hfinfo.len); \ hfinfo=gpa_hfinfo.hfi[hfindex]; /* Finds a record in the hf_info_records array by id. */ header_field_info* proto_registrar_get_nth(guint hfindex) { register header_field_info *hfinfo; PROTO_REGISTRAR_GET_NTH(hfindex, hfinfo); return hfinfo; } /* Finds a record in the hf_info_records array by name. */ header_field_info* proto_registrar_get_byname(char *field_name) { g_assert(field_name != NULL); return g_tree_lookup(gpa_name_tree, field_name); } /* Add a text-only node, leaving it to our caller to fill the text in */ static proto_item * proto_tree_add_text_node(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_text_node(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_text_node(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_text_node(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; float floatval; double doubleval; char *string; GHashTable *hash; GPtrArray *ptrs; if (!tree) return(NULL); new_fi = alloc_field_info(tree, hfindex, tvb, start, &length); if (new_fi == NULL) return(NULL); /* there is a possibility here that we might raise an exception * and thus would lose track of the field_info. * store it in a temp so that if we come here again we can reclaim * the field_info without leaking memory. */ /* XXX this only keeps track of one field_info struct, if we ever go multithreaded for calls to this function we have to change this code to use per thread variable. */ if(field_info_tmp){ /* oops, last one we got must have been lost due * to an exception. * good thing we saved it, now we can reverse the * memory leak and reclaim it. */ SLAB_FREE(field_info_tmp, field_info); } /* we might throw an exception, keep track of this one * across the "dangerous" section below. */ field_info_tmp=new_fi; switch(new_fi->hfinfo->type) { case FT_NONE: /* no value to set for FT_NONE */ break; case FT_PROTOCOL: proto_tree_set_protocol_tvb(new_fi, tvb); break; case FT_BYTES: proto_tree_set_bytes_tvb(new_fi, tvb, start, length); break; case FT_UINT_BYTES: n = get_uint_value(tvb, start, length, little_endian); proto_tree_set_bytes_tvb(new_fi, tvb, start + length, 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 + 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; case FT_INT64: case FT_UINT64: g_assert(length == 8); proto_tree_set_uint64_tvb(new_fi, tvb, start, 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, little_endian ? GUINT32_SWAP_LE_BE(value) : 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_FLOAT: g_assert(length == 4); if (little_endian) floatval = tvb_get_letohieee_float(tvb, start); else floatval = tvb_get_ntohieee_float(tvb, start); proto_tree_set_float(new_fi, floatval); break; case FT_DOUBLE: g_assert(length == 8); if (little_endian) doubleval = tvb_get_letohieee_double(tvb, start); else doubleval = tvb_get_ntohieee_double(tvb, start); proto_tree_set_double(new_fi, doubleval); 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: if (length != 0) { /* XXX - Should we throw an exception instead? */ /* Instead of calling proto_item_set_len(), * since we don't yet have a proto_item, we * set the field_info's length ourselves. * * XXX - our caller can't use that length to * advance an offset unless they arrange that * there always be a protocol tree into which * we're putting this item. */ if (length == -1) { /* This can throw an exception */ length = tvb_strsize(tvb, start); /* This g_malloc'ed memory is freed in proto_tree_free_node() */ string = g_malloc(length); tvb_memcpy(tvb, string, start, length); new_fi->length = length; } else { /* In this case, length signifies * the length of the string. * * This could either be a null-padded * string, which doesn't necessarily * have a '\0' at the end, or a * null-terminated string, with a * trailing '\0'. (Yes, there are * cases where you have a string * that's both counted and null- * terminated.) * * In the first case, we must * allocate a buffer of length * "length+1", to make room for * a trailing '\0'. * * In the second case, we don't * assume that there is a trailing * '\0' there, as the packet might * be malformed. (XXX - should we * throw an exception if there's no * trailing '\0'?) Therefore, we * allocate a buffer of length * "length+1", and put in a trailing * '\0', just to be safe. * * (XXX - this would change if * we made string values counted * rather than null-terminated.) */ /* This g_malloc'ed memory is freed * in proto_tree_free_node() */ string = tvb_get_string(tvb, start, length); new_fi->length = length; } proto_tree_set_string(new_fi, string, TRUE); } 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 + length, 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. * * XXX - our caller can't use that length to * advance an offset unless they arrange that * there always be a protocol tree into which * we're putting this item. */ new_fi->length = n + length; break; default: g_error("new_fi->hfinfo->type %d (%s) not handled\n", new_fi->hfinfo->type, ftype_name(new_fi->hfinfo->type)); g_assert_not_reached(); break; } /* Don't add new node 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); /* we did not raise an exception so we dont have to remember this * field_info struct any more. */ field_info_tmp=NULL; /* If the proto_tree wants to keep a record of this finfo * for quick lookup, then record it. */ hash = PTREE_DATA(tree)->interesting_hfids; ptrs = g_hash_table_lookup(hash, GINT_TO_POINTER(hfindex)); if (ptrs) { g_ptr_array_add(ptrs, 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; pi = proto_tree_add_item(tree, hfindex, tvb, start, length, little_endian); if (pi == NULL) return(NULL); PROTO_ITEM_SET_HIDDEN(pi); return pi; } /* Add a FT_NONE to a proto_tree */ proto_item * proto_tree_add_none_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); PROTO_REGISTRAR_GET_NTH(hfindex, hfinfo); 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; } static void proto_tree_set_protocol_tvb(field_info *fi, tvbuff_t *tvb) { fvalue_set(&fi->value, tvb, TRUE); } /* Add a FT_PROTOCOL 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; field_info *new_fi; if (!tree) return (NULL); PROTO_REGISTRAR_GET_NTH(hfindex, hfinfo); g_assert(hfinfo->type == FT_PROTOCOL); pi = proto_tree_add_pi(tree, hfindex, tvb, start, &length, &new_fi); va_start(ap, format); proto_tree_set_representation(pi, format, ap); va_end(ap); if (start == 0) { proto_tree_set_protocol_tvb(new_fi, tvb); } else { proto_tree_set_protocol_tvb(new_fi, NULL); } 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); PROTO_REGISTRAR_GET_NTH(hfindex, hfinfo); 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; pi = proto_tree_add_bytes(tree, hfindex, tvb, start, length, start_ptr); if (pi == NULL) return (NULL); PROTO_ITEM_SET_HIDDEN(pi); 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; } static void proto_tree_set_bytes(field_info *fi, const guint8* start_ptr, gint length) { GByteArray *bytes; bytes = g_byte_array_new(); if (length > 0) { g_byte_array_append(bytes, start_ptr, length); } fvalue_set(&fi->value, bytes, TRUE); } static void proto_tree_set_bytes_tvb(field_info *fi, tvbuff_t *tvb, gint offset, gint length) { proto_tree_set_bytes(fi, tvb_get_ptr(tvb, offset, length), length); } /* 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, nstime_t *value_ptr) { proto_item *pi; field_info *new_fi; header_field_info *hfinfo; if (!tree) return (NULL); PROTO_REGISTRAR_GET_NTH(hfindex, hfinfo); 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, nstime_t *value_ptr) { proto_item *pi; pi = proto_tree_add_time(tree, hfindex, tvb, start, length, value_ptr); if (pi == NULL) return (NULL); PROTO_ITEM_SET_HIDDEN(pi); return pi; } proto_item * proto_tree_add_time_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, gint length, nstime_t *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, nstime_t *value_ptr) { fvalue_set(&fi->value, value_ptr, FALSE); } /* 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); PROTO_REGISTRAR_GET_NTH(hfindex, hfinfo); 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; pi = proto_tree_add_ipxnet(tree, hfindex, tvb, start, length, value); if (pi == NULL) return (NULL); PROTO_ITEM_SET_HIDDEN(pi); 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) { fvalue_set_integer(&fi->value, 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); PROTO_REGISTRAR_GET_NTH(hfindex, hfinfo); 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; pi = proto_tree_add_ipv4(tree, hfindex, tvb, start, length, value); if (pi == NULL) return (NULL); PROTO_ITEM_SET_HIDDEN(pi); 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) { fvalue_set_integer(&fi->value, value); } /* 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); PROTO_REGISTRAR_GET_NTH(hfindex, hfinfo); 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; pi = proto_tree_add_ipv6(tree, hfindex, tvb, start, length, value_ptr); if (pi == NULL) return (NULL); PROTO_ITEM_SET_HIDDEN(pi); 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) { fvalue_set(&fi->value, (gpointer) value_ptr, FALSE); } static void proto_tree_set_ipv6_tvb(field_info *fi, tvbuff_t *tvb, gint start) { proto_tree_set_ipv6(fi, tvb_get_ptr(tvb, start, 16)); } static void proto_tree_set_uint64(field_info *fi, guint64 value) { fvalue_set_integer64(&fi->value, value); } static void proto_tree_set_uint64_tvb(field_info *fi, tvbuff_t *tvb, gint start, gboolean little_endian) { guint64 value; value = little_endian ? tvb_get_letoh64(tvb, start) : tvb_get_ntoh64(tvb, start); proto_tree_set_uint64(fi, value); } /* Add a FT_STRING or FT_STRINGZ to a proto_tree. Creates own copy of string, * and frees it when the proto_tree is destroyed. */ 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); PROTO_REGISTRAR_GET_NTH(hfindex, hfinfo); g_assert(hfinfo->type == FT_STRING || hfinfo->type == FT_STRINGZ); pi = proto_tree_add_pi(tree, hfindex, tvb, start, &length, &new_fi); g_assert(length >= 0); proto_tree_set_string(new_fi, value, FALSE); 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; pi = proto_tree_add_string(tree, hfindex, tvb, start, length, value); if (pi == NULL) return (NULL); PROTO_ITEM_SET_HIDDEN(pi); 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; } /* Appends string data to a FT_STRING or FT_STRINGZ, allowing progressive * field info update instead of only updating the representation as does * proto_item_append_text() */ void proto_item_append_string(proto_item *pi, const char *str) { field_info *fi; header_field_info *hfinfo; gchar *old_str, *new_str; if (!pi) return; if (!*str) return; fi = PITEM_FINFO(pi); hfinfo = fi->hfinfo; g_assert(hfinfo->type == FT_STRING || hfinfo->type == FT_STRINGZ); old_str = fvalue_get(&fi->value); new_str = g_malloc(strlen(old_str) + strlen(str) + 1); sprintf(new_str, "%s%s", old_str, str); fvalue_set(&fi->value, new_str, TRUE); } /* Set the FT_STRING value */ static void proto_tree_set_string(field_info *fi, const char* value, gboolean already_allocated) { fvalue_set(&fi->value, (gpointer) value, already_allocated); } static void proto_tree_set_string_tvb(field_info *fi, tvbuff_t *tvb, gint start, gint length) { gchar *string; if (length == -1) { length = tvb_ensure_length_remaining(tvb, start); } /* This memory is freed in proto_tree_free_node() */ string = tvb_get_string(tvb, start, length); proto_tree_set_string(fi, string, TRUE); } /* 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); PROTO_REGISTRAR_GET_NTH(hfindex, hfinfo); 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; pi = proto_tree_add_ether(tree, hfindex, tvb, start, length, value); if (pi == NULL) return (NULL); PROTO_ITEM_SET_HIDDEN(pi); 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) { fvalue_set(&fi->value, (gpointer) value, FALSE); } static void proto_tree_set_ether_tvb(field_info *fi, tvbuff_t *tvb, gint start) { proto_tree_set_ether(fi, tvb_get_ptr(tvb, 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); PROTO_REGISTRAR_GET_NTH(hfindex, hfinfo); 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; pi = proto_tree_add_boolean(tree, hfindex, tvb, start, length, value); if (pi == NULL) return (NULL); PROTO_ITEM_SET_HIDDEN(pi); 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_FLOAT to a proto_tree */ proto_item * proto_tree_add_float(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, gint length, float value) { proto_item *pi; field_info *new_fi; header_field_info *hfinfo; if (!tree) return (NULL); PROTO_REGISTRAR_GET_NTH(hfindex, hfinfo); g_assert(hfinfo->type == FT_FLOAT); pi = proto_tree_add_pi(tree, hfindex, tvb, start, &length, &new_fi); proto_tree_set_float(new_fi, value); return pi; } proto_item * proto_tree_add_float_hidden(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, gint length, float value) { proto_item *pi; pi = proto_tree_add_float(tree, hfindex, tvb, start, length, value); if (pi == NULL) return (NULL); PROTO_ITEM_SET_HIDDEN(pi); return pi; } proto_item * proto_tree_add_float_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, gint length, float value, const char *format, ...) { proto_item *pi; va_list ap; pi = proto_tree_add_float(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_FLOAT value */ static void proto_tree_set_float(field_info *fi, float value) { fvalue_set_floating(&fi->value, 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); PROTO_REGISTRAR_GET_NTH(hfindex, hfinfo); 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; pi = proto_tree_add_double(tree, hfindex, tvb, start, length, value); if (pi == NULL) return (NULL); PROTO_ITEM_SET_HIDDEN(pi); 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) { fvalue_set_floating(&fi->value, value); } /* Add FT_UINT{8,16,24,32} 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); PROTO_REGISTRAR_GET_NTH(hfindex, hfinfo); switch(hfinfo->type) { case FT_UINT8: case FT_UINT16: case FT_UINT24: case FT_UINT32: case FT_FRAMENUM: 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; pi = proto_tree_add_uint(tree, hfindex, tvb, start, length, value); if (pi == NULL) return (NULL); PROTO_ITEM_SET_HIDDEN(pi); 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{8,16,24,32} value */ static void proto_tree_set_uint(field_info *fi, guint32 value) { header_field_info *hfinfo; guint32 integer; hfinfo = fi->hfinfo; integer = value; if (hfinfo->bitmask) { /* Mask out irrelevant portions */ integer &= hfinfo->bitmask; /* Shift bits */ if (hfinfo->bitshift > 0) { integer >>= hfinfo->bitshift; } } fvalue_set_integer(&fi->value, integer); } /* Add FT_UINT64 to a proto_tree */ proto_item * proto_tree_add_uint64(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, gint length, guint64 value) { proto_item *pi = NULL; field_info *new_fi; header_field_info *hfinfo; if (!tree) return (NULL); PROTO_REGISTRAR_GET_NTH(hfindex, hfinfo); g_assert(hfinfo->type == FT_UINT64); pi = proto_tree_add_pi(tree, hfindex, tvb, start, &length, &new_fi); proto_tree_set_uint64(new_fi, value); return pi; } proto_item * proto_tree_add_uint64_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, gint length, guint64 value, const char *format, ...) { proto_item *pi; va_list ap; pi = proto_tree_add_uint64(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; } /* Add FT_INT{8,16,24,32} 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); PROTO_REGISTRAR_GET_NTH(hfindex, hfinfo); 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; pi = proto_tree_add_int(tree, hfindex, tvb, start, length, value); if (pi == NULL) return (NULL); PROTO_ITEM_SET_HIDDEN(pi); 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{8,16,24,32} value */ static void proto_tree_set_int(field_info *fi, gint32 value) { header_field_info *hfinfo; guint32 integer; hfinfo = fi->hfinfo; integer = (guint32) value; if (hfinfo->bitmask) { /* Mask out irrelevant portions */ integer &= hfinfo->bitmask; /* Shift bits */ if (hfinfo->bitshift > 0) { integer >>= hfinfo->bitshift; } } fvalue_set_integer(&fi->value, integer); } /* Add FT_INT64 to a proto_tree */ proto_item * proto_tree_add_int64(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, gint length, gint64 value) { proto_item *pi = NULL; field_info *new_fi; header_field_info *hfinfo; if (!tree) return (NULL); PROTO_REGISTRAR_GET_NTH(hfindex, hfinfo); g_assert(hfinfo->type == FT_INT64); pi = proto_tree_add_pi(tree, hfindex, tvb, start, &length, &new_fi); proto_tree_set_uint64(new_fi, (guint64)value); return pi; } proto_item * proto_tree_add_int64_format(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, gint length, gint64 value, const char *format, ...) { proto_item *pi; va_list ap; pi = proto_tree_add_int64(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; } /* Add a field_info struct to the proto_tree, encapsulating it in a proto_node */ static proto_item * proto_tree_add_node(proto_tree *tree, field_info *fi) { proto_node *pnode, *tnode, *sibling; field_info *tfi; /* * Make sure "tree" is ready to have subtrees under it, by * checking whether it's been given an ett_ value. * * "tnode->finfo" may be null; that's the case for the root * node of the protocol tree. That node is not displayed, * so it doesn't need an ett_ value to remember whether it * was expanded. */ tnode = tree; tfi = tnode->finfo; g_assert(tfi == NULL || (tfi->tree_type >= 0 && tfi->tree_type < num_tree_types)); PROTO_NODE_NEW(pnode); pnode->parent = tnode; pnode->finfo = fi; pnode->tree_data = PTREE_DATA(tree); if (tnode->last_child != NULL) { sibling = tnode->last_child; g_assert(sibling->next == NULL); sibling->next = pnode; } else tnode->first_child = pnode; tnode->last_child = pnode; return (proto_item*)pnode; } /* 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; GHashTable *hash; GPtrArray *ptrs; if (!tree) return(NULL); fi = alloc_field_info(tree, hfindex, tvb, start, length); pi = proto_tree_add_node(tree, fi); /* If the proto_tree wants to keep a record of this finfo * for quick lookup, then record it. */ hash = PTREE_DATA(tree)->interesting_hfids; ptrs = g_hash_table_lookup(hash, GINT_TO_POINTER(hfindex)); if (ptrs) { g_ptr_array_add(ptrs, fi); } /* Does the caller want to know the fi pointer? */ if (pfi) { *pfi = fi; } return pi; } static field_info * alloc_field_info(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, gint *length) { header_field_info *hfinfo; field_info *fi; /* * We only allow a null tvbuff if the item has a zero length, * i.e. if there's no data backing it. */ g_assert(tvb != NULL || *length == 0); PROTO_REGISTRAR_GET_NTH(hfindex, hfinfo); if (*length == -1) { /* * For FT_NONE, FT_PROTOCOL, FT_BYTES, and FT_STRING fields, * a length of -1 means "set the length to what remains in * the tvbuff". * * The assumption is either that * * 1) the length of the item can only be determined * by dissection (typically true of items with * subitems, which are probably FT_NONE or * FT_PROTOCOL) * * or * * 2) if the tvbuff is "short" (either due to a short * snapshot length or due to lack of reassembly of * fragments/segments/whatever), we want to display * what's available in the field (probably FT_BYTES * or FT_STRING) and then throw an exception later * * or * * 3) the field is defined to be "what's left in the * packet" * * so we set the length to what remains in the tvbuff so * that, if we throw an exception while dissecting, it * has what is probably the right value. * * For FT_STRINGZ, it means "the string is null-terminated, * not null-padded; set the length to the actual length * of the string", and if the tvbuff if short, we just * throw an exception. * * It's not valid for any other type of field. */ switch (hfinfo->type) { case FT_PROTOCOL: /* * We allow this to be zero-length - for * example, an ONC RPC NULL procedure has * neither arguments nor reply, so the * payload for that protocol is empty. * * However, if the length is negative, the * start offset is *past* the byte past the * end of the tvbuff, so we throw an * exception. */ *length = tvb_length_remaining(tvb, start); if (*length < 0) { /* * Use "tvb_ensure_bytes_exist()" * to force the appropriate exception * to be thrown. */ tvb_ensure_bytes_exist(tvb, start, 0); } g_assert(*length >= 0); break; case FT_NONE: case FT_BYTES: case FT_STRING: *length = tvb_ensure_length_remaining(tvb, start); g_assert(*length >= 0); break; case FT_STRINGZ: /* * Leave the length as -1, so our caller knows * it was -1. */ break; default: g_assert_not_reached(); } } else g_assert(*length >= 0); FIELD_INFO_NEW(fi); fi->hfinfo = hfinfo; fi->start = start; fi->start+=(tvb)?TVB_RAW_OFFSET(tvb):0; fi->length = *length; fi->tree_type = -1; fi->flags = 0; if (!PTREE_DATA(tree)->visible) FI_SET_FLAG(fi, FI_HIDDEN); fvalue_init(&fi->value, fi->hfinfo->type); fi->rep = NULL; /* add the data source tvbuff */ fi->ds_tvb=tvb?TVB_GET_DS_TVB(tvb):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) { int ret; /*tmp return value */ field_info *fi = PITEM_FINFO(pi); if (!PROTO_ITEM_IS_HIDDEN(pi)) { ITEM_LABEL_NEW(fi->rep); ret = vsnprintf(fi->rep->representation, ITEM_LABEL_LENGTH, format, ap); if ((ret == -1) || (ret >= ITEM_LABEL_LENGTH)) fi->rep->representation[ITEM_LABEL_LENGTH - 1] = '\0'; } } /* Set text of proto_item after having already been created. */ void proto_item_set_text(proto_item *pi, const char *format, ...) { field_info *fi = NULL; va_list ap; if (pi==NULL) { return; } fi = PITEM_FINFO(pi); if(fi->rep){ ITEM_LABEL_FREE(fi->rep); } va_start(ap, format); proto_tree_set_representation(pi, format, ap); va_end(ap); } /* Append to text of proto_item after having already been created. */ void proto_item_append_text(proto_item *pi, const char *format, ...) { field_info *fi = NULL; size_t curlen; va_list ap; int ret; /*tmp return value */ if (pi==NULL) { return; } fi = PITEM_FINFO(pi); if (!PROTO_ITEM_IS_HIDDEN(pi)) { va_start(ap, format); /* * If we don't already have a representation, * generate the default representation. */ if (fi->rep == NULL) { ITEM_LABEL_NEW(fi->rep); proto_item_fill_label(fi, fi->rep->representation); } curlen = strlen(fi->rep->representation); if (ITEM_LABEL_LENGTH > curlen) { ret = vsnprintf(fi->rep->representation + curlen, ITEM_LABEL_LENGTH - curlen, format, ap); if ((ret == -1) || (ret >= (int)(ITEM_LABEL_LENGTH - curlen))) fi->rep->representation[ITEM_LABEL_LENGTH - 1] = '\0'; } va_end(ap); } } void proto_item_set_len(proto_item *pi, gint length) { field_info *fi; if (pi == NULL) return; fi = PITEM_FINFO(pi); g_assert(length >= 0); fi->length = length; } /* * Sets the length of the item based on its start and on the specified * offset, which is the offset past the end of the item; as the start * in the item is relative to the beginning of the data source tvbuff, * we need to pass in a tvbuff - the end offset is relative to the beginning * of that tvbuff. */ void proto_item_set_end(proto_item *pi, tvbuff_t *tvb, gint end) { field_info *fi; if (pi == NULL) return; fi = PITEM_FINFO(pi); end += TVB_RAW_OFFSET(tvb); g_assert(end >= fi->start); fi->length = end - fi->start; } int proto_item_get_len(proto_item *pi) { field_info *fi = PITEM_FINFO(pi); return fi->length; } proto_tree* proto_tree_create_root(void) { proto_node *pnode; /* Initialize the proto_node */ PROTO_NODE_NEW(pnode); pnode->parent = NULL; pnode->finfo = NULL; pnode->tree_data = g_new(tree_data_t, 1); /* Initialize the tree_data_t */ pnode->tree_data->interesting_hfids = g_hash_table_new(g_direct_hash, g_direct_equal); /* Set the default to FALSE so it's easier to * find errors; if we expect to see the protocol tree * but for some reason the default 'visible' is not * changed, then we'll find out very quickly. */ pnode->tree_data->visible = FALSE; return (proto_tree*) pnode; } /* "prime" a proto_tree with a single hfid that a dfilter * is interested in. */ void proto_tree_prime_hfid(proto_tree *tree, gint hfid) { g_hash_table_insert(PTREE_DATA(tree)->interesting_hfids, GINT_TO_POINTER(hfid), g_ptr_array_new()); } proto_tree* proto_item_add_subtree(proto_item *pi, gint idx) { field_info *fi; if (!pi) return(NULL); fi = PITEM_FINFO(pi); g_assert(idx >= 0 && idx < num_tree_types); fi->tree_type = idx; return (proto_tree*) pi; } proto_tree* proto_item_get_subtree(proto_item *pi) { field_info *fi; if (!pi) return(NULL); fi = PITEM_FINFO(pi); if (fi->tree_type == -1) return(NULL); return (proto_tree*) pi; } proto_item* proto_item_get_parent(proto_item *ti) { if (!ti) return (NULL); return ti->parent; } proto_item* proto_item_get_parent_nth(proto_item *ti, int gen) { if (!ti) return (NULL); while (gen--) { ti = ti->parent; if (!ti) return (NULL); } return ti; } proto_item* proto_tree_get_parent(proto_tree *tree) { if (!tree) return (NULL); return (proto_item*) tree; } static gint proto_match_short_name(gconstpointer p_arg, gconstpointer name_arg) { const protocol_t *p = p_arg; const char *name = name_arg; return g_strcasecmp(p->short_name, name); } static gint proto_match_name(gconstpointer p_arg, gconstpointer name_arg) { const protocol_t *p = p_arg; const char *name = name_arg; return g_strcasecmp(p->name, name); } static gint proto_match_filter_name(gconstpointer p_arg, gconstpointer name_arg) { const protocol_t *p = p_arg; const char *name = name_arg; return g_strcasecmp(p->filter_name, name); } static gint proto_compare_name(gconstpointer p1_arg, gconstpointer p2_arg) { const protocol_t *p1 = p1_arg; const protocol_t *p2 = p2_arg; return g_strcasecmp(p1->short_name, p2->short_name); } int proto_register_protocol(char *name, char *short_name, char *filter_name) { protocol_t *protocol; header_field_info *hfinfo; int proto_id; /* * Make sure there's not already a protocol with any of those * names. Crash if there is, as that's an error in the code, * and the code has to be fixed not to register more than one * protocol with the same name. */ g_assert(g_list_find_custom(protocols, name, proto_match_name) == NULL); g_assert(g_list_find_custom(protocols, short_name, proto_match_short_name) == NULL); g_assert(g_list_find_custom(protocols, filter_name, proto_match_filter_name) == NULL); /* Add this protocol to the list of known protocols; the list is sorted by protocol short name. */ protocol = g_malloc(sizeof (protocol_t)); protocol->name = name; protocol->short_name = short_name; protocol->filter_name = filter_name; protocol->fields = NULL; protocol->is_enabled = TRUE; /* protocol is enabled by default */ protocol->can_toggle = TRUE; protocols = g_list_insert_sorted(protocols, protocol, proto_compare_name); /* Here we do allocate a new header_field_info struct */ hfinfo = g_mem_chunk_alloc(gmc_hfinfo); hfinfo->name = name; hfinfo->abbrev = filter_name; hfinfo->type = FT_PROTOCOL; hfinfo->strings = NULL; hfinfo->bitmask = 0; hfinfo->bitshift = 0; hfinfo->blurb = ""; hfinfo->parent = -1; /* this field differentiates protos and fields */ proto_id = proto_register_field_init(hfinfo, hfinfo->parent); protocol->proto_id = proto_id; return proto_id; } /* * Routines to use to iterate over the protocols. * The argument passed to the iterator routines is an opaque cookie to * their callers; it's the GList pointer for the current element in * the list. * The ID of the protocol is returned, or -1 if there is no protocol. */ int proto_get_first_protocol(void **cookie) { protocol_t *protocol; if (protocols == NULL) return -1; *cookie = protocols; protocol = protocols->data; return protocol->proto_id; } int proto_get_next_protocol(void **cookie) { GList *list_item = *cookie; protocol_t *protocol; list_item = g_list_next(list_item); if (list_item == NULL) return -1; *cookie = list_item; protocol = list_item->data; return protocol->proto_id; } header_field_info * proto_get_first_protocol_field(int proto_id, void **cookie) { protocol_t *protocol = find_protocol_by_id(proto_id); hf_register_info *ptr; if ((protocol == NULL) || (protocol->fields == NULL)) return NULL; *cookie = protocol->fields; ptr = protocol->fields->data; return &ptr->hfinfo; } header_field_info * proto_get_next_protocol_field(void **cookie) { GList *list_item = *cookie; hf_register_info *ptr; list_item = g_list_next(list_item); if (list_item == NULL) return NULL; *cookie = list_item; ptr = list_item->data; return &ptr->hfinfo; } /* * Find the protocol list entry for a protocol given its field ID. */ static gint compare_proto_id(gconstpointer proto_arg, gconstpointer id_arg) { const protocol_t *protocol = proto_arg; const int *id_ptr = id_arg; return (protocol->proto_id == *id_ptr) ? 0 : 1; } protocol_t * find_protocol_by_id(int proto_id) { GList *list_entry; list_entry = g_list_find_custom(protocols, &proto_id, compare_proto_id); if (list_entry == NULL) return NULL; return list_entry->data; } static gint compare_filter_name(gconstpointer proto_arg, gconstpointer filter_name) { const protocol_t *protocol = proto_arg; const gchar* f_name = filter_name; return (strcmp(protocol->filter_name, f_name)); } int proto_get_id(protocol_t *protocol) { return protocol->proto_id; } int proto_get_id_by_filter_name(gchar* filter_name) { GList *list_entry; protocol_t *protocol; list_entry = g_list_find_custom(protocols, filter_name, compare_filter_name); if (list_entry == NULL) return -1; protocol = list_entry->data; return protocol->proto_id; } char * proto_get_protocol_name(int proto_id) { protocol_t *protocol; protocol = find_protocol_by_id(proto_id); return protocol->name; } char * proto_get_protocol_short_name(protocol_t *protocol) { if (protocol == NULL) return "(none)"; return protocol->short_name; } char * proto_get_protocol_filter_name(int proto_id) { protocol_t *protocol; protocol = find_protocol_by_id(proto_id); return protocol->filter_name; } gboolean proto_is_protocol_enabled(protocol_t *protocol) { return protocol->is_enabled; } gboolean proto_can_toggle_protocol(int proto_id) { protocol_t *protocol; protocol = find_protocol_by_id(proto_id); return protocol->can_toggle; } void proto_set_decoding(int proto_id, gboolean enabled) { protocol_t *protocol; protocol = find_protocol_by_id(proto_id); g_assert(protocol->can_toggle); protocol->is_enabled = enabled; } void proto_set_cant_toggle(int proto_id) { protocol_t *protocol; protocol = find_protocol_by_id(proto_id); protocol->can_toggle = FALSE; } /* for use with static arrays only, since we don't allocate our own copies of the header_field_info struct contained within 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; protocol_t *proto; proto = find_protocol_by_id(parent); for (i = 0; i < num_records; i++, ptr++) { /* * Make sure we haven't registered this yet. * Most fields have variables associated with them * that are initialized to -1; some have array elements, * or possibly uninitialized variables, so we also allow * 0 (which is unlikely to be the field ID we get back * from "proto_register_field_init()"). */ g_assert(*ptr->p_id == -1 || *ptr->p_id == 0); if (proto != NULL) { if (proto->fields == NULL) { proto->fields = g_list_append(NULL, ptr); proto->last_field = proto->fields; } else { proto->last_field = g_list_append(proto->last_field, ptr)->next; } } 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) { /* The field must have names */ g_assert(hfinfo->name); g_assert(hfinfo->abbrev); /* 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) || (hfinfo->type == FT_FRAMENUM) )); switch (hfinfo->type) { case FT_UINT8: case FT_UINT16: case FT_UINT24: case FT_UINT32: case FT_INT8: case FT_INT16: case FT_INT24: case FT_INT32: /* Require integral types (other than frame number, which is always displayed in decimal) to have a number base */ g_assert(hfinfo->display != BASE_NONE); break; case FT_FRAMENUM: /* Don't allow bitfields or value strings for frame numbers */ g_assert(hfinfo->bitmask == 0); g_assert(hfinfo->strings == NULL); break; default: break; } /* if this is a bitfield, compute bitshift */ if (hfinfo->bitmask) { while ((hfinfo->bitmask & (1 << hfinfo->bitshift)) == 0) hfinfo->bitshift++; } hfinfo->parent = parent; hfinfo->same_name_next = NULL; hfinfo->same_name_prev = NULL; /* if we always add and never delete, then id == len - 1 is correct */ if(gpa_hfinfo.len>=gpa_hfinfo.allocated_len){ if(!gpa_hfinfo.hfi){ gpa_hfinfo.allocated_len=1000; gpa_hfinfo.hfi=g_malloc(sizeof(header_field_info *)*1000); } else { gpa_hfinfo.allocated_len+=1000; gpa_hfinfo.hfi=g_realloc(gpa_hfinfo.hfi, sizeof(header_field_info *)*gpa_hfinfo.allocated_len); } } gpa_hfinfo.hfi[gpa_hfinfo.len]=hfinfo; gpa_hfinfo.len++; hfinfo->id = gpa_hfinfo.len - 1; /* if we have real names, enter this field in the name tree */ if ((hfinfo->name[0] != 0) && (hfinfo->abbrev[0] != 0 )) { header_field_info *same_name_hfinfo, *same_name_next_hfinfo; char *p; guchar c; /* Check that the filter name (abbreviation) is legal; * it must contain only alphanumerics, '-', "_", and ".". */ for (p = hfinfo->abbrev; (c = *p) != '\0'; p++) g_assert(isalnum(c) || c == '-' || c == '_' || c == '.'); /* We allow multiple hfinfo's to be registered under the same * abbreviation. This was done for X.25, as, depending * on whether it's modulo-8 or modulo-128 operation, * some bitfield fields may be in different bits of * a byte, and we want to be able to refer to that field * with one name regardless of whether the packets * are modulo-8 or modulo-128 packets. */ same_name_hfinfo = g_tree_lookup(gpa_name_tree, hfinfo->abbrev); if (same_name_hfinfo) { /* There's already a field with this name. * Put it after that field in the list of * fields with this name, then allow the code * after this if{} block to replace the old * hfinfo with the new hfinfo in the GTree. Thus, * we end up with a linked-list of same-named hfinfo's, * with the root of the list being the hfinfo in the GTree */ same_name_next_hfinfo = same_name_hfinfo->same_name_next; hfinfo->same_name_next = same_name_next_hfinfo; if (same_name_next_hfinfo) same_name_next_hfinfo->same_name_prev = hfinfo; same_name_hfinfo->same_name_next = hfinfo; hfinfo->same_name_prev = same_name_hfinfo; } g_tree_insert(gpa_name_tree, hfinfo->abbrev, hfinfo); } return hfinfo->id; } void proto_register_subtree_array(gint **indices, int num_indices) { int i; gint **ptr = indices; /* * Make sure we haven't already allocated the array of "tree is * expanded" flags. * * XXX - if it's *really* important to allow more ett_ values to * be given out after "proto_init()" is called, we could expand * the array. */ g_assert(tree_is_expanded == NULL); /* * 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", and update * "num_tree_types" appropriately. */ for (i = 0; i < num_indices; i++, ptr++, num_tree_types++) **ptr = num_tree_types; } void proto_item_fill_label(field_info *fi, gchar *label_str) { header_field_info *hfinfo = fi->hfinfo; guint8 *bytes; guint32 integer; ipv4_addr *ipv4; guint32 n_addr; /* network-order IPv4 address */ int ret; /*tmp return value */ switch(hfinfo->type) { case FT_NONE: case FT_PROTOCOL: ret = snprintf(label_str, ITEM_LABEL_LENGTH, "%s", hfinfo->name); if ((ret == -1) || (ret >= ITEM_LABEL_LENGTH)) label_str[ITEM_LABEL_LENGTH - 1] = '\0'; break; case FT_BOOLEAN: fill_label_boolean(fi, label_str); break; case FT_BYTES: case FT_UINT_BYTES: bytes = fvalue_get(&fi->value); if (bytes) { ret = snprintf(label_str, ITEM_LABEL_LENGTH, "%s: %s", hfinfo->name, bytes_to_str(bytes, fvalue_length(&fi->value))); if ((ret == -1) || (ret >= ITEM_LABEL_LENGTH)) label_str[ITEM_LABEL_LENGTH - 1] = '\0'; } else { ret = snprintf(label_str, ITEM_LABEL_LENGTH, "%s: ", hfinfo->name); if ((ret == -1) || (ret >= ITEM_LABEL_LENGTH)) label_str[ITEM_LABEL_LENGTH - 1] = '\0'; } 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: case FT_FRAMENUM: 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_UINT64: fill_label_uint64(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_INT64: fill_label_int64(fi, label_str); break; case FT_FLOAT: ret = snprintf(label_str, ITEM_LABEL_LENGTH, "%s: %." STRINGIFY(FLT_DIG) "f", hfinfo->name, fvalue_get_floating(&fi->value)); if ((ret == -1) || (ret >= ITEM_LABEL_LENGTH)) label_str[ITEM_LABEL_LENGTH - 1] = '\0'; break; case FT_DOUBLE: ret = snprintf(label_str, ITEM_LABEL_LENGTH, "%s: %." STRINGIFY(DBL_DIG) "g", hfinfo->name, fvalue_get_floating(&fi->value)); if ((ret == -1) || (ret >= ITEM_LABEL_LENGTH)) label_str[ITEM_LABEL_LENGTH - 1] = '\0'; break; case FT_ABSOLUTE_TIME: ret = snprintf(label_str, ITEM_LABEL_LENGTH, "%s: %s", hfinfo->name, abs_time_to_str(fvalue_get(&fi->value))); if ((ret == -1) || (ret >= ITEM_LABEL_LENGTH)) label_str[ITEM_LABEL_LENGTH - 1] = '\0'; break; case FT_RELATIVE_TIME: ret = snprintf(label_str, ITEM_LABEL_LENGTH, "%s: %s seconds", hfinfo->name, rel_time_to_secs_str(fvalue_get(&fi->value))); if ((ret == -1) || (ret >= ITEM_LABEL_LENGTH)) label_str[ITEM_LABEL_LENGTH - 1] = '\0'; break; case FT_IPXNET: integer = fvalue_get_integer(&fi->value); ret = snprintf(label_str, ITEM_LABEL_LENGTH, "%s: 0x%08X (%s)", hfinfo->name, integer, get_ipxnet_name(integer)); if ((ret == -1) || (ret >= ITEM_LABEL_LENGTH)) label_str[ITEM_LABEL_LENGTH - 1] = '\0'; break; case FT_ETHER: bytes = fvalue_get(&fi->value); ret = snprintf(label_str, ITEM_LABEL_LENGTH, "%s: %s (%s)", hfinfo->name, ether_to_str(bytes), get_ether_name(bytes)); if ((ret == -1) || (ret >= ITEM_LABEL_LENGTH)) label_str[ITEM_LABEL_LENGTH - 1] = '\0'; break; case FT_IPv4: ipv4 = fvalue_get(&fi->value); n_addr = ipv4_get_net_order_addr(ipv4); ret = snprintf(label_str, ITEM_LABEL_LENGTH, "%s: %s (%s)", hfinfo->name, get_hostname(n_addr), ip_to_str((guint8*)&n_addr)); if ((ret == -1) || (ret >= ITEM_LABEL_LENGTH)) label_str[ITEM_LABEL_LENGTH - 1] = '\0'; break; case FT_IPv6: bytes = fvalue_get(&fi->value); ret = snprintf(label_str, ITEM_LABEL_LENGTH, "%s: %s (%s)", hfinfo->name, get_hostname6((struct e_in6_addr *)bytes), ip6_to_str((struct e_in6_addr*)bytes)); if ((ret == -1) || (ret >= ITEM_LABEL_LENGTH)) label_str[ITEM_LABEL_LENGTH - 1] = '\0'; break; case FT_STRING: case FT_STRINGZ: case FT_UINT_STRING: bytes = fvalue_get(&fi->value); ret = snprintf(label_str, ITEM_LABEL_LENGTH, "%s: %s", hfinfo->name, format_text(bytes, strlen(bytes))); if ((ret == -1) || (ret >= ITEM_LABEL_LENGTH)) label_str[ITEM_LABEL_LENGTH - 1] = '\0'; break; default: g_error("hfinfo->type %d (%s) not handled\n", hfinfo->type, 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; guint32 value; int ret; /*tmp return value */ header_field_info *hfinfo = fi->hfinfo; static const true_false_string default_tf = { "True", "False" }; const true_false_string *tfstring = &default_tf; if (hfinfo->strings) { tfstring = (const struct true_false_string*) hfinfo->strings; } value = fvalue_get_integer(&fi->value); if (hfinfo->bitmask) { /* Figure out the bit width */ bitwidth = hfinfo_bitwidth(hfinfo); /* Un-shift bits */ unshifted_value = value; 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 */ ret = snprintf(p, ITEM_LABEL_LENGTH - bitfield_byte_length, "%s: %s", hfinfo->name, value ? tfstring->true_string : tfstring->false_string); if ((ret == -1) || (ret >= (ITEM_LABEL_LENGTH - bitfield_byte_length))) label_str[ITEM_LABEL_LENGTH - 1] = '\0'; } /* 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; guint32 value; int ret; /*tmp return value */ 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 = fvalue_get_integer(&fi->value); value = unshifted_value; 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 */ ret = snprintf(p, ITEM_LABEL_LENGTH - bitfield_byte_length, format, hfinfo->name, val_to_str(value, cVALS(hfinfo->strings), "Unknown"), value); if ((ret == -1) || (ret >= (ITEM_LABEL_LENGTH - bitfield_byte_length))) label_str[ITEM_LABEL_LENGTH - 1] = '\0'; } static void fill_label_numeric_bitfield(field_info *fi, gchar *label_str) { char *format = NULL, *p; int bitfield_byte_length, bitwidth; guint32 unshifted_value; guint32 value; int ret; /*tmp return value */ 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 = fvalue_get_integer(&fi->value); value = unshifted_value; 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 */ ret = snprintf(p, ITEM_LABEL_LENGTH - bitfield_byte_length, format, hfinfo->name, value); if ((ret == -1) || (ret >= (ITEM_LABEL_LENGTH - bitfield_byte_length))) label_str[ITEM_LABEL_LENGTH - 1] = '\0'; } static void fill_label_enumerated_uint(field_info *fi, gchar *label_str) { char *format = NULL; header_field_info *hfinfo = fi->hfinfo; guint32 value; int ret; /*tmp return value */ /* Pick the proper format string */ format = hfinfo_uint_vals_format(hfinfo); value = fvalue_get_integer(&fi->value); /* Fill in the textual info */ ret = snprintf(label_str, ITEM_LABEL_LENGTH, format, hfinfo->name, val_to_str(value, cVALS(hfinfo->strings), "Unknown"), value); if ((ret == -1) || (ret >= ITEM_LABEL_LENGTH)) label_str[ITEM_LABEL_LENGTH - 1] = '\0'; } static void fill_label_uint(field_info *fi, gchar *label_str) { char *format = NULL; header_field_info *hfinfo = fi->hfinfo; guint32 value; int ret; /*tmp return value */ /* Pick the proper format string */ format = hfinfo_uint_format(hfinfo); value = fvalue_get_integer(&fi->value); /* Fill in the textual info */ ret = snprintf(label_str, ITEM_LABEL_LENGTH, format, hfinfo->name, value); if ((ret == -1) || (ret >= ITEM_LABEL_LENGTH)) label_str[ITEM_LABEL_LENGTH - 1] = '\0'; } static void fill_label_uint64(field_info *fi, gchar *label_str) { char *format = NULL; header_field_info *hfinfo = fi->hfinfo; guint64 value; int ret; /*tmp return value */ /* Pick the proper format string */ format = hfinfo_uint64_format(hfinfo); value = fvalue_get_integer64(&fi->value); /* Fill in the textual info */ ret = snprintf(label_str, ITEM_LABEL_LENGTH, format, hfinfo->name, value); if ((ret == -1) || (ret >= ITEM_LABEL_LENGTH)) label_str[ITEM_LABEL_LENGTH - 1] = '\0'; } static void fill_label_enumerated_int(field_info *fi, gchar *label_str) { char *format = NULL; header_field_info *hfinfo = fi->hfinfo; guint32 value; int ret; /*tmp return value */ /* Pick the proper format string */ format = hfinfo_int_vals_format(hfinfo); value = fvalue_get_integer(&fi->value); /* Fill in the textual info */ ret = snprintf(label_str, ITEM_LABEL_LENGTH, format, hfinfo->name, val_to_str(value, cVALS(hfinfo->strings), "Unknown"), value); if ((ret == -1) || (ret >= ITEM_LABEL_LENGTH)) label_str[ITEM_LABEL_LENGTH - 1] = '\0'; } static void fill_label_int(field_info *fi, gchar *label_str) { char *format = NULL; header_field_info *hfinfo = fi->hfinfo; guint32 value; int ret; /*tmp return value */ /* Pick the proper format string */ format = hfinfo_int_format(hfinfo); value = fvalue_get_integer(&fi->value); /* Fill in the textual info */ ret = snprintf(label_str, ITEM_LABEL_LENGTH, format, hfinfo->name, value); if ((ret == -1) || (ret >= ITEM_LABEL_LENGTH)) label_str[ITEM_LABEL_LENGTH - 1] = '\0'; } static void fill_label_int64(field_info *fi, gchar *label_str) { char *format = NULL; header_field_info *hfinfo = fi->hfinfo; guint64 value; int ret; /*tmp return value */ /* Pick the proper format string */ format = hfinfo_int64_format(hfinfo); value = fvalue_get_integer64(&fi->value); /* Fill in the textual info */ ret = snprintf(label_str, ITEM_LABEL_LENGTH, format, hfinfo->name, value); if ((ret == -1) || (ret >= ITEM_LABEL_LENGTH)) label_str[ITEM_LABEL_LENGTH - 1] = '\0'; } 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: format = "%s: %s (%u)"; break; case BASE_OCT: /* I'm lazy */ format = "%s: %s (%o)"; 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 */ if (hfinfo->type == FT_FRAMENUM) { /* * Frame numbers are always displayed in decimal. */ format = "%s: %u"; } else { switch(hfinfo->display) { case BASE_DEC: format = "%s: %u"; break; case BASE_OCT: /* I'm lazy */ format = "%s: %o"; 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: format = "%s: %s (%d)"; break; case BASE_OCT: /* I'm lazy */ format = "%s: %s (%o)"; 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_uint64_format(header_field_info *hfinfo) { char *format = NULL; /* Pick the proper format string */ switch(hfinfo->display) { case BASE_DEC: format = "%s: %" PRIu64; break; case BASE_OCT: /* I'm lazy */ format = "%s: %" PRIo64; break; case BASE_HEX: format = "%s: 0x%016" PRIx64; 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: format = "%s: %d"; break; case BASE_OCT: /* I'm lazy */ format = "%s: %o"; 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; } static char* hfinfo_int64_format(header_field_info *hfinfo) { char *format = NULL; /* Pick the proper format string */ switch(hfinfo->display) { case BASE_DEC: format = "%s: %" PRId64; break; case BASE_OCT: /* I'm lazy */ format = "%s: %" PRIo64; break; case BASE_HEX: format = "%s: 0x%016" PRIx64; break; default: g_assert_not_reached(); ; } return format; } int proto_registrar_n(void) { return gpa_hfinfo.len; } char* proto_registrar_get_name(int n) { header_field_info *hfinfo; PROTO_REGISTRAR_GET_NTH(n, hfinfo); return hfinfo->name; } char* proto_registrar_get_abbrev(int n) { header_field_info *hfinfo; PROTO_REGISTRAR_GET_NTH(n, hfinfo); return hfinfo->abbrev; } int proto_registrar_get_ftype(int n) { header_field_info *hfinfo; PROTO_REGISTRAR_GET_NTH(n, hfinfo); return hfinfo->type; } int proto_registrar_get_parent(int n) { header_field_info *hfinfo; PROTO_REGISTRAR_GET_NTH(n, hfinfo); return hfinfo->parent; } gboolean proto_registrar_is_protocol(int n) { header_field_info *hfinfo; PROTO_REGISTRAR_GET_NTH(n, hfinfo); return (hfinfo->parent == -1 ? TRUE : 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) { header_field_info *hfinfo; PROTO_REGISTRAR_GET_NTH(n, hfinfo); return ftype_length(hfinfo->type); } /* 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) { GPtrArray *ptrs = proto_get_finfo_ptr_array(tree, id); if (!ptrs) { return FALSE; } else if (g_ptr_array_len(ptrs) > 0) { return TRUE; } else { return FALSE; } } /* Return GPtrArray* of field_info pointers for all hfindex that appear in tree. * This only works if the hfindex was "primed" before the dissection * took place, as we just pass back the already-created GPtrArray*. * The caller should *not* free the GPtrArray*; proto_tree_free_node() * handles that. */ GPtrArray* proto_get_finfo_ptr_array(proto_tree *tree, int id) { return g_hash_table_lookup(PTREE_DATA(tree)->interesting_hfids, GINT_TO_POINTER(id)); } /* Helper struct and function for proto_find_info() */ typedef struct { GPtrArray *array; int id; } ffdata_t; static gboolean find_finfo(proto_node *node, gpointer data) { field_info *fi = PITEM_FINFO(node); if (fi && fi->hfinfo) { if (fi->hfinfo->id == ((ffdata_t*)data)->id) { g_ptr_array_add(((ffdata_t*)data)->array, fi); } } /* Don't stop traversing. */ return FALSE; } /* Return GPtrArray* of field_info pointers for all hfindex that appear in a tree. * This works on any proto_tree, primed or unprimed, but actually searches * the tree, so it is slower than using proto_get_finfo_ptr_array on a primed tree. * The caller does need to free the returned GPtrArray with * g_ptr_array_free(, FALSE). */ GPtrArray* proto_find_finfo(proto_tree *tree, int id) { ffdata_t ffdata; ffdata.array = g_ptr_array_new(); ffdata.id = id; proto_tree_traverse_pre_order(tree, find_finfo, &ffdata); return ffdata.array; } typedef struct { guint offset; field_info *finfo; tvbuff_t *tvb; } offset_search_t; static gboolean check_for_offset(proto_node *node, gpointer data) { field_info *fi = PITEM_FINFO(node); offset_search_t *offsearch = data; /* !fi == the top most container node which holds nothing */ if (fi && !PROTO_ITEM_IS_HIDDEN(node) && fi->ds_tvb && offsearch->tvb == fi->ds_tvb) { if (offsearch->offset >= (guint) fi->start && offsearch->offset < (guint) (fi->start + fi->length)) { offsearch->finfo = fi; return FALSE; /* keep traversing */ } } return FALSE; /* keep traversing */ } /* Search a proto_tree backwards (from leaves to root) looking for the field * whose start/length occupies 'offset' */ /* XXX - I couldn't find an easy way to search backwards, so I search * forwards, w/o stopping. Therefore, the last finfo I find will the be * the one I want to return to the user. This algorithm is inefficient * and could be re-done, but I'd have to handle all the children and * siblings of each node myself. When I have more time I'll do that. * (yeah right) */ field_info* proto_find_field_from_offset(proto_tree *tree, guint offset, tvbuff_t *tvb) { offset_search_t offsearch; offsearch.offset = offset; offsearch.finfo = NULL; offsearch.tvb = tvb; proto_tree_traverse_pre_order(tree, check_for_offset, &offsearch); return offsearch.finfo; } /* Dumps the protocols in the registration database to stdout. An independent * program can take this output and format it into nice tables or HTML or * whatever. * * There is one record per line. The fields are tab-delimited. * * Field 1 = protocol name * Field 2 = protocol short name * Field 3 = protocol filter name */ void proto_registrar_dump_protocols(void) { protocol_t *protocol; int i; void *cookie; for (i = proto_get_first_protocol(&cookie); i != -1; i = proto_get_next_protocol(&cookie)) { protocol = find_protocol_by_id(i); printf("%s\t%s\t%s\n", protocol->name, protocol->short_name, protocol->filter_name); } } /* Dumps the value_string and true/false strings for fields that have * them. There is one record per line. Fields are tab-delimited. * There are two types of records, Value String records and True/False * String records. The first field, 'V' or 'T', indicates the type * of record. * * Value Strings * ------------- * Field 1 = 'V' * Field 2 = field abbreviation to which this value string corresponds * Field 3 = Integer value * Field 4 = String * * True/False Strings * ------------------ * Field 1 = 'T' * Field 2 = field abbreviation to which this true/false string corresponds * Field 3 = True String * Field 4 = False String */ void proto_registrar_dump_values(void) { header_field_info *hfinfo, *parent_hfinfo; int i, len, vi; const value_string *vals; const true_false_string *tfs; len = gpa_hfinfo.len; for (i = 0; i < len ; i++) { PROTO_REGISTRAR_GET_NTH(i, hfinfo); if (hfinfo->id == hf_text_only) { continue; } /* ignore protocols */ if (proto_registrar_is_protocol(i)) { continue; } /* process header fields */ else { /* * If this field isn't at the head of the list of * fields with this name, skip this field - all * fields with the same name are really just versions * of the same field stored in different bits, and * should have the same type/radix/value list, and * just differ in their bit masks. (If a field isn't * a bitfield, but can be, say, 1 or 2 bytes long, * it can just be made FT_UINT16, meaning the * *maximum* length is 2 bytes, and be used * for all lengths.) */ if (hfinfo->same_name_prev != NULL) continue; PROTO_REGISTRAR_GET_NTH(hfinfo->parent, parent_hfinfo); vals = NULL; tfs = NULL; if (hfinfo->type == FT_UINT8 || hfinfo->type == FT_UINT16 || hfinfo->type == FT_UINT24 || hfinfo->type == FT_UINT32 || hfinfo->type == FT_UINT64 || hfinfo->type == FT_INT8 || hfinfo->type == FT_INT16 || hfinfo->type == FT_INT24 || hfinfo->type == FT_INT32 || hfinfo->type == FT_INT64) { vals = hfinfo->strings; } else if (hfinfo->type == FT_BOOLEAN) { tfs = hfinfo->strings; } /* Print value strings? */ if (vals) { vi = 0; while (vals[vi].strptr) { /* Print in the proper base */ if (hfinfo->display == BASE_HEX) { printf("V\t%s\t0x%x\t%s\n", hfinfo->abbrev, vals[vi].value, vals[vi].strptr); } else { printf("V\t%s\t%u\t%s\n", hfinfo->abbrev, vals[vi].value, vals[vi].strptr); } vi++; } } /* Print true/false strings? */ else if (tfs) { printf("T\t%s\t%s\t%s\n", hfinfo->abbrev, tfs->true_string, tfs->false_string); } } } } /* 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 = descriptive protocol name * Field 3 = protocol abbreviation * * Header Fields * ------------- * (format 1) * Field 1 = 'F' * Field 2 = descriptive field name * Field 3 = field abbreviation * Field 4 = type ( textual representation of the the ftenum type ) * Field 5 = parent protocol abbreviation * * (format 2 adds these fields:) * Field 6 = base for display (for integer types) * Field 7 = blurb describing field */ void proto_registrar_dump_fields(int format) { header_field_info *hfinfo, *parent_hfinfo; int i, len; const char *enum_name; const char *base_name; len = gpa_hfinfo.len; for (i = 0; i < len ; i++) { PROTO_REGISTRAR_GET_NTH(i, hfinfo); /* * Skip fields with zero-length names or abbreviations; * the pseudo-field for "proto_tree_add_text()" is such * a field, and we don't want it in the list of filterable * fields. * * * XXX - perhaps the name and abbrev field should be null * pointers rather than null strings for that pseudo-field, * but we'd have to add checks for null pointers in some * places if we did that. * * Or perhaps protocol tree items added with * "proto_tree_add_text()" should have -1 as the field index, * with no pseudo-field being used, but that might also * require special checks for -1 to be added. */ /* XXX - we could just skip the special text * pseudo-field by testing: if (hfinfo->id == hf_text_only) * */ if (hfinfo->name[0] == 0 || hfinfo->abbrev[0] == 0) continue; /* 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 { /* * If this field isn't at the head of the list of * fields with this name, skip this field - all * fields with the same name are really just versions * of the same field stored in different bits, and * should have the same type/radix/value list, and * just differ in their bit masks. (If a field isn't * a bitfield, but can be, say, 1 or 2 bytes long, * it can just be made FT_UINT16, meaning the * *maximum* length is 2 bytes, and be used * for all lengths.) */ if (hfinfo->same_name_prev != NULL) continue; PROTO_REGISTRAR_GET_NTH(hfinfo->parent, parent_hfinfo); enum_name = ftype_name(hfinfo->type); base_name = ""; if (format > 1) { if (hfinfo->type == FT_UINT8 || hfinfo->type == FT_UINT16 || hfinfo->type == FT_UINT24 || hfinfo->type == FT_UINT32 || hfinfo->type == FT_UINT64 || hfinfo->type == FT_INT8 || hfinfo->type == FT_INT16 || hfinfo->type == FT_INT24 || hfinfo->type == FT_INT32 || hfinfo->type == FT_INT64) { switch(hfinfo->display) { case BASE_NONE: base_name = "BASE_NONE"; break; case BASE_DEC: base_name = "BASE_DEC"; break; case BASE_HEX: base_name = "BASE_HEX"; break; case BASE_OCT: base_name = "BASE_OCT"; break; } } } if (format == 1) { printf("F\t%s\t%s\t%s\t%s\t%s\n", hfinfo->name, hfinfo->abbrev, enum_name,parent_hfinfo->abbrev, hfinfo->blurb); } else if (format == 2) { printf("F\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n", hfinfo->name, hfinfo->abbrev, enum_name,parent_hfinfo->abbrev, hfinfo->blurb, base_name, hfinfo->blurb); } } } } static char* hfinfo_numeric_format(header_field_info *hfinfo) { char *format = NULL; /* Pick the proper format string */ if (hfinfo->type == FT_FRAMENUM) { /* * Frame numbers are always displayed in decimal. */ format = "%s == %u"; } else { /* Pick the proper format string */ switch(hfinfo->display) { case BASE_DEC: case BASE_OCT: /* I'm lazy */ switch(hfinfo->type) { case FT_UINT8: case FT_UINT16: case FT_UINT24: case FT_UINT32: format = "%s == %u"; break; case FT_UINT64: format = "%s == %" PRIu64; break; case FT_INT8: case FT_INT16: case FT_INT24: case FT_INT32: format = "%s == %d"; break; case FT_INT64: format = "%s == %" PRId64; break; default: g_assert_not_reached(); ; } 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; case FT_UINT64: format = "%s == 0x%016" PRIx64; break; default: g_assert_not_reached(); ; } break; default: g_assert_not_reached(); ; } } return format; } /* * Returns TRUE if we can do a "match selected" on the field, FALSE * otherwise. */ gboolean proto_can_match_selected(field_info *finfo, epan_dissect_t *edt) { header_field_info *hfinfo; gint length; hfinfo = finfo->hfinfo; g_assert(hfinfo); switch(hfinfo->type) { case FT_BOOLEAN: case FT_UINT8: case FT_UINT16: case FT_UINT24: case FT_UINT32: case FT_INT8: case FT_INT16: case FT_INT24: case FT_INT32: case FT_FRAMENUM: case FT_UINT64: case FT_INT64: case FT_IPv4: case FT_IPXNET: case FT_IPv6: case FT_FLOAT: case FT_DOUBLE: case FT_ABSOLUTE_TIME: case FT_RELATIVE_TIME: case FT_STRING: case FT_STRINGZ: case FT_UINT_STRING: case FT_ETHER: case FT_BYTES: case FT_UINT_BYTES: case FT_PROTOCOL: /* * These all have values, so we can match. */ return TRUE; default: /* * This doesn't have a value, so we'd match * on the raw bytes at this address. * * Should we be allowed to access to the raw bytes? * If "edt" is NULL, the answer is "no". */ if (edt == NULL) return FALSE; /* * Is this field part of the raw frame tvbuff? * If not, we can't use "frame[N:M]" to match * it. * * XXX - should this be frame-relative, or * protocol-relative? * * XXX - does this fallback for non-registered * fields even make sense? */ if (finfo->ds_tvb != edt->tvb) return FALSE; /* * If the length is 0, there's nothing to match, so * we can't match. (Also check for negative values, * just in case, as we'll cast it to an unsigned * value later.) */ length = finfo->length; if (length <= 0) return FALSE; /* * Don't go past the end of that tvbuff. */ if ((guint)length > tvb_length(finfo->ds_tvb)) length = tvb_length(finfo->ds_tvb); if (length <= 0) return FALSE; return TRUE; } } char* proto_construct_dfilter_string(field_info *finfo, epan_dissect_t *edt) { header_field_info *hfinfo; int abbrev_len; char *buf, *stringified, *format, *ptr; int dfilter_len, i; gint start, length, length_remaining; guint8 c; hfinfo = finfo->hfinfo; g_assert(hfinfo); abbrev_len = strlen(hfinfo->abbrev); /* * XXX - we should add "val_to_string_repr" and "string_repr_len" * functions for more types, and use them whenever possible. * * The FT_UINT and FT_INT types are the only tricky ones, as * we choose the base in the string expression based on the * display base of the field. * * Note that the base does matter, as this is also used for * the protocolinfo tap. * * It might be nice to use that in "proto_item_fill_label()" * as well, although, there, you'd have to deal with the base * *and* with resolved values for addresses. * * Perhaps we need two different val_to_string routines, one * to generate items for display filters and one to generate * strings for display, and pass to both of them the * "display" and "strings" values in the header_field_info * structure for the field, so they can get the base and, * if the field is Boolean or an enumerated integer type, * the tables used to generate human-readable values. */ switch(hfinfo->type) { case FT_UINT8: case FT_UINT16: case FT_UINT24: case FT_UINT32: case FT_INT8: case FT_INT16: case FT_INT24: case FT_INT32: case FT_FRAMENUM: /* * 4 bytes for " == ". * 11 bytes for: * * a sign + up to 10 digits of 32-bit integer, * in decimal; * * "0x" + 8 digits of 32-bit integer, in hex; * * 11 digits of 32-bit integer, in octal. * (No, we don't do octal, but this way, * we know that if we do, this will still * work.) * * 1 byte for the trailing '\0'. */ dfilter_len = abbrev_len + 4 + 11 + 1; buf = g_malloc0(dfilter_len); format = hfinfo_numeric_format(hfinfo); snprintf(buf, dfilter_len, format, hfinfo->abbrev, fvalue_get_integer(&finfo->value)); break; case FT_INT64: case FT_UINT64: /* * 4 bytes for " == ". * 22 bytes for: * * a sign + up to 20 digits of 32-bit integer, * in decimal; * * "0x" + 16 digits of 32-bit integer, in hex; * * 22 digits of 32-bit integer, in octal. * (No, we don't do octal, but this way, * we know that if we do, this will still * work.) * * 1 byte for the trailing '\0'. */ dfilter_len = abbrev_len + 4 + 22 + 1; buf = g_malloc0(dfilter_len); format = hfinfo_numeric_format(hfinfo); snprintf(buf, dfilter_len, format, hfinfo->abbrev, fvalue_get_integer64(&finfo->value)); break; case FT_IPXNET: /* * 4 bytes for " == ". * 2 bytes for "0x". * 8 bytes for 8 digits of 32-bit hex number. * 1 byte for the trailing '\0'. */ dfilter_len = abbrev_len + 4 + 2 + 8 + 1; buf = g_malloc0(dfilter_len); snprintf(buf, dfilter_len, "%s == 0x%08x", hfinfo->abbrev, fvalue_get_integer(&finfo->value)); break; case FT_IPv6: /* * 4 bytes for " == ". * N bytes for the string for the address. * 1 byte for the trailing '\0'. */ stringified = ip6_to_str((struct e_in6_addr*) fvalue_get(&finfo->value)); dfilter_len = abbrev_len + 4 + strlen(stringified) + 1; buf = g_malloc0(dfilter_len); snprintf(buf, dfilter_len, "%s == %s", hfinfo->abbrev, stringified); break; /* These use the fvalue's "to_string_repr" method. */ case FT_BOOLEAN: case FT_STRING: case FT_ETHER: case FT_BYTES: case FT_UINT_BYTES: case FT_FLOAT: case FT_DOUBLE: case FT_ABSOLUTE_TIME: case FT_RELATIVE_TIME: case FT_IPv4: /* Figure out the string length needed. * The ft_repr length. * 4 bytes for " == ". * 1 byte for trailing NUL. */ dfilter_len = fvalue_string_repr_len(&finfo->value, FTREPR_DFILTER); dfilter_len += abbrev_len + 4 + 1; buf = g_malloc0(dfilter_len); /* Create the string */ snprintf(buf, dfilter_len, "%s == ", hfinfo->abbrev); fvalue_to_string_repr(&finfo->value, FTREPR_DFILTER, &buf[abbrev_len + 4]); break; case FT_PROTOCOL: buf = g_strdup(finfo->hfinfo->abbrev); break; default: /* * This doesn't have a value, so we'd match * on the raw bytes at this address. * * Should we be allowed to access to the raw bytes? * If "edt" is NULL, the answer is "no". */ if (edt == NULL) return FALSE; /* * Is this field part of the raw frame tvbuff? * If not, we can't use "frame[N:M]" to match * it. * * XXX - should this be frame-relative, or * protocol-relative? * * XXX - does this fallback for non-registered * fields even make sense? */ if (finfo->ds_tvb != edt->tvb) return NULL; /* you lose */ /* * If the length is 0, there's nothing to match, so * we can't match. (Also check for negative values, * just in case, as we'll cast it to an unsigned * value later.) */ length = finfo->length; if (length <= 0) return NULL; /* * Don't go past the end of that tvbuff. */ length_remaining = tvb_length_remaining(finfo->ds_tvb, finfo->start); if (length > length_remaining) length = length_remaining; if (length <= 0) return NULL; start = finfo->start; buf = g_malloc0(32 + length * 3); ptr = buf; sprintf(ptr, "frame[%d:%d] == ", finfo->start, length); ptr = buf+strlen(buf); for (i=0;ids_tvb, start); start++; if (i == 0 ) { sprintf(ptr, "%02x", c); } else { sprintf(ptr, ":%02x", c); } ptr = buf+strlen(buf); } break; } return buf; }