wireshark/epan/dissectors/packet-xmpp-utils.c
Stig Bjørlykke ce9a4b212e xmpp/xml: Store item length in xml_frame.
The created XML proto_item can be faked (if not visible and not referenced),
so ensure we store the correct item length to be used in XMPP.

This will avoid an invalid "Malformed Packet" for some XMPP packets.

Change-Id: I79d805b725dbeb93f26a38b72bdcc84187aee16f
Reviewed-on: https://code.wireshark.org/review/17324
Petri-Dish: Stig Bjørlykke <stig@bjorlykke.org>
Tested-by: Petri Dish Buildbot <buildbot-no-reply@wireshark.org>
Reviewed-by: Alexis La Goutte <alexis.lagoutte@gmail.com>
2016-08-25 15:13:26 +00:00

1093 lines
33 KiB
C

/* packet-xmpp-utils.c
* Wireshark's XMPP dissector.
*
* Copyright 2011, Mariusz Okroj <okrojmariusz[]gmail.com>
*
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@wireshark.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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config.h"
#include <epan/packet.h>
#include <epan/strutil.h>
#include "packet-xmpp.h"
#include "packet-xmpp-core.h"
#include "packet-xmpp-utils.h"
void
xmpp_iq_reqresp_track(packet_info *pinfo, xmpp_element_t *packet, xmpp_conv_info_t *xmpp_info)
{
xmpp_transaction_t *xmpp_trans = NULL;
xmpp_attr_t *attr_id;
char *id;
attr_id = xmpp_get_attr(packet, "id");
if (!attr_id) {
return;
}
id = wmem_strdup(wmem_packet_scope(), attr_id->value);
if (!pinfo->fd->flags.visited) {
xmpp_trans = (xmpp_transaction_t *)wmem_tree_lookup_string(xmpp_info->req_resp, id, WMEM_TREE_STRING_NOCASE);
if (xmpp_trans) {
xmpp_trans->resp_frame = pinfo->num;
} else {
char *se_id = wmem_strdup(wmem_file_scope(), id);
xmpp_trans = wmem_new(wmem_file_scope(), xmpp_transaction_t);
xmpp_trans->req_frame = pinfo->num;
xmpp_trans->resp_frame = 0;
wmem_tree_insert_string(xmpp_info->req_resp, se_id, (void *) xmpp_trans, WMEM_TREE_STRING_NOCASE);
}
} else {
wmem_tree_lookup_string(xmpp_info->req_resp, id, WMEM_TREE_STRING_NOCASE);
}
}
void
xmpp_jingle_session_track(packet_info *pinfo, xmpp_element_t *packet, xmpp_conv_info_t *xmpp_info)
{
xmpp_element_t *jingle_packet;
GList *jingle_packet_l;
jingle_packet_l = xmpp_find_element_by_name(packet,"jingle");
jingle_packet = (xmpp_element_t *)(jingle_packet_l?jingle_packet_l->data:NULL);
if (jingle_packet && !pinfo->fd->flags.visited) {
xmpp_attr_t *attr_id;
xmpp_attr_t *attr_sid;
char *se_id;
char *se_sid;
attr_id = xmpp_get_attr(packet, "id");
if (!attr_id) {
return;
}
attr_sid = xmpp_get_attr(jingle_packet, "sid");
if (!attr_sid) {
return;
}
se_id = wmem_strdup(wmem_file_scope(), attr_id->value);
se_sid = wmem_strdup(wmem_file_scope(), attr_sid->value);
wmem_tree_insert_string(xmpp_info->jingle_sessions, se_id, (void*) se_sid, WMEM_TREE_STRING_NOCASE);
}
}
void
xmpp_gtalk_session_track(packet_info *pinfo, xmpp_element_t *packet, xmpp_conv_info_t *xmpp_info)
{
xmpp_element_t *gtalk_packet;
GList *gtalk_packet_l;
gtalk_packet_l = xmpp_find_element_by_name(packet,"session");
gtalk_packet = (xmpp_element_t *)(gtalk_packet_l?gtalk_packet_l->data:NULL);
if (gtalk_packet && !pinfo->fd->flags.visited) {
xmpp_attr_t *attr_id;
xmpp_attr_t *attr_sid;
char *se_id;
char *se_sid;
xmpp_attr_t *xmlns = xmpp_get_attr(gtalk_packet, "xmlns");
if(xmlns && strcmp(xmlns->value,"http://www.google.com/session") != 0)
return;
attr_id = xmpp_get_attr(packet, "id");
if (!attr_id) {
return;
}
attr_sid = xmpp_get_attr(gtalk_packet, "id");
if (!attr_sid) {
return;
}
se_id = wmem_strdup(wmem_file_scope(), attr_id->value);
se_sid = wmem_strdup(wmem_file_scope(), attr_sid->value);
wmem_tree_insert_string(xmpp_info->gtalk_sessions, se_id, (void*) se_sid, WMEM_TREE_STRING_NOCASE);
}
}
void
xmpp_ibb_session_track(packet_info *pinfo, xmpp_element_t *packet, xmpp_conv_info_t *xmpp_info)
{
xmpp_element_t *ibb_packet = NULL;
GList *ibb_packet_l;
if(strcmp(packet->name, "message") == 0)
{
ibb_packet_l = xmpp_find_element_by_name(packet,"data");
ibb_packet = (xmpp_element_t *)(ibb_packet_l?ibb_packet_l->data:NULL);
} else if(strcmp(packet->name, "iq") == 0)
{
ibb_packet_l = xmpp_find_element_by_name(packet,"open");
if(!ibb_packet_l)
ibb_packet_l = xmpp_find_element_by_name(packet,"close");
if(!ibb_packet_l)
ibb_packet_l = xmpp_find_element_by_name(packet,"data");
ibb_packet = (xmpp_element_t *)(ibb_packet_l?ibb_packet_l->data:NULL);
}
if (ibb_packet && !pinfo->fd->flags.visited) {
xmpp_attr_t *attr_id;
xmpp_attr_t *attr_sid;
char *se_id;
char *se_sid;
attr_id = xmpp_get_attr(packet, "id");
attr_sid = xmpp_get_attr(ibb_packet, "sid");
if(attr_id && attr_sid)
{
se_id = wmem_strdup(wmem_file_scope(), attr_id->value);
se_sid = wmem_strdup(wmem_file_scope(), attr_sid->value);
wmem_tree_insert_string(xmpp_info->ibb_sessions, se_id, (void*) se_sid, WMEM_TREE_STRING_NOCASE);
}
}
}
static void
xmpp_unknown_items(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo, xmpp_element_t *element, guint level)
{
GList *childs = element->elements;
DISSECTOR_ASSERT( level < ETT_UNKNOWN_LEN );
xmpp_unknown_attrs(tree, tvb, pinfo, element, TRUE);
if(element->data)
{
proto_tree_add_string(tree, hf_xmpp_cdata, tvb, element->data->offset, element->data->length, element->data->value);
}
while(childs)
{
xmpp_element_t *child = (xmpp_element_t *)childs->data;
proto_item *child_item;
proto_tree *child_tree = proto_tree_add_subtree(tree, tvb, child->offset, child->length,
ett_unknown[level], &child_item, xmpp_ep_string_upcase(child->name));
if(child->default_ns_abbrev)
proto_item_append_text(child_item, "(%s)", child->default_ns_abbrev);
xmpp_unknown_items(child_tree, tvb, pinfo, child, level +1);
childs = childs->next;
}
}
void
xmpp_unknown(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo, xmpp_element_t *element)
{
GList *childs = element->elements;
/*element has unrecognized elements*/
while(childs)
{
xmpp_element_t *child = (xmpp_element_t *)childs->data;
if(!child->was_read)
{
proto_item *unknown_item;
proto_tree *unknown_tree;
unknown_item = proto_tree_add_string_format(tree,
hf_xmpp_unknown, tvb, child->offset, child->length, child->name,
"%s", xmpp_ep_string_upcase(child->name));
unknown_tree = proto_item_add_subtree(unknown_item, ett_unknown[0]);
/*Add COL_INFO only if root element is IQ*/
if(strcmp(element->name,"iq")==0)
col_append_fstr(pinfo->cinfo, COL_INFO, "%s ", xmpp_ep_string_upcase(child->name));
if(child->default_ns_abbrev)
proto_item_append_text(unknown_item,"(%s)",child->default_ns_abbrev);
xmpp_unknown_items(unknown_tree, tvb, pinfo, child, 1);
proto_item_append_text(unknown_item, " [UNKNOWN]");
expert_add_info_format(pinfo, unknown_item, &ei_xmpp_unknown_element, "Unknown element: %s", child->name);
}
childs = childs->next;
}
}
void
xmpp_unknown_attrs(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo _U_, xmpp_element_t *element, gboolean displ_short_list)
{
proto_item *item = proto_tree_get_parent(tree);
GList *keys = g_hash_table_get_keys(element->attrs);
GList *values = g_hash_table_get_values(element->attrs);
GList *keys_head = keys, *values_head = values;
gboolean short_list_started = FALSE;
while(keys && values)
{
xmpp_attr_t *attr = (xmpp_attr_t*) values->data;
if (!attr->was_read) {
if (displ_short_list) {
if (!short_list_started)
proto_item_append_text(item, " [");
else
proto_item_append_text(item, " ");
proto_item_append_text(item, "%s=\"%s\"", (gchar*) keys->data, attr->value);
short_list_started = TRUE;
}
/*If unknown element has xmlns attrib then header field hf_xmpp_xmlns is added to the tree.
In other case only text.*/
if (strcmp((const char *)keys->data, "xmlns") == 0)
proto_tree_add_string(tree, hf_xmpp_xmlns, tvb, attr->offset, attr->length, attr->value);
else {
/*xmlns may looks like xmlns:abbrev="sth"*/
const gchar *xmlns_needle = epan_strcasestr((const char *)keys->data, "xmlns:");
if (xmlns_needle && xmlns_needle == keys->data) {
proto_tree_add_string_format(tree, hf_xmpp_xmlns, tvb, attr->offset, attr->length, attr->value,"%s: %s", (gchar*)keys->data, attr->value);
} else {
proto_item* unknown_attr_item;
unknown_attr_item = proto_tree_add_string_format(tree,
hf_xmpp_unknown_attr, tvb, attr->offset, attr->length,
attr->name, "%s: %s", attr->name, attr->value);
proto_item_append_text(unknown_attr_item, " [UNKNOWN ATTR]");
expert_add_info_format(pinfo, unknown_attr_item, &ei_xmpp_unknown_attribute, "Unknown attribute %s", attr->name);
}
}
}
keys = keys->next;
values = values->next;
}
if(short_list_started && displ_short_list)
proto_item_append_text(item, "]");
g_list_free(keys_head);
g_list_free(values_head);
}
void
xmpp_cdata(proto_tree *tree, tvbuff_t *tvb, xmpp_element_t *element, gint hf)
{
if(element->data)
{
if (hf == -1) {
proto_tree_add_string(tree, hf_xmpp_cdata, tvb, element->data->offset, element->data->length, element->data->value);
} else {
proto_tree_add_string(tree, hf, tvb, element->data->offset, element->data->length, element->data->value);
}
} else
{
if (hf == -1) {
proto_tree_add_string_format_value(tree, hf_xmpp_cdata, tvb, 0, 0, "", "(empty)");
} else {
proto_tree_add_string(tree, hf, tvb, 0, 0, "");
}
}
}
/* displays element that looks like <element_name>element_value</element_name>
* ELEMENT_NAME: element_value as TEXT in PROTO_TREE
*/
void
xmpp_simple_cdata_elem(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo _U_, xmpp_element_t *element)
{
proto_tree_add_string_format(tree, hf_xmpp_cdata, tvb, element->offset, element->length, xmpp_elem_cdata(element),
"%s: %s", xmpp_ep_string_upcase(element->name), xmpp_elem_cdata(element));
}
xmpp_array_t*
xmpp_ep_init_array_t(const gchar** array, gint len)
{
xmpp_array_t *result;
result = wmem_new(wmem_packet_scope(), xmpp_array_t);
result->data = (gpointer) array;
result->length = len;
return result;
}
xmpp_attr_t*
xmpp_ep_init_attr_t(const gchar *value, gint offset, gint length)
{
xmpp_attr_t *result;
result = wmem_new(wmem_packet_scope(), xmpp_attr_t);
result->value = value;
result->offset = offset;
result->length = length;
result->name = NULL;
return result;
}
gchar*
xmpp_ep_string_upcase(const gchar* string)
{
gint len = (int)strlen(string);
gint i;
gchar* result = (gchar *)wmem_alloc0(wmem_packet_scope(), len+1);
for(i=0; i<len; i++)
{
result[i] = string[i];
if(string[i]>='a' && string[i]<='z')
result[i]-='a'-'A';
}
return result;
}
gint
xmpp_element_t_cmp(gconstpointer a, gconstpointer b)
{
gint result = strcmp(((const xmpp_element_t*)a)->name,((const xmpp_element_t*)b)->name);
if(result == 0 && ((const xmpp_element_t*)a)->was_read)
result = -1;
return result;
}
GList*
xmpp_find_element_by_name(xmpp_element_t *packet,const gchar *name)
{
GList *found_elements;
xmpp_element_t *search_element;
/*create fake element only with name*/
search_element = wmem_new(wmem_packet_scope(), xmpp_element_t);
search_element->name = wmem_strdup(wmem_packet_scope(), name);
found_elements = g_list_find_custom(packet->elements, search_element, xmpp_element_t_cmp);
if(found_elements)
return found_elements;
else
return NULL;
}
/* steal_*
* function searches element in packet and sets it as read.
* if element doesn't exist, NULL is returned.
* If element is set as read, it is invisible for these functions.*/
xmpp_element_t*
xmpp_steal_element_by_name(xmpp_element_t *packet,const gchar *name)
{
GList *element_l;
xmpp_element_t *element = NULL;
element_l = xmpp_find_element_by_name(packet, name);
if(element_l)
{
element = (xmpp_element_t *)element_l->data;
element->was_read = TRUE;
}
return element;
}
xmpp_element_t*
xmpp_steal_element_by_names(xmpp_element_t *packet, const gchar **names, gint names_len)
{
gint i;
xmpp_element_t *el = NULL;
for(i = 0; i<names_len; i++)
{
if((el = xmpp_steal_element_by_name(packet, names[i])))
break;
}
return el;
}
xmpp_element_t*
xmpp_steal_element_by_attr(xmpp_element_t *packet, const gchar *attr_name, const gchar *attr_value)
{
GList *childs = packet->elements;
xmpp_element_t *result = NULL;
while (childs) {
xmpp_element_t *child_elem = (xmpp_element_t *)childs->data;
xmpp_attr_t *attr = xmpp_get_attr(child_elem, attr_name);
if(attr)
attr->was_read = FALSE;
if (!child_elem->was_read && attr && strcmp(attr->value, attr_value) == 0) {
result = (xmpp_element_t *)childs->data;
result->was_read = TRUE;
break;
} else
childs = childs->next;
}
return result;
}
xmpp_element_t*
xmpp_steal_element_by_name_and_attr(xmpp_element_t *packet, const gchar *name, const gchar *attr_name, const gchar *attr_value)
{
GList *childs = packet->elements;
xmpp_element_t *result = NULL;
while (childs) {
xmpp_element_t *child_elem = (xmpp_element_t *)childs->data;
xmpp_attr_t *attr = xmpp_get_attr(child_elem, attr_name);
if(attr)
attr->was_read = FALSE;
if (!child_elem->was_read && attr && strcmp(child_elem->name, name) == 0 && strcmp(attr->value, attr_value) == 0) {
result = (xmpp_element_t *)childs->data;
result->was_read = TRUE;
break;
} else
childs = childs->next;
}
return result;
}
xmpp_element_t*
xmpp_get_first_element(xmpp_element_t *packet)
{
if(packet->elements && packet->elements->data)
return (xmpp_element_t *)packet->elements->data;
else
return NULL;
}
/*
Function converts xml_frame_t structure to xmpp_element_t (simpler representation)
*/
xmpp_element_t*
xmpp_xml_frame_to_element_t(xml_frame_t *xml_frame, xmpp_element_t *parent, tvbuff_t *tvb)
{
xml_frame_t *child;
xmpp_element_t *node = wmem_new0(wmem_packet_scope(), xmpp_element_t);
tvbparse_t* tt;
tvbparse_elem_t* elem;
node->attrs = g_hash_table_new(g_str_hash, g_str_equal);
node->elements = NULL;
node->data = NULL;
node->was_read = FALSE;
node->default_ns_abbrev = NULL;
node->name = wmem_strdup(wmem_packet_scope(), xml_frame->name_orig_case);
node->offset = 0;
node->length = 0;
node->namespaces = g_hash_table_new(g_str_hash, g_str_equal);
if(parent)
{
xmpp_copy_hash_table(parent->namespaces, node->namespaces);
} else
{
g_hash_table_insert(node->namespaces, (gpointer)"", (gpointer)"jabber:client");
}
node->offset = xml_frame->start_offset;
node->length = xml_frame->length;
tt = tvbparse_init(tvb,node->offset,-1,NULL,want_ignore);
if((elem = tvbparse_get(tt,want_stream_end_with_ns))!=NULL)
{
node->default_ns_abbrev = tvb_get_string_enc(wmem_packet_scope(), elem->sub->tvb, elem->sub->offset, elem->sub->len, ENC_ASCII);
}
child = xml_frame->first_child;
while(child)
{
if(child->type != XML_FRAME_TAG)
{
if(child->type == XML_FRAME_ATTRIB)
{
gint l;
gchar *value = NULL;
const gchar *xmlns_needle = NULL;
xmpp_attr_t *attr = wmem_new(wmem_packet_scope(), xmpp_attr_t);
attr->length = 0;
attr->offset = 0;
attr->was_read = FALSE;
if (child->value != NULL) {
l = tvb_reported_length(child->value);
value = (gchar *)wmem_alloc0(wmem_packet_scope(), l + 1);
tvb_memcpy(child->value, value, 0, l);
}
attr->offset = child->start_offset;
attr->length = child->length;
attr->value = value;
attr->name = wmem_strdup(wmem_packet_scope(), child->name_orig_case);
g_hash_table_insert(node->attrs,(gpointer)attr->name,(gpointer)attr);
/*checking that attr->name looks like xmlns:ns*/
xmlns_needle = epan_strcasestr(attr->name, "xmlns");
if(xmlns_needle == attr->name)
{
if(attr->name[5] == ':' && strlen(attr->name) > 6)
{
g_hash_table_insert(node->namespaces, (gpointer)wmem_strdup(wmem_packet_scope(), &attr->name[6]), (gpointer)wmem_strdup(wmem_packet_scope(), attr->value));
} else if(attr->name[5] == '\0')
{
g_hash_table_insert(node->namespaces, (gpointer)"", (gpointer)wmem_strdup(wmem_packet_scope(), attr->value));
}
}
}
else if( child->type == XML_FRAME_CDATA)
{
xmpp_data_t *data = NULL;
gint l;
gchar* value = NULL;
data = wmem_new(wmem_packet_scope(), xmpp_data_t);
data->length = 0;
data->offset = 0;
if (child->value != NULL) {
l = tvb_reported_length(child->value);
value = (gchar *)wmem_alloc0(wmem_packet_scope(), l + 1);
tvb_memcpy(child->value, value, 0, l);
}
data->value = value;
data->offset = child->start_offset;
data->length = child->length;
node->data = data;
}
} else
{
node->elements = g_list_append(node->elements,(gpointer)xmpp_xml_frame_to_element_t(child, node,tvb));
}
child = child->next_sibling;
}
return node;
}
void
xmpp_element_t_tree_free(xmpp_element_t *root)
{
GList *childs = root->elements;
g_hash_table_destroy(root->attrs);
g_hash_table_destroy(root->namespaces);
while(childs)
{
xmpp_element_t *child = (xmpp_element_t *)childs->data;
xmpp_element_t_tree_free(child);
childs = childs->next;
}
g_list_free(root->elements);
}
/*Function recognize attribute names if they looks like xmlns:ns*/
static gboolean
attr_find_pred(gpointer key, gpointer value _U_, gpointer user_data)
{
gchar *attr_name = (gchar*) user_data;
if( strcmp(attr_name, "xmlns") == 0 )
{
const gchar *first_occur = epan_strcasestr((const char *)key, "xmlns:");
if(first_occur && first_occur == key)
return TRUE;
else
return FALSE;
}
return FALSE;
}
/*Functions returns element's attribute by name and set as read*/
xmpp_attr_t*
xmpp_get_attr(xmpp_element_t *element, const gchar* attr_name)
{
xmpp_attr_t *result = (xmpp_attr_t *)g_hash_table_lookup(element->attrs, attr_name);
if(!result)
{
result = (xmpp_attr_t *)g_hash_table_find(element->attrs, attr_find_pred, (gpointer)attr_name);
}
if(result)
result->was_read = TRUE;
return result;
}
/*Functions returns element's attribute by name and namespace abbrev*/
static xmpp_attr_t*
xmpp_get_attr_ext(xmpp_element_t *element, const gchar* attr_name, const gchar* ns_abbrev)
{
gchar* search_phrase;
xmpp_attr_t *result;
if(strcmp(ns_abbrev,"")==0)
search_phrase = wmem_strdup(wmem_packet_scope(), attr_name);
else if(strcmp(attr_name, "xmlns") == 0)
search_phrase = wmem_strdup_printf(wmem_packet_scope(), "%s:%s",attr_name, ns_abbrev);
else
search_phrase = wmem_strdup_printf(wmem_packet_scope(), "%s:%s", ns_abbrev, attr_name);
result = (xmpp_attr_t *)g_hash_table_lookup(element->attrs, search_phrase);
if(!result)
{
result = (xmpp_attr_t *)g_hash_table_find(element->attrs, attr_find_pred, (gpointer)attr_name);
}
if(result)
result->was_read = TRUE;
return result;
}
gchar*
xmpp_element_to_string(tvbuff_t *tvb, xmpp_element_t *element)
{
gchar *buff = NULL;
if(tvb_offset_exists(tvb, element->offset+element->length-1))
{
buff = tvb_get_string_enc(wmem_packet_scope(), tvb, element->offset, element->length, ENC_ASCII);
}
return buff;
}
gchar*
xmpp_attr_to_string(tvbuff_t *tvb, xmpp_attr_t *attr)
{
gchar *buff = NULL;
if(tvb_offset_exists(tvb, attr->offset + attr->length-1))
{
buff = tvb_get_string_enc(wmem_packet_scope(), tvb, attr->offset, attr->length, ENC_ASCII);
}
return buff;
}
static void
children_foreach_hide_func(proto_node *node, gpointer data)
{
int *i = (int *)data;
if((*i) == 0)
PROTO_ITEM_SET_HIDDEN(node);
(*i)++;
}
static void
children_foreach_show_func(proto_node *node, gpointer data)
{
int *i = (int *)data;
if((*i) == 0)
PROTO_ITEM_SET_VISIBLE(node);
(*i)++;
}
void
xmpp_proto_tree_hide_first_child(proto_tree *tree)
{
int i = 0;
proto_tree_children_foreach(tree, children_foreach_hide_func, &i);
}
void
xmpp_proto_tree_show_first_child(proto_tree *tree)
{
int i = 0;
proto_tree_children_foreach(tree, children_foreach_show_func, &i);
}
gchar*
proto_item_get_text(proto_item *item)
{
field_info *fi = NULL;
gchar *result;
if(item == NULL)
return NULL;
fi = PITEM_FINFO(item);
if(fi==NULL)
return NULL;
if (fi->rep == NULL)
return NULL;
result = wmem_strdup(wmem_packet_scope(), fi->rep->representation);
return result;
}
void
xmpp_display_attrs(proto_tree *tree, xmpp_element_t *element, packet_info *pinfo, tvbuff_t *tvb, const xmpp_attr_info *attrs, guint n)
{
proto_item *item = proto_tree_get_parent(tree);
xmpp_attr_t *attr;
guint i;
gboolean short_list_started = FALSE;
if(element->default_ns_abbrev)
proto_item_append_text(item, "(%s)",element->default_ns_abbrev);
proto_item_append_text(item," [");
for(i = 0; i < n && attrs!=NULL; i++)
{
attr = xmpp_get_attr(element, attrs[i].name);
if(attr)
{
if(attrs[i].phf != NULL)
{
if(attr->name)
proto_tree_add_string_format(tree, *attrs[i].phf, tvb, attr->offset, attr->length, attr->value,"%s: %s", attr->name, attr->value);
else
proto_tree_add_string(tree, *attrs[i].phf, tvb, attr->offset, attr->length, attr->value);
}
else
{
proto_tree_add_string_format(tree, hf_xmpp_attribute, tvb, attr->offset, attr->length, attr->value,
"%s: %s", attr->name?attr->name:attrs[i].name, attr->value);
}
if(attrs[i].in_short_list)
{
if(short_list_started)
{
proto_item_append_text(item," ");
}
proto_item_append_text(item,"%s=\"%s\"",attr->name?attr->name:attrs[i].name, attr->value);
short_list_started = TRUE;
}
} else if(attrs[i].is_required)
{
expert_add_info_format(pinfo, item, &ei_xmpp_required_attribute, "Required attribute \"%s\" doesn't appear in \"%s\".", attrs[i].name, element->name);
}
if(attrs[i].val_func)
{
if(attr)
attrs[i].val_func(pinfo, item, attrs[i].name, attr->value, attrs[i].data);
else
attrs[i].val_func(pinfo, item, attrs[i].name, NULL, attrs[i].data);
}
}
proto_item_append_text(item,"]");
/*displays attributes that weren't recognized*/
xmpp_unknown_attrs(tree, tvb, pinfo, element, FALSE);
}
void
xmpp_display_attrs_ext(proto_tree *tree, xmpp_element_t *element, packet_info *pinfo, tvbuff_t *tvb, const xmpp_attr_info_ext *attrs, guint n)
{
proto_item *item = proto_tree_get_parent(tree);
xmpp_attr_t *attr;
guint i;
gboolean short_list_started = FALSE;
GList *ns_abbrevs_head, *ns_abbrevs = g_hash_table_get_keys(element->namespaces);
GList *ns_fullnames_head, *ns_fullnames = g_hash_table_get_values(element->namespaces);
ns_abbrevs_head = ns_abbrevs;
ns_fullnames_head = ns_fullnames;
if(element->default_ns_abbrev)
proto_item_append_text(item, "(%s)",element->default_ns_abbrev);
proto_item_append_text(item," [");
while(ns_abbrevs && ns_fullnames)
{
for (i = 0; i < n && attrs != NULL; i++) {
if(strcmp((const char *)(ns_fullnames->data), attrs[i].ns) == 0)
{
attr = xmpp_get_attr_ext(element, attrs[i].info.name, (const gchar *)(ns_abbrevs->data));
if(!attr && element->default_ns_abbrev && strcmp((const char *)ns_abbrevs->data, element->default_ns_abbrev)==0)
attr = xmpp_get_attr_ext(element, attrs[i].info.name, "");
if (attr) {
if (attrs[i].info.phf != NULL) {
if (attr->name)
proto_tree_add_string_format(tree, *attrs[i].info.phf, tvb, attr->offset, attr->length, attr->value, "%s: %s", attr->name, attr->value);
else
proto_tree_add_string(tree, *attrs[i].info.phf, tvb, attr->offset, attr->length, attr->value);
} else {
proto_tree_add_string_format(tree, hf_xmpp_attribute, tvb, attr->offset, attr->length, attr->value,
"%s: %s", attr->name ? attr->name : attrs[i].info.name, attr->value);
}
if (attrs[i].info.in_short_list) {
if (short_list_started) {
proto_item_append_text(item, " ");
}
proto_item_append_text(item, "%s=\"%s\"", attr->name ? attr->name : attrs[i].info.name, attr->value);
short_list_started = TRUE;
}
} else if (attrs[i].info.is_required) {
expert_add_info_format(pinfo, item, &ei_xmpp_required_attribute, "Required attribute \"%s\" doesn't appear in \"%s\".", attrs[i].info.name, element->name);
}
if (attrs[i].info.val_func) {
if (attr)
attrs[i].info.val_func(pinfo, item, attrs[i].info.name, attr->value, attrs[i].info.data);
else
attrs[i].info.val_func(pinfo, item, attrs[i].info.name, NULL, attrs[i].info.data);
}
}
}
ns_abbrevs = ns_abbrevs->next;
ns_fullnames = ns_fullnames->next;
}
proto_item_append_text(item,"]");
/*displays attributes that weren't recognized*/
xmpp_unknown_attrs(tree, tvb, pinfo, element, FALSE);
g_list_free(ns_abbrevs_head);
g_list_free(ns_fullnames_head);
}
typedef struct _name_attr_t
{
const gchar *name;
const gchar *attr_name;
const gchar *attr_value;
} name_attr_t;
/*
returns pointer to the struct that contains 3 strings(element name, attribute name, attribute value)
*/
gpointer
xmpp_name_attr_struct(const gchar *name, const gchar *attr_name, const gchar *attr_value)
{
name_attr_t *result;
result = wmem_new(wmem_packet_scope(), name_attr_t);
result->name = name;
result->attr_name = attr_name;
result->attr_value = attr_value;
return result;
}
void
xmpp_display_elems(proto_tree *tree, xmpp_element_t *parent, packet_info *pinfo, tvbuff_t *tvb, xmpp_elem_info *elems, guint n)
{
guint i;
for(i = 0; i < n && elems!=NULL; i++)
{
xmpp_element_t *elem = NULL;
if(elems[i].type == NAME_AND_ATTR)
{
gboolean loop = TRUE;
const name_attr_t *a = (const name_attr_t *)(elems[i].data);
while(loop && (elem = xmpp_steal_element_by_name_and_attr(parent, a->name, a->attr_name, a->attr_value))!=NULL)
{
elems[i].elem_func(tree, tvb, pinfo, elem);
if(elems[i].occurrence == ONE)
loop = FALSE;
}
} else if(elems[i].type == NAME)
{
gboolean loop = TRUE;
const gchar *name = (const gchar *)(elems[i].data);
while(loop && (elem = xmpp_steal_element_by_name(parent, name))!=NULL)
{
elems[i].elem_func(tree, tvb, pinfo, elem);
if(elems[i].occurrence == ONE)
loop = FALSE;
}
}
else if(elems[i].type == ATTR)
{
gboolean loop = TRUE;
const name_attr_t *attr = (const name_attr_t *)(elems[i].data);
while(loop && (elem = xmpp_steal_element_by_attr(parent, attr->attr_name, attr->attr_value))!=NULL)
{
elems[i].elem_func(tree, tvb, pinfo, elem);
if(elems[i].occurrence == ONE)
loop = FALSE;
}
} else if(elems[i].type == NAMES)
{
gboolean loop = TRUE;
const xmpp_array_t *names = (const xmpp_array_t *)(elems[i].data);
while(loop && (elem = xmpp_steal_element_by_names(parent, (const gchar**)names->data, names->length))!=NULL)
{
elems[i].elem_func(tree, tvb, pinfo, elem);
if(elems[i].occurrence == ONE)
loop = FALSE;
}
}
}
xmpp_unknown(tree, tvb, pinfo, parent);
}
/*
function checks that variable value is in array ((xmpp_array_t)data)->data
*/
void
xmpp_val_enum_list(packet_info *pinfo, proto_item *item, const gchar *name, const gchar *value, gconstpointer data)
{
const xmpp_array_t *enums_array = (const xmpp_array_t *)data;
gint i;
gboolean value_in_enums = FALSE;
gchar **enums = (char**)enums_array->data;
if (value != NULL) {
for (i = 0; i < enums_array->length; i++) {
if (strcmp(value, enums[i]) == 0) {
value_in_enums = TRUE;
break;
}
}
if (!value_in_enums) {
expert_add_info_format(pinfo, item, &ei_xmpp_field_unexpected_value, "Field \"%s\" has unexpected value \"%s\"", name, value);
}
}
}
void
xmpp_change_elem_to_attrib(const gchar *elem_name, const gchar *attr_name, xmpp_element_t *parent, xmpp_attr_t* (*transform_func)(xmpp_element_t *element))
{
xmpp_element_t *element = NULL;
xmpp_attr_t *fake_attr = NULL;
element = xmpp_steal_element_by_name(parent, elem_name);
if(element)
fake_attr = transform_func(element);
if(fake_attr)
g_hash_table_insert(parent->attrs, (gpointer)attr_name, fake_attr);
}
xmpp_attr_t*
xmpp_transform_func_cdata(xmpp_element_t *elem)
{
xmpp_attr_t *result = xmpp_ep_init_attr_t(elem->data?elem->data->value:"", elem->offset, elem->length);
return result;
}
static void
xmpp_copy_hash_table_func(gpointer key, gpointer value, gpointer user_data)
{
GHashTable *dst = (GHashTable *)user_data;
g_hash_table_insert(dst, key, value);
}
void xmpp_copy_hash_table(GHashTable *src, GHashTable *dst)
{
g_hash_table_foreach(src, xmpp_copy_hash_table_func, dst);
}
#if 0
static void
printf_hash_table_func(gpointer key, gpointer value, gpointer user_data _U_)
{
printf("'%s' '%s'\n", (gchar*)key, (gchar*)value);
}
void
printf_elements(xmpp_element_t *root)
{
GList *elems = root->elements;
printf("%s\n", root->name);
g_hash_table_foreach(root->namespaces, printf_hash_table_func, NULL);
while(elems)
{
printf_elements(elems->data);
elems = elems->next;
}
}
#endif
/*
* Editor modelines - http://www.wireshark.org/tools/modelines.html
*
* Local variables:
* c-basic-offset: 4
* tab-width: 8
* indent-tabs-mode: nil
* End:
*
* ex: set shiftwidth=4 tabstop=8 expandtab:
* :indentSize=4:tabSize=8:noTabs=true:
*/