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