/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 | | */ |