2 * Routines for GSM SMS TP-UD (GSM 03.40) dissection
4 * Refer to the AUTHORS file or the AUTHORS section in the man page
5 * for contacting the author(s) of this file.
7 * Separated from the SMPP dissector by Chris Wilson.
9 * UDH and WSP dissection of SMS message, Short Message reassembly,
10 * "Decode Short Message with Port Number UDH as CL-WSP" preference,
11 * "Always try subdissection of 1st fragment" preference,
12 * provided by Olivier Biot.
14 * Note on SMS Message reassembly
15 * ------------------------------
16 * The current Short Message reassembly is possible thanks to the
17 * message identifier (8 or 16 bit identifier). It is able to reassemble
18 * short messages that are sent over either the same SMPP connection or
19 * distinct SMPP connections. Normally the reassembly code is able to deal
20 * with duplicate message identifiers since the fragment_add_seq_check()
23 * The SMS TP-UD preference "always try subdissection of 1st fragment" allows
24 * a subdissector to be called for the first Short Message fragment,
25 * even if reassembly is not possible. This way partial dissection
26 * is still possible. This preference is switched off by default.
28 * Note on Short Message decoding as CL-WSP
29 * ----------------------------------------
30 * The SMS TP-UD preference "port_number_udh_means_wsp" is switched off
31 * by default. If it is enabled, then any Short Message with a Port Number
32 * UDH will be decoded as CL-WSP if:
33 * - The Short Message is not segmented
34 * - The entire segmented Short Message is reassembled
35 * - It is the 1st segment of an unreassembled Short Message (if the
36 * "always try subdissection of 1st fragment" preference is enabled)
38 * Wireshark - Network traffic analyzer
39 * By Gerald Combs <gerald@wireshark.org>
40 * Copyright 1998 Gerald Combs
42 * SPDX-License-Identifier: GPL-2.0-or-later
47 #include <epan/packet.h>
49 #include <epan/prefs.h>
50 #include <epan/reassemble.h>
51 #include "packet-gsm_sms.h"
52 #include "packet-smpp.h"
54 void proto_register_gsm_sms_ud(void);
55 void proto_reg_handoff_gsm_sms_ud(void);
57 static int proto_gsm_sms_ud
;
60 * Short Message fragment handling
62 static int hf_gsm_sms_ud_fragments
;
63 static int hf_gsm_sms_ud_fragment
;
64 static int hf_gsm_sms_ud_fragment_overlap
;
65 static int hf_gsm_sms_ud_fragment_overlap_conflicts
;
66 static int hf_gsm_sms_ud_fragment_multiple_tails
;
67 static int hf_gsm_sms_ud_fragment_too_long_fragment
;
68 static int hf_gsm_sms_ud_fragment_error
;
69 static int hf_gsm_sms_ud_fragment_count
;
70 static int hf_gsm_sms_ud_reassembled_in
;
71 static int hf_gsm_sms_ud_reassembled_length
;
72 static int hf_gsm_sms_ud_short_msg
;
74 static int ett_gsm_sms
;
75 static int ett_gsm_sms_ud_fragment
;
76 static int ett_gsm_sms_ud_fragments
;
78 /* Subdissector declarations */
79 static dissector_table_t gsm_sms_dissector_table
;
81 /* Short Message reassembly */
82 static reassembly_table sm_reassembly_table
;
84 static const fragment_items sm_frag_items
= {
85 /* Fragment subtrees */
86 &ett_gsm_sms_ud_fragment
,
87 &ett_gsm_sms_ud_fragments
,
89 &hf_gsm_sms_ud_fragments
,
90 &hf_gsm_sms_ud_fragment
,
91 &hf_gsm_sms_ud_fragment_overlap
,
92 &hf_gsm_sms_ud_fragment_overlap_conflicts
,
93 &hf_gsm_sms_ud_fragment_multiple_tails
,
94 &hf_gsm_sms_ud_fragment_too_long_fragment
,
95 &hf_gsm_sms_ud_fragment_error
,
96 &hf_gsm_sms_ud_fragment_count
,
97 /* Reassembled in field */
98 &hf_gsm_sms_ud_reassembled_in
,
99 /* Reassembled length field */
100 &hf_gsm_sms_ud_reassembled_length
,
101 /* Reassembled data field */
104 "Short Message fragments"
107 /* Dissect all SM data as WSP if the UDH contains a Port Number IE */
108 static bool port_number_udh_means_wsp
;
110 /* Always try dissecting the 1st fragment of a SM,
111 * even if it is not reassembled */
112 static bool try_dissect_1st_frag
;
114 /* Prevent subdissectors changing column data */
115 static bool prevent_subdissectors_changing_columns
;
117 static dissector_handle_t wsp_handle
;
119 /* Parse Short Message. This function is only called from the SMPP
120 * dissector if the UDH present, or if the UDH fields were obtained
121 * elsewhere in SMPP TLVs.
122 * Call WSP dissector if port matches WSP traffic.
125 parse_gsm_sms_ud_message(proto_tree
*sm_tree
, tvbuff_t
*tvb
, packet_info
*pinfo
, smpp_data_t
*smpp_data
)
127 tvbuff_t
*sm_tvb
= NULL
;
128 proto_tree
*top_tree
;
129 unsigned sm_len
= tvb_reported_length(tvb
);
131 /* Multiple Messages UDH */
132 bool is_fragmented
= false;
133 fragment_head
*fd_sm
= NULL
;
134 bool save_fragmented
= false;
135 bool try_gsm_sms_ud_reassemble
= false;
136 /* SMS Message reassembly */
137 bool reassembled
= false;
138 uint32_t reassembled_in
= 0;
140 gsm_sms_udh_fields_t
*udh_fields
= NULL
;
142 udh_fields
= smpp_data
->udh_fields
;
145 top_tree
= proto_tree_get_parent_tree(sm_tree
);
148 udh_fields
= wmem_new0(pinfo
->pool
, gsm_sms_udh_fields_t
);
151 if (smpp_data
&& smpp_data
->udhi
) {
152 /* XXX: We don't handle different encodings in this dissector yet,
153 * so just treat everything as 8-bit binary encoding. */
154 uint8_t fill_bits
= 0;
155 uint8_t udl
= sm_len
;
156 dis_field_udh(tvb
, pinfo
, sm_tree
, &i
, &sm_len
, &udl
, OTHER
, &fill_bits
, udh_fields
);
159 if (tvb_reported_length_remaining(tvb
, i
) <= 0)
160 return; /* No more data */
162 if (udh_fields
->frags
> 1) {
163 is_fragmented
= true;
167 * Try reassembling the packets.
168 * XXX - fragment numbers are 1-origin, but the fragment number
170 * Should we flag a fragmented message with a fragment number field
172 * What if the fragment count is 0? Should we flag that as well?
174 if (is_fragmented
&& udh_fields
->frag
!= 0 && udh_fields
->frags
!= 0 &&
175 tvb_bytes_exist(tvb
, i
, sm_len
)) {
176 try_gsm_sms_ud_reassemble
= true;
177 save_fragmented
= pinfo
->fragmented
;
178 pinfo
->fragmented
= true;
179 fd_sm
= fragment_add_seq_check(&sm_reassembly_table
,
182 udh_fields
->sm_id
, /* uint32_t ID for fragments belonging together */
184 udh_fields
->frag
-1, /* uint32_t fragment sequence number */
185 sm_len
, /* uint32_t fragment length */
186 (udh_fields
->frag
!= udh_fields
->frags
)); /* More fragments? */
189 reassembled_in
= fd_sm
->reassembled_in
;
191 sm_tvb
= process_reassembled_data(tvb
, i
, pinfo
,
192 "Reassembled Short Message", fd_sm
, &sm_frag_items
,
194 if (reassembled
) { /* Reassembled */
195 col_append_str(pinfo
->cinfo
, COL_INFO
,
196 " (Short Message Reassembled)");
198 /* Not last packet of reassembled Short Message */
199 col_append_fstr(pinfo
->cinfo
, COL_INFO
,
200 " (Short Message fragment %u of %u)", udh_fields
->frag
, udh_fields
->frags
);
202 } /* Else: not fragmented */
204 if (! sm_tvb
) /* One single Short Message, or not reassembled */
205 sm_tvb
= tvb_new_subset_remaining(tvb
, i
);
206 /* Try calling a subdissector */
208 if ((reassembled
&& pinfo
->num
== reassembled_in
)
209 || udh_fields
->frag
==0 || (udh_fields
->frag
==1 && try_dissect_1st_frag
)) {
210 /* Try calling a subdissector only if:
211 * - the Short Message is reassembled in this very packet,
212 * - the Short Message consists of only one "fragment",
213 * - the preference "Always Try Dissection for 1st SM fragment"
214 * is switched on, and this is the SM's 1st fragment. */
215 if (udh_fields
->port_src
|| udh_fields
->port_dst
) {
216 bool disallow_write
= false; /* true if we changed writability
217 of the columns of the summary */
218 if (prevent_subdissectors_changing_columns
&& col_get_writable(pinfo
->cinfo
, -1)) {
219 disallow_write
= true;
220 col_set_writable(pinfo
->cinfo
, -1, false);
223 if (port_number_udh_means_wsp
) {
224 call_dissector(wsp_handle
, sm_tvb
, pinfo
, top_tree
);
226 if (! dissector_try_uint(gsm_sms_dissector_table
, udh_fields
->port_src
,
227 sm_tvb
, pinfo
, top_tree
)) {
228 if (! dissector_try_uint(gsm_sms_dissector_table
, udh_fields
->port_dst
,
229 sm_tvb
, pinfo
, top_tree
)) {
230 if (sm_tree
) { /* Only display if needed */
231 proto_tree_add_item(sm_tree
, hf_gsm_sms_ud_short_msg
, sm_tvb
, 0, -1, ENC_NA
);
238 col_set_writable(pinfo
->cinfo
, -1, true);
239 } else { /* No ports IE */
240 proto_tree_add_item(sm_tree
, hf_gsm_sms_ud_short_msg
, sm_tvb
, 0, -1, ENC_NA
);
243 /* The packet is not reassembled,
244 * or it is reassembled in another packet */
245 proto_tree_add_bytes_format(sm_tree
, hf_gsm_sms_ud_short_msg
, sm_tvb
, 0, -1,
246 NULL
, "Unreassembled Short Message fragment %u of %u",
247 udh_fields
->frag
, udh_fields
->frags
);
251 if (try_gsm_sms_ud_reassemble
) /* Clean up defragmentation */
252 pinfo
->fragmented
= save_fragmented
;
257 dissect_gsm_sms_ud(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void* data
)
261 smpp_data_t
*smpp_data
= NULL
;
264 smpp_data
= (smpp_data_t
*)data
;
267 ti
= proto_tree_add_item(tree
, proto_gsm_sms_ud
, tvb
, 0, -1, ENC_NA
);
268 subtree
= proto_item_add_subtree(ti
, ett_gsm_sms
);
269 parse_gsm_sms_ud_message(subtree
, tvb
, pinfo
, smpp_data
);
270 return tvb_captured_length(tvb
);
273 /* Register the protocol with Wireshark */
275 proto_register_gsm_sms_ud(void)
277 module_t
*gsm_sms_ud_module
; /* Preferences for GSM SMS UD */
279 /* Setup list of header fields */
280 static hf_register_info hf
[] = {
282 * Short Message fragment reassembly
284 { &hf_gsm_sms_ud_fragments
,
285 { "Short Message fragments", "gsm_sms_ud.fragments",
286 FT_NONE
, BASE_NONE
, NULL
, 0x00,
287 "GSM Short Message fragments",
291 { &hf_gsm_sms_ud_fragment
,
292 { "Short Message fragment", "gsm_sms_ud.fragment",
293 FT_FRAMENUM
, BASE_NONE
, NULL
, 0x00,
294 "GSM Short Message fragment",
298 { &hf_gsm_sms_ud_fragment_overlap
,
299 { "Short Message fragment overlap", "gsm_sms_ud.fragment.overlap",
300 FT_BOOLEAN
, BASE_NONE
, NULL
, 0x0,
301 "GSM Short Message fragment overlaps with other fragment(s)",
305 { &hf_gsm_sms_ud_fragment_overlap_conflicts
,
306 { "Short Message fragment overlapping with conflicting data",
307 "gsm_sms_ud.fragment.overlap.conflicts",
308 FT_BOOLEAN
, BASE_NONE
, NULL
, 0x0,
309 "GSM Short Message fragment overlaps with conflicting data",
313 { &hf_gsm_sms_ud_fragment_multiple_tails
,
314 { "Short Message has multiple tail fragments",
315 "gsm_sms_ud.fragment.multiple_tails",
316 FT_BOOLEAN
, BASE_NONE
, NULL
, 0x0,
317 "GSM Short Message fragment has multiple tail fragments",
321 { &hf_gsm_sms_ud_fragment_too_long_fragment
,
322 { "Short Message fragment too long",
323 "gsm_sms_ud.fragment.too_long_fragment",
324 FT_BOOLEAN
, BASE_NONE
, NULL
, 0x0,
325 "GSM Short Message fragment data goes beyond the packet end",
329 { &hf_gsm_sms_ud_fragment_error
,
330 { "Short Message defragmentation error", "gsm_sms_ud.fragment.error",
331 FT_FRAMENUM
, BASE_NONE
, NULL
, 0x00,
332 "GSM Short Message defragmentation error due to illegal fragments",
336 { &hf_gsm_sms_ud_fragment_count
,
337 { "Short Message fragment count", "gsm_sms_ud.fragment.count",
338 FT_UINT32
, BASE_DEC
, NULL
, 0x00,
343 { &hf_gsm_sms_ud_reassembled_in
,
345 "gsm_sms_ud.reassembled.in",
346 FT_FRAMENUM
, BASE_NONE
, NULL
, 0x00,
347 "GSM Short Message has been reassembled in this packet.",
351 { &hf_gsm_sms_ud_reassembled_length
,
352 { "Reassembled Short Message length",
353 "gsm_sms_ud.reassembled.length",
354 FT_UINT32
, BASE_DEC
, NULL
, 0x00,
355 "The total length of the reassembled payload",
359 { &hf_gsm_sms_ud_short_msg
,
360 { "Short Message body",
361 "gsm_sms_ud.short_msg",
362 FT_BYTES
, BASE_NONE
, NULL
, 0x00, NULL
, HFILL
367 static int *ett
[] = {
369 &ett_gsm_sms_ud_fragment
,
370 &ett_gsm_sms_ud_fragments
,
372 /* Register the protocol name and description */
373 proto_gsm_sms_ud
= proto_register_protocol("GSM Short Message Service User Data", "GSM SMS UD", "gsm_sms_ud");
375 /* Required function calls to register header fields and subtrees used */
376 proto_register_field_array(proto_gsm_sms_ud
, hf
, array_length(hf
));
377 proto_register_subtree_array(ett
, array_length(ett
));
379 /* Subdissector code */
380 gsm_sms_dissector_table
= register_dissector_table("gsm_sms_ud.udh.port",
381 "GSM SMS port IE in UDH", proto_gsm_sms_ud
, FT_UINT16
, BASE_DEC
);
383 /* Preferences for GSM SMS UD */
384 gsm_sms_ud_module
= prefs_register_protocol(proto_gsm_sms_ud
, NULL
);
385 /* For reading older preference files with "smpp-gsm-sms." preferences */
386 prefs_register_module_alias("smpp-gsm-sms", gsm_sms_ud_module
);
387 prefs_register_bool_preference(gsm_sms_ud_module
,
388 "port_number_udh_means_wsp",
389 "Port Number IE in UDH always triggers CL-WSP dissection",
390 "Always decode a GSM Short Message as Connectionless WSP "
391 "if a Port Number Information Element is present "
392 "in the SMS User Data Header.",
393 &port_number_udh_means_wsp
);
394 prefs_register_bool_preference(gsm_sms_ud_module
, "try_dissect_1st_fragment",
395 "Always try subdissection of 1st Short Message fragment",
396 "Always try subdissection of the 1st fragment of a fragmented "
397 "GSM Short Message. If reassembly is possible, the Short Message "
398 "may be dissected twice (once as a short frame, once in its "
400 &try_dissect_1st_frag
);
401 prefs_register_bool_preference(gsm_sms_ud_module
, "prevent_dissectors_chg_cols",
402 "Prevent sub-dissectors from changing column data",
403 "Prevent sub-dissectors from replacing column data with their "
404 "own. Eg. Prevent WSP dissector overwriting SMPP information.",
405 &prevent_subdissectors_changing_columns
);
407 register_dissector("gsm_sms_ud", dissect_gsm_sms_ud
, proto_gsm_sms_ud
);
409 reassembly_table_register(&sm_reassembly_table
,
410 &addresses_reassembly_table_functions
);
414 proto_reg_handoff_gsm_sms_ud(void)
416 wsp_handle
= find_dissector_add_dependency("wsp-cl", proto_gsm_sms_ud
);
417 DISSECTOR_ASSERT(wsp_handle
);
421 * Editor modelines - https://www.wireshark.org/tools/modelines.html
426 * indent-tabs-mode: nil
429 * vi: set shiftwidth=4 tabstop=8 expandtab:
430 * :indentSize=4:tabSize=8:noTabs=true: