Coverage Report

Created: 2026-03-30 07:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wireshark/wiretap/nettrace_3gpp_32_423.c
Line
Count
Source
1
/* nettrace_3gpp_32_423.c
2
 *
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).
5
 *
6
 * SPDX-License-Identifier: GPL-2.0-or-later
7
 *
8
 * Ref: https://portal.3gpp.org/desktopmodules/Specifications/SpecificationDetails.aspx?specificationId=2010
9
 */
10
11
#include "config.h"
12
0
#define WS_LOG_DOMAIN "nettrace_3gpp"
13
#include "nettrace_3gpp_32_423.h"
14
15
#include <sys/types.h>
16
17
#ifdef HAVE_UNISTD_H
18
#include <unistd.h>
19
#endif
20
21
#include <stdlib.h>
22
#include <string.h>
23
#include <time.h>
24
25
#include "wtap_module.h"
26
#include "file_wrappers.h"
27
28
#include <wsutil/exported_pdu_tlvs.h>
29
#include <wsutil/buffer.h>
30
#include <wsutil/pint.h>
31
#include "wsutil/tempfile.h"
32
#include "wsutil/os_version_info.h"
33
#include "wsutil/str_util.h"
34
#include <wsutil/inet_addr.h>
35
#include <wsutil/ws_assert.h>
36
#include <libxml/tree.h>
37
#include <libxml/parser.h>
38
#include <libxml/xpath.h>
39
#include <glib.h>
40
41
/* String constants sought in the XML data.
42
 * Written as strings instead of lists of chars for readability.
43
 * Use the CLEN() macro to get the length of the constant without counting
44
 * the null byte at the end.
45
 */
46
0
#define CLEN(x) (sizeof(x)-1)
47
static const char c_s_msg[] = "<msg";
48
static const unsigned char c_e_msg[] = "</msg>";
49
50
/* These are protocol names we may put in the exported-pdu data based on
51
 * what's in the XML. They're defined here as constants so we can use
52
 * sizeof()/CLEN() on them and slightly reduce our use of magic constants
53
 * for their size. (Modern compilers should make this no slower than that.)
54
 */
55
static const char c_sai_req[] = "gsm_map.v3.arg.opcode";
56
static const char c_sai_rsp[] = "gsm_map.v3.res.opcode";
57
static const char c_nas_eps[] = "nas-eps_plain";
58
static const char c_nas_5gs[] = "nas-5gs";
59
60
61
0
#define RINGBUFFER_START_SIZE INT_MAX
62
0
#define RINGBUFFER_CHUNK_SIZE 1024
63
64
0
#define MAX_FUNCTION_LEN 64
65
0
#define MAX_NAME_LEN 128
66
0
#define MAX_PROTO_LEN 16
67
#define MAX_DTBL_LEN 32
68
69
/* We expect to find all the info we need to tell if this file is ours
70
 * within this many bytes. Must include the beginTime attribute.
71
 */
72
#define MAGIC_BUF_SIZE 1024
73
74
typedef struct nettrace_3gpp_32_423_file_info {
75
  GByteArray *buffer;   // holds current chunk of file
76
  int64_t start_offset;   // where in the file the start of the buffer points
77
  nstime_t start_time;    // from <traceCollec beginTime=""> attribute
78
} nettrace_3gpp_32_423_file_info_t;
79
80
81
typedef struct exported_pdu_info {
82
  uint32_t presence_flags;
83
  uint8_t src_ip[16];
84
  uint32_t ptype; /* Based on epan/address.h port_type valid for both src and dst*/
85
  uint32_t src_port;
86
  uint8_t dst_ip[16];
87
  uint32_t dst_port;
88
  char* proto_col_str;
89
} exported_pdu_info_t;
90
91
/* flags for exported_pdu_info.presence_flags */
92
0
#define EXP_PDU_TAG_IP_SRC_BIT    0x001
93
0
#define EXP_PDU_TAG_IP_DST_BIT    0x002
94
0
#define EXP_PDU_TAG_SRC_PORT_BIT  0x004
95
0
#define EXP_PDU_TAG_DST_PORT_BIT  0x008
96
#define EXP_PDU_TAG_ORIG_FNO_BIT  0x010
97
#define EXP_PDU_TAG_SS7_OPC_BIT   0x020
98
#define EXP_PDU_TAG_SS7_DPC_BIT   0x040
99
0
#define EXP_PDU_TAG_IP6_SRC_BIT   0x080
100
0
#define EXP_PDU_TAG_IP6_DST_BIT   0x100
101
#define EXP_PDU_TAG_DVBCI_EVT_BIT 0x0100
102
0
#define EXP_PDU_TAG_COL_PROT_BIT  0x0200
103
104
105
static int nettrace_3gpp_32_423_file_type_subtype = -1;
106
107
void register_nettrace_3gpp_32_423(void);
108
109
/* Parse a string IPv4 or IPv6 address into bytes for exported_pdu_info.
110
 * Also parses the port pairs and transport layer type.
111
 */
112
static void
113
nettrace_parse_address(char* curr_pos, bool is_src_addr, exported_pdu_info_t *exported_pdu_info)
114
0
{
115
0
  unsigned port=0;
116
0
  ws_in6_addr ip6_addr;
117
0
  uint32_t ip4_addr;
118
0
  char *err; //for strtol function
119
120
0
  GMatchInfo *match_info;
121
0
  static GRegex *regex = NULL;
122
0
  char *matched_ipaddress = NULL;
123
0
  char *matched_port = NULL;
124
0
  char *matched_transport = NULL;
125
126
  /* Example from one trace, unsure if it's generic...
127
   * {address == 192.168.73.1, port == 5062, transport == Udp}
128
   * {address == [2001:1b70:8294:210a::78], port...
129
   * {address == 2001:1B70:8294:210A::90, port...
130
   *  Address=198.142.204.199,Port=2123
131
   */
132
133
0
  if (regex == NULL) {
134
0
    regex = g_regex_new (
135
0
      "^.*address\\s*=*\\s*" //curr_pos will begin with address
136
0
      "\\[?(?P<ipaddress>(?:" //store ipv4 or ipv6 address in named group "ipaddress"
137
0
        "(?:\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3})" //match an IPv4 address
138
0
        "|" // or
139
0
        "(?:[0-9a-f:]*)))\\]?" //match an IPv6 address.
140
0
      "(?:.*port\\s*=*\\s*(?P<port>\\d{1,5}))?" //match a port store it in named group "port"
141
0
      "(?:.*transport\\s*=*\\s*(?P<transport>\\w+))?", //match a transport store it in named group "transport"
142
0
      G_REGEX_CASELESS | G_REGEX_FIRSTLINE, 0, NULL);
143
0
   }
144
145
  /* curr_pos pointing to first char of "address" */
146
0
  g_regex_match (regex, curr_pos, 0, &match_info);
147
148
0
  if (g_match_info_matches (match_info)) {
149
0
    matched_ipaddress = g_match_info_fetch_named(match_info, "ipaddress"); //will be empty string if no ipv4 or ipv6
150
0
    matched_port = g_match_info_fetch_named(match_info, "port"); //will be empty string if port not in trace
151
0
    if (matched_port != NULL) {
152
0
      port = (unsigned) strtol(matched_port, &err, 10);
153
0
      g_free(matched_port);
154
0
    }
155
0
    matched_transport = g_match_info_fetch_named(match_info, "transport"); //will be empty string if transport not in trace
156
0
  } else {
157
0
    g_match_info_free(match_info);
158
0
    return;
159
0
  }
160
161
0
  g_match_info_free(match_info);
162
0
  if (ws_inet_pton6(matched_ipaddress, &ip6_addr)) {
163
0
    if (is_src_addr) {
164
0
      exported_pdu_info->presence_flags |= EXP_PDU_TAG_IP6_SRC_BIT;
165
0
      memcpy(exported_pdu_info->src_ip, ip6_addr.bytes, EXP_PDU_TAG_IPV6_LEN);
166
0
    }
167
0
    else {
168
0
      exported_pdu_info->presence_flags |= EXP_PDU_TAG_IP6_DST_BIT;
169
0
      memcpy(exported_pdu_info->dst_ip, ip6_addr.bytes, EXP_PDU_TAG_IPV6_LEN);
170
0
    }
171
0
  }
172
0
  else if (ws_inet_pton4(matched_ipaddress, &ip4_addr)) {
173
0
    if (is_src_addr) {
174
0
      exported_pdu_info->presence_flags |= EXP_PDU_TAG_IP_SRC_BIT;
175
0
      memcpy(exported_pdu_info->src_ip, &ip4_addr, EXP_PDU_TAG_IPV4_LEN);
176
0
    }
177
0
    else {
178
0
      exported_pdu_info->presence_flags |= EXP_PDU_TAG_IP_DST_BIT;
179
0
      memcpy(exported_pdu_info->dst_ip, &ip4_addr, EXP_PDU_TAG_IPV4_LEN);
180
0
    }
181
0
  }
182
183
0
  if (port > 0) {
184
    /* Only add port_type once */
185
0
    if (exported_pdu_info->ptype == EXP_PDU_PT_NONE) {
186
0
      if (g_ascii_strncasecmp(matched_transport, "udp", 3) == 0) {
187
0
        exported_pdu_info->ptype = EXP_PDU_PT_UDP;
188
0
      }
189
0
      else if (g_ascii_strncasecmp(matched_transport, "tcp", 3) == 0) {
190
0
        exported_pdu_info->ptype = EXP_PDU_PT_TCP;
191
0
      }
192
0
      else if (g_ascii_strncasecmp(matched_transport, "sctp", 4) == 0) {
193
0
        exported_pdu_info->ptype = EXP_PDU_PT_SCTP;
194
0
      }
195
0
      else {
196
        /* fall to something so that ports are shown in column */
197
0
        exported_pdu_info->ptype = EXP_PDU_PT_TCP;
198
0
      }
199
0
    }
200
0
    if (is_src_addr) {
201
0
      exported_pdu_info->presence_flags |= EXP_PDU_TAG_SRC_PORT_BIT;
202
0
      exported_pdu_info->src_port = port;
203
0
    }
204
0
    else {
205
0
      exported_pdu_info->presence_flags |= EXP_PDU_TAG_DST_PORT_BIT;
206
0
      exported_pdu_info->dst_port = port;
207
0
    }
208
0
  }
209
0
  g_free(matched_ipaddress);
210
0
  g_free(matched_transport);
211
0
}
212
213
/* Parse a <msg ...><rawMsg ...>XXXX</rawMsg></msg> into packet data. */
214
static bool
215
nettrace_msg_to_packet(wtap* wth, wtap_rec* rec, const char* text, size_t len, int* err, char** err_info)
216
0
{
217
0
  nettrace_3gpp_32_423_file_info_t* file_info = (nettrace_3gpp_32_423_file_info_t*)wth->priv;
218
0
  xmlDocPtr           doc;
219
0
  xmlNodePtr root_element;
220
0
  exported_pdu_info_t  exported_pdu_info = { 0 };
221
0
  exported_pdu_info_t  proxy_exported_pdu_info = { 0 };
222
0
  char function_str[MAX_FUNCTION_LEN + 1];
223
0
  char name_str[MAX_NAME_LEN + 1];
224
0
  char proto_name_str[MAX_PROTO_LEN + 1];
225
0
  char dissector_table_str[MAX_DTBL_LEN + 1];
226
0
  int dissector_table_val = 0;
227
0
  bool found_raw = false;
228
0
  bool use_proto_table = false;
229
0
  bool status = true;
230
231
0
  doc = xmlParseMemory(text, (int)len);
232
0
  if (doc == NULL) {
233
0
    return false;
234
0
  }
235
236
0
  root_element = xmlDocGetRootElement(doc);
237
0
  if (root_element == NULL) {
238
0
    ws_debug("empty xml doc");
239
0
    status = false;
240
0
    goto end;
241
0
  }
242
243
  //Sanity check
244
0
  if (xmlStrcmp(root_element->name, (const xmlChar*)"msg") != 0) {
245
0
    *err = WTAP_ERR_BAD_FILE;
246
0
    *err_info = ws_strdup("nettrace_3gpp_32_423: Did not start with \"<msg\"");
247
0
    status = false;
248
0
    goto end;
249
0
  }
250
251
0
  if (root_element->children == NULL) {
252
    /* There is no rawmsg here. */
253
0
    *err = WTAP_ERR_BAD_FILE;
254
0
    *err_info = g_strdup("Had \"<msg />\" with no \"<rawMsg>\"");
255
0
    status = false;
256
0
    goto end;
257
0
  }
258
259
0
  wtap_setup_packet_rec(rec, wth->file_encap);
260
0
  rec->block = wtap_block_create(WTAP_BLOCK_PACKET);
261
0
  rec->presence_flags = 0; /* start out assuming no special features */
262
0
  rec->ts.secs = 0;
263
0
  rec->ts.nsecs = 0;
264
265
  /* Clear for each iteration */
266
0
  exported_pdu_info.presence_flags = 0;
267
0
  exported_pdu_info.ptype = EXP_PDU_PT_NONE;
268
0
  proxy_exported_pdu_info.presence_flags = 0;
269
0
  proxy_exported_pdu_info.ptype = EXP_PDU_PT_NONE;
270
271
  //Start with attributes not existing
272
0
  function_str[0] = '\0';
273
0
  name_str[0] = '\0';
274
275
  /* Walk the attributes of the message */
276
0
  for (xmlAttrPtr attr = root_element->properties; attr; attr = attr->next) {
277
0
    if (xmlStrcmp(attr->name, (const xmlChar*)"function") == 0) {
278
0
      xmlChar* str = xmlNodeListGetString(root_element->doc, attr->children, 1);
279
0
      if (str != NULL) {
280
0
        size_t function_str_len = strlen((const char*)str);
281
0
        if (function_str_len > MAX_FUNCTION_LEN) {
282
0
          *err = WTAP_ERR_BAD_FILE;
283
0
          *err_info = ws_strdup_printf("nettrace_3gpp_32_423: function_str_len > %d", MAX_FUNCTION_LEN);
284
0
          xmlFree(str);
285
0
          status = false;
286
0
          goto end;
287
0
        }
288
289
0
        (void)g_strlcpy(function_str, (const char*)str, (size_t)function_str_len + 1);
290
0
        ascii_strdown_inplace(function_str);
291
292
0
        xmlFree(str);
293
0
      }
294
0
    }
295
0
    else if (xmlStrcmp(attr->name, (const xmlChar*)"name") == 0) {
296
0
      xmlChar* str = xmlNodeListGetString(root_element->doc, attr->children, 1);
297
0
      if (str != NULL) {
298
0
        size_t name_str_len = strlen((const char*)str);
299
0
        if (name_str_len > MAX_NAME_LEN) {
300
0
          *err = WTAP_ERR_BAD_FILE;
301
0
          *err_info = ws_strdup_printf("nettrace_3gpp_32_423: name_str_len > %d", MAX_NAME_LEN);
302
0
          xmlFree(str);
303
0
          status = false;
304
0
          goto end;
305
0
        }
306
307
0
        (void)g_strlcpy(name_str, (const char*)str, (size_t)name_str_len + 1);
308
0
        ascii_strdown_inplace(name_str);
309
0
        xmlFree(str);
310
0
      }
311
0
    }
312
0
    else if (xmlStrcmp(attr->name, (const xmlChar*)"changeTime") == 0) {
313
314
      /* Check if we have a time stamp "changeTime"
315
       * expressed in number of seconds and milliseconds (nbsec.ms).
316
       * Only needed if we have a "beginTime" for this file.
317
       */
318
0
      if (!nstime_is_unset(&(file_info->start_time))) {
319
0
        int scan_found;
320
0
        unsigned second = 0, ms = 0;
321
322
0
        xmlChar* str_time = xmlNodeListGetString(root_element->doc, attr->children, 1);
323
0
        if (str_time != NULL) {
324
0
          scan_found = sscanf((const char*)str_time, "%u.%u", &second, &ms);
325
326
0
          if (scan_found == 2) {
327
0
            unsigned start_ms = file_info->start_time.nsecs / 1000000;
328
0
            unsigned elapsed_ms = start_ms + ms;
329
0
            if (elapsed_ms > 1000) {
330
0
              elapsed_ms -= 1000;
331
0
              second++;
332
0
            }
333
0
            rec->presence_flags |= WTAP_HAS_TS;
334
0
            rec->ts.secs = file_info->start_time.secs + second;
335
0
            rec->ts.nsecs = (elapsed_ms * 1000000);
336
0
          }
337
338
0
          xmlFree(str_time);
339
0
        }
340
0
      }
341
0
    }
342
0
  }
343
344
  /* Check the children of the msg root */
345
0
  proto_name_str[0] = '\0';
346
0
  dissector_table_str[0] = '\0';
347
0
  for (xmlNodePtr cur = root_element->children; cur != NULL; cur = cur->next) {
348
0
    if (cur->type == XML_ELEMENT_NODE) {
349
0
      if (xmlStrcmp(cur->name, (const xmlChar*)"initiator") == 0) {
350
0
        xmlChar* initiator_content = xmlNodeGetContent(cur);
351
352
0
        nettrace_parse_address((char*)initiator_content, true/* SRC */, &exported_pdu_info);
353
0
        xmlFree(initiator_content);
354
0
      }
355
0
      else if (xmlStrcmp(cur->name, (const xmlChar*)"target") == 0) {
356
0
        xmlChar* target_content = xmlNodeGetContent(cur);
357
358
0
        nettrace_parse_address((char*)target_content, false/* DST */, &exported_pdu_info);
359
0
        xmlFree(target_content);
360
0
      }
361
0
      else if (xmlStrcmp(cur->name, (const xmlChar*)"proxy") == 0) {
362
0
        xmlChar* proxy_content = xmlNodeGetContent(cur);
363
364
        /* proxy info will be save in destination ip/port */
365
0
        nettrace_parse_address((char*)proxy_content, false/* SRC */, &proxy_exported_pdu_info);
366
0
        xmlFree(proxy_content);
367
0
      }
368
0
      else if (xmlStrcmp(cur->name, (const xmlChar*)"rawMsg") == 0) {
369
0
        bool found_protocol = false;
370
0
        xmlChar* raw_content;
371
0
        xmlNodePtr raw_node = cur;
372
373
0
        for (xmlAttrPtr attr = raw_node->properties; attr; attr = attr->next) {
374
0
          if (xmlStrcmp(attr->name, (const xmlChar*)"protocol") == 0) {
375
376
0
            xmlChar* str = xmlNodeListGetString(raw_node->doc, attr->children, 1);
377
0
            if (str != NULL) {
378
0
              size_t proto_str_len = strlen((char*)str);
379
0
              if (proto_str_len > MAX_PROTO_LEN) {
380
0
                xmlFree(str);
381
0
                status = false;
382
0
                goto end;
383
0
              }
384
0
              (void)g_strlcpy(proto_name_str, (const char*)str, (size_t)proto_str_len + 1);
385
0
              ascii_strdown_inplace(proto_name_str);
386
0
              found_protocol = true;
387
0
            }
388
0
          }
389
0
        }
390
391
0
        if (!found_protocol) {
392
0
          *err = WTAP_ERR_BAD_FILE;
393
0
          *err_info = ws_strdup("nettrace_3gpp_32_423: Did not find \"protocol\"");
394
0
          status = false;
395
0
          goto end;
396
0
        }
397
398
        /* Do string matching and replace with Wiresharks protocol name */
399
0
        if (strcmp(proto_name_str, "gtpv2-c") == 0) {
400
          /* Change to gtpv2 */
401
0
          proto_name_str[5] = '\0';
402
0
        }
403
0
        else if (strcmp(proto_name_str, "nas") == 0) {
404
0
          if (strcmp(function_str, "s1") == 0) {
405
            /* Change to nas-eps_plain */
406
0
            (void)g_strlcpy(proto_name_str, c_nas_eps, sizeof(c_nas_eps));
407
0
          }
408
0
          else if (strcmp(function_str, "n1") == 0) {
409
            /* Change to nas-5gs */
410
0
            (void)g_strlcpy(proto_name_str, c_nas_5gs, sizeof(c_nas_5gs));
411
0
          }
412
0
          else {
413
0
            *err = WTAP_ERR_BAD_FILE;
414
0
            *err_info = ws_strdup_printf("nettrace_3gpp_32_423: No handle of message \"%s\" on function \"%s\" ", proto_name_str, function_str);
415
0
            status = false;
416
0
            goto end;
417
0
          }
418
0
        }
419
0
        else if (strcmp(proto_name_str, "map") == 0) {
420
          /* For GSM map, it looks like the message data is stored like SendAuthenticationInfoArg
421
           * use the GSM MAP dissector table to dissect the content.
422
           */
423
0
          exported_pdu_info.proto_col_str = g_strdup("GSM MAP");
424
425
0
          if (strcmp(name_str, "sai_request") == 0) {
426
0
            use_proto_table = true;
427
0
            (void)g_strlcpy(dissector_table_str, c_sai_req, sizeof(c_sai_req));
428
0
            dissector_table_val = 56;
429
0
            exported_pdu_info.presence_flags |= EXP_PDU_TAG_COL_PROT_BIT;
430
0
          }
431
0
          else if (strcmp(name_str, "sai_response") == 0) {
432
0
            use_proto_table = true;
433
0
            (void)g_strlcpy(dissector_table_str, c_sai_rsp, sizeof(c_sai_rsp));
434
0
            dissector_table_val = 56;
435
0
            exported_pdu_info.presence_flags |= EXP_PDU_TAG_COL_PROT_BIT;
436
0
          }
437
0
          else {
438
0
            g_free(exported_pdu_info.proto_col_str);
439
0
            exported_pdu_info.proto_col_str = NULL;
440
0
          }
441
0
        }
442
443
        /* Fill packet buff */
444
0
        ws_buffer_clean(&rec->data);
445
0
        if (use_proto_table == false) {
446
0
          wtap_buffer_append_epdu_tag(&rec->data, EXP_PDU_TAG_DISSECTOR_NAME, (const uint8_t*)proto_name_str, (uint16_t)strlen(proto_name_str));
447
0
        }
448
0
        else {
449
0
          wtap_buffer_append_epdu_tag(&rec->data, EXP_PDU_TAG_DISSECTOR_TABLE_NAME, (const uint8_t*)dissector_table_str, (uint16_t)strlen(dissector_table_str));
450
0
          wtap_buffer_append_epdu_uint(&rec->data, EXP_PDU_TAG_DISSECTOR_TABLE_NAME_NUM_VAL, dissector_table_val);
451
0
        }
452
453
0
        if (exported_pdu_info.presence_flags & EXP_PDU_TAG_COL_PROT_BIT) {
454
0
          if (exported_pdu_info.proto_col_str) {
455
0
            wtap_buffer_append_epdu_string(&rec->data, EXP_PDU_TAG_COL_PROT_TEXT, exported_pdu_info.proto_col_str);
456
0
            g_free(exported_pdu_info.proto_col_str);
457
0
            exported_pdu_info.proto_col_str = NULL;
458
0
          }
459
0
        }
460
461
0
        if (exported_pdu_info.presence_flags & EXP_PDU_TAG_IP_SRC_BIT) {
462
0
          wtap_buffer_append_epdu_tag(&rec->data, EXP_PDU_TAG_IPV4_SRC, exported_pdu_info.src_ip, EXP_PDU_TAG_IPV4_LEN);
463
0
        }
464
0
        else if (proxy_exported_pdu_info.presence_flags & EXP_PDU_TAG_IP_DST_BIT) {
465
0
          wtap_buffer_append_epdu_tag(&rec->data, EXP_PDU_TAG_IPV4_SRC, proxy_exported_pdu_info.dst_ip, EXP_PDU_TAG_IPV4_LEN);
466
0
        }
467
0
        if (exported_pdu_info.presence_flags & EXP_PDU_TAG_IP_DST_BIT) {
468
0
          wtap_buffer_append_epdu_tag(&rec->data, EXP_PDU_TAG_IPV4_DST, exported_pdu_info.dst_ip, EXP_PDU_TAG_IPV4_LEN);
469
0
        }
470
0
        else if (proxy_exported_pdu_info.presence_flags & EXP_PDU_TAG_IP_DST_BIT) {
471
0
          wtap_buffer_append_epdu_tag(&rec->data, EXP_PDU_TAG_IPV4_DST, proxy_exported_pdu_info.dst_ip, EXP_PDU_TAG_IPV4_LEN);
472
0
        }
473
474
0
        if (exported_pdu_info.presence_flags & EXP_PDU_TAG_IP6_SRC_BIT) {
475
0
          wtap_buffer_append_epdu_tag(&rec->data, EXP_PDU_TAG_IPV6_SRC, exported_pdu_info.src_ip, EXP_PDU_TAG_IPV6_LEN);
476
0
        }
477
0
        else if (proxy_exported_pdu_info.presence_flags & EXP_PDU_TAG_IP6_DST_BIT) {
478
0
          wtap_buffer_append_epdu_tag(&rec->data, EXP_PDU_TAG_IPV6_SRC, proxy_exported_pdu_info.dst_ip, EXP_PDU_TAG_IPV6_LEN);
479
0
        }
480
0
        if (exported_pdu_info.presence_flags & EXP_PDU_TAG_IP6_DST_BIT) {
481
0
          wtap_buffer_append_epdu_tag(&rec->data, EXP_PDU_TAG_IPV6_DST, exported_pdu_info.dst_ip, EXP_PDU_TAG_IPV6_LEN);
482
0
        }
483
0
        else if (proxy_exported_pdu_info.presence_flags & EXP_PDU_TAG_IP6_DST_BIT) {
484
0
          wtap_buffer_append_epdu_tag(&rec->data, EXP_PDU_TAG_IPV6_DST, proxy_exported_pdu_info.dst_ip, EXP_PDU_TAG_IPV6_LEN);
485
0
        }
486
487
0
        if (exported_pdu_info.presence_flags & (EXP_PDU_TAG_SRC_PORT_BIT | EXP_PDU_TAG_DST_PORT_BIT)) {
488
0
          wtap_buffer_append_epdu_uint(&rec->data, EXP_PDU_TAG_PORT_TYPE, exported_pdu_info.ptype);
489
0
        }
490
0
        else if (proxy_exported_pdu_info.presence_flags & (EXP_PDU_TAG_SRC_PORT_BIT | EXP_PDU_TAG_DST_PORT_BIT)) {
491
0
          wtap_buffer_append_epdu_uint(&rec->data, EXP_PDU_TAG_PORT_TYPE, proxy_exported_pdu_info.ptype);
492
0
        }
493
0
        if (exported_pdu_info.presence_flags & EXP_PDU_TAG_SRC_PORT_BIT) {
494
0
          wtap_buffer_append_epdu_uint(&rec->data, EXP_PDU_TAG_SRC_PORT, exported_pdu_info.src_port);
495
0
        }
496
0
        else if (proxy_exported_pdu_info.presence_flags & EXP_PDU_TAG_SRC_PORT_BIT) {
497
0
          wtap_buffer_append_epdu_uint(&rec->data, EXP_PDU_TAG_SRC_PORT, proxy_exported_pdu_info.src_port);
498
0
        }
499
0
        if (exported_pdu_info.presence_flags & EXP_PDU_TAG_DST_PORT_BIT) {
500
0
          wtap_buffer_append_epdu_uint(&rec->data, EXP_PDU_TAG_DST_PORT, exported_pdu_info.dst_port);
501
0
        }
502
0
        else if (proxy_exported_pdu_info.presence_flags & EXP_PDU_TAG_DST_PORT_BIT) {
503
0
          wtap_buffer_append_epdu_uint(&rec->data, EXP_PDU_TAG_DST_PORT, proxy_exported_pdu_info.dst_port);
504
0
        }
505
506
        /* Add end of options */
507
0
        int exp_pdu_tags_len = wtap_buffer_append_epdu_end(&rec->data);
508
509
        /* Convert the hex raw msg data to binary and write to the packet buf*/
510
0
        raw_content = xmlNodeGetContent(raw_node);
511
0
        size_t raw_data_len = raw_content ? strlen((const char*)raw_content) : 0;
512
0
        if (raw_data_len > 0) {
513
0
          size_t pkt_data_len = raw_data_len / 2;
514
0
          ws_buffer_assure_space(&rec->data, pkt_data_len);
515
0
          uint8_t* packet_buf = ws_buffer_end_ptr(&rec->data);
516
517
0
          const char* curr_pos = (const char*)raw_content;
518
0
          for (size_t i = 0; i < pkt_data_len; i++) {
519
0
            char chr1, chr2;
520
0
            int val1, val2;
521
522
0
            chr1 = *curr_pos++;
523
0
            chr2 = *curr_pos++;
524
0
            val1 = g_ascii_xdigit_value(chr1);
525
0
            val2 = g_ascii_xdigit_value(chr2);
526
0
            if ((val1 != -1) && (val2 != -1)) {
527
0
              *packet_buf++ = ((uint8_t)val1 * 16) + val2;
528
0
            }
529
0
            else {
530
              /* Something wrong, bail out */
531
0
              *err_info = ws_strdup_printf("nettrace_3gpp_32_423: Could not parse hex data, bufsize %zu index %zu %c%c",
532
0
                (pkt_data_len + exp_pdu_tags_len),
533
0
                i, chr1, chr2);
534
0
              *err = WTAP_ERR_BAD_FILE;
535
0
              xmlFree(raw_content);
536
0
              status = false;
537
0
              goto end;
538
0
            }
539
0
          }
540
0
          ws_buffer_increase_length(&rec->data, pkt_data_len);
541
0
        }
542
543
0
        rec->rec_header.packet_header.caplen = (uint32_t)ws_buffer_length(&rec->data);
544
0
        rec->rec_header.packet_header.len = (uint32_t)ws_buffer_length(&rec->data);
545
546
0
        found_raw = true;
547
0
        xmlFree(raw_content);
548
0
      }
549
0
    }
550
0
  }
551
552
0
  if (!found_raw) {
553
0
    *err = WTAP_ERR_BAD_FILE;
554
0
    *err_info = ws_strdup("nettrace_3gpp_32_423: Did not find \"<rawMsg\"");
555
0
    status = false;
556
0
    goto end;
557
0
  }
558
0
end:
559
0
  xmlFreeDoc(doc);
560
0
  return status;
561
0
}
562
563
/* Read from fh and store into buffer, until buffer contains needle.
564
 * Returns location of needle once found, or NULL if it's never found
565
 * (due to either EOF or read error).
566
 */
567
static uint8_t *
568
read_until(GByteArray *buffer, const unsigned char *needle, FILE_T fh, int *err, char **err_info)
569
0
{
570
0
  uint8_t read_buffer[RINGBUFFER_CHUNK_SIZE];
571
0
  uint8_t *found_it;
572
0
  int bytes_read = 0;
573
574
0
  while (NULL == (found_it = (uint8_t*)g_strstr_len((const char*)buffer->data, buffer->len, (const char*)needle))) {
575
0
    bytes_read = file_read(read_buffer, RINGBUFFER_CHUNK_SIZE, fh);
576
0
    if (bytes_read < 0) {
577
0
      *err = file_error(fh, err_info);
578
0
      break;
579
0
    }
580
0
    if (bytes_read == 0) {
581
0
      break;
582
0
    }
583
0
    g_byte_array_append(buffer, read_buffer, bytes_read);
584
0
  }
585
0
  return found_it;
586
0
}
587
588
/* Find a complete packet, parse and return it to wiretap.
589
 * Set as the subtype_read function in the file_open function below.
590
 */
591
static bool
592
nettrace_read(wtap *wth, wtap_rec *rec, int *err, char **err_info, int64_t *data_offset)
593
0
{
594
0
  nettrace_3gpp_32_423_file_info_t *file_info = (nettrace_3gpp_32_423_file_info_t *)wth->priv;
595
0
  uint8_t *buf_start;
596
0
  uint8_t *msg_start, *msg_end;
597
0
  unsigned msg_offset = 0;
598
0
  size_t msg_len = 0;
599
0
  bool status = false;
600
601
  /* Make sure we have a start and end of message in our buffer -- end first */
602
0
  msg_end = read_until(file_info->buffer, c_e_msg, wth->fh, err, err_info);
603
0
  if (msg_end == NULL) {
604
0
    goto end;
605
0
  }
606
607
0
  buf_start = file_info->buffer->data;
608
  /* Now search backwards for the message start
609
   * (doing it this way should skip over any empty "<msg ... />" tags we have)
610
   */
611
0
  msg_start = (uint8_t*)g_strrstr_len((const char*)buf_start, msg_end - buf_start, c_s_msg);
612
0
  if (msg_start == NULL || msg_start > msg_end) {
613
0
    *err_info = ws_strdup_printf("nettrace_3gpp_32_423: Found \"%s\" without matching \"%s\"", c_e_msg, c_s_msg);
614
0
    *err = WTAP_ERR_BAD_FILE;
615
0
    goto end;
616
0
  }
617
618
  /* We know we have a message, what's its offset from the buffer start? */
619
0
  msg_offset = (unsigned)(msg_start - buf_start);
620
0
  msg_end += CLEN(c_e_msg);
621
0
  msg_len = (unsigned)(msg_end - msg_start);
622
623
  /* Tell Wireshark to put us at the start of the "<msg" for seek_read later */
624
0
  *data_offset = file_info->start_offset + msg_offset;
625
626
  /* pass all of <msg....</msg> to nettrace_msg_to_packet() */
627
0
  status = nettrace_msg_to_packet(wth, rec, (const char*)msg_start, msg_len, err, err_info);
628
629
  /* Finally, shift our buffer to the end of this message to get ready for the next one.
630
   * Re-use msg_len to get the length of the data we're done with.
631
   */
632
0
  msg_len = msg_end - file_info->buffer->data;
633
0
  while (G_UNLIKELY(msg_len > UINT_MAX)) {
634
0
    g_byte_array_remove_range(file_info->buffer, 0, UINT_MAX);
635
0
    msg_len -= UINT_MAX;
636
0
  }
637
0
  g_byte_array_remove_range(file_info->buffer, 0, (unsigned)msg_len);
638
0
  file_info->start_offset += msg_len;
639
640
0
end:
641
0
  if (status == false) {
642
    /* There's no more to read. Empty out the buffer */
643
0
    g_byte_array_set_size(file_info->buffer, 0);
644
0
  }
645
646
0
  return status;
647
0
}
648
649
/* Seek to the complete packet at the offset, parse and return it to wiretap.
650
 * Set as the subtype_seek_read function in the file_open function below.
651
 */
652
static bool
653
nettrace_seek_read(wtap *wth, int64_t seek_off, wtap_rec *rec, int *err, char **err_info)
654
0
{
655
0
  nettrace_3gpp_32_423_file_info_t *file_info = (nettrace_3gpp_32_423_file_info_t *)wth->priv;
656
0
  bool status = false;
657
0
  uint8_t *msg_end;
658
0
  unsigned msg_len = 0;
659
660
  /* We stored the offset of the "<msg" for this packet */
661
0
  if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
662
0
    return false;
663
664
0
  msg_end = read_until(file_info->buffer, c_e_msg, wth->random_fh, err, err_info);
665
0
  if (msg_end == NULL) {
666
0
    return false;
667
0
  }
668
0
  msg_end += CLEN(c_e_msg);
669
0
  msg_len = (unsigned)(msg_end - file_info->buffer->data);
670
671
0
  status = nettrace_msg_to_packet(wth, rec, (const char*)file_info->buffer->data, msg_len, err, err_info);
672
0
  g_byte_array_set_size(file_info->buffer, 0);
673
0
  return status;
674
0
}
675
676
/* Clean up any memory we allocated for dealing with this file.
677
 * Set as the subtype_close function in the file_open function below.
678
 * (wiretap frees wth->priv itself)
679
 */
