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