wireshark/proto.c

1348 lines
32 KiB
C

/* proto.c
* Routines for protocol tree
*
* $Id: proto.c,v 1.52 2000/01/22 04:59:55 guy Exp $
*
* Ethereal - Network traffic analyzer
* By Gerald Combs <gerald@zing.org>
* Copyright 1998 Gerald Combs
*
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#ifdef HAVE_SYS_TYPES_H
# include <sys/types.h>
#endif
#ifndef _STDIO_H
#include <stdio.h>
#endif
#include <stdarg.h>
#ifndef _STRING_H
#include <string.h>
#endif
#ifdef NEED_SNPRINTF_H
# include "snprintf.h"
#endif
#ifndef __G_LIB_H__
#include <glib.h>
#endif
#ifndef __PROTO_H__
#include "proto.h"
#endif
#ifndef __PACKET_H__
#include "packet.h"
#endif
#ifndef __RESOLV_H__
#include "resolv.h"
#endif
#ifndef __REGISTER_H__
#include "register.h"
#endif
#include "packet-ipv6.h"
#define cVALS(x) (const value_string*)(x)
static gboolean
proto_tree_free_node(GNode *node, gpointer data);
static struct header_field_info*
find_hfinfo_record(int hfindex);
static proto_item *
proto_tree_add_item_value(proto_tree *tree, int hfindex, gint start,
gint length, int include_format, int visible, va_list ap);
static void fill_label_boolean(field_info *fi, gchar *label_str);
static void fill_label_uint(field_info *fi, gchar *label_str);
static void fill_label_enumerated_uint(field_info *fi, gchar *label_str);
static void fill_label_enumerated_bitfield(field_info *fi, gchar *label_str);
static void fill_label_numeric_bitfield(field_info *fi, gchar *label_str);
static void fill_label_int(field_info *fi, gchar *label_str);
static void fill_label_enumerated_int(field_info *fi, gchar *label_str);
static int hfinfo_bitwidth(header_field_info *hfinfo);
static char* hfinfo_uint_vals_format(header_field_info *hfinfo);
static char* hfinfo_uint_format(header_field_info *hfinfo);
static char* hfinfo_int_vals_format(header_field_info *hfinfo);
static char* hfinfo_int_format(header_field_info *hfinfo);
static gboolean check_for_protocol_or_field_id(GNode *node, gpointer data);
static gboolean check_for_field_within_protocol(GNode *node, gpointer data);
static int proto_register_field_init(header_field_info *hfinfo, int parent);
/* special-case header field used within proto.c */
int hf_text_only = 1;
/* Contains information about protocols and header fields. Used when
* dissectors register their data */
GMemChunk *gmc_hfinfo = NULL;
/* Contains information about a field when a dissector calls
* proto_tree_add_item. */
GMemChunk *gmc_field_info = NULL;
/* String space for protocol and field items for the GUI */
GMemChunk *gmc_item_labels = NULL;
/* List which stores protocols and fields that have been registered */
GPtrArray *gpa_hfinfo = NULL;
/* Points to the first element of an array of Booleans, indexed by
a subtree item type; that array element is TRUE if subtrees of
an item of that type are to be expanded. */
gboolean *tree_is_expanded;
/* Number of elements in that array. */
int num_tree_types;
/* Is the parsing being done for a visible proto_tree or an invisible one?
* By setting this correctly, the proto_tree creation is sped up by not
* having to call vsnprintf and copy strings around.
*/
gboolean proto_tree_is_visible = TRUE;
/* initialize data structures and register protocols and fields */
void
proto_init(void)
{
static hf_register_info hf[] = {
{ &hf_text_only,
{ "Text", "text", FT_TEXT_ONLY, BASE_NONE, NULL, 0x0,
"" }},
};
if (gmc_hfinfo)
g_mem_chunk_destroy(gmc_hfinfo);
if (gmc_field_info)
g_mem_chunk_destroy(gmc_field_info);
if (gmc_item_labels)
g_mem_chunk_destroy(gmc_item_labels);
if (gpa_hfinfo)
g_ptr_array_free(gpa_hfinfo, FALSE);
if (tree_is_expanded != NULL)
g_free(tree_is_expanded);
gmc_hfinfo = g_mem_chunk_new("gmc_hfinfo",
sizeof(struct header_field_info), 50 * sizeof(struct
header_field_info), G_ALLOC_ONLY);
gmc_field_info = g_mem_chunk_new("gmc_field_info",
sizeof(struct field_info), 200 * sizeof(struct field_info),
G_ALLOC_AND_FREE);
gmc_item_labels = g_mem_chunk_new("gmc_item_labels",
ITEM_LABEL_LENGTH, 20 * ITEM_LABEL_LENGTH,
G_ALLOC_AND_FREE);
gpa_hfinfo = g_ptr_array_new();
/* Allocate "tree_is_expanded", with one element for ETT_NONE,
and initialize that element to FALSE. */
tree_is_expanded = g_malloc(sizeof (gint));
tree_is_expanded[0] = FALSE;
num_tree_types = 1;
/* Have each dissector register its protocols and fields. */
register_all_protocols();
/* Register one special-case FT_TEXT_ONLY field for use when
converting ethereal to new-style proto_tree. These fields
are merely strings on the GUI tree; they are not filterable */
proto_register_field_array(-1, hf, array_length(hf));
/* We've assigned all the subtree type values; allocate the array
for them, and zero it out. */
tree_is_expanded = g_malloc(num_tree_types*sizeof (gint *));
memset(tree_is_expanded, '\0', num_tree_types*sizeof (gint *));
}
void
proto_cleanup(void)
{
if (gmc_hfinfo)
g_mem_chunk_destroy(gmc_hfinfo);
if (gmc_field_info)
g_mem_chunk_destroy(gmc_field_info);
if (gmc_item_labels)
g_mem_chunk_destroy(gmc_item_labels);
if (gpa_hfinfo)
g_ptr_array_free(gpa_hfinfo, FALSE);
}
/* frees the resources that the dissection a proto_tree uses */
void
proto_tree_free(proto_tree *tree)
{
g_node_traverse((GNode*)tree, G_IN_ORDER, G_TRAVERSE_ALL, -1,
proto_tree_free_node, NULL);
g_node_destroy((GNode*)tree);
}
static gboolean
proto_tree_free_node(GNode *node, gpointer data)
{
field_info *fi = (field_info*) (node->data);
if (fi != NULL) {
if (fi->representation)
g_mem_chunk_free(gmc_item_labels, fi->representation);
if (fi->hfinfo->type == FT_STRING)
g_free(fi->value.string);
else if (fi->hfinfo->type == FT_BYTES)
g_free(fi->value.bytes);
g_mem_chunk_free(gmc_field_info, fi);
}
return FALSE; /* FALSE = do not end traversal of GNode tree */
}
/* Finds a record in the hf_info_records array. */
static struct header_field_info*
find_hfinfo_record(int hfindex)
{
g_assert(hfindex >= 0 && hfindex < gpa_hfinfo->len);
return g_ptr_array_index(gpa_hfinfo, hfindex);
}
proto_item *
proto_tree_add_item(proto_tree *tree, int hfindex, gint start, gint length, ...)
{
proto_item *pi;
va_list ap;
va_start(ap, length);
pi = proto_tree_add_item_value(tree, hfindex, start, length, 0, 1, ap);
va_end(ap);
return pi;
}
proto_item *
proto_tree_add_item_hidden(proto_tree *tree, int hfindex, gint start, gint length, ...)
{
proto_item *pi;
va_list ap;
va_start(ap, length);
pi = proto_tree_add_item_value(tree, hfindex, start, length, 0, 0, ap);
va_end(ap);
return pi;
}
proto_item *
proto_tree_add_item_format(proto_tree *tree, int hfindex, gint start, gint length, ...)
{
proto_item *pi;
va_list ap;
va_start(ap, length);
pi = proto_tree_add_item_value(tree, hfindex, start, length, 1, 1, ap);
va_end(ap);
return pi;
}
proto_item *
proto_tree_add_notext(proto_tree *tree, gint start, gint length, ...)
{
proto_item *pi;
va_list ap;
va_start(ap, length);
pi = proto_tree_add_item_value(tree, hf_text_only, start, length, 0, 1, ap);
va_end(ap);
return pi;
}
proto_item *
proto_tree_add_text(proto_tree *tree, gint start, gint length, ...)
{
proto_item *pi;
va_list ap;
va_start(ap, length);
pi = proto_tree_add_item_value(tree, hf_text_only, start, length, 1, 1, ap);
va_end(ap);
return pi;
}
static proto_item *
proto_tree_add_item_value(proto_tree *tree, int hfindex, gint start,
gint length, int include_format, int visible, va_list ap)
{
proto_item *pi;
field_info *fi;
char *junk, *format;
header_field_info *hfinfo;
if (!tree)
return(NULL);
/* either visibility flag can nullify the other */
visible = proto_tree_is_visible && visible;
fi = g_mem_chunk_alloc(gmc_field_info);
fi->hfinfo = find_hfinfo_record(hfindex);
g_assert(fi->hfinfo != NULL);
fi->start = start;
fi->length = length;
fi->tree_type = ETT_NONE;
fi->visible = visible;
/* for convenience */
hfinfo = fi->hfinfo;
/* from the stdarg man page on Solaris 2.6:
NOTES
It is up to the calling routine to specify in some manner
how many arguments there are, since it is not always possi-
ble to determine the number of arguments from the stack
frame. For example, execl is passed a zero pointer to sig-
nal the end of the list. printf can tell how many arguments
there are by the format. It is non-portable to specify a
second argument of char, short, or float to va_arg, because
arguments seen by the called function are not char, short,
or float. C converts char and short arguments to int and
converts float arguments to double before passing them to a
function.
*/
switch(hfinfo->type) {
case FT_NONE:
junk = va_arg(ap, guint8*);
break;
case FT_BYTES:
/* This g_malloc'ed memory is freed in
proto_tree_free_node() */
fi->value.bytes = (guint8 *)g_malloc(length);
memcpy(fi->value.bytes, va_arg(ap, guint8*), length);
break;
case FT_BOOLEAN:
case FT_UINT8:
case FT_UINT16:
case FT_UINT24:
case FT_UINT32:
case FT_INT8:
case FT_INT16:
case FT_INT24:
case FT_INT32:
fi->value.numeric = va_arg(ap, unsigned int);
if (hfinfo->bitmask) {
/* Mask out irrelevant portions */
fi->value.numeric &= hfinfo->bitmask;
/* Shift bits */
if (hfinfo->bitshift > 0) {
fi->value.numeric >>= hfinfo->bitshift;
}
}
break;
case FT_IPv4:
ipv4_addr_set_net_order_addr(&(fi->value.ipv4), va_arg(ap, unsigned int));
ipv4_addr_set_netmask_bits(&(fi->value.ipv4), 32);
break;
case FT_IPXNET:
fi->value.numeric = va_arg(ap, unsigned int);
break;
case FT_IPv6:
memcpy(fi->value.ipv6, va_arg(ap, guint8*), 16);
break;
case FT_DOUBLE:
fi->value.floating = va_arg(ap, double);
break;
case FT_ETHER:
memcpy(fi->value.ether, va_arg(ap, guint8*), 6);
break;
case FT_ABSOLUTE_TIME:
case FT_RELATIVE_TIME:
memcpy(&fi->value.time, va_arg(ap, struct timeval*),
sizeof(struct timeval));
break;
case FT_STRING:
/* This g_strdup'ed memory is freed in proto_tree_free_node() */
fi->value.string = g_strdup(va_arg(ap, char*));
break;
case FT_TEXT_ONLY:
; /* nothing */
break;
default:
g_error("hfinfo->type %d not handled\n", hfinfo->type);
break;
}
pi = (proto_item*) g_node_new(fi);
g_node_append((GNode*)tree, (GNode*)pi);
/* are there any formatting arguments? */
if (visible && include_format) {
format = va_arg(ap, char*);
fi->representation = g_mem_chunk_alloc(gmc_item_labels);
vsnprintf(fi->representation, ITEM_LABEL_LENGTH,
format, ap);
}
else {
fi->representation = NULL;
}
return pi;
}
void
proto_item_set_text(proto_item *pi, ...)
{
field_info *fi = (field_info*) (((GNode*)pi)->data);
va_list ap;
char *format;
if (fi->representation)
g_mem_chunk_free(gmc_item_labels, fi->representation);
fi->representation = g_mem_chunk_alloc(gmc_item_labels);
va_start(ap, pi);
format = va_arg(ap, char*);
vsnprintf(fi->representation, ITEM_LABEL_LENGTH,
format, ap);
va_end(ap);
}
void
proto_item_set_len(proto_item *pi, gint length)
{
field_info *fi = (field_info*) (((GNode*)pi)->data);
fi->length = length;
}
proto_tree*
proto_tree_create_root(void)
{
return (proto_tree*) g_node_new(NULL);
}
proto_tree*
proto_item_add_subtree(proto_item *pi, gint idx) {
field_info *fi = (field_info*) (((GNode*)pi)->data);
g_assert(idx >= 0 && idx < num_tree_types);
fi->tree_type = idx;
return (proto_tree*) pi;
}
int
proto_register_protocol(char *name, char *abbrev)
{
struct header_field_info *hfinfo;
/* Here we do allocate a new header_field_info struct */
hfinfo = g_mem_chunk_alloc(gmc_hfinfo);
hfinfo->name = name;
hfinfo->abbrev = abbrev;
hfinfo->type = FT_NONE;
hfinfo->strings = NULL;
hfinfo->bitmask = 0;
hfinfo->bitshift = 0;
hfinfo->blurb = "";
hfinfo->parent = -1; /* this field differentiates protos and fields */
return proto_register_field_init(hfinfo, hfinfo->parent);
}
/* for use with static arrays only, since we don't allocate our own copies
of the header_field_info struct contained withing the hf_register_info struct */
void
proto_register_field_array(int parent, hf_register_info *hf, int num_records)
{
int field_id, i;
hf_register_info *ptr = hf;
for (i = 0; i < num_records; i++, ptr++) {
field_id = proto_register_field_init(&ptr->hfinfo, parent);
*ptr->p_id = field_id;
}
}
static int
proto_register_field_init(header_field_info *hfinfo, int parent)
{
/* These types of fields are allowed to have value_strings or true_false_strings */
g_assert((hfinfo->strings == NULL) || (
(hfinfo->type == FT_UINT8) ||
(hfinfo->type == FT_UINT16) ||
(hfinfo->type == FT_UINT24) ||
(hfinfo->type == FT_UINT32) ||
(hfinfo->type == FT_INT8) ||
(hfinfo->type == FT_INT16) ||
(hfinfo->type == FT_INT24) ||
(hfinfo->type == FT_INT32) ||
(hfinfo->type == FT_BOOLEAN) ));
/* if this is a bitfield, compure bitshift */
if (hfinfo->bitmask) {
while ((hfinfo->bitmask & (1 << hfinfo->bitshift)) == 0)
hfinfo->bitshift++;
}
hfinfo->parent = parent;
/* if we always add and never delete, then id == len - 1 is correct */
g_ptr_array_add(gpa_hfinfo, hfinfo);
hfinfo->id = gpa_hfinfo->len - 1;
return hfinfo->id;
}
void
proto_register_subtree_array(gint **indices, int num_indices)
{
int i;
gint **ptr = indices;
/*
* Add "num_indices" elements to "tree_is_expanded".
*/
tree_is_expanded = g_realloc(tree_is_expanded,
(num_tree_types + num_indices)*sizeof (gint));
/*
* Assign "num_indices" subtree numbers starting at "num_tree_types",
* returning the indices through the pointers in the array whose
* first element is pointed to by "indices", set to FALSE the
* elements to which those subtree numbers refer, and update
* "num_tree_types" appropriately.
*/
for (i = 0; i < num_indices; i++, ptr++, num_tree_types++) {
tree_is_expanded[num_tree_types] = FALSE;
**ptr = num_tree_types;
}
}
void
proto_item_fill_label(field_info *fi, gchar *label_str)
{
struct header_field_info *hfinfo = fi->hfinfo;
guint32 n_addr; /* network-order IPv4 address */
switch(hfinfo->type) {
case FT_NONE:
snprintf(label_str, ITEM_LABEL_LENGTH,
"%s", hfinfo->name);
break;
case FT_BOOLEAN:
fill_label_boolean(fi, label_str);
break;
case FT_BYTES:
snprintf(label_str, ITEM_LABEL_LENGTH,
"%s: %s", hfinfo->name,
bytes_to_str(fi->value.bytes, fi->length));
break;
/* Four types of integers to take care of:
* Bitfield, with val_string
* Bitfield, w/o val_string
* Non-bitfield, with val_string
* Non-bitfield, w/o val_string
*/
case FT_UINT8:
case FT_UINT16:
case FT_UINT24:
case FT_UINT32:
if (hfinfo->bitmask) {
if (hfinfo->strings) {
fill_label_enumerated_bitfield(fi, label_str);
}
else {
fill_label_numeric_bitfield(fi, label_str);
}
}
else {
if (hfinfo->strings) {
fill_label_enumerated_uint(fi, label_str);
}
else {
fill_label_uint(fi, label_str);
}
}
break;
case FT_INT8:
case FT_INT16:
case FT_INT24:
case FT_INT32:
g_assert(!hfinfo->bitmask);
if (hfinfo->strings) {
fill_label_enumerated_int(fi, label_str);
}
else {
fill_label_int(fi, label_str);
}
break;
case FT_DOUBLE:
snprintf(label_str, ITEM_LABEL_LENGTH,
"%s: %g", fi->hfinfo->name,
fi->value.floating);
break;
case FT_ABSOLUTE_TIME:
snprintf(label_str, ITEM_LABEL_LENGTH,
"%s: %s", fi->hfinfo->name,
abs_time_to_str(&fi->value.time));
break;
case FT_RELATIVE_TIME:
snprintf(label_str, ITEM_LABEL_LENGTH,
"%s: %s seconds", fi->hfinfo->name,
rel_time_to_str(&fi->value.time));
break;
case FT_IPXNET:
snprintf(label_str, ITEM_LABEL_LENGTH,
"%s: 0x%08X (%s)", fi->hfinfo->name,
fi->value.numeric, get_ipxnet_name(fi->value.numeric));
break;
case FT_ETHER:
snprintf(label_str, ITEM_LABEL_LENGTH,
"%s: %s (%s)", fi->hfinfo->name,
ether_to_str(fi->value.ether),
get_ether_name(fi->value.ether));
break;
case FT_IPv4:
n_addr = ipv4_get_net_order_addr(&fi->value.ipv4);
snprintf(label_str, ITEM_LABEL_LENGTH,
"%s: %s (%s)", fi->hfinfo->name,
get_hostname(n_addr),
ip_to_str((guint8*)&n_addr));
break;
case FT_IPv6:
snprintf(label_str, ITEM_LABEL_LENGTH,
"%s: %s (%s)", fi->hfinfo->name,
get_hostname6((struct e_in6_addr *)fi->value.ipv6),
ip6_to_str((struct e_in6_addr*)fi->value.ipv6));
break;
case FT_STRING:
snprintf(label_str, ITEM_LABEL_LENGTH,
"%s: %s", fi->hfinfo->name, fi->value.string);
break;
default:
g_error("hfinfo->type %d not handled\n", fi->hfinfo->type);
break;
}
}
static void
fill_label_boolean(field_info *fi, gchar *label_str)
{
char *p = label_str;
int bitfield_byte_length = 0, bitwidth;
guint32 unshifted_value;
struct header_field_info *hfinfo = fi->hfinfo;
struct true_false_string default_tf = { "True", "False" };
struct true_false_string *tfstring = &default_tf;
if (hfinfo->strings) {
tfstring = (struct true_false_string*) hfinfo->strings;
}
if (hfinfo->bitmask) {
/* Figure out the bit width */
bitwidth = hfinfo_bitwidth(hfinfo);
/* Un-shift bits */
unshifted_value = fi->value.numeric;
if (hfinfo->bitshift > 0) {
unshifted_value <<= hfinfo->bitshift;
}
/* Create the bitfield first */
p = decode_bitfield_value(label_str, unshifted_value, hfinfo->bitmask, bitwidth);
bitfield_byte_length = p - label_str;
}
/* Fill in the textual info */
snprintf(p, ITEM_LABEL_LENGTH - bitfield_byte_length,
"%s: %s", hfinfo->name,
fi->value.numeric ? tfstring->true_string : tfstring->false_string);
}
/* Fills data for bitfield ints with val_strings */
static void
fill_label_enumerated_bitfield(field_info *fi, gchar *label_str)
{
char *format = NULL, *p;
int bitfield_byte_length, bitwidth;
guint32 unshifted_value;
struct header_field_info *hfinfo = fi->hfinfo;
/* Figure out the bit width */
bitwidth = hfinfo_bitwidth(hfinfo);
/* Pick the proper format string */
format = hfinfo_uint_vals_format(hfinfo);
/* Un-shift bits */
unshifted_value = fi->value.numeric;
if (hfinfo->bitshift > 0) {
unshifted_value <<= hfinfo->bitshift;
}
/* Create the bitfield first */
p = decode_bitfield_value(label_str, unshifted_value, hfinfo->bitmask, bitwidth);
bitfield_byte_length = p - label_str;
/* Fill in the textual info using stored (shifted) value */
snprintf(p, ITEM_LABEL_LENGTH - bitfield_byte_length,
format, hfinfo->name,
val_to_str(fi->value.numeric, cVALS(hfinfo->strings), "Unknown"),
fi->value.numeric);
}
static void
fill_label_numeric_bitfield(field_info *fi, gchar *label_str)
{
char *format = NULL, *p;
int bitfield_byte_length, bitwidth;
guint32 unshifted_value;
struct header_field_info *hfinfo = fi->hfinfo;
/* Figure out the bit width */
bitwidth = hfinfo_bitwidth(hfinfo);
/* Pick the proper format string */
format = hfinfo_uint_format(hfinfo);
/* Un-shift bits */
unshifted_value = fi->value.numeric;
if (hfinfo->bitshift > 0) {
unshifted_value <<= hfinfo->bitshift;
}
/* Create the bitfield using */
p = decode_bitfield_value(label_str, unshifted_value, hfinfo->bitmask, bitwidth);
bitfield_byte_length = p - label_str;
/* Fill in the textual info using stored (shifted) value */
snprintf(p, ITEM_LABEL_LENGTH - bitfield_byte_length,
format, hfinfo->name, fi->value.numeric);
}
static void
fill_label_enumerated_uint(field_info *fi, gchar *label_str)
{
char *format = NULL;
struct header_field_info *hfinfo = fi->hfinfo;
/* Pick the proper format string */
format = hfinfo_uint_vals_format(hfinfo);
/* Fill in the textual info */
snprintf(label_str, ITEM_LABEL_LENGTH,
format, hfinfo->name,
val_to_str(fi->value.numeric, cVALS(hfinfo->strings), "Unknown"),
fi->value.numeric);
}
static void
fill_label_uint(field_info *fi, gchar *label_str)
{
char *format = NULL;
struct header_field_info *hfinfo = fi->hfinfo;
/* Pick the proper format string */
format = hfinfo_uint_format(hfinfo);
/* Fill in the textual info */
snprintf(label_str, ITEM_LABEL_LENGTH,
format, hfinfo->name, fi->value.numeric);
}
static void
fill_label_enumerated_int(field_info *fi, gchar *label_str)
{
char *format = NULL;
struct header_field_info *hfinfo = fi->hfinfo;
/* Pick the proper format string */
format = hfinfo_int_vals_format(hfinfo);
/* Fill in the textual info */
snprintf(label_str, ITEM_LABEL_LENGTH,
format, hfinfo->name,
val_to_str(fi->value.numeric, cVALS(hfinfo->strings), "Unknown"),
fi->value.numeric);
}
static void
fill_label_int(field_info *fi, gchar *label_str)
{
char *format = NULL;
struct header_field_info *hfinfo = fi->hfinfo;
/* Pick the proper format string */
format = hfinfo_int_format(hfinfo);
/* Fill in the textual info */
snprintf(label_str, ITEM_LABEL_LENGTH,
format, hfinfo->name, fi->value.numeric);
}
static int
hfinfo_bitwidth(header_field_info *hfinfo)
{
int bitwidth = 0;
if (!hfinfo->bitmask) {
return 0;
}
switch(hfinfo->type) {
case FT_UINT8:
case FT_INT8:
bitwidth = 8;
break;
case FT_UINT16:
case FT_INT16:
bitwidth = 16;
break;
case FT_UINT24:
case FT_INT24:
bitwidth = 24;
break;
case FT_UINT32:
case FT_INT32:
bitwidth = 32;
break;
case FT_BOOLEAN:
bitwidth = hfinfo->display; /* hacky? :) */
break;
default:
g_assert_not_reached();
;
}
return bitwidth;
}
static char*
hfinfo_uint_vals_format(header_field_info *hfinfo)
{
char *format = NULL;
switch(hfinfo->display) {
case BASE_DEC:
case BASE_NONE:
case BASE_OCT: /* I'm lazy */
case BASE_BIN: /* I'm lazy */
format = "%s: %s (%u)";
break;
case BASE_HEX:
switch(hfinfo->type) {
case FT_UINT8:
format = "%s: %s (0x%02x)";
break;
case FT_UINT16:
format = "%s: %s (0x%04x)";
break;
case FT_UINT24:
format = "%s: %s (0x%06x)";
break;
case FT_UINT32:
format = "%s: %s (0x%08x)";
break;
default:
g_assert_not_reached();
;
}
break;
default:
g_assert_not_reached();
;
}
return format;
}
static char*
hfinfo_uint_format(header_field_info *hfinfo)
{
char *format = NULL;
/* Pick the proper format string */
switch(hfinfo->display) {
case BASE_DEC:
case BASE_NONE:
case BASE_OCT: /* I'm lazy */
case BASE_BIN: /* I'm lazy */
format = "%s: %u";
break;
case BASE_HEX:
switch(hfinfo->type) {
case FT_UINT8:
format = "%s: 0x%02x";
break;
case FT_UINT16:
format = "%s: 0x%04x";
break;
case FT_UINT24:
format = "%s: 0x%06x";
break;
case FT_UINT32:
format = "%s: 0x%08x";
break;
default:
g_assert_not_reached();
;
}
break;
default:
g_assert_not_reached();
;
}
return format;
}
static char*
hfinfo_int_vals_format(header_field_info *hfinfo)
{
char *format = NULL;
switch(hfinfo->display) {
case BASE_DEC:
case BASE_NONE:
case BASE_OCT: /* I'm lazy */
case BASE_BIN: /* I'm lazy */
format = "%s: %s (%d)";
break;
case BASE_HEX:
switch(hfinfo->type) {
case FT_INT8:
format = "%s: %s (0x%02x)";
break;
case FT_INT16:
format = "%s: %s (0x%04x)";
break;
case FT_INT24:
format = "%s: %s (0x%06x)";
break;
case FT_INT32:
format = "%s: %s (0x%08x)";
break;
default:
g_assert_not_reached();
;
}
break;
default:
g_assert_not_reached();
;
}
return format;
}
static char*
hfinfo_int_format(header_field_info *hfinfo)
{
char *format = NULL;
/* Pick the proper format string */
switch(hfinfo->display) {
case BASE_DEC:
case BASE_NONE:
case BASE_OCT: /* I'm lazy */
case BASE_BIN: /* I'm lazy */
format = "%s: %d";
break;
case BASE_HEX:
switch(hfinfo->type) {
case FT_INT8:
format = "%s: 0x%02x";
break;
case FT_INT16:
format = "%s: 0x%04x";
break;
case FT_INT24:
format = "%s: 0x%06x";
break;
case FT_INT32:
format = "%s: 0x%08x";
break;
default:
g_assert_not_reached();
;
}
break;
default:
g_assert_not_reached();
;
}
return format;
}
int
proto_registrar_n(void)
{
return gpa_hfinfo->len;
}
char*
proto_registrar_get_name(int n)
{
struct header_field_info *hfinfo;
hfinfo = find_hfinfo_record(n);
if (hfinfo)
return hfinfo->name;
else return NULL;
}
char*
proto_registrar_get_abbrev(int n)
{
struct header_field_info *hfinfo;
hfinfo = find_hfinfo_record(n);
if (hfinfo)
return hfinfo->abbrev;
else
return NULL;
}
int
proto_registrar_get_ftype(int n)
{
struct header_field_info *hfinfo;
hfinfo = find_hfinfo_record(n);
if (hfinfo)
return hfinfo->type;
else
return -1;
}
int
proto_registrar_get_parent(int n)
{
struct header_field_info *hfinfo;
hfinfo = find_hfinfo_record(n);
if (hfinfo)
return hfinfo->parent;
else
return -2;
}
gboolean
proto_registrar_is_protocol(int n)
{
struct header_field_info *hfinfo;
hfinfo = find_hfinfo_record(n);
if (hfinfo)
return (hfinfo->parent == -1 ? TRUE : FALSE);
else
return FALSE;
}
/* Returns length of field in packet (not necessarily the length
* in our internal representation, as in the case of IPv4).
* 0 means undeterminable at time of registration
* -1 means the field is not registered. */
gint
proto_registrar_get_length(int n)
{
struct header_field_info *hfinfo;
hfinfo = find_hfinfo_record(n);
if (!hfinfo)
return -1;
switch (hfinfo->type) {
case FT_TEXT_ONLY: /* not filterable */
case NUM_FIELD_TYPES: /* satisfy picky compilers */
return -1;
case FT_NONE:
case FT_BYTES:
case FT_BOOLEAN:
case FT_STRING:
case FT_DOUBLE:
case FT_ABSOLUTE_TIME:
case FT_RELATIVE_TIME:
return 0;
case FT_UINT8:
case FT_INT8:
return 1;
case FT_UINT16:
case FT_INT16:
return 2;
case FT_UINT24:
case FT_INT24:
return 3;
case FT_UINT32:
case FT_INT32:
case FT_IPXNET:
case FT_IPv4:
return 4;
case FT_ETHER:
return 6;
case FT_IPv6:
return 16;
}
g_assert_not_reached();
return -1;
}
/* Looks for a protocol or a field in a proto_tree. Returns TRUE if
* it exists anywhere, or FALSE if it exists nowhere. */
gboolean
proto_check_for_protocol_or_field(proto_tree* tree, int id)
{
proto_tree_search_info sinfo;
sinfo.target = id;
sinfo.result.node = NULL;
sinfo.parent = -1;
sinfo.traverse_func = NULL;
/* do a quicker check if target is a protocol */
if (proto_registrar_is_protocol(id) == TRUE) {
proto_find_protocol_multi(tree, id, &check_for_protocol_or_field_id, &sinfo);
}
else {
/* find the field's parent protocol */
sinfo.parent = proto_registrar_get_parent(id);
/* Go through each protocol subtree, checking if the protocols
* is the parent protocol of the field that we're looking for.
* We may have protocols that occur more than once (e.g., IP in IP),
* so we do indeed have to check all protocol subtrees, looking
* for the parent protocol. That's why proto_find_protocol()
* is not used --- it assumes a protocol occurs only once. */
g_node_traverse((GNode*)tree, G_IN_ORDER, G_TRAVERSE_ALL, 2,
check_for_field_within_protocol, &sinfo);
}
if (sinfo.result.node)
return TRUE;
else
return FALSE;
}
static gboolean
check_for_protocol_or_field_id(GNode *node, gpointer data)
{
field_info *fi = (field_info*) (node->data);
proto_tree_search_info *sinfo = (proto_tree_search_info*) data;
if (fi) { /* !fi == the top most container node which holds nothing */
if (fi->hfinfo->id == sinfo->target) {
sinfo->result.node = node;
return TRUE; /* halt traversal */
}
}
return FALSE; /* keep traversing */
}
static gboolean
check_for_field_within_protocol(GNode *node, gpointer data)
{
field_info *fi = (field_info*) (node->data);
proto_tree_search_info *sinfo = (proto_tree_search_info*) data;
if (fi) { /* !fi == the top most container node which holds nothing */
if (fi->hfinfo->id == sinfo->parent) {
g_node_traverse(node, G_IN_ORDER, G_TRAVERSE_ALL, -1,
check_for_protocol_or_field_id, sinfo);
if (sinfo->result.node)
return TRUE; /* halt traversal */
}
}
return FALSE; /* keep traversing */
}
/* Looks for a protocol at the top layer of the tree. The protocol can occur
* more than once, for those encapsulated protocols. For each protocol subtree
* that is found, the callback function is called.
*/
void
proto_find_protocol_multi(proto_tree* tree, int target, GNodeTraverseFunc callback,
proto_tree_search_info *sinfo)
{
g_assert(callback != NULL);
g_node_traverse((GNode*)tree, G_IN_ORDER, G_TRAVERSE_ALL, 2, callback, (gpointer*)sinfo);
}
/* Simple wrappter to traverse all nodes, calling the sinfo traverse function with sinfo as an arg */
gboolean
proto_get_field_values(proto_tree* subtree, proto_tree_search_info *sinfo)
{
/* Don't try to check value of top-level NULL GNode */
if (!((GNode*)subtree)->data) {
return FALSE; /* don't halt */
}
g_node_traverse((GNode*)subtree, G_IN_ORDER, G_TRAVERSE_ALL, -1, sinfo->traverse_func, (gpointer*)sinfo);
return FALSE; /* don't halt */
}
/* Dumps the contents of the registration database to stdout. An indepedent program can take
* this output and format it into nice tables or HTML or whatever.
*
* There is one record per line. Each record is either a protocol or a header
* field, differentiated by the first field. The fields are tab-delimited.
*
* Protocols
* ---------
* Field 1 = 'P'
* Field 2 = protocol name
* Field 3 = protocol abbreviation
*
* Header Fields
* -------------
* Field 1 = 'F'
* Field 2 = field name
* Field 3 = field abbreviation
* Field 4 = type ( textual representation of the the ftenum type )
* Field 5 = parent protocol abbreviation
*/
void
proto_registrar_dump(void)
{
header_field_info *hfinfo, *parent_hfinfo;
int i, len;
const char *enum_name;
len = gpa_hfinfo->len;
for (i = 0; i < len ; i++) {
hfinfo = find_hfinfo_record(i);
/* format for protocols */
if (proto_registrar_is_protocol(i)) {
printf("P\t%s\t%s\n", hfinfo->name, hfinfo->abbrev);
}
/* format for header fields */
else {
parent_hfinfo = find_hfinfo_record(hfinfo->parent);
g_assert(parent_hfinfo);
switch(hfinfo->type) {
case FT_NONE:
enum_name = "FT_NONE";
break;
case FT_BOOLEAN:
enum_name = "FT_BOOLEAN";
break;
case FT_UINT8:
enum_name = "FT_UINT8";
break;
case FT_UINT16:
enum_name = "FT_UINT16";
break;
case FT_UINT24:
enum_name = "FT_UINT24";
break;
case FT_UINT32:
enum_name = "FT_UINT32";
break;
case FT_INT8:
enum_name = "FT_INT8";
break;
case FT_INT16:
enum_name = "FT_INT16";
break;
case FT_INT24:
enum_name = "FT_INT24";
break;
case FT_INT32:
enum_name = "FT_INT32";
break;
case FT_DOUBLE:
enum_name = "FT_DOUBLE";
break;
case FT_ABSOLUTE_TIME:
enum_name = "FT_ABSOLUTE_TIME";
break;
case FT_RELATIVE_TIME:
enum_name = "FT_RELATIVE_TIME";
break;
case FT_STRING:
enum_name = "FT_STRING";
break;
case FT_ETHER:
enum_name = "FT_ETHER";
break;
case FT_BYTES:
enum_name = "FT_BYTES";
break;
case FT_IPv4:
enum_name = "FT_IPv4";
break;
case FT_IPv6:
enum_name = "FT_IPv6";
break;
case FT_IPXNET:
enum_name = "FT_IPXNET";
break;
case FT_TEXT_ONLY:
enum_name = "FT_TEXT_ONLY";
break;
default:
enum_name = "UNKNOWN";
break;
}
printf("F\t%s\t%s\t%s\t%s\n", hfinfo->name, hfinfo->abbrev,
enum_name,parent_hfinfo->abbrev);
}
}
}