680
static void
681
nettrace_close(wtap *wth)
682
0
{
683
0
  nettrace_3gpp_32_423_file_info_t *file_info = (nettrace_3gpp_32_423_file_info_t *)wth->priv;
684
685
0
  if (file_info != NULL && file_info->buffer != NULL) {
686
0
    g_byte_array_free(file_info->buffer, true);
687
0
    file_info->buffer = NULL;
688
0
  }
689
0
}
690
691
/* Test the current file to see if it's one we can read.
692
 * Set in file_access.c as the function to be called for this file type.
693
 */
694
wtap_open_return_val
695
nettrace_3gpp_32_423_file_open(wtap *wth, int *err _U_, char **err_info _U_)
696
0
{
697
0
  nstime_t start_time = NSTIME_INIT_UNSET;
698
0
  nettrace_3gpp_32_423_file_info_t *file_info;
699
0
  xmlDocPtr doc;
700
0
  xmlNodePtr root_element = NULL;
701
702
0
  doc = xmlReadFile(wth->pathname, NULL, XML_PARSE_NONET | XML_PARSE_NOERROR);
703
0
  if (doc == NULL) {
704
    //const xmlError * error = xmlGetLastError();
705
    //if (error) {
706
    //  ws_warning("Failed to parse =%s", error->message);
707
    //}
708
0
    return WTAP_OPEN_NOT_MINE;
709
0
  }
710
711
0
  root_element = xmlDocGetRootElement(doc);
712
0
  if (root_element == NULL) {
713
0
    xmlFreeDoc(doc);
714
0
    return WTAP_OPEN_NOT_MINE;
715
0
  }
716
717
  //Sanity check
718
0
  if (xmlStrcmp(root_element->name, (const xmlChar*)"traceCollecFile") != 0) {
719
    //traceCollecFile note no t(Collec t )
720
0
    ws_debug("traceCollecFile did not match root_element->name %s", root_element->name);
721
0
    xmlFreeDoc(doc);
722
0
    return WTAP_OPEN_NOT_MINE;
723
0
  }
724
725
0
  if (root_element->children == NULL) {
726
0
    xmlFreeDoc(doc);
727
0
    return WTAP_OPEN_NOT_MINE;
728
0
  }
729
730
0
  for (xmlNodePtr cur = root_element->children; cur != NULL; cur = cur->next) {
731
0
    if (cur->type == XML_ELEMENT_NODE) {
732
0
      if (xmlStrcmp(cur->name, (const xmlChar*)"fileHeader") == 0) {
733
        /* Walk the attributes of the fileHeader */
734
0
        for (xmlAttrPtr attr = cur->properties; attr; attr = attr->next) {
735
0
          if (xmlStrcmp(attr->name, (const xmlChar*)"fileFormatVersion") == 0) {
736
0
            xmlChar* str_fileformatversion = xmlNodeListGetString(cur->doc, attr->children, 1);
737
0
            if (str_fileformatversion != NULL) {
738
0
              if (strncmp((const char*)str_fileformatversion, "32.423", strlen("32.423")) != 0)
739
0
                return WTAP_OPEN_NOT_MINE;
740
0
            } else {
741
0
              xmlFreeDoc(doc);
742
0
              return WTAP_OPEN_NOT_MINE;
743
0
            }
744
0
          }
745
0
        }
746
        /* Check the children of the fileHeader root */
747
0
        for (xmlNodePtr fileHeader_node = cur->children; fileHeader_node != NULL; fileHeader_node = fileHeader_node->next) {
748
0
          if (fileHeader_node->type == XML_ELEMENT_NODE) {
749
0
            if (xmlStrcmp(fileHeader_node->name, (const xmlChar*)"traceCollec") == 0) {
750
              /* Walk the attributes of the fileHeader */
751
0
              for (xmlAttrPtr attr = cur->properties; attr; attr = attr->next) {
752
0
                if (xmlStrcmp(attr->name, (const xmlChar*)"beginTime") == 0) {
753
0
                  xmlChar* str_begintime = xmlNodeListGetString(cur->doc, attr->children, 1);
754
0
                  if (str_begintime != NULL) {
755
0
                    iso8601_to_nstime(&start_time, (const char*)str_begintime, ISO8601_DATETIME);
756
0
                    xmlFree(str_begintime);
757
0
                  }
758
0
                }
759
0
              }
760
0
            }
761
0
          }
762
0
        }
763
0
      }
764
0
    }
765
0
  }
766
767
  /* Ok it's our file. From here we'll need to free memory */
768
0
  file_info = g_new0(nettrace_3gpp_32_423_file_info_t, 1);
769
0
  file_info->start_time = start_time;
770
0
  file_info->start_offset = 0;
771
0
  file_info->buffer = g_byte_array_sized_new(RINGBUFFER_START_SIZE);
772
773
0
  wth->file_type_subtype = nettrace_3gpp_32_423_file_type_subtype;
774
0
  wth->file_encap = WTAP_ENCAP_WIRESHARK_UPPER_PDU;
775
0
  wth->file_tsprec = WTAP_TSPREC_MSEC;
776
0
  wth->subtype_read = nettrace_read;
777
0
  wth->subtype_seek_read = nettrace_seek_read;
778
0
  wth->subtype_close = nettrace_close;
779
0
  wth->snapshot_length = 0;
780
0
  wth->priv = (void*)file_info;
781
782
0
  return WTAP_OPEN_MINE;
783
0
}
784
785
static const struct supported_block_type nettrace_3gpp_32_423_blocks_supported[] = {
786
  /*
787
   * We support packet blocks, with no comments or other options.
788
   */
789
  { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
790
};
791
792
static const struct file_type_subtype_info nettrace_3gpp_32_423_info = {
793
  "3GPP TS 32.423 Trace", "3gpp32423", NULL, NULL,
794
  false, BLOCKS_SUPPORTED(nettrace_3gpp_32_423_blocks_supported),
795
  NULL, NULL, NULL
796
};
797
798
void register_nettrace_3gpp_32_423(void)
799
14
{
800
14
  nettrace_3gpp_32_423_file_type_subtype = wtap_register_file_type_subtype(&nettrace_3gpp_32_423_info);
801
802
  /*
803
   * Register name for backwards compatibility with the
804
   * wtap_filetypes table in Lua.
805
   */
806
14
  wtap_register_backwards_compatibility_lua_name("NETTRACE_3GPP_32_423",
807
14
      nettrace_3gpp_32_423_file_type_subtype);
808
14
}
809
810
/*
811
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
812
 *
813
 * Local variables:
814
 * c-basic-offset: 8
815
 * tab-width: 8
816
 * indent-tabs-mode: t
817
 * End:
818
 *
819
 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
820
 * :indentSize=8:tabSize=8:noTabs=false:
821
 */