/* packet-smpp.c
* Routines for Short Message Peer to Peer dissection
- * Copyright 2001, Tom Uijldert <tom.uijldert@cmg.nl>
+ * Copyright 2001, Tom Uijldert.
*
- * UDH and WSP dissection of SMS message, Short Message reassembly,
- * "Decode Short Message with Port Number UDH as CL-WSP" preference,
- * "Always try subdissection of 1st fragment" preference,
* Data Coding Scheme decoding for GSM (SMS and CBS),
* provided by Olivier Biot.
*
- * $Id: packet-smpp.c,v 1.21 2003/12/12 23:47:42 obiot Exp $
+ * Dissection of multiple SMPP PDUs within one packet
+ * provided by Chris Wilson.
*
- * Note on SMS Message reassembly
- * ------------------------------
- * The current Short Message reassembly is possible thanks to the
- * message identifier (8 or 16 bit identifier). It is able to reassemble
- * short messages that are sent over either the same SMPP connection or
- * distinct SMPP connections. Normally the reassembly code is able to deal
- * with duplicate message identifiers since the fragment_add_seq_check()
- * call is used.
+ * $Id$
*
- * The SMPP preference "always try subdissection of 1st fragment" allows
- * a subdissector to be called for the first Short Message fragment,
- * even if reassembly is not possible. This way partial dissection
- * is still possible. This preference is switched off by default.
- *
- * Note on Short Message decoding as CL-WSP
- * ----------------------------------------
- * The SMPP preference "port_number_udh_means_wsp" is switched off
- * by default. If it is enabled, then any Short Message with a Port Number
- * UDH will be decoded as CL-WSP if:
- * - The Short Message is not segmented
- * - The entire segmented Short Message is reassembled
- * - It is the 1st segment of an unreassembled Short Message (if the
- * "always try subdissection of 1st fragment" preference is enabled)
+ * Refer to the AUTHORS file or the AUTHORS section in the man page
+ * for contacting the author(s) of this file.
*
* Ethereal - Network traffic analyzer
* By Gerald Combs <gerald@ethereal.com>
#include <epan/packet.h>
#include "prefs.h"
-#include "reassemble.h"
+#include "packet-tcp.h"
+
+/* General-purpose debug logger.
+ * Requires double parentheses because of variable arguments of printf().
+ *
+ * Enable debug logging for SMPP by defining AM_CFLAGS
+ * so that it contains "-DDEBUG_smpp"
+ */
+#ifdef DEBUG_smpp
+#define DebugLog(x) \
+ printf("%s:%u: ", __FILE__, __LINE__); \
+ printf x; \
+ fflush(stdout)
+#else
+#define DebugLog(x) ;
+#endif
/* Forward declarations */
-static void dissect_smpp(tvbuff_t *, packet_info *, proto_tree *t);
+static void dissect_smpp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree);
+static guint get_smpp_pdu_len(tvbuff_t *tvb, int offset);
+static void dissect_smpp_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree);
/*
* Initialize the protocol and registered fields
static int hf_smpp_dcs_wap_class = -1;
static int hf_smpp_dcs_cbs_class = -1;
-/*
- * User Data Header section
- */
-static int hf_smpp_udh_length = -1;
-static int hf_smpp_udh_iei = -1;
-static int hf_smpp_udh_multiple_messages = -1;
-static int hf_smpp_udh_multiple_messages_msg_id = -1;
-static int hf_smpp_udh_multiple_messages_msg_parts = -1;
-static int hf_smpp_udh_multiple_messages_msg_part = -1;
-static int hf_smpp_udh_ports = -1;
-static int hf_smpp_udh_ports_src = -1;
-static int hf_smpp_udh_ports_dst = -1;
-
-/*
- * Short Message fragment handling
- */
-static int hf_sm_fragments = -1;
-static int hf_sm_fragment = -1;
-static int hf_sm_fragment_overlap = -1;
-static int hf_sm_fragment_overlap_conflicts = -1;
-static int hf_sm_fragment_multiple_tails = -1;
-static int hf_sm_fragment_too_long_fragment = -1;
-static int hf_sm_fragment_error = -1;
-
/* Initialize the subtree pointers */
static gint ett_smpp = -1;
static gint ett_dlist = -1;
static gint ett_dlist_resp = -1;
static gint ett_opt_param = -1;
-static gint ett_dcs = -1;
-static gint ett_udh = -1;
-static gint ett_udh_multiple_messages = -1;
-static gint ett_udh_ports = -1;
-static gint ett_sm_fragment = -1;
-static gint ett_sm_fragments = -1;
-
-/* Subdissector declarations */
-static dissector_table_t smpp_dissector_table;
-
-/* Short Message reassembly */
-static GHashTable *sm_fragment_table = NULL;
-static GHashTable *sm_reassembled_table = NULL;
-
-static const fragment_items sm_frag_items = {
- /* Fragment subtrees */
- &ett_sm_fragment,
- &ett_sm_fragments,
- /* Fragment fields */
- &hf_sm_fragments,
- &hf_sm_fragment,
- &hf_sm_fragment_overlap,
- &hf_sm_fragment_overlap_conflicts,
- &hf_sm_fragment_multiple_tails,
- &hf_sm_fragment_too_long_fragment,
- &hf_sm_fragment_error,
- /* Reassembled in field */
- NULL,
- /* Tag */
- "Short Message fragments"
-};
+static gint ett_dcs = -1;
-/* Dissect all SM data as WSP if the UDH contains a Port Number IE */
-static gboolean port_number_udh_means_wsp = FALSE;
-/* Always try dissecting the 1st fragment of a SM,
- * even if it is not reassembled */
-static gboolean try_dissect_1st_frag = FALSE;
-
-static dissector_handle_t wsp_handle;
-
-static void
-sm_defragment_init (void)
-{
- fragment_table_init (&sm_fragment_table);
- reassembled_table_init(&sm_reassembled_table);
-}
+/* Reassemble SMPP TCP segments */
+static gboolean reassemble_over_tcp = FALSE;
/*
* Value-arrays for field-contents
{ 0x00, NULL }
};
-
-/* 3GPP TS 23.040 V6.1.0 (2003-06) */
-static const value_string vals_udh_iei[] = {
- { 0x00, "SMS - Concatenated short messages, 8-bit reference number" },
- { 0x01, "SMS - Special SMS Message Indication" },
- { 0x02, "Reserved" },
- { 0x03, "Value not used to avoid misinterpretation as <LF> character" },
- { 0x04, "SMS - Application port addressing scheme, 8 bit address" },
- { 0x05, "SMS - Application port addressing scheme, 16 bit address" },
- { 0x06, "SMS - SMSC Control Parameters" },
- { 0x07, "SMS - UDH Source Indicator" },
- { 0x08, "SMS - Concatenated short message, 16-bit reference number" },
- { 0x09, "SMS - Wireless Control Message Protocol" },
- { 0x0A, "EMS - Text Formatting" },
- { 0x0B, "EMS - Predefined Sound" },
- { 0x0C, "EMS - User Defined Sound (iMelody max 128 bytes)" },
- { 0x0D, "EMS - Predefined Animation" },
- { 0x0E, "EMS - Large Animation (16*16 times 4 = 32*4 =128 bytes)" },
- { 0x0F, "EMS - Small Animation (8*8 times 4 = 8*4 =32 bytes)" },
- { 0x10, "EMS - Large Picture (32*32 = 128 bytes)" },
- { 0x11, "EMS - Small Picture (16*16 = 32 bytes)" },
- { 0x12, "EMS - Variable Picture" },
- { 0x13, "EMS - User prompt indicator" },
- { 0x14, "EMS - Extended Object" },
- { 0x15, "EMS - Reused Extended Object" },
- { 0x16, "EMS - Compression Control" },
- { 0x17, "EMS - Object Distribution Indicator" },
- { 0x18, "EMS - Standard WVG object" },
- { 0x19, "EMS - Character Size WVG object" },
- { 0x1A, "EMS - Extended Object Data Request Command" },
- { 0x20, "SMS - RFC 822 E-Mail Header" },
- { 0x21, "SMS - Hyperlink format element" },
- { 0x22, "SMS - Reply Address Element" },
- { 0x00, NULL }
-};
+static dissector_handle_t gsm_sms_handle;
/*!
* SMPP equivalent of mktime() (3). Convert date to standard 'time_t' format
len = tvb_strsize(tvb, *offset);
if (len > 1) {
proto_tree_add_string(tree, field, tvb, *offset, len,
- tvb_get_ptr(tvb, *offset, len));
+ (const char *) tvb_get_ptr(tvb, *offset, len));
+ }
+ (*offset) += len;
+}
+
+/* NOTE - caller must free the returned string! */
+static char *
+smpp_handle_string_return(proto_tree *tree, tvbuff_t *tvb, int field, int *offset)
+{
+ gint len;
+ char *str;
+
+ len = tvb_strsize(tvb, *offset);
+ if (len > 1) {
+ str = (char *)tvb_get_stringz(tvb, *offset, &len);
+ proto_tree_add_string(tree, field, tvb, *offset, len, str);
+ } else {
+ str = g_malloc(1 * sizeof(char));
+ str[0] = '\0';
}
(*offset) += len;
+ return str;
}
static void
smpp_handle_string_z(proto_tree *tree, tvbuff_t *tvb, int field, int *offset,
const char *null_string)
{
- guint len;
+ gint len;
len = tvb_strsize(tvb, *offset);
if (len > 1) {
- proto_tree_add_string(tree, field, tvb, *offset, len,
- tvb_get_ptr(tvb, *offset, len));
+ proto_tree_add_string(tree, field, tvb, *offset, len,
+ (const char *)tvb_get_ptr(tvb, *offset, len));
} else {
- proto_tree_add_string(tree, field, tvb, *offset, len, null_string);
- }
+ proto_tree_add_string(tree, field, tvb, *offset, len, null_string);
+ }
(*offset) += len;
}
gint len;
nstime_t tmptime;
- strval = tvb_get_stringz(tvb, *offset, &len);
+ strval = (char *) tvb_get_stringz(tvb, *offset, &len);
if (*strval)
{
if (smpp_mktime(strval, &tmptime.secs, &tmptime.nsecs))
smpp_handle_string(tree, tvb, hf_smpp_password, &offset);
}
-/* Parse Short Message, only if UDH present
- * (otherwise this function is not called).
- * Call WSP dissector if port matches WSP traffic.
- */
static void
-parse_sm_message(proto_tree *sm_tree, tvbuff_t *tvb, packet_info *pinfo)
-{
- tvbuff_t *sm_tvb = NULL;
- proto_item *subtree, *tree;
- guint8 udh_len, udh, len;
- guint8 sm_len = tvb_reported_length (tvb);
- guint8 sm_data_len;
- guint32 i = 0;
- /* Multiple Messages UDH */
- fragment_data *fd_sm = NULL;
- guint16 sm_id = 0, frags = 0, frag = 0;
- gboolean save_fragmented = FALSE, try_sm_reassemble = FALSE;
- /* Port Number UDH */
- guint16 p_src = 0, p_dst = 0;
- gboolean ports_available = FALSE;
-
- udh_len = tvb_get_guint8(tvb, i++);
- tree = proto_tree_add_uint(sm_tree, hf_smpp_udh_length, tvb, 0, 1, udh_len);
- tree = proto_item_add_subtree(tree, ett_udh);
- sm_data_len = sm_len - (1 + udh_len);
- while (i < udh_len) {
- udh = tvb_get_guint8(tvb, i++);
- len = tvb_get_guint8(tvb, i++);
- subtree = proto_tree_add_uint(tree, hf_smpp_udh_iei,
- tvb, i-2, 2+len, udh);
- switch (udh) {
- case 0x00: /* Multiple messages - 8-bit message ID */
- if (len == 3) {
- sm_id = tvb_get_guint8(tvb, i++);
- frags = tvb_get_guint8(tvb, i++);
- frag = tvb_get_guint8(tvb, i++);
- proto_item_append_text(subtree,
- ": message %u, part %u of %u", sm_id, frag, frags);
- subtree = proto_item_add_subtree(subtree,
- ett_udh_multiple_messages);
- proto_tree_add_uint (subtree,
- hf_smpp_udh_multiple_messages_msg_id,
- tvb, i-3, 1, sm_id);
- proto_tree_add_uint (subtree,
- hf_smpp_udh_multiple_messages_msg_parts,
- tvb, i-2, 1, frags);
- proto_tree_add_uint (subtree,
- hf_smpp_udh_multiple_messages_msg_part,
- tvb, i-1, 1, frag);
- } else {
- proto_item_append_text(subtree, " - Invalid format!");
- i += len;
- }
- break;
-
- case 0x08: /* Multiple messages - 16-bit message ID */
- if (len == 4) {
- sm_id = tvb_get_ntohs(tvb, i); i += 2;
- frags = tvb_get_guint8(tvb, i++);
- frag = tvb_get_guint8(tvb, i++);
- proto_item_append_text(subtree,
- ": message %u, part %u of %u", sm_id, frag, frags);
- subtree = proto_item_add_subtree(subtree,
- ett_udh_multiple_messages);
- proto_tree_add_uint (subtree,
- hf_smpp_udh_multiple_messages_msg_id,
- tvb, i-4, 2, sm_id);
- proto_tree_add_uint (subtree,
- hf_smpp_udh_multiple_messages_msg_parts,
- tvb, i-2, 1, frags);
- proto_tree_add_uint (subtree,
- hf_smpp_udh_multiple_messages_msg_part,
- tvb, i-1, 1, frag);
- } else {
- proto_item_append_text(subtree, " - Invalid format!");
- i += len;
- }
- break;
-
- case 0x04: /* Port Number UDH - 8-bit address */
- if (len == 2) { /* Port fields */
- p_dst = tvb_get_guint8(tvb, i++);
- p_src = tvb_get_guint8(tvb, i++);
- proto_item_append_text(subtree,
- ": source port %u, destination port %u",
- p_src, p_dst);
- subtree = proto_item_add_subtree(subtree, ett_udh_ports);
- proto_tree_add_uint (subtree, hf_smpp_udh_ports_dst,
- tvb, i-2, 1, p_dst);
- proto_tree_add_uint (subtree, hf_smpp_udh_ports_src,
- tvb, i-1, 1, p_src);
- ports_available = TRUE;
- } else {
- proto_item_append_text(subtree, " - Invalid format!");
- i += len;
- }
- break;
-
- case 0x05: /* Port Number UDH - 16-bit address */
- if (len == 4) { /* Port fields */
- p_dst = tvb_get_ntohs(tvb, i); i += 2;
- p_src = tvb_get_ntohs(tvb, i); i += 2;
- proto_item_append_text(subtree,
- ": source port %u, destination port %u",
- p_src, p_dst);
- subtree = proto_item_add_subtree(subtree, ett_udh_ports);
- proto_tree_add_uint (subtree, hf_smpp_udh_ports_dst,
- tvb, i-4, 2, p_dst);
- proto_tree_add_uint (subtree, hf_smpp_udh_ports_src,
- tvb, i-2, 2, p_src);
- ports_available = TRUE;
- } else {
- proto_item_append_text(subtree, " - Invalid format!");
- i += len;
- }
- break;
-
- default:
- i += len;
- break;
- }
- }
- if (tvb_reported_length_remaining(tvb, i) <= 0)
- return; /* No more data */
-
- /* Try reassembling the packets */
- if ( frags && tvb_bytes_exist (tvb, i, sm_data_len) ) {
- try_sm_reassemble = TRUE;
- save_fragmented = pinfo->fragmented;
- pinfo->fragmented = TRUE;
- fd_sm = fragment_add_seq_check (tvb, i, pinfo,
- sm_id, /* guint32 ID for fragments belonging together */
- sm_fragment_table, /* list of message fragments */
- sm_reassembled_table, /* list of reassembled messages */
- frag-1, /* guint32 fragment sequence number */
- sm_data_len, /* guint32 fragment length */
- (frag != frags)); /* More fragments? */
- if (fd_sm) { /* Reassembled */
- sm_tvb = tvb_new_real_data (fd_sm->data, fd_sm->len, fd_sm->len);
- tvb_set_child_real_data_tvbuff (tvb, sm_tvb);
- add_new_data_source (pinfo, sm_tvb, "Reassembled Short Message");
- pinfo->fragmented = FALSE;
- /* Show all fragments */
- show_fragment_seq_tree (fd_sm, &sm_frag_items,
- sm_tree, pinfo, sm_tvb);
- if (check_col (pinfo->cinfo, COL_INFO))
- col_append_str (pinfo->cinfo, COL_INFO,
- " (Short Message Reassembled)");
- } else {
- /* Not last packet of reassembled Short Message */
- if (check_col (pinfo->cinfo, COL_INFO))
- col_append_fstr (pinfo->cinfo, COL_INFO,
- " (Short Message fragment %u of %u)", frag, frags);
- }
- }
-
- if (! sm_tvb) /* One single Short Message, or not reassembled */
- sm_tvb = tvb_new_subset (tvb, i, -1, -1);
- /* Try calling a subdissector */
- if (sm_tvb) {
- if (fd_sm || frag==0 || (frag==1 && try_dissect_1st_frag)) {
- /* Try calling a subdissector only if:
- * - the Short Message is reassembled,
- * - the Short Message consists of only one "fragment",
- * - the preference "Always Try Dissection for 1st SM fragment"
- * is switched on, and this is the SM's 1st fragment. */
- if ( ports_available ) {
- if ( port_number_udh_means_wsp ) {
- call_dissector (wsp_handle, sm_tvb, pinfo, sm_tree);
- } else {
- if (! dissector_try_port(smpp_dissector_table, p_src,
- sm_tvb, pinfo, sm_tree)) {
- if (! dissector_try_port(smpp_dissector_table, p_dst,
- sm_tvb, pinfo, sm_tree)) {
- if (sm_tree) { /* Only display if needed */
- proto_tree_add_text (sm_tree, sm_tvb, 0, -1,
- "Short Message body");
- }
- }
- }
- }
- } else { /* No ports IE */
- proto_tree_add_text (sm_tree, sm_tvb, 0, -1,
- "Short Message body");
- }
- } else { /* Not 1st fragment and not reassembled */
- proto_tree_add_text (sm_tree, sm_tvb, 0, -1,
- "Unreassembled Short Message fragment %u of %u",
- frag, frags);
- }
- }
-
- if (try_sm_reassemble) /* Clean up defragmentation */
- pinfo->fragmented = save_fragmented;
- return;
-}
-
-static void
-submit_sm(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo)
+submit_sm(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo,
+ proto_tree *top_tree)
{
tvbuff_t *tvb_msg;
int offset = 0;
guint8 flag, udhi;
guint8 length;
+ char *src_str = NULL;
+ char *dst_str = NULL;
+ address save_src, save_dst;
smpp_handle_string_z(tree, tvb, hf_smpp_service_type, &offset, "(Default)");
smpp_handle_int1(tree, tvb, hf_smpp_source_addr_ton, &offset);
smpp_handle_int1(tree, tvb, hf_smpp_source_addr_npi, &offset);
- smpp_handle_string(tree, tvb, hf_smpp_source_addr, &offset);
+ src_str = smpp_handle_string_return(tree, tvb, hf_smpp_source_addr, &offset);
smpp_handle_int1(tree, tvb, hf_smpp_dest_addr_ton, &offset);
smpp_handle_int1(tree, tvb, hf_smpp_dest_addr_npi, &offset);
- smpp_handle_string(tree, tvb, hf_smpp_destination_addr, &offset);
+ dst_str = smpp_handle_string_return(tree, tvb, hf_smpp_destination_addr, &offset);
flag = tvb_get_guint8(tvb, offset);
udhi = flag & 0x40;
proto_tree_add_item(tree, hf_smpp_esm_submit_msg_mode,
tvb, offset, length, FALSE);
if (udhi) /* UDHI indicator present */
{
+ DebugLog(("UDHI present - set addresses\n"));
+ /* Save original addresses */
+ COPY_ADDRESS(&save_src, &(pinfo->src));
+ COPY_ADDRESS(&save_dst, &(pinfo->dst));
+ /* Set SMPP source and destination address */
+ SET_ADDRESS(&(pinfo->src), AT_STRINGZ, 1+strlen(src_str), src_str);
+ SET_ADDRESS(&(pinfo->dst), AT_STRINGZ, 1+strlen(dst_str), dst_str);
tvb_msg = tvb_new_subset (tvb, offset,
- MIN(length, tvb_reported_length(tvb) - offset), length);
- parse_sm_message(tree, tvb_msg, pinfo);
+ MIN(length, tvb_reported_length(tvb) - offset), length);
+ call_dissector (gsm_sms_handle, tvb_msg, pinfo, top_tree);
+ /* Restore original addresses */
+ COPY_ADDRESS(&(pinfo->src), &save_src);
+ COPY_ADDRESS(&(pinfo->dst), &save_dst);
+ /* Get rid of SMPP text string addresses */
+ g_free(src_str);
+ g_free(dst_str);
}
offset += length;
}
smpp_handle_tlv(tree, tvb, &offset);
}
-#define deliver_sm(a, b, c) submit_sm(a, b, c)
+#define deliver_sm(a, b, c, d) submit_sm(a, b, c, d)
static void
replace_sm(proto_tree *tree, tvbuff_t *tvb)
flag = tvb_get_guint8(tvb, offset);
proto_tree_add_item(tree, hf_smpp_esm_submit_msg_mode,
- tvb, offset, 1, flag);
+ tvb, offset, 1, flag);
proto_tree_add_item(tree, hf_smpp_esm_submit_msg_type,
- tvb, offset, 1, flag);
+ tvb, offset, 1, flag);
proto_tree_add_item(tree, hf_smpp_esm_submit_features,
- tvb, offset, 1, flag);
+ tvb, offset, 1, flag);
offset++;
smpp_handle_int1(tree, tvb, hf_smpp_protocol_id, &offset);
smpp_handle_int1(tree, tvb, hf_smpp_priority_flag, &offset);
- if (tvb_get_guint8(tvb,offset)) {
- smpp_handle_time(tree, tvb, hf_smpp_schedule_delivery_time,
- hf_smpp_schedule_delivery_time_r, &offset);
- } else { /* Time = NULL means Immediate delivery */
- proto_tree_add_text(tree, tvb, offset++, 1,
- "Scheduled delivery time: Immediate delivery");
- }
- if (tvb_get_guint8(tvb,offset)) {
- smpp_handle_time(tree, tvb, hf_smpp_validity_period,
- hf_smpp_validity_period_r, &offset);
- } else { /* Time = NULL means SMSC default validity */
- proto_tree_add_text(tree, tvb, offset++, 1,
- "Validity period: SMSC default validity period");
- }
+ if (tvb_get_guint8(tvb,offset)) {
+ smpp_handle_time(tree, tvb, hf_smpp_schedule_delivery_time,
+ hf_smpp_schedule_delivery_time_r, &offset);
+ } else { /* Time = NULL means Immediate delivery */
+ proto_tree_add_text(tree, tvb, offset++, 1,
+ "Scheduled delivery time: Immediate delivery");
+ }
+ if (tvb_get_guint8(tvb,offset)) {
+ smpp_handle_time(tree, tvb, hf_smpp_validity_period,
+ hf_smpp_validity_period_r, &offset);
+ } else { /* Time = NULL means SMSC default validity */
+ proto_tree_add_text(tree, tvb, offset++, 1,
+ "Validity period: SMSC default validity period");
+ }
flag = tvb_get_guint8(tvb, offset);
proto_tree_add_item(tree, hf_smpp_regdel_receipt, tvb, offset, 1, flag);
proto_tree_add_item(tree, hf_smpp_regdel_acks, tvb, offset, 1, flag);
proto_tree_add_item(tree, hf_smpp_regdel_notif, tvb, offset, 1, flag);
offset++;
smpp_handle_int1(tree, tvb, hf_smpp_replace_if_present_flag, &offset);
- smpp_handle_dcs(tree, tvb, &offset);
+ smpp_handle_dcs(tree, tvb, &offset);
smpp_handle_int1(tree, tvb, hf_smpp_sm_default_msg_id, &offset);
length = tvb_get_guint8(tvb, offset);
proto_tree_add_uint(tree, hf_smpp_sm_length, tvb, offset++, 1, length);
if (length)
proto_tree_add_item(tree, hf_smpp_short_message,
- tvb, offset, length, FALSE);
+ tvb, offset, length, FALSE);
offset += length;
smpp_handle_tlv(tree, tvb, &offset);
}
return TRUE;
}
-/* Code to actually dissect the packets */
+static guint
+get_smpp_pdu_len(tvbuff_t *tvb, int offset)
+{
+ return tvb_get_ntohl(tvb, offset);
+}
+
+/*
+ * This global SMPP variable is used to determine whether the PDU to dissect
+ * is the first SMPP PDU in the packet (or reassembled buffer), requiring
+ * different column update code than subsequent SMPP PDUs within this packet
+ * (or reassembled buffer).
+ *
+ * FIXME - This approach is NOT dissection multi-thread safe!
+ */
+static gboolean first = TRUE;
+
static void
dissect_smpp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+ first = TRUE;
+ if (pinfo->ptype == PT_TCP) { /* are we running on top of TCP */
+ tcp_dissect_pdus(tvb, pinfo, tree,
+ reassemble_over_tcp, /* Do we try to reassemble */
+ 16, /* Length of fixed header */
+ get_smpp_pdu_len, /* Function returning PDU len */
+ dissect_smpp_pdu); /* PDU dissector */
+ } else { /* no? probably X.25 */
+ guint32 offset = 0;
+ while (tvb_reported_length_remaining(tvb, offset) > 0) {
+ guint16 pdu_len = tvb_get_ntohl(tvb, offset);
+ gint pdu_real_len = tvb_length_remaining(tvb, offset);
+ tvbuff_t *pdu_tvb;
+
+ if (pdu_real_len <= 0)
+ return;
+ if (pdu_real_len > pdu_len)
+ pdu_real_len = pdu_len;
+ pdu_tvb = tvb_new_subset(tvb, offset, pdu_real_len, pdu_len);
+ dissect_smpp_pdu(pdu_tvb, pinfo, tree);
+ offset += pdu_len;
+ first = FALSE;
+ }
+ }
+}
+
+
+/* Dissect a single SMPP PDU contained within "tvb". */
+static void
+dissect_smpp_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
int offset = 0; /* Offset within tvbuff */
guint command_length; /* length of PDU */
guint command_id; /* SMPP command */
guint command_status; /* Status code */
guint sequence_number; /* ...of command */
-
+ gchar *command_str;
+ gchar *command_status_str = NULL;
/* Set up structures needed to add the protocol subtree and manage it */
- proto_item *ti;
- proto_tree *smpp_tree;
- tvbuff_t *tmp_tvb;
+ proto_item *ti = NULL;
+ proto_tree *smpp_tree = NULL;
/*
- * Safety: don't even try it when the mandatory header isn't present.
+ * Safety: don't even try to dissect the PDU
+ * when the mandatory header isn't present.
*/
if (tvb_reported_length(tvb) < 4 * 4)
return;
command_length = tvb_get_ntohl(tvb, offset);
offset += 4;
command_id = tvb_get_ntohl(tvb, offset);
+ command_str = val_to_str(command_id, vals_command_id,
+ "(Unknown SMPP Operation 0x%08X)");
offset += 4;
command_status = tvb_get_ntohl(tvb, offset);
- offset +=4;
+ if (command_id & 0x80000000) {
+ command_status_str = val_to_str(command_status, vals_command_status,
+ "(Reserved Error 0x%08X)");
+ }
+ offset += 4;
sequence_number = tvb_get_ntohl(tvb, offset);
offset += 4;
- /* Make entries in Protocol column and Info column on summary display */
- if (check_col(pinfo->cinfo, COL_PROTOCOL))
- col_set_str(pinfo->cinfo, COL_PROTOCOL, "SMPP");
- if (check_col(pinfo->cinfo, COL_INFO))
- {
- col_clear(pinfo->cinfo, COL_INFO);
- col_add_fstr(pinfo->cinfo, COL_INFO, "SMPP %s",
- val_to_str(command_id,vals_command_id,"unknown operation"));
- if (command_id & 0x80000000)
- col_append_fstr(pinfo->cinfo, COL_INFO, ": \"%s\"",
- val_to_str(command_status, vals_command_status,
- "reserved error"));
- if (command_length > tvb_reported_length(tvb))
- col_append_str(pinfo->cinfo, COL_INFO, " [short packet]");
- if (command_length < tvb_reported_length(tvb))
- col_append_str(pinfo->cinfo, COL_INFO, " [trailing data]");
+ /*
+ * Update the protocol column.
+ */
+ if (first == TRUE) {
+ if (check_col(pinfo->cinfo, COL_PROTOCOL))
+ col_set_str(pinfo->cinfo, COL_PROTOCOL, "SMPP");
}
- /* In the interest of speed, if "tree" is NULL, don't do any work not
- * necessary to generate protocol tree items.
- *
- * Exception: sm_submit (command_id == 0x00000004) - for SMS reassembly
+ /*
+ * Create display subtree for the protocol
*/
- if (tree || (command_id == 4)) {
-
- /* create display subtree for the protocol */
- ti = proto_tree_add_item(tree, proto_smpp, tvb, 0,
- command_length, FALSE);
- smpp_tree = proto_item_add_subtree(ti, ett_smpp);
-
- /* add an item to the subtree */
- proto_tree_add_uint(smpp_tree, hf_smpp_command_length, tvb,
- 0, 4, command_length);
- proto_tree_add_uint(smpp_tree, hf_smpp_command_id, tvb,
- 4, 4, command_id);
- proto_item_append_text (ti, ", %s",
- match_strval (command_id, vals_command_id));
- /* Status is only meaningful with responses */
- if (command_id & 0x80000000) {
- proto_tree_add_uint(smpp_tree, hf_smpp_command_status, tvb,
- 8, 4, command_status);
- proto_item_append_text (ti, ": \"%s\"",
- match_strval (command_status, vals_command_status));
- }
- proto_tree_add_uint(smpp_tree, hf_smpp_sequence_number, tvb,
- 12, 4, sequence_number);
- proto_item_append_text (ti, ", Seq: %u, Len: %u",
- sequence_number, command_length);
+ if (tree) {
+ ti = proto_tree_add_item (tree, proto_smpp, tvb, 0, tvb->length, FALSE);
+ smpp_tree = proto_item_add_subtree (ti, ett_smpp);
+ }
+
+ /*
+ * Cycle over the encapsulated PDUs
+ */
+ {
+ tvbuff_t *pdu_tvb;
+
/*
- * End of header. Don't dissect variable part if it is shortened.
+ * Make entries in the Info column on the summary display
*/
- if (command_length > tvb_reported_length(tvb))
- return;
- tmp_tvb = tvb_new_subset(tvb, offset, -1, command_length - offset);
- if (command_id & 0x80000000)
- {
- switch (command_id & 0x7FFFFFFF) {
+ if (check_col(pinfo->cinfo, COL_INFO)) {
+ if (first == TRUE) {
+ /*
+ * First PDU - We already computed the fixed header
+ */
+ col_clear(pinfo->cinfo, COL_INFO);
+ col_add_fstr(pinfo->cinfo, COL_INFO, "SMPP %s", command_str);
+ first = FALSE;
+ } else {
/*
- * All of these only have a fixed header
+ * Subsequent PDUs
*/
- case 0: /* Generic nack */
- case 6: /* Unbind resp */
- case 7: /* Replace SM resp */
- case 8: /* Cancel SM resp */
- case 21: /* Enquire link resp */
- break;
- case 1:
- if (!command_status)
- bind_receiver_resp(smpp_tree, tmp_tvb);
- break;
- case 2:
- if (!command_status)
- bind_transmitter_resp(smpp_tree, tmp_tvb);
- break;
- case 3:
- if (!command_status)
- query_sm_resp(smpp_tree, tmp_tvb);
- break;
- case 4:
- if (!command_status)
- submit_sm_resp(smpp_tree, tmp_tvb);
- break;
- case 5:
- if (!command_status)
- deliver_sm_resp(smpp_tree, tmp_tvb);
- break;
- case 9:
- if (!command_status)
- bind_transceiver_resp(smpp_tree, tmp_tvb);
- break;
- case 33:
- if (!command_status)
- submit_multi_resp(smpp_tree, tmp_tvb);
- break;
- case 259:
- if (!command_status)
- data_sm_resp(smpp_tree, tmp_tvb);
- break;
- default:
- break;
+ col_append_fstr(pinfo->cinfo, COL_INFO, ", %s", command_str);
+ }
+ /*
+ * Display command status of responses in Info column
+ */
+ if (command_id & 0x80000000) {
+ col_append_fstr(pinfo->cinfo, COL_INFO, ": \"%s\"",
+ command_status_str);
}
}
- else
+
+ /*
+ * Create a tvb for the current PDU.
+ * Physical length: at most command_length
+ * Reported length: command_length
+ */
+ if (tvb_length_remaining(tvb, offset - 16 + command_length) > 0) {
+ pdu_tvb = tvb_new_subset(tvb, offset - 16,
+ command_length, /* Physical length */
+ command_length); /* Length reported by the protocol */
+ } else {
+ pdu_tvb = tvb_new_subset(tvb, offset - 16,
+ tvb_length_remaining(tvb, offset - 16),/* Physical length */
+ command_length); /* Length reported by the protocol */
+ }
+
+ /*
+ * Dissect the PDU
+ *
+ * If "tree" is NULL, Ethereal is only interested in creation
+ * of conversations, reassembly and subdissection but not in
+ * the detailed protocol tree.
+ * In the interest of speed, skip the generation of protocol tree
+ * items when "tree" is NULL.
+ *
+ * The only PDU which requires subdissection currently is the
+ * sm_submit PDU (command ID = 0x00000004).
+ */
+ if (tree || (command_id == 4))
{
- switch (command_id) {
- case 1:
- bind_receiver(smpp_tree, tmp_tvb);
- break;
- case 2:
- bind_transmitter(smpp_tree, tmp_tvb);
- break;
- case 3:
- query_sm(smpp_tree, tmp_tvb);
- break;
- case 4:
- submit_sm(smpp_tree, tmp_tvb, pinfo);
- break;
- case 5:
- deliver_sm(smpp_tree, tmp_tvb, pinfo);
- break;
- case 6: /* Unbind */
- case 21: /* Enquire link */
- break;
- case 7:
- replace_sm(smpp_tree, tmp_tvb);
- break;
- case 8:
- cancel_sm(smpp_tree, tmp_tvb);
- break;
- case 9:
- bind_transceiver(smpp_tree, tmp_tvb);
- break;
- case 11:
- outbind(smpp_tree, tmp_tvb);
- break;
- case 33:
- submit_multi(smpp_tree, tmp_tvb);
- break;
- case 258:
- alert_notification(smpp_tree, tmp_tvb);
- break;
- case 259:
- data_sm(smpp_tree, tmp_tvb);
- break;
- default:
- break;
+ /*
+ * Create display subtree for the PDU
+ */
+ if (tree) {
+ proto_tree_add_uint(smpp_tree, hf_smpp_command_length,
+ pdu_tvb, 0, 4, command_length);
+ proto_tree_add_uint(smpp_tree, hf_smpp_command_id,
+ pdu_tvb, 4, 4, command_id);
+ proto_item_append_text(ti, ", Command: %s", command_str);
+
+ /*
+ * Status is only meaningful with responses
+ */
+ if (command_id & 0x80000000) {
+ proto_tree_add_uint(smpp_tree, hf_smpp_command_status,
+ pdu_tvb, 8, 4, command_status);
+ proto_item_append_text (ti, ", Status: \"%s\"",
+ command_status_str);
+ }
+ proto_tree_add_uint(smpp_tree, hf_smpp_sequence_number,
+ pdu_tvb, 12, 4, sequence_number);
+ proto_item_append_text(ti, ", Seq: %u, Len: %u",
+ sequence_number, command_length);
}
- }
+
+ /*
+ * End of fixed header.
+ * Don't dissect variable part if it is shortened.
+ *
+ * FIXME - We then do not report a Short Frame or Malformed Packet
+ */
+ if (command_length <= tvb_reported_length(pdu_tvb))
+ {
+ tvbuff_t *tmp_tvb = tvb_new_subset(pdu_tvb, 16,
+ -1, command_length - 16);
+ if (command_id & 0x80000000)
+ {
+ switch (command_id & 0x7FFFFFFF) {
+ /*
+ * All of these only have a fixed header
+ */
+ case 0: /* Generic nack */
+ case 6: /* Unbind resp */
+ case 7: /* Replace SM resp */
+ case 8: /* Cancel SM resp */
+ case 21: /* Enquire link resp */
+ break;
+ case 1:
+ if (!command_status)
+ bind_receiver_resp(smpp_tree, tmp_tvb);
+ break;
+ case 2:
+ if (!command_status)
+ bind_transmitter_resp(smpp_tree, tmp_tvb);
+ break;
+ case 3:
+ if (!command_status)
+ query_sm_resp(smpp_tree, tmp_tvb);
+ break;
+ case 4:
+ if (!command_status)
+ submit_sm_resp(smpp_tree, tmp_tvb);
+ break;
+ case 5:
+ if (!command_status)
+ deliver_sm_resp(smpp_tree, tmp_tvb);
+ break;
+ case 9:
+ if (!command_status)
+ bind_transceiver_resp(smpp_tree, tmp_tvb);
+ break;
+ case 33:
+ if (!command_status)
+ submit_multi_resp(smpp_tree, tmp_tvb);
+ break;
+ case 259:
+ if (!command_status)
+ data_sm_resp(smpp_tree, tmp_tvb);
+ break;
+ default:
+ break;
+ } /* switch (command_id & 0x7FFFFFFF) */
+ }
+ else
+ {
+ switch (command_id) {
+ case 1:
+ bind_receiver(smpp_tree, tmp_tvb);
+ break;
+ case 2:
+ bind_transmitter(smpp_tree, tmp_tvb);
+ break;
+ case 3:
+ query_sm(smpp_tree, tmp_tvb);
+ break;
+ case 4:
+ submit_sm(smpp_tree, tmp_tvb, pinfo, tree);
+ break;
+ case 5:
+ deliver_sm(smpp_tree, tmp_tvb, pinfo, tree);
+ break;
+ case 6: /* Unbind */
+ case 21: /* Enquire link */
+ break;
+ case 7:
+ replace_sm(smpp_tree, tmp_tvb);
+ break;
+ case 8:
+ cancel_sm(smpp_tree, tmp_tvb);
+ break;
+ case 9:
+ bind_transceiver(smpp_tree, tmp_tvb);
+ break;
+ case 11:
+ outbind(smpp_tree, tmp_tvb);
+ break;
+ case 33:
+ submit_multi(smpp_tree, tmp_tvb);
+ break;
+ case 258:
+ alert_notification(smpp_tree, tmp_tvb);
+ break;
+ case 259:
+ data_sm(smpp_tree, tmp_tvb);
+ break;
+ default:
+ break;
+ } /* switch (command_id) */
+ } /* if (command_id & 0x80000000) */
+ } /* if (command_length <= tvb_reported_length(pdu_tvb)) */
+ offset += command_length;
+ } /* if (tree || (command_id == 4)) */
+ first = FALSE;
}
- /* If this protocol has a sub-dissector call it here. */
+
return;
}
+
/* Register the protocol with Ethereal */
void
proto_register_smpp(void)
{
- module_t *smpp_module; /* Preferences for SMPP */
+ module_t *smpp_module; /* Preferences for SMPP */
/* Setup list of header fields */
static hf_register_info hf[] = {
HFILL
}
},
+ /*
+ * Data Coding Scheme
+ */
{ &hf_smpp_dcs,
{ "SMPP Data Coding Scheme", "smpp.dcs",
FT_UINT8, BASE_HEX, VALS(vals_data_coding), 0x00,
"as specified by the WAP Forum (WAP over GSM USSD).", HFILL
}
},
- { &hf_smpp_udh_iei,
- { "IE Id", "smpp.udh.iei",
- FT_UINT8, BASE_HEX, VALS(vals_udh_iei), 0x00,
- "Name of the User Data Header Information Element.",
- HFILL
- }
- },
- { &hf_smpp_udh_length,
- { "UDH Length", "smpp.udh.len",
- FT_UINT8, BASE_DEC, NULL, 0x00,
- "Length of the User Data Header (bytes)",
- HFILL
- }
- },
- { &hf_smpp_udh_multiple_messages,
- { "Multiple messages UDH", "smpp.udh.mm",
- FT_NONE, BASE_NONE, NULL, 0x00,
- "Multiple messages User Data Header",
- HFILL
- }
- },
- { &hf_smpp_udh_multiple_messages_msg_id,
- { "Message identifier", "smpp.udh.mm.msg_id",
- FT_UINT16, BASE_DEC, NULL, 0x00,
- "Identification of the message",
- HFILL
- }
- },
- { &hf_smpp_udh_multiple_messages_msg_parts,
- { "Message parts", "smpp.udh.mm.msg_parts",
- FT_UINT8, BASE_DEC, NULL, 0x00,
- "Total number of message parts (fragments)",
- HFILL
- }
- },
- { &hf_smpp_udh_multiple_messages_msg_part,
- { "Message part number", "smpp.udh.mm.msg_part",
- FT_UINT8, BASE_DEC, NULL, 0x00,
- "Message part (fragment) sequence number",
- HFILL
- }
- },
- { &hf_smpp_udh_ports,
- { "Port number UDH", "smpp.udh.ports",
- FT_NONE, BASE_NONE, NULL, 0x00,
- "Port number User Data Header",
- HFILL
- }
- },
- { &hf_smpp_udh_ports_src,
- { "Source port", "smpp.udh.ports.src",
- FT_UINT8, BASE_DEC, NULL, 0x00,
- "Source port",
- HFILL
- }
- },
- { &hf_smpp_udh_ports_dst,
- { "Destination port", "smpp.udh.ports.dst",
- FT_UINT8, BASE_DEC, NULL, 0x00,
- "Destination port",
- HFILL
- }
- },
- /* Short Message fragment reassembly */
- { &hf_sm_fragments,
- { "Short Message fragments", "smpp.fragments",
- FT_NONE, BASE_NONE, NULL, 0x00,
- "SMPP Short Message fragments",
- HFILL
- }
- },
- { &hf_sm_fragment,
- { "Short Message fragment", "smpp.fragment",
- FT_FRAMENUM, BASE_NONE, NULL, 0x00,
- "SMPP Short Message fragment",
- HFILL
- }
- },
- { &hf_sm_fragment_overlap,
- { "Short Message fragment overlap", "smpp.fragment.overlap",
- FT_BOOLEAN, BASE_NONE, NULL, 0x00,
- "SMPP Short Message fragment overlaps with other fragment(s)",
- HFILL
- }
- },
- { &hf_sm_fragment_overlap_conflicts,
- { "Short Message fragment overlapping with conflicting data",
- "smpp.fragment.overlap.conflicts",
- FT_BOOLEAN, BASE_NONE, NULL, 0x00,
- "SMPP Short Message fragment overlaps with conflicting data",
- HFILL
- }
- },
- { &hf_sm_fragment_multiple_tails,
- { "Short Message has multiple tail fragments",
- "smpp.fragment.multiple_tails",
- FT_BOOLEAN, BASE_NONE, NULL, 0x00,
- "SMPP Short Message fragment has multiple tail fragments",
- HFILL
- }
- },
- { &hf_sm_fragment_too_long_fragment,
- { "Short Message fragment too long",
- "smpp.fragment.too_long_fragment",
- FT_BOOLEAN, BASE_NONE, NULL, 0x00,
- "SMPP Short Message fragment data goes beyond the packet end",
- HFILL
- }
- },
- { &hf_sm_fragment_error,
- { "Short Message defragmentation error", "smpp.fragment.error",
- FT_FRAMENUM, BASE_NONE, NULL, 0x00,
- "SMPP Short Message defragmentation error due to illegal fragments",
- HFILL
- }
- },
};
/* Setup protocol subtree array */
static gint *ett[] = {
- &ett_smpp,
- &ett_dlist,
- &ett_dlist_resp,
- &ett_opt_param,
- &ett_dcs,
- &ett_udh,
- &ett_udh_multiple_messages,
- &ett_udh_ports,
- &ett_sm_fragment,
- &ett_sm_fragments,
+ &ett_smpp,
+ &ett_dlist,
+ &ett_dlist_resp,
+ &ett_opt_param,
+ &ett_dcs,
};
+ DebugLog(("Registering SMPP dissector\n"));
/* Register the protocol name and description */
proto_smpp = proto_register_protocol("Short Message Peer to Peer",
"SMPP", "smpp");
proto_register_field_array(proto_smpp, hf, array_length(hf));
proto_register_subtree_array(ett, array_length(ett));
- /* Subdissector code */
- smpp_dissector_table = register_dissector_table("smpp.udh.port",
- "SMPP UDH port", FT_UINT16, BASE_DEC);
-
- /* Preferences for SMPP */
- smpp_module = prefs_register_protocol (proto_smpp, NULL);
- prefs_register_bool_preference (smpp_module, "port_number_udh_means_wsp",
- "Port Number IE in UDH always triggers CL-WSP dissection",
- "Always decode a Short Message as Connectionless WSP "
- "if a Port Number Information Element is present "
- "in the User Data Header.",
- &port_number_udh_means_wsp);
- prefs_register_bool_preference (smpp_module, "try_dissect_1st_fragment",
- "Always try subdissection of 1st Short Message fragment",
- "Always try subdissection of the 1st fragment of a fragmented "
- "Short Message. If reassembly is possible, the Short Message "
- "may be dissected twice (once as a short frame, once in its "
- "entirety).",
- &try_dissect_1st_frag);
-
- /* SMPP dissector initialization routines */
- register_init_routine (sm_defragment_init);
+ /* Preferences */
+ smpp_module = prefs_register_protocol (proto_smpp, NULL);
+ prefs_register_bool_preference (smpp_module,
+ "reassemble_smpp_over_tcp",
+ "Reassemble SMPP over TCP",
+ "Reassemble TCP segments that convey SMPP traffic.",
+ &reassemble_over_tcp);
}
/*
heur_dissector_add("x.25", dissect_smpp_heur, proto_smpp);
/* Required for call_dissector() */
- wsp_handle = find_dissector ("wsp-cl");
- assert (wsp_handle);
+ DebugLog(("Finding gsm-sms-ud subdissector\n"));
+ gsm_sms_handle = find_dissector("gsm-sms-ud");
+ g_assert (gsm_sms_handle);
}