NEEDED??? epan/dissectors/packet-dcerpc.c dissect_ndr_c_and_or_v_array_core dissect_n...
[wireshark-sm.git] / wiretap / nettrace_3gpp_32_423.c
blob809fca2fef208ac5af15f4bfae3aac4bece863be
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
9 */
11 #include "config.h"
12 #include "nettrace_3gpp_32_423.h"
14 #include <sys/types.h>
16 #ifdef HAVE_UNISTD_H
17 #include <unistd.h>
18 #endif
20 #include <stdlib.h>
21 #include <string.h>
22 #include <time.h>
24 #include "wtap-int.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>
34 #include <glib.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;
93 uint8_t src_ip[16];
94 uint32_t ptype; /* Based on epan/address.h port_type valid for both src and dst*/
95 uint32_t src_port;
96 uint8_t dst_ip[16];
97 uint32_t dst_port;
98 char* proto_col_str;
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.
122 static char*
123 nettrace_parse_address(char* curr_pos, char* next_pos, bool is_src_addr, exported_pdu_info_t *exported_pdu_info)
125 unsigned port=0;
126 ws_in6_addr ip6_addr;
127 uint32_t ip4_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
144 if (regex == NULL) {
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
149 "|" // or
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
171 } else {
172 g_match_info_free(match_info);
173 *next_pos = saved_next_char;
174 return next_pos;
177 g_match_info_free(match_info);
178 *next_pos = saved_next_char;
180 if (ws_inet_pton6(matched_ipaddress, &ip6_addr)) {
181 if (is_src_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);
185 else {
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)) {
191 if (is_src_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);
195 else {
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);
201 if (port > 0) {
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;
214 if (is_src_addr) {
215 exported_pdu_info->presence_flags |= EXP_PDU_TAG_SRC_PORT_BIT;
216 exported_pdu_info->src_port = port;
218 else {
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);
225 return next_pos;
229 /* Parse a <msg ...><rawMsg ...>XXXX</rawMsg></msg> into packet data. */
230 static bool
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)
236 bool status = true;
237 char *curr_pos, *next_msg_pos, *next_pos, *prev_pos;
238 exported_pdu_info_t exported_pdu_info = {0};
240 char* raw_msg_pos;
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;
251 uint8_t *packet_buf;
252 int val1, val2;
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);
259 return false;
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 */
267 rec->ts.secs = 0;
268 rec->ts.nsecs = 0;
270 /* Clear for each iteration */
271 exported_pdu_info.presence_flags = 0;
272 exported_pdu_info.ptype = EXP_PDU_PT_NONE;
274 prev_pos = curr_pos;
275 /* Look for the end of the tag first */
276 next_msg_pos = STRNSTR(curr_pos, ">");
277 if (!next_msg_pos) {
278 /* Something's wrong, bail out */
279 *err = WTAP_ERR_BAD_FILE;
280 *err_info = g_strdup("Did not find end of tag \">\"");
281 status = false;
282 goto end;
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>\"");
289 status = false;
290 goto end;
292 start_msg_tag_cont = curr_pos = prev_pos;
293 next_msg_pos = STRNSTR(curr_pos, c_e_msg);
294 if (!next_msg_pos) {
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);
298 status = false;
299 goto end;
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))) {
307 int scan_found;
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) {
320 elapsed_ms -= 1000;
321 second++;
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);
341 goto end;
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);
360 goto end;
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);
375 /* Find address */
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);
390 /* Find address */
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);
403 status = false;
404 goto end;
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);
410 status = false;
411 goto end;
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){
417 status = false;
418 goto end;
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';
427 proto_str_len = 5;
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);
438 } else {
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);
441 status = false;
442 goto end;
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;
465 } else {
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);
480 else {
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++) {
524 char chr1, chr2;
526 chr1 = *curr_pos++;
527 chr2 = *curr_pos++;
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;
533 else {
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),
538 chr1,
539 chr2);
540 *err = WTAP_ERR_BAD_FILE;
541 status = false;
542 goto end;
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);
550 end:
551 return status;
552 #undef STRNSTR
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).
559 static uint8_t *
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];
563 uint8_t *found_it;
564 int bytes_read = 0;
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);
570 break;
572 if (bytes_read == 0) {
573 break;
575 g_byte_array_append(buffer, read_buffer, bytes_read);
577 return found_it;
580 /* Find a complete packet, parse and return it to wiretap.
581 * Set as the subtype_read function in the file_open function below.
583 static bool
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;
587 uint8_t *buf_start;
588 uint8_t *msg_start, *msg_end;
589 unsigned msg_offset = 0;
590 size_t msg_len = 0;
591 bool status = false;
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) {
596 goto end;
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;
607 goto end;
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);
627 msg_len -= UINT_MAX;
629 g_byte_array_remove_range(file_info->buffer, 0, (unsigned)msg_len);
630 file_info->start_offset += msg_len;
632 end:
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);
638 return status;
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.
644 static bool
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;
648 bool status = false;
649 uint8_t *msg_end;
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)
654 return false;
656 msg_end = read_until(file_info->buffer, c_e_msg, wth->random_fh, err, err_info);
657 if (msg_end == NULL) {
658 return false;
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);
665 return status;
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)
672 static void
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.
686 wtap_open_return_val
687 nettrace_3gpp_32_423_file_open(wtap *wth, int *err, char **err_info)
689 char magic_buf[MAGIC_BUF_SIZE+1];
690 int bytes_read;
691 const char *curr_pos;
692 nstime_t start_time;
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);
712 if (!curr_pos) {
713 return WTAP_OPEN_NOT_MINE;
715 curr_pos = g_strstr_len(curr_pos, bytes_read-(curr_pos-magic_buf), c_file_format_version);
716 if (!curr_pos) {
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);
725 if (!curr_pos) {
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);
731 if (!curr_pos) {
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),
764 NULL, NULL, NULL
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
782 * Local variables:
783 * c-basic-offset: 8
784 * tab-width: 8
785 * indent-tabs-mode: t
786 * End:
788 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
789 * :indentSize=8:tabSize=8:noTabs=false: