1 /* nettrace_3gpp_32_423.c
3 * Decoder for 3GPP TS 32.423 file format for the Wiretap library.
4 * The main purpose is to have Wireshark decode raw message content (<rawMsg> tag).
6 * SPDX-License-Identifier: GPL-2.0-or-later
8 * Ref: https://portal.3gpp.org/desktopmodules/Specifications/SpecificationDetails.aspx?specificationId=2010
12 #include "nettrace_3gpp_32_423.h"
14 #include <sys/types.h>
25 #include "file_wrappers.h"
27 #include <wsutil/exported_pdu_tlvs.h>
28 #include <wsutil/buffer.h>
29 #include "wsutil/tempfile.h"
30 #include "wsutil/os_version_info.h"
31 #include "wsutil/str_util.h"
32 #include <wsutil/inet_addr.h>
33 #include <wsutil/ws_assert.h>
36 /* String constants sought in the XML data.
37 * Written as strings instead of lists of chars for readability.
38 * Use the CLEN() macro to get the length of the constant without counting
39 * the null byte at the end.
41 #define CLEN(x) (sizeof(x)-1)
42 static const unsigned char c_xml_magic
[] = "<?xml";
43 static const unsigned char c_file_header
[] = "<fileHeader";
44 static const unsigned char c_file_format_version
[] = "fileFormatVersion=\"";
45 static const unsigned char c_threegpp_doc_no
[] = "32.423";
46 static const unsigned char c_begin_time
[] = "<traceCollec beginTime=\"";
47 static const unsigned char c_s_msg
[] = "<msg";
48 static const unsigned char c_e_msg
[] = "</msg>";
49 static const unsigned char c_s_rawmsg
[] = "<rawMsg";
50 static const unsigned char c_change_time
[] = "changeTime=\"";
51 static const unsigned char c_function
[] = "function=\"";
52 static const unsigned char c_proto_name
[] = "name=\"";
53 //static const unsigned char c_address[] = "ddress"; /* omit the 'a' to cater for "Address" */
54 static const unsigned char c_s_initiator
[] = "<initiator";
55 static const unsigned char c_e_initiator
[] = "</initiator>";
56 static const unsigned char c_s_target
[] = "<target";
57 static const unsigned char c_e_target
[] = "</target>";
58 static const unsigned char c_protocol
[] = "protocol=\"";
60 /* These are protocol names we may put in the exported-pdu data based on
61 * what's in the XML. They're defined here as constants so we can use
62 * sizeof()/CLEN() on them and slightly reduce our use of magic constants
63 * for their size. (Modern compilers should make this no slower than that.)
65 static const unsigned char c_sai_req
[] = "gsm_map.v3.arg.opcode";
66 static const unsigned char c_sai_rsp
[] = "gsm_map.v3.res.opcode";
67 static const unsigned char c_nas_eps
[] = "nas-eps_plain";
68 static const unsigned char c_nas_5gs
[] = "nas-5gs";
71 #define RINGBUFFER_START_SIZE INT_MAX
72 #define RINGBUFFER_CHUNK_SIZE 1024
74 #define MAX_FUNCTION_LEN 64
75 #define MAX_NAME_LEN 128
76 #define MAX_PROTO_LEN 16
77 #define MAX_DTBL_LEN 32
79 /* We expect to find all the info we need to tell if this file is ours
80 * within this many bytes. Must include the beginTime attribute.
82 #define MAGIC_BUF_SIZE 1024
84 typedef struct nettrace_3gpp_32_423_file_info
{
85 GByteArray
*buffer
; // holds current chunk of file
86 int64_t start_offset
; // where in the file the start of the buffer points
87 nstime_t start_time
; // from <traceCollec beginTime=""> attribute
88 } nettrace_3gpp_32_423_file_info_t
;
91 typedef struct exported_pdu_info
{
92 uint32_t presence_flags
;
94 uint32_t ptype
; /* Based on epan/address.h port_type valid for both src and dst*/
99 } exported_pdu_info_t
;
101 /* flags for exported_pdu_info.presence_flags */
102 #define EXP_PDU_TAG_IP_SRC_BIT 0x001
103 #define EXP_PDU_TAG_IP_DST_BIT 0x002
104 #define EXP_PDU_TAG_SRC_PORT_BIT 0x004
105 #define EXP_PDU_TAG_DST_PORT_BIT 0x008
106 #define EXP_PDU_TAG_ORIG_FNO_BIT 0x010
107 #define EXP_PDU_TAG_SS7_OPC_BIT 0x020
108 #define EXP_PDU_TAG_SS7_DPC_BIT 0x040
109 #define EXP_PDU_TAG_IP6_SRC_BIT 0x080
110 #define EXP_PDU_TAG_IP6_DST_BIT 0x100
111 #define EXP_PDU_TAG_DVBCI_EVT_BIT 0x0100
112 #define EXP_PDU_TAG_COL_PROT_BIT 0x0200
115 static int nettrace_3gpp_32_423_file_type_subtype
= -1;
117 void register_nettrace_3gpp_32_423(void);
119 /* Parse a string IPv4 or IPv6 address into bytes for exported_pdu_info.
120 * Also parses the port pairs and transport layer type.
123 nettrace_parse_address(char* curr_pos
, char* next_pos
, bool is_src_addr
, exported_pdu_info_t
*exported_pdu_info
)
126 ws_in6_addr ip6_addr
;
128 char *err
; //for strtol function
129 char saved_next_char
;
131 GMatchInfo
*match_info
;
132 static GRegex
*regex
= NULL
;
133 char *matched_ipaddress
= NULL
;
134 char *matched_port
= NULL
;
135 char *matched_transport
= NULL
;
137 /* Excample from one trace, unsure if it's generic...
138 * {address == 192.168.73.1, port == 5062, transport == Udp}
139 * {address == [2001:1b70:8294:210a::78], port...
140 * {address == 2001:1B70:8294:210A::90, port...
141 * Address=198.142.204.199,Port=2123
145 regex
= g_regex_new (
146 "^.*address\\s*=*\\s*" //curr_pos will begin with address
147 "\\[?(?P<ipaddress>(?:" //store ipv4 or ipv6 address in named group "ipaddress"
148 "(?:\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3})" //match an IPv4 address
150 "(?:[0-9a-f:]*)))\\]?" //match an IPv6 address.
151 "(?:.*port\\s*=*\\s*(?P<port>\\d{1,5}))?" //match a port store it in named group "port"
152 "(?:.*transport\\s*=*\\s*(?P<transport>\\w+))?", //match a transport store it in named group "transport"
153 G_REGEX_CASELESS
| G_REGEX_FIRSTLINE
, 0, NULL
);
156 /* curr_pos pointing to first char of "address" */
157 /* Ensure we don't overrun the intended input. The input will always
158 * be a mutable buffer, so modifying it is safe. */
159 saved_next_char
= *next_pos
;
160 *next_pos
= '\0'; /* Insert a NUL terminator. */
161 g_regex_match (regex
, curr_pos
, 0, &match_info
);
163 if (g_match_info_matches (match_info
)) {
164 matched_ipaddress
= g_match_info_fetch_named(match_info
, "ipaddress"); //will be empty string if no ipv4 or ipv6
165 matched_port
= g_match_info_fetch_named(match_info
, "port"); //will be empty string if port not in trace
166 if (matched_port
!= NULL
) {
167 port
= (unsigned) strtol(matched_port
, &err
, 10);
168 g_free(matched_port
);
170 matched_transport
= g_match_info_fetch_named(match_info
, "transport"); //will be empty string if transport not in trace
172 g_match_info_free(match_info
);
173 *next_pos
= saved_next_char
;
177 g_match_info_free(match_info
);
178 *next_pos
= saved_next_char
;
180 if (ws_inet_pton6(matched_ipaddress
, &ip6_addr
)) {
182 exported_pdu_info
->presence_flags
|= EXP_PDU_TAG_IP6_SRC_BIT
;
183 memcpy(exported_pdu_info
->src_ip
, ip6_addr
.bytes
, EXP_PDU_TAG_IPV6_LEN
);
186 exported_pdu_info
->presence_flags
|= EXP_PDU_TAG_IP6_DST_BIT
;
187 memcpy(exported_pdu_info
->dst_ip
, ip6_addr
.bytes
, EXP_PDU_TAG_IPV6_LEN
);
190 else if (ws_inet_pton4(matched_ipaddress
, &ip4_addr
)) {
192 exported_pdu_info
->presence_flags
|= EXP_PDU_TAG_IP_SRC_BIT
;
193 memcpy(exported_pdu_info
->src_ip
, &ip4_addr
, EXP_PDU_TAG_IPV4_LEN
);
196 exported_pdu_info
->presence_flags
|= EXP_PDU_TAG_IP_DST_BIT
;
197 memcpy(exported_pdu_info
->dst_ip
, &ip4_addr
, EXP_PDU_TAG_IPV4_LEN
);
202 /* Only add port_type once */
203 if (exported_pdu_info
->ptype
== EXP_PDU_PT_NONE
) {
204 if (g_ascii_strncasecmp(matched_transport
, "udp", 3) == 0) {
205 exported_pdu_info
->ptype
= EXP_PDU_PT_UDP
;
207 else if (g_ascii_strncasecmp(matched_transport
, "tcp", 3) == 0) {
208 exported_pdu_info
->ptype
= EXP_PDU_PT_TCP
;
210 else if (g_ascii_strncasecmp(matched_transport
, "sctp", 4) == 0) {
211 exported_pdu_info
->ptype
= EXP_PDU_PT_SCTP
;
215 exported_pdu_info
->presence_flags
|= EXP_PDU_TAG_SRC_PORT_BIT
;
216 exported_pdu_info
->src_port
= port
;
219 exported_pdu_info
->presence_flags
|= EXP_PDU_TAG_DST_PORT_BIT
;
220 exported_pdu_info
->dst_port
= port
;
223 g_free(matched_ipaddress
);
224 g_free(matched_transport
);
229 /* Parse a <msg ...><rawMsg ...>XXXX</rawMsg></msg> into packet data. */
231 nettrace_msg_to_packet(nettrace_3gpp_32_423_file_info_t
*file_info
, wtap_rec
*rec
, Buffer
*buf
, uint8_t *input
, size_t len
, int *err
, char **err_info
)
233 /* Convenience macro. haystack must be >= input! */
234 #define STRNSTR(haystack, needle) g_strstr_len(haystack, (len - ((uint8_t*)(haystack) - (uint8_t*)input)), needle)
237 char *curr_pos
, *next_msg_pos
, *next_pos
, *prev_pos
;
238 exported_pdu_info_t exported_pdu_info
= {0};
241 char* start_msg_tag_cont
;
242 char function_str
[MAX_FUNCTION_LEN
+1];
243 char name_str
[MAX_NAME_LEN
+1];
244 char proto_name_str
[MAX_PROTO_LEN
+1];
245 char dissector_table_str
[MAX_DTBL_LEN
+1];
246 int dissector_table_val
= 0;
248 int function_str_len
= 0;
249 int name_str_len
= 0;
250 int proto_str_len
, dissector_table_str_len
, raw_data_len
, pkt_data_len
, exp_pdu_tags_len
, i
;
253 bool use_proto_table
= false;
255 /* We should always and only be called with a <msg....</msg> payload */
256 if (0 != strncmp(input
, c_s_msg
, CLEN(c_s_msg
))) {
257 *err
= WTAP_ERR_BAD_FILE
;
258 *err_info
= ws_strdup_printf("nettrace_3gpp_32_423: Did not start with \"%s\"", c_s_msg
);
262 curr_pos
= input
+ CLEN(c_s_msg
);
264 rec
->rec_type
= REC_TYPE_PACKET
;
265 rec
->block
= wtap_block_create(WTAP_BLOCK_PACKET
);
266 rec
->presence_flags
= 0; /* start out assuming no special features */
270 /* Clear for each iteration */
271 exported_pdu_info
.presence_flags
= 0;
272 exported_pdu_info
.ptype
= EXP_PDU_PT_NONE
;
275 /* Look for the end of the tag first */
276 next_msg_pos
= STRNSTR(curr_pos
, ">");
278 /* Something's wrong, bail out */
279 *err
= WTAP_ERR_BAD_FILE
;
280 *err_info
= g_strdup("Did not find end of tag \">\"");
284 /* Check if its a tag close "/>" */
285 if (*(next_msg_pos
- 1) == '/') {
286 /* There is no rawmsg here. Should have been caught before we got called */
287 *err
= WTAP_ERR_INTERNAL
;
288 *err_info
= g_strdup("Had \"<msg />\" with no \"<rawMsg>\"");
292 start_msg_tag_cont
= curr_pos
= prev_pos
;
293 next_msg_pos
= STRNSTR(curr_pos
, c_e_msg
);
295 /* Something's wrong, bail out */
296 *err
= WTAP_ERR_BAD_FILE
;
297 *err_info
= ws_strdup_printf("nettrace_3gpp_32_423: Did not find \"%s\"", c_e_msg
);
302 /* Check if we have a time stamp "changeTime"
303 * expressed in number of seconds and milliseconds (nbsec.ms).
304 * Only needed if we have a "beginTime" for this file.
306 if (!nstime_is_unset(&(file_info
->start_time
))) {
308 unsigned second
= 0, ms
= 0;
310 curr_pos
= STRNSTR(start_msg_tag_cont
, c_change_time
);
311 /* Check if we have the tag or if we passed the end of the current message */
312 if (curr_pos
!= NULL
) {
313 curr_pos
+= CLEN(c_change_time
);
314 scan_found
= sscanf(curr_pos
, "%u.%u", &second
, &ms
);
316 if (scan_found
== 2) {
317 unsigned start_ms
= file_info
->start_time
.nsecs
/ 1000000;
318 unsigned elapsed_ms
= start_ms
+ ms
;
319 if (elapsed_ms
> 1000) {
323 rec
->presence_flags
|= WTAP_HAS_TS
;
324 rec
->ts
.secs
= file_info
->start_time
.secs
+ second
;
325 rec
->ts
.nsecs
= (elapsed_ms
* 1000000);
330 /* See if we have a "function" */
331 function_str
[0] = '\0'; /* if we don't have a function */
332 curr_pos
= STRNSTR(start_msg_tag_cont
, c_function
);
333 if (curr_pos
!= NULL
) {
334 /* extract the function */
335 curr_pos
+= CLEN(c_function
);
336 next_pos
= STRNSTR(curr_pos
, "\"");
337 function_str_len
= (int)(next_pos
- curr_pos
);
338 if (function_str_len
> MAX_FUNCTION_LEN
) {
339 *err
= WTAP_ERR_BAD_FILE
;
340 *err_info
= ws_strdup_printf("nettrace_3gpp_32_423: function_str_len > %d", MAX_FUNCTION_LEN
);
344 (void) g_strlcpy(function_str
, curr_pos
, (size_t)function_str_len
+ 1);
345 ascii_strdown_inplace(function_str
);
349 /* See if we have a "name" */
350 name_str
[0] = '\0'; /* if we don't have a name */
351 curr_pos
= STRNSTR(start_msg_tag_cont
, c_proto_name
);
352 if (curr_pos
!= NULL
) {
353 /* extract the name */
354 curr_pos
+= CLEN(c_proto_name
);
355 next_pos
= STRNSTR(curr_pos
, "\"");
356 name_str_len
= (int)(next_pos
- curr_pos
);
357 if (name_str_len
> MAX_NAME_LEN
) {
358 *err
= WTAP_ERR_BAD_FILE
;
359 *err_info
= ws_strdup_printf("nettrace_3gpp_32_423: name_str_len > %d", MAX_NAME_LEN
);
363 (void) g_strlcpy(name_str
, curr_pos
, (size_t)name_str_len
+ 1);
364 ascii_strdown_inplace(name_str
);
367 /* Check if we have "<initiator>"
368 * It might contain an address
370 curr_pos
= STRNSTR(start_msg_tag_cont
, c_s_initiator
);
371 /* Check if we have the tag or if we passed the end of the current message */
372 if (curr_pos
!= NULL
) {
373 curr_pos
+= CLEN(c_s_initiator
);
374 next_pos
= STRNSTR(curr_pos
, c_e_initiator
);
376 //curr_pos = STRNSTR(curr_pos, c_address) - 1; //Not needed due to regex
377 if (curr_pos
!= NULL
) {
378 nettrace_parse_address(curr_pos
, next_pos
, true/* SRC */, &exported_pdu_info
);
382 /* Check if we have "<target>"
383 * It might contain an address
385 curr_pos
= STRNSTR(start_msg_tag_cont
, c_s_target
);
386 /* Check if we have the tag or if we passed the end of the current message */
387 if (curr_pos
!= NULL
) {
388 curr_pos
+= CLEN(c_s_target
);
389 next_pos
= STRNSTR(curr_pos
, c_e_target
);
391 //curr_pos = STRNSTR(curr_pos, c_address) - 1; //Not needed due to regex
392 if (curr_pos
!= NULL
) {
393 /* curr_pos set below */
394 nettrace_parse_address(curr_pos
, next_pos
, false/* DST */, &exported_pdu_info
);
398 /* Do we have a raw message in the <msg> </msg> section? */
399 raw_msg_pos
= STRNSTR(start_msg_tag_cont
, c_s_rawmsg
);
400 if (raw_msg_pos
== NULL
) {
401 *err
= WTAP_ERR_BAD_FILE
;
402 *err_info
= ws_strdup_printf("nettrace_3gpp_32_423: Did not find \"%s\"", c_s_rawmsg
);
406 curr_pos
= STRNSTR(raw_msg_pos
, c_protocol
);
407 if (curr_pos
== NULL
) {
408 *err
= WTAP_ERR_BAD_FILE
;
409 *err_info
= ws_strdup_printf("nettrace_3gpp_32_423: Did not find \"%s\"", c_protocol
);
413 curr_pos
+= CLEN(c_protocol
);
414 next_pos
= STRNSTR(curr_pos
, "\"");
415 proto_str_len
= (int)(next_pos
- curr_pos
);
416 if (proto_str_len
> MAX_PROTO_LEN
){
420 (void) g_strlcpy(proto_name_str
, curr_pos
, (size_t)proto_str_len
+1);
421 ascii_strdown_inplace(proto_name_str
);
423 /* Do string matching and replace with Wiresharks protocol name */
424 if (strcmp(proto_name_str
, "gtpv2-c") == 0) {
425 /* Change to gtpv2 */
426 proto_name_str
[5] = '\0';
429 if (strcmp(proto_name_str
, "nas") == 0) {
430 if (strcmp(function_str
, "s1") == 0) {
431 /* Change to nas-eps_plain */
432 (void) g_strlcpy(proto_name_str
, c_nas_eps
, sizeof(c_nas_eps
));
433 proto_str_len
= CLEN(c_nas_eps
);
434 } else if (strcmp(function_str
, "n1") == 0) {
435 /* Change to nas-5gs */
436 (void) g_strlcpy(proto_name_str
, c_nas_5gs
, sizeof(c_nas_5gs
));
437 proto_str_len
= CLEN(c_nas_5gs
);
439 *err
= WTAP_ERR_BAD_FILE
;
440 *err_info
= ws_strdup_printf("nettrace_3gpp_32_423: No handle of message \"%s\" on function \"%s\" ", proto_name_str
, function_str
);
446 if (strcmp(proto_name_str
, "map") == 0) {
447 /* For GSM map, it looks like the message data is stored like SendAuthenticationInfoArg
448 * use the GSM MAP dissector table to dissect the content.
450 exported_pdu_info
.proto_col_str
= g_strdup("GSM MAP");
452 if (strcmp(name_str
, "sai_request") == 0) {
453 use_proto_table
= true;
454 (void) g_strlcpy(dissector_table_str
, c_sai_req
, sizeof(c_sai_req
));
455 dissector_table_str_len
= CLEN(c_sai_req
);
456 dissector_table_val
= 56;
457 exported_pdu_info
.presence_flags
|= EXP_PDU_TAG_COL_PROT_BIT
;
459 else if (strcmp(name_str
, "sai_response") == 0) {
460 use_proto_table
= true;
461 (void) g_strlcpy(dissector_table_str
, c_sai_rsp
, sizeof(c_sai_rsp
));
462 dissector_table_str_len
= CLEN(c_sai_rsp
);
463 dissector_table_val
= 56;
464 exported_pdu_info
.presence_flags
|= EXP_PDU_TAG_COL_PROT_BIT
;
466 g_free(exported_pdu_info
.proto_col_str
);
467 exported_pdu_info
.proto_col_str
= NULL
;
470 /* Find the start of the raw data */
471 curr_pos
= STRNSTR(next_pos
, ">") + 1;
472 next_pos
= STRNSTR(curr_pos
, "<");
473 raw_data_len
= (int)(next_pos
- curr_pos
);
475 /* Fill packet buff */
476 ws_buffer_clean(buf
);
477 if (use_proto_table
== false) {
478 wtap_buffer_append_epdu_tag(buf
, EXP_PDU_TAG_DISSECTOR_NAME
, proto_name_str
, proto_str_len
);
481 wtap_buffer_append_epdu_tag(buf
, EXP_PDU_TAG_DISSECTOR_TABLE_NAME
, dissector_table_str
, dissector_table_str_len
);
482 wtap_buffer_append_epdu_uint(buf
, EXP_PDU_TAG_DISSECTOR_TABLE_NAME_NUM_VAL
, dissector_table_val
);
485 if (exported_pdu_info
.presence_flags
& EXP_PDU_TAG_COL_PROT_BIT
) {
486 wtap_buffer_append_epdu_string(buf
, EXP_PDU_TAG_COL_PROT_TEXT
, exported_pdu_info
.proto_col_str
);
487 g_free(exported_pdu_info
.proto_col_str
);
488 exported_pdu_info
.proto_col_str
= NULL
;
491 if (exported_pdu_info
.presence_flags
& EXP_PDU_TAG_IP_SRC_BIT
) {
492 wtap_buffer_append_epdu_tag(buf
, EXP_PDU_TAG_IPV4_SRC
, exported_pdu_info
.src_ip
, EXP_PDU_TAG_IPV4_LEN
);
494 if (exported_pdu_info
.presence_flags
& EXP_PDU_TAG_IP_DST_BIT
) {
495 wtap_buffer_append_epdu_tag(buf
, EXP_PDU_TAG_IPV4_DST
, exported_pdu_info
.dst_ip
, EXP_PDU_TAG_IPV4_LEN
);
498 if (exported_pdu_info
.presence_flags
& EXP_PDU_TAG_IP6_SRC_BIT
) {
499 wtap_buffer_append_epdu_tag(buf
, EXP_PDU_TAG_IPV6_SRC
, exported_pdu_info
.src_ip
, EXP_PDU_TAG_IPV6_LEN
);
501 if (exported_pdu_info
.presence_flags
& EXP_PDU_TAG_IP6_DST_BIT
) {
502 wtap_buffer_append_epdu_tag(buf
, EXP_PDU_TAG_IPV6_DST
, exported_pdu_info
.dst_ip
, EXP_PDU_TAG_IPV6_LEN
);
505 if (exported_pdu_info
.presence_flags
& (EXP_PDU_TAG_SRC_PORT_BIT
| EXP_PDU_TAG_DST_PORT_BIT
)) {
506 wtap_buffer_append_epdu_uint(buf
, EXP_PDU_TAG_PORT_TYPE
, exported_pdu_info
.ptype
);
508 if (exported_pdu_info
.presence_flags
& EXP_PDU_TAG_SRC_PORT_BIT
) {
509 wtap_buffer_append_epdu_uint(buf
, EXP_PDU_TAG_SRC_PORT
, exported_pdu_info
.src_port
);
511 if (exported_pdu_info
.presence_flags
& EXP_PDU_TAG_DST_PORT_BIT
) {
512 wtap_buffer_append_epdu_uint(buf
, EXP_PDU_TAG_DST_PORT
, exported_pdu_info
.dst_port
);
515 /* Add end of options */
516 exp_pdu_tags_len
= wtap_buffer_append_epdu_end(buf
);
518 /* Convert the hex raw msg data to binary and write to the packet buf*/
519 pkt_data_len
= raw_data_len
/ 2;
520 ws_buffer_assure_space(buf
, pkt_data_len
);
521 packet_buf
= ws_buffer_end_ptr(buf
);
523 for (i
= 0; i
< pkt_data_len
; i
++) {
528 val1
= g_ascii_xdigit_value(chr1
);
529 val2
= g_ascii_xdigit_value(chr2
);
530 if ((val1
!= -1) && (val2
!= -1)) {
531 *packet_buf
++ = ((uint8_t)val1
* 16) + val2
;
534 /* Something wrong, bail out */
535 *err_info
= ws_strdup_printf("nettrace_3gpp_32_423: Could not parse hex data, bufsize %u index %u %c%c",
536 (pkt_data_len
+ exp_pdu_tags_len
),
540 *err
= WTAP_ERR_BAD_FILE
;
545 ws_buffer_increase_length(buf
, pkt_data_len
);
547 rec
->rec_header
.packet_header
.caplen
= (uint32_t)ws_buffer_length(buf
);
548 rec
->rec_header
.packet_header
.len
= (uint32_t)ws_buffer_length(buf
);
555 /* Read from fh and store into buffer, until buffer contains needle.
556 * Returns location of needle once found, or NULL if it's never found
557 * (due to either EOF or read error).
560 read_until(GByteArray
*buffer
, const unsigned char *needle
, FILE_T fh
, int *err
, char **err_info
)
562 uint8_t read_buffer
[RINGBUFFER_CHUNK_SIZE
];
566 while (NULL
== (found_it
= g_strstr_len(buffer
->data
, buffer
->len
, needle
))) {
567 bytes_read
= file_read(read_buffer
, RINGBUFFER_CHUNK_SIZE
, fh
);
568 if (bytes_read
< 0) {
569 *err
= file_error(fh
, err_info
);
572 if (bytes_read
== 0) {
575 g_byte_array_append(buffer
, read_buffer
, bytes_read
);
580 /* Find a complete packet, parse and return it to wiretap.
581 * Set as the subtype_read function in the file_open function below.
584 nettrace_read(wtap
*wth
, wtap_rec
*rec
, Buffer
*buf
, int *err
, char **err_info
, int64_t *data_offset
)
586 nettrace_3gpp_32_423_file_info_t
*file_info
= (nettrace_3gpp_32_423_file_info_t
*)wth
->priv
;
588 uint8_t *msg_start
, *msg_end
;
589 unsigned msg_offset
= 0;
593 /* Make sure we have a start and end of message in our buffer -- end first */
594 msg_end
= read_until(file_info
->buffer
, c_e_msg
, wth
->fh
, err
, err_info
);
595 if (msg_end
== NULL
) {
599 buf_start
= file_info
->buffer
->data
;
600 /* Now search backwards for the message start
601 * (doing it this way should skip over any empty "<msg ... />" tags we have)
603 msg_start
= g_strrstr_len(buf_start
, (unsigned)(msg_end
- buf_start
), c_s_msg
);
604 if (msg_start
== NULL
|| msg_start
> msg_end
) {
605 *err_info
= ws_strdup_printf("nettrace_3gpp_32_423: Found \"%s\" without matching \"%s\"", c_e_msg
, c_s_msg
);
606 *err
= WTAP_ERR_BAD_FILE
;
610 /* We know we have a message, what's its offset from the buffer start? */
611 msg_offset
= (unsigned)(msg_start
- buf_start
);
612 msg_end
+= CLEN(c_e_msg
);
613 msg_len
= (unsigned)(msg_end
- msg_start
);
615 /* Tell Wireshark to put us at the start of the "<msg" for seek_read later */
616 *data_offset
= file_info
->start_offset
+ msg_offset
;
618 /* pass all of <msg....</msg> to nettrace_msg_to_packet() */
619 status
= nettrace_msg_to_packet(file_info
, rec
, buf
, msg_start
, msg_len
, err
, err_info
);
621 /* Finally, shift our buffer to the end of this message to get ready for the next one.
622 * Re-use msg_len to get the length of the data we're done with.
624 msg_len
= msg_end
- file_info
->buffer
->data
;
625 while (G_UNLIKELY(msg_len
> UINT_MAX
)) {
626 g_byte_array_remove_range(file_info
->buffer
, 0, UINT_MAX
);
629 g_byte_array_remove_range(file_info
->buffer
, 0, (unsigned)msg_len
);
630 file_info
->start_offset
+= msg_len
;
633 if (status
== false) {
634 /* There's no more to read. Empty out the buffer */
635 g_byte_array_set_size(file_info
->buffer
, 0);
641 /* Seek to the complete packet at the offset, parse and return it to wiretap.
642 * Set as the subtype_seek_read function in the file_open function below.
645 nettrace_seek_read(wtap
*wth
, int64_t seek_off
, wtap_rec
*rec
, Buffer
*buf
, int *err
, char **err_info
)
647 nettrace_3gpp_32_423_file_info_t
*file_info
= (nettrace_3gpp_32_423_file_info_t
*)wth
->priv
;
650 unsigned msg_len
= 0;
652 /* We stored the offset of the "<msg" for this packet */
653 if (file_seek(wth
->random_fh
, seek_off
, SEEK_SET
, err
) == -1)
656 msg_end
= read_until(file_info
->buffer
, c_e_msg
, wth
->random_fh
, err
, err_info
);
657 if (msg_end
== NULL
) {
660 msg_end
+= CLEN(c_e_msg
);
661 msg_len
= (unsigned)(msg_end
- file_info
->buffer
->data
);
663 status
= nettrace_msg_to_packet(file_info
, rec
, buf
, file_info
->buffer
->data
, msg_len
, err
, err_info
);
664 g_byte_array_set_size(file_info
->buffer
, 0);
668 /* Clean up any memory we allocated for dealing with this file.
669 * Set as the subtype_close function in the file_open function below.
670 * (wiretap frees wth->priv itself)
673 nettrace_close(wtap
*wth
)
675 nettrace_3gpp_32_423_file_info_t
*file_info
= (nettrace_3gpp_32_423_file_info_t
*)wth
->priv
;
677 if (file_info
!= NULL
&& file_info
->buffer
!= NULL
) {
678 g_byte_array_free(file_info
->buffer
, true);
679 file_info
->buffer
= NULL
;
683 /* Test the current file to see if it's one we can read.
684 * Set in file_access.c as the function to be called for this file type.
687 nettrace_3gpp_32_423_file_open(wtap
*wth
, int *err
, char **err_info
)
689 char magic_buf
[MAGIC_BUF_SIZE
+1];
691 const char *curr_pos
;
693 nettrace_3gpp_32_423_file_info_t
*file_info
;
694 int64_t start_offset
;
696 start_offset
= file_tell(wth
->fh
); // Most likely 0 but doesn't hurt to check
697 bytes_read
= file_read(magic_buf
, MAGIC_BUF_SIZE
, wth
->fh
);
699 if (bytes_read
< 0) {
700 *err
= file_error(wth
->fh
, err_info
);
701 return WTAP_OPEN_ERROR
;
703 if (bytes_read
== 0){
704 return WTAP_OPEN_NOT_MINE
;
707 if (memcmp(magic_buf
, c_xml_magic
, CLEN(c_xml_magic
)) != 0){
708 return WTAP_OPEN_NOT_MINE
;
711 curr_pos
= g_strstr_len(magic_buf
, bytes_read
, c_file_header
);
713 return WTAP_OPEN_NOT_MINE
;
715 curr_pos
= g_strstr_len(curr_pos
, bytes_read
-(curr_pos
-magic_buf
), c_file_format_version
);
717 return WTAP_OPEN_NOT_MINE
;
719 curr_pos
+= CLEN(c_file_format_version
);
720 if (memcmp(curr_pos
, c_threegpp_doc_no
, CLEN(c_threegpp_doc_no
)) != 0){
721 return WTAP_OPEN_NOT_MINE
;
723 /* Next we expect something like <traceCollec beginTime="..."/> */
724 curr_pos
= g_strstr_len(curr_pos
, bytes_read
-(curr_pos
-magic_buf
), c_begin_time
);
726 return WTAP_OPEN_NOT_MINE
;
728 curr_pos
+= CLEN(c_begin_time
);
729 /* Next we expect an ISO 8601-format time */
730 curr_pos
= iso8601_to_nstime(&start_time
, curr_pos
, ISO8601_DATETIME
);
732 return WTAP_OPEN_NOT_MINE
;
735 /* Ok it's our file. From here we'll need to free memory */
736 file_info
= g_new0(nettrace_3gpp_32_423_file_info_t
, 1);
737 file_info
->start_time
= start_time
;
738 file_info
->start_offset
= start_offset
+ (curr_pos
- magic_buf
);
739 file_info
->buffer
= g_byte_array_sized_new(RINGBUFFER_START_SIZE
);
740 g_byte_array_append(file_info
->buffer
, curr_pos
, (unsigned)(bytes_read
- (curr_pos
- magic_buf
)));
742 wth
->file_type_subtype
= nettrace_3gpp_32_423_file_type_subtype
;
743 wth
->file_encap
= WTAP_ENCAP_WIRESHARK_UPPER_PDU
;
744 wth
->file_tsprec
= WTAP_TSPREC_MSEC
;
745 wth
->subtype_read
= nettrace_read
;
746 wth
->subtype_seek_read
= nettrace_seek_read
;
747 wth
->subtype_close
= nettrace_close
;
748 wth
->snapshot_length
= 0;
749 wth
->priv
= (void*)file_info
;
751 return WTAP_OPEN_MINE
;
754 static const struct supported_block_type nettrace_3gpp_32_423_blocks_supported
[] = {
756 * We support packet blocks, with no comments or other options.
758 { WTAP_BLOCK_PACKET
, MULTIPLE_BLOCKS_SUPPORTED
, NO_OPTIONS_SUPPORTED
}
761 static const struct file_type_subtype_info nettrace_3gpp_32_423_info
= {
762 "3GPP TS 32.423 Trace", "3gpp32423", NULL
, NULL
,
763 false, BLOCKS_SUPPORTED(nettrace_3gpp_32_423_blocks_supported
),
767 void register_nettrace_3gpp_32_423(void)
769 nettrace_3gpp_32_423_file_type_subtype
= wtap_register_file_type_subtype(&nettrace_3gpp_32_423_info
);
772 * Register name for backwards compatibility with the
773 * wtap_filetypes table in Lua.
775 wtap_register_backwards_compatibility_lua_name("NETTRACE_3GPP_32_423",
776 nettrace_3gpp_32_423_file_type_subtype
);
780 * Editor modelines - https://www.wireshark.org/tools/modelines.html
785 * indent-tabs-mode: t
788 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
789 * :indentSize=8:tabSize=8:noTabs=false: