diff options
author | Gilbert Ramirez <gram@alumni.rice.edu> | 1999-07-07 22:52:57 +0000 |
---|---|---|
committer | Gilbert Ramirez <gram@alumni.rice.edu> | 1999-07-07 22:52:57 +0000 |
commit | 07f42b5b31f9523deeb147226521f77a2c8dd797 (patch) | |
tree | c720d2a42f11c9d14ac1a161eb785b438c2c4b14 /proto.c | |
parent | fba49cfe85d4b23ebbffa97fae126a379e913ecd (diff) |
Created a new protocol tree implementation and a new display filter
mechanism that is built into ethereal. Wiretap is now used to read all
file formats. Libpcap is used only for capturing.
svn path=/trunk/; revision=342
Diffstat (limited to 'proto.c')
-rw-r--r-- | proto.c | 620 |
1 files changed, 620 insertions, 0 deletions
diff --git a/proto.c b/proto.c new file mode 100644 index 0000000000..7bdd2f54b4 --- /dev/null +++ b/proto.c @@ -0,0 +1,620 @@ +/* proto.c + * Routines for protocol tree + * + * $Id: proto.c,v 1.1 1999/07/07 22:51:59 gram Exp $ + * + * Ethereal - Network traffic analyzer + * By Gerald Combs <gerald@zing.org> + * Copyright 1998 Gerald Combs + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif + +#ifndef _STDIO_H +#include <stdio.h> +#endif + +#include <stdarg.h> + +#ifndef _STRING_H +#include <string.h> +#endif + +#ifdef NEED_SNPRINTF_H +# include "snprintf.h" +#endif + +#ifndef __G_LIB_H__ +#include <glib.h> +#endif + +#ifndef __PROTO_H__ +#include "proto.h" +#endif + +#ifndef __PACKET_H__ +#include "packet.h" +#endif + +#ifndef __RESOLV_H__ +#include "resolv.h" +#endif + +#define cVALS(x) (const value_string*)(x) + +static void +proto_tree_free_node(GNode *node, gpointer data); + +static struct header_field_info* +find_hfinfo_record(int hfindex); + +static proto_item * +proto_tree_add_item_value(proto_tree *tree, int hfindex, gint start, + gint length, int include_format, int visible, va_list ap); + +static gboolean proto_check_id(GNode *node, gpointer data); + +void dfilter_yacc_init(void); + +/* centralization of registration functions */ +void proto_register_data(void); +void proto_register_eth(void); +void proto_register_fddi(void); +void proto_register_frame(void); +void proto_register_ip(void); +void proto_register_llc(void); +void proto_register_null(void); +void proto_register_tr(void); + +int hf_text_only = 1; + +/* Contains information about protocols and header fields. Used when + * dissectors register their data */ +GMemChunk *gmc_hfinfo = NULL; + +/* Contains information about a field when a dissector calls + * proto_tree_add_item. */ +GMemChunk *gmc_field_info = NULL; + +/* String space for protocol and field items for the GUI */ +GMemChunk *gmc_item_labels = NULL; + +/* List which stores protocols and fields that have been registered */ +GPtrArray *gpa_hfinfo = NULL; + + +/* initialize data structures and register protocols and fields */ +void +proto_init(void) +{ + if (gmc_hfinfo) + g_mem_chunk_destroy(gmc_hfinfo); + if (gmc_field_info) + g_mem_chunk_destroy(gmc_field_info); + if (gmc_item_labels) + g_mem_chunk_destroy(gmc_item_labels); + if (gpa_hfinfo) + g_ptr_array_free(gpa_hfinfo, FALSE); /* ever needs to be TRUE? */ + + gmc_hfinfo = g_mem_chunk_new("gmc_hfinfo", + sizeof(struct header_field_info), 50 * sizeof(struct + header_field_info), G_ALLOC_ONLY); + gmc_field_info = g_mem_chunk_new("gmc_field_info", + sizeof(struct field_info), 200 * sizeof(struct field_info), + G_ALLOC_AND_FREE); + gmc_item_labels = g_mem_chunk_new("gmc_item_labels", + ITEM_LABEL_LENGTH, 20 * ITEM_LABEL_LENGTH, + G_ALLOC_AND_FREE); + gpa_hfinfo = g_ptr_array_new(); + + /* Have each dissector register its protocols and fields. The + * order doesn't matter. Put the calls in alphabetical order + * just to make it easy. */ + proto_register_data(); + proto_register_eth(); + proto_register_fddi(); + proto_register_frame(); + proto_register_ip(); + proto_register_llc(); + proto_register_null(); + proto_register_tr(); + + /* Register one special-case FT_TEXT_ONLY field for use when + converting ethereal to new-style proto_tree. These fields + are merely strings on the GUI tree; they are not filterable */ + hf_text_only = proto_register_field ( + /* name */ "Text", + /* abbrev */ "text", + /* ftype */ FT_TEXT_ONLY, + /* parent */ -1, + /* vals[] */ NULL ); + + dfilter_yacc_init(); +} + +/* frees the resources that the dissection a proto_tree uses */ +void +proto_tree_free(proto_tree *tree) +{ + g_node_traverse((GNode*)tree, G_IN_ORDER, G_TRAVERSE_ALL, -1, + (GNodeTraverseFunc)proto_tree_free_node, NULL); +} + +static void +proto_tree_free_node(GNode *node, gpointer data) +{ + field_info *fi = (field_info*) (node->data); + if (fi->representation) + g_mem_chunk_free(gmc_item_labels, fi->representation); + if (fi->hfinfo->type == FT_STRING) + g_free(fi->value.string); + g_mem_chunk_free(gmc_field_info, fi); +} + +/* Finds a record in the hf_info_records array. */ +static struct header_field_info* +find_hfinfo_record(int hfindex) +{ + g_assert(hfindex >= 0 && hfindex < gpa_hfinfo->len); + return g_ptr_array_index(gpa_hfinfo, hfindex); +} + +proto_item * +proto_tree_add_item(proto_tree *tree, int hfindex, gint start, gint length, ...) +{ + proto_item *pi; + va_list ap; + + va_start(ap, length); + pi = proto_tree_add_item_value(tree, hfindex, start, length, 0, 1, ap); + va_end(ap); + + return pi; +} + +proto_item * +proto_tree_add_item_hidden(proto_tree *tree, int hfindex, gint start, gint length, ...) +{ + proto_item *pi; + va_list ap; + + va_start(ap, length); + pi = proto_tree_add_item_value(tree, hfindex, start, length, 0, 0, ap); + va_end(ap); + + return pi; +} + +proto_item * +proto_tree_add_item_format(proto_tree *tree, int hfindex, gint start, gint length, ...) +{ + proto_item *pi; + va_list ap; + + va_start(ap, length); + pi = proto_tree_add_item_value(tree, hfindex, start, length, 1, 1, ap); + va_end(ap); + + return pi; +} + +proto_item * +proto_tree_add_text(proto_tree *tree, gint start, gint length, ...) +{ + proto_item *pi; + va_list ap; + + va_start(ap, length); + pi = proto_tree_add_item_value(tree, hf_text_only, start, length, 1, 1, ap); + va_end(ap); + + return pi; +} + +static proto_item * +proto_tree_add_item_value(proto_tree *tree, int hfindex, gint start, + gint length, int include_format, int visible, va_list ap) +{ + proto_item *pi; + field_info *fi; + char *junk, *format; + + if (!tree) + return(NULL); + + fi = g_mem_chunk_alloc(gmc_field_info); + + fi->hfinfo = find_hfinfo_record(hfindex); + g_assert(fi->hfinfo != NULL); + fi->start = start; + fi->length = length; + fi->tree_type = ETT_NONE; + fi->visible = visible; + +/* from the stdarg man page on Solaris 2.6: +NOTES + It is up to the calling routine to specify in some manner + how many arguments there are, since it is not always possi- + ble to determine the number of arguments from the stack + frame. For example, execl is passed a zero pointer to sig- + nal the end of the list. printf can tell how many arguments + there are by the format. It is non-portable to specify a + second argument of char, short, or float to va_arg, because + arguments seen by the called function are not char, short, + or float. C converts char and short arguments to int and + converts float arguments to double before passing them to a + function. +*/ + switch(fi->hfinfo->type) { + case FT_NONE: + junk = va_arg(ap, guint8*); + break; + + case FT_BOOLEAN: + fi->value.boolean = va_arg(ap, unsigned int) ? TRUE : FALSE; + break; + + case FT_UINT8: + case FT_VALS_UINT8: + fi->value.numeric = va_arg(ap, unsigned int); + break; + + case FT_UINT16: + case FT_VALS_UINT16: + fi->value.numeric = va_arg(ap, unsigned int); + break; + + case FT_UINT32: + case FT_VALS_UINT24: + case FT_VALS_UINT32: + case FT_RELATIVE_TIME: + case FT_IPv4: + case FT_IPXSERVER: + fi->value.numeric = va_arg(ap, guint32); + break; + + case FT_ETHER: + case FT_ETHER_VENDOR: +/* fi->value.ether = va_arg(ap, guint8*);*/ + memcpy(fi->value.ether, va_arg(ap, guint8*), 6); + break; + + case FT_ABSOLUTE_TIME: + memcpy(&fi->value.abs_time, va_arg(ap, struct timeval*), + sizeof(struct timeval)); + break; + + case FT_STRING: + fi->value.string = g_strdup(va_arg(ap, char*)); /* XXX */ + break; + + case FT_TEXT_ONLY: + ; /* nothing */ + break; + + default: + g_error("hfinfo->type %d not handled\n", fi->hfinfo->type); + break; + } + + pi = (proto_item*) g_node_new(fi); + g_node_append((GNode*)tree, (GNode*)pi); + + /* are there any formatting arguments? */ + if (visible && include_format) { + fi->representation = g_mem_chunk_alloc(gmc_item_labels); + format = va_arg(ap, char*); + vsnprintf(fi->representation, ITEM_LABEL_LENGTH, + format, ap); + } + else { + fi->representation = NULL; + } + + return pi; +} + +void +proto_item_set_len(proto_item *pi, gint length) +{ + field_info *fi = (field_info*) (((GNode*)pi)->data); + fi->length = length; +} + +proto_tree* +proto_tree_create_root(void) +{ + return (proto_tree*) g_node_new(NULL); +} + +proto_tree* +proto_item_add_subtree(proto_item *pi, gint idx) { + field_info *fi = (field_info*) (((GNode*)pi)->data); + fi->tree_type = idx; + return (proto_tree*) pi; +} + + +int +proto_register_protocol(char *name, char *abbrev) +{ + return proto_register_field(name, abbrev, FT_NONE, -1, NULL); +} + +void +proto_register_field_array(int parent, const hf_register_info *hf, int num_records) +{ + int field_id, i; + const hf_register_info *ptr = hf; + + for (i = 0; i < num_records; i++, ptr++) { + field_id = proto_register_field(ptr->name, ptr->abbrev, + ptr->type, parent, ptr->vals); + *ptr->p_id = field_id; + } +} + + +int +proto_register_field(char *name, char *abbrev, enum ftenum type, int parent, + struct value_string* vals) +{ + struct header_field_info *hfinfo; + + hfinfo = g_mem_chunk_alloc(gmc_hfinfo); + hfinfo->name = name; /* should I g_strdup? */ + hfinfo->abbrev = abbrev; /* should I g_strdup? */ + hfinfo->type = type; + hfinfo->parent = parent; /* this field differentiates protos and fields */ + hfinfo->vals = vals; + + g_assert((vals == NULL) || (type == FT_VALS_UINT8 || type == FT_VALS_UINT16 || + type == FT_VALS_UINT24 || type == FT_VALS_UINT32)); + + /* if we always add and never delete, then id == len - 1 is correct */ + g_ptr_array_add(gpa_hfinfo, hfinfo); + hfinfo->id = gpa_hfinfo->len - 1; + return hfinfo->id; +} + +void +proto_item_fill_label(field_info *fi, gchar *label_str) +{ + char *s; + + switch(fi->hfinfo->type) { + case FT_NONE: + snprintf(label_str, ITEM_LABEL_LENGTH, + "%s", fi->hfinfo->name); + break; + + case FT_BOOLEAN: + snprintf(label_str, ITEM_LABEL_LENGTH, + "%s: %s", fi->hfinfo->name, + fi->value.boolean == TRUE ? "True" : "False"); + break; + + case FT_UINT8: + case FT_UINT16: + case FT_UINT32: + snprintf(label_str, ITEM_LABEL_LENGTH, + "%s: %d", fi->hfinfo->name, + fi->value.numeric); + break; + + case FT_ABSOLUTE_TIME: + snprintf(label_str, ITEM_LABEL_LENGTH, + "%s: %s", fi->hfinfo->name, + abs_time_to_str(&fi->value.abs_time)); + break; + + case FT_VALS_UINT8: + s = match_strval(fi->value.numeric, cVALS(fi->hfinfo->vals)); + snprintf(label_str, ITEM_LABEL_LENGTH, + "%s: %s (0x%02x)", fi->hfinfo->name, + (s ? s : "Unknown"), fi->value.numeric); + break; + + case FT_VALS_UINT16: + s = match_strval(fi->value.numeric, cVALS(fi->hfinfo->vals)); + snprintf(label_str, ITEM_LABEL_LENGTH, + "%s: %s (0x%04x)", fi->hfinfo->name, + (s ? s : "Unknown"), fi->value.numeric); + break; + + case FT_VALS_UINT24: + s = match_strval(fi->value.numeric, cVALS(fi->hfinfo->vals)); + snprintf(label_str, ITEM_LABEL_LENGTH, + "%s: %s (0x%06x)", fi->hfinfo->name, + (s ? s : "Unknown"), fi->value.numeric); + break; + + + case FT_VALS_UINT32: + s = match_strval(fi->value.numeric, cVALS(fi->hfinfo->vals)); + snprintf(label_str, ITEM_LABEL_LENGTH, + "%s: %s (0x%08x)", fi->hfinfo->name, + (s ? s : "Unknown"), fi->value.numeric); + break; + + case FT_ETHER: + snprintf(label_str, ITEM_LABEL_LENGTH, + "%s: %s (%s)", fi->hfinfo->name, + ether_to_str(fi->value.ether), + get_ether_name(fi->value.ether)); + break; + + case FT_ETHER_VENDOR: + snprintf(label_str, ITEM_LABEL_LENGTH, + "%s: %02x:%02x:%02x (%s)", fi->hfinfo->name, + fi->value.ether[0], + fi->value.ether[1], + fi->value.ether[2], + get_manuf_name(fi->value.ether)); + break; + + case FT_IPv4: + snprintf(label_str, ITEM_LABEL_LENGTH, + "%s: %s (%s)", fi->hfinfo->name, + get_hostname(fi->value.numeric), + ip_to_str((guint8*)&fi->value.numeric)); + break; + + case FT_STRING: + snprintf(label_str, ITEM_LABEL_LENGTH, + "%s: %s", fi->hfinfo->name, fi->value.string); + break; + + default: + g_error("hfinfo->type %d not handled\n", fi->hfinfo->type); + break; + } +} + +int +proto_registrar_n(void) +{ + return gpa_hfinfo->len; +} + +char* +proto_registrar_get_abbrev(int n) +{ + struct header_field_info *hfinfo; + + hfinfo = find_hfinfo_record(n); + if (hfinfo) + return hfinfo->abbrev; + else + return NULL; +} + +int +proto_registrar_get_ftype(int n) +{ + struct header_field_info *hfinfo; + + hfinfo = find_hfinfo_record(n); + if (hfinfo) + return hfinfo->type; + else + return -1; +} + +int +proto_registrar_get_parent(int n) +{ + struct header_field_info *hfinfo; + + hfinfo = find_hfinfo_record(n); + if (hfinfo) + return hfinfo->parent; + else + return -2; +} + +gboolean +proto_registrar_is_protocol(int n) +{ + struct header_field_info *hfinfo; + + hfinfo = find_hfinfo_record(n); + if (hfinfo) + return (hfinfo->parent == -1 ? TRUE : FALSE); + else + return FALSE; +} + +typedef struct find_id_info { + int target; + GNode *result; +} find_id_info; + +/* looks for a protocol or a header field in a proto_tree. Assumes that protocols + * are at the top level, and header fields only occur underneath their parent's + * subtree. Returns NULL if field not found + */ +proto_item* +proto_find_field(proto_tree* tree, int id) +{ + find_id_info fiinfo; + int parent_protocol; + proto_tree *subtree; + + fiinfo.target = id; + fiinfo.result = NULL; + + /* do a quicker check if field is a protocol */ + if (proto_registrar_is_protocol(id) == TRUE) { + return proto_find_protocol(tree, id); + } + + /* find the field's parent protocol */ + parent_protocol = proto_registrar_get_parent(id); + subtree = proto_find_protocol(tree, parent_protocol); + + /* if there is a tree with that protocol, search it for the field */ + if (subtree) + g_node_traverse((GNode*)subtree, G_IN_ORDER, G_TRAVERSE_ALL, -1, proto_check_id, &fiinfo); + + return (proto_item*) fiinfo.result; +} + + +/* Looks for a protocol at the top layer of the tree. + * Assumption: a protocol can occur only once in a proto_tree. + */ +proto_item* +proto_find_protocol(proto_tree* tree, int protocol_id) +{ + find_id_info fiinfo; + + fiinfo.target = protocol_id; + fiinfo.result = NULL; + + g_node_traverse((GNode*)tree, G_IN_ORDER, G_TRAVERSE_ALL, 2, proto_check_id, &fiinfo); + return (proto_item*) fiinfo.result; +} + + +static gboolean +proto_check_id(GNode *node, gpointer data) +{ + field_info *fi = (field_info*) (node->data); + find_id_info *fiinfo = (find_id_info*) data; + + if (fi) { /* !fi == the top most container node which holds nothing */ + if (fi->hfinfo->id == fiinfo->target) { + fiinfo->result = node; + return TRUE; /* halt traversal */ + } + } + return FALSE; /* keep traversing */ +} + +void +proto_get_field_values(proto_tree* subtree, GNodeTraverseFunc fill_array_func, proto_tree_search_info *sinfo) +{ + g_node_traverse((GNode*)subtree, G_IN_ORDER, G_TRAVERSE_ALL, -1, fill_array_func, sinfo); +} |