/src/wireshark/wiretap/dct3trace.c
Line | Count | Source |
1 | | /* dct3trace.c |
2 | | * Routines for reading signalling traces generated by Gammu (www.gammu.org) |
3 | | * from Nokia DCT3 phones in Netmonitor mode. |
4 | | * |
5 | | * gammu --nokiadebug nhm5_587.txt v18-19 |
6 | | * |
7 | | * Duncan Salerno <duncan.salerno@googlemail.com> |
8 | | * |
9 | | * Wiretap Library |
10 | | * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu> |
11 | | * |
12 | | * SPDX-License-Identifier: GPL-2.0-or-later |
13 | | */ |
14 | | |
15 | | #include "config.h" |
16 | | #include "dct3trace.h" |
17 | | #include "wtap_module.h" |
18 | | #include "file_wrappers.h" |
19 | | |
20 | | #include <stdlib.h> |
21 | | #include <string.h> |
22 | | #include <errno.h> |
23 | | |
24 | | #include <wsutil/strtoi.h> |
25 | | #include <libxml/tree.h> |
26 | | #include <libxml/parser.h> |
27 | | #include <libxml/xpath.h> |
28 | | |
29 | | /* |
30 | | Example downlink data: |
31 | | |
32 | | <?xml version="1.0"?> |
33 | | <dump> |
34 | | <l1 direction="down" logicalchannel="96" physicalchannel="19" sequence="268116" error="0" timeshift="2992" bsic="22" data="31063F100DD0297A53E1020103C802398E0B2B2B2B2B2B" > |
35 | | <l2 data="063F100DD0297A53E1020103" rest="C802398E0B2B2B2B2B2B" > |
36 | | </l2> |
37 | | </l1> |
38 | | </dump> |
39 | | |
40 | | Example uplink data (no raw L1): |
41 | | |
42 | | <?xml version="1.0"?> |
43 | | <dump> |
44 | | <l1 direction="up" logicalchannel="112" > |
45 | | <l2 type="U" subtype="Unknown" p="0" data="061500400000000000000000000000000000" > |
46 | | </l2> |
47 | | </l1> |
48 | | </dump> |
49 | | |
50 | | */ |
51 | | |
52 | | typedef struct dct3trace_file_info { |
53 | | GByteArray* buffer; // holds current chunk of file |
54 | | int64_t start_offset; // where in the file the start of the buffer points |
55 | | } dct3trace_file_info_t; |
56 | | |
57 | | /* We expect to find all the info we need to tell if this file is ours |
58 | | * within this many bytes. Must include the beginTime attribute. |
59 | | */ |
60 | 0 | #define MAGIC_BUF_SIZE 512 |
61 | | |
62 | | /* String constants sought in the XML data. |
63 | | * Written as strings instead of lists of chars for readability. |
64 | | * Use the CLEN() macro to get the length of the constant without counting |
65 | | * the null byte at the end. |
66 | | */ |
67 | 0 | #define CLEN(x) (sizeof(x)-1) |
68 | | |
69 | | /* Magic text to check */ |
70 | | static const unsigned char c_xml_magic[] = "<?xml"; |
71 | | static const char dct3trace_magic_line2[] = "<dump>"; |
72 | | static const char dct3trace_magic_record_start[] = "<l1 "; |
73 | | static const char dct3trace_magic_record_end[] = "</l1>"; |
74 | | |
75 | 0 | #define RINGBUFFER_START_SIZE INT_MAX |
76 | 0 | #define RINGBUFFER_CHUNK_SIZE 1024 |
77 | | |
78 | 0 | #define MAX_PACKET_LEN 23 |
79 | | |
80 | | static int dct3trace_file_type_subtype = -1; |
81 | | |
82 | | void register_dct3trace(void); |
83 | | |
84 | | /* |
85 | | * Following 3 functions taken from gsmdecode-0.7bis, with permission: |
86 | | * |
87 | | * https://web.archive.org/web/20091218112927/http://wiki.thc.org/gsm |
88 | | */ |
89 | | |
90 | | static int |
91 | | hc2b(unsigned char hex) |
92 | 0 | { |
93 | 0 | hex = g_ascii_tolower(hex); |
94 | 0 | if ((hex >= '0') && (hex <= '9')) |
95 | 0 | return hex - '0'; |
96 | 0 | if ((hex >= 'a') && (hex <= 'f')) |
97 | 0 | return hex - 'a' + 10; |
98 | 0 | return -1; |
99 | 0 | } |
100 | | |
101 | | static int |
102 | | hex2bin(uint8_t *out, uint8_t *out_end, char *in) |
103 | 0 | { |
104 | 0 | uint8_t *out_start = out; |
105 | 0 | int is_low = 0; |
106 | 0 | int c; |
107 | |
|
108 | 0 | while (*in != '\0') |
109 | 0 | { |
110 | 0 | c = hc2b(*(unsigned char *)in); |
111 | 0 | if (c < 0) |
112 | 0 | { |
113 | 0 | in++; |
114 | 0 | continue; |
115 | 0 | } |
116 | 0 | if (out >= out_end) |
117 | 0 | { |
118 | | /* Too much data */ |
119 | 0 | return -1; |
120 | 0 | } |
121 | 0 | if (is_low == 0) |
122 | 0 | { |
123 | 0 | *out = c << 4; |
124 | 0 | is_low = 1; |
125 | 0 | } else { |
126 | 0 | *out |= (c & 0x0f); |
127 | 0 | is_low = 0; |
128 | 0 | out++; |
129 | 0 | } |
130 | 0 | in++; |
131 | 0 | } |
132 | | |
133 | 0 | return (int)(out - out_start); |
134 | 0 | } |
135 | | |
136 | | static bool |
137 | | xml_get_int(const char* name, xmlChar* str, int* val, int* err, char** err_info) |
138 | 0 | { |
139 | 0 | if (str != NULL) { |
140 | 0 | if (!ws_strtoi32((const char*)str, NULL, val)) { |
141 | 0 | *err = WTAP_ERR_BAD_FILE; |
142 | 0 | if (errno == ERANGE) { |
143 | 0 | if (*val < 0) |
144 | 0 | *err_info = ws_strdup_printf("dct3trace: %s value is too small, minimum is %d", name, *val); |
145 | 0 | else |
146 | 0 | *err_info = ws_strdup_printf("dct3trace: %s value is too large, maximum is %d", name, *val); |
147 | 0 | } |
148 | 0 | else |
149 | 0 | *err_info = ws_strdup_printf("dct3trace: %s value \"%s\" not a number", name, str); |
150 | 0 | return false; |
151 | 0 | } |
152 | 0 | } |
153 | | |
154 | 0 | return true; |
155 | 0 | } |
156 | | |
157 | | static bool |
158 | | dct3trace_get_packet(wtap* wth, wtap_rec* rec, const char* text, size_t len, int* err, char** err_info) |
159 | 0 | { |
160 | 0 | xmlDocPtr doc; |
161 | 0 | xmlNodePtr root_element; |
162 | 0 | bool status = true; |
163 | 0 | uint8_t databuf[MAX_PACKET_LEN], * bufp; |
164 | 0 | int local_len = 0; |
165 | 0 | bool have_data = false; |
166 | |
|
167 | 0 | doc = xmlParseMemory(text, (int)len); |
168 | 0 | if (doc == NULL) { |
169 | 0 | return false; |
170 | 0 | } |
171 | | |
172 | 0 | root_element = xmlDocGetRootElement(doc); |
173 | 0 | if (root_element == NULL) { |
174 | 0 | ws_debug("empty xml doc"); |
175 | 0 | status = false; |
176 | 0 | goto end; |
177 | 0 | } |
178 | | |
179 | | //Sanity check |
180 | 0 | if (xmlStrcmp(root_element->name, (const xmlChar*)"l1") != 0) { |
181 | 0 | *err = WTAP_ERR_BAD_FILE; |
182 | 0 | *err_info = ws_strdup("dct3trace: Did not start with \"<l1\""); |
183 | 0 | status = false; |
184 | 0 | goto end; |
185 | 0 | } |
186 | | |
187 | | /* Walk the attributes of the l1 tag */ |
188 | 0 | bufp = &databuf[0]; |
189 | 0 | for (xmlAttrPtr attr = root_element->properties; attr; attr = attr->next) { |
190 | 0 | int channel = 0, tmp = 0; |
191 | 0 | if (xmlStrcmp(attr->name, (const xmlChar*)"direction") == 0) { |
192 | 0 | xmlChar* str = xmlNodeListGetString(root_element->doc, attr->children, 1); |
193 | 0 | if (str != NULL) { |
194 | 0 | rec->rec_header.packet_header.pseudo_header.gsm_um.uplink = !strstr((const char*)str, "down"); |
195 | 0 | xmlFree(str); |
196 | 0 | } |
197 | 0 | } |
198 | 0 | else if (xmlStrcmp(attr->name, (const xmlChar*)"logicalchannel") == 0) { |
199 | 0 | xmlChar* str = xmlNodeListGetString(root_element->doc, attr->children, 1); |
200 | 0 | status = xml_get_int("logicalchannel", str, &channel, err, err_info); |
201 | 0 | xmlFree(str); |
202 | 0 | if (!status) |
203 | 0 | goto end; |
204 | | |
205 | 0 | switch (channel) |
206 | 0 | { |
207 | 0 | case 128: rec->rec_header.packet_header.pseudo_header.gsm_um.channel = GSM_UM_CHANNEL_SDCCH; break; |
208 | 0 | case 112: rec->rec_header.packet_header.pseudo_header.gsm_um.channel = GSM_UM_CHANNEL_SACCH; break; |
209 | 0 | case 176: rec->rec_header.packet_header.pseudo_header.gsm_um.channel = GSM_UM_CHANNEL_FACCH; break; |
210 | 0 | case 96: rec->rec_header.packet_header.pseudo_header.gsm_um.channel = GSM_UM_CHANNEL_CCCH; break; |
211 | 0 | case 80: rec->rec_header.packet_header.pseudo_header.gsm_um.channel = GSM_UM_CHANNEL_BCCH; break; |
212 | 0 | default: rec->rec_header.packet_header.pseudo_header.gsm_um.channel = GSM_UM_CHANNEL_UNKNOWN; break; |
213 | 0 | } |
214 | 0 | } |
215 | 0 | else if (xmlStrcmp(attr->name, (const xmlChar*)"data") == 0) { |
216 | 0 | have_data = true; /* Found data */ |
217 | 0 | xmlChar* str = xmlNodeListGetString(root_element->doc, attr->children, 1); |
218 | 0 | local_len = hex2bin(bufp, &databuf[MAX_PACKET_LEN], (char*)str); |
219 | 0 | xmlFree(str); |
220 | 0 | if (local_len == -1) |
221 | 0 | { |
222 | 0 | *err = WTAP_ERR_BAD_FILE; |
223 | 0 | *err_info = ws_strdup_printf("dct3trace: record length %d too long", rec->rec_header.packet_header.caplen); |
224 | 0 | status = false; |
225 | 0 | goto end; |
226 | 0 | } |
227 | |
|
228 | 0 | } |
229 | 0 | else if (!rec->rec_header.packet_header.pseudo_header.gsm_um.uplink) { |
230 | | /* Parse downlink only fields */ |
231 | |
|
232 | 0 | if (xmlStrcmp(attr->name, (const xmlChar*)"physicalchannel") == 0) { |
233 | 0 | xmlChar* str = xmlNodeListGetString(root_element->doc, attr->children, 1); |
234 | 0 | status = xml_get_int("physicalchannel", str, &tmp, err, err_info); |
235 | 0 | xmlFree(str); |
236 | 0 | if (!status) |
237 | 0 | goto end; |
238 | | |
239 | 0 | rec->rec_header.packet_header.pseudo_header.gsm_um.arfcn = tmp; |
240 | 0 | } |
241 | 0 | else if (xmlStrcmp(attr->name, (const xmlChar*)"sequence") == 0) { |
242 | 0 | xmlChar* str = xmlNodeListGetString(root_element->doc, attr->children, 1); |
243 | 0 | status = xml_get_int("sequence", str, &tmp, err, err_info); |
244 | 0 | xmlFree(str); |
245 | 0 | if (!status) |
246 | 0 | goto end; |
247 | | |
248 | 0 | rec->rec_header.packet_header.pseudo_header.gsm_um.tdma_frame = tmp; |
249 | 0 | } |
250 | 0 | else if (xmlStrcmp(attr->name, (const xmlChar*)"bsic") == 0) { |
251 | 0 | xmlChar* str = xmlNodeListGetString(root_element->doc, attr->children, 1); |
252 | 0 | status = xml_get_int("bsic", str, &tmp, err, err_info); |
253 | 0 | xmlFree(str); |
254 | 0 | if (!status) |
255 | 0 | goto end; |
256 | | |
257 | 0 | rec->rec_header.packet_header.pseudo_header.gsm_um.bsic = tmp; |
258 | 0 | } |
259 | 0 | else if (xmlStrcmp(attr->name, (const xmlChar*)"error") == 0) { |
260 | 0 | xmlChar* str = xmlNodeListGetString(root_element->doc, attr->children, 1); |
261 | 0 | status = xml_get_int("error", str, &tmp, err, err_info); |
262 | 0 | xmlFree(str); |
263 | 0 | if (!status) |
264 | 0 | goto end; |
265 | | |
266 | 0 | rec->rec_header.packet_header.pseudo_header.gsm_um.error = tmp; |
267 | 0 | } |
268 | 0 | else if (xmlStrcmp(attr->name, (const xmlChar*)"timeshift") == 0) { |
269 | 0 | xmlChar* str = xmlNodeListGetString(root_element->doc, attr->children, 1); |
270 | 0 | status = xml_get_int("timeshift", str, &tmp, err, err_info); |
271 | 0 | xmlFree(str); |
272 | 0 | if (!status) |
273 | 0 | goto end; |
274 | | |
275 | 0 | rec->rec_header.packet_header.pseudo_header.gsm_um.timeshift = tmp; |
276 | 0 | } |
277 | 0 | } |
278 | 0 | } |
279 | | |
280 | 0 | if (!have_data) |
281 | 0 | { |
282 | 0 | for (xmlNodePtr cur = root_element->children; cur != NULL; cur = cur->next) { |
283 | | /* Get data from l2 tag */ |
284 | 0 | if ((cur->type == XML_ELEMENT_NODE) && (xmlStrcmp(cur->name, (const xmlChar*)"l2") == 0)) { |
285 | 0 | for (xmlAttrPtr attr = cur->properties; attr; attr = attr->next) { |
286 | | |
287 | | /* For uplink packets we might not get the raw L1, so have to recreate it from the L2 */ |
288 | | /* Parse L2 header if didn't get data from L1 <l2 ...> */ |
289 | 0 | if (xmlStrcmp(attr->name, (const xmlChar*)"data") == 0) { |
290 | 0 | int data_len; |
291 | |
|
292 | 0 | have_data = true; /* Found data */ |
293 | | |
294 | | /* |
295 | | * We know we have no data already, so we know |
296 | | * we have enough room for the header. |
297 | | */ |
298 | 0 | switch(rec->rec_header.packet_header.pseudo_header.gsm_um.channel) |
299 | 0 | { |
300 | 0 | case GSM_UM_CHANNEL_SACCH: |
301 | 0 | case GSM_UM_CHANNEL_FACCH: |
302 | 0 | case GSM_UM_CHANNEL_SDCCH: |
303 | | /* Add LAPDm B header */ |
304 | 0 | memset(bufp, 0x1, 2); |
305 | 0 | local_len = 3; |
306 | 0 | break; |
307 | 0 | default: |
308 | | /* Add LAPDm Bbis header */ |
309 | 0 | local_len = 1; |
310 | 0 | break; |
311 | 0 | } |
312 | 0 | bufp += local_len; |
313 | |
|
314 | 0 | xmlChar* str = xmlNodeListGetString(cur->doc, attr->children, 1); |
315 | 0 | data_len = hex2bin(bufp, &databuf[MAX_PACKET_LEN], (char*)str); |
316 | 0 | xmlFree(str); |
317 | 0 | if (data_len == -1) |
318 | 0 | { |
319 | 0 | *err = WTAP_ERR_BAD_FILE; |
320 | 0 | *err_info = ws_strdup_printf("dct3trace: record length %d too long", rec->rec_header.packet_header.caplen); |
321 | 0 | status = false; |
322 | 0 | goto end; |
323 | 0 | } |
324 | 0 | local_len += data_len; |
325 | | |
326 | | /* Add LAPDm length byte */ |
327 | 0 | *(bufp - 1) = data_len << 2 | 0x1; |
328 | 0 | } |
329 | 0 | } |
330 | | /* There should be only a single "l2" child element |
331 | | * within a "l1" element, so break. (This keeps us |
332 | | * from writing to bufp a seconds time.) */ |
333 | 0 | break; |
334 | 0 | } |
335 | 0 | } |
336 | 0 | } |
337 | | |
338 | 0 | if (have_data) { |
339 | | /* We've got a full packet! */ |
340 | 0 | wtap_setup_packet_rec(rec, wth->file_encap); |
341 | 0 | rec->block = wtap_block_create(WTAP_BLOCK_PACKET); |
342 | 0 | rec->presence_flags = 0; /* no time stamp, no separate "on the wire" length */ |
343 | 0 | rec->ts.secs = 0; |
344 | 0 | rec->ts.nsecs = 0; |
345 | 0 | rec->rec_header.packet_header.caplen = local_len; |
346 | 0 | rec->rec_header.packet_header.len = local_len; |
347 | | |
348 | | /* Append data to the packet buffer */ |
349 | 0 | ws_buffer_append(&rec->data, databuf, rec->rec_header.packet_header.caplen); |
350 | 0 | } |
351 | 0 | else { |
352 | | /* If not got any data return error */ |
353 | 0 | *err = WTAP_ERR_BAD_FILE; |
354 | 0 | *err_info = g_strdup("dct3trace: record without data"); |
355 | 0 | status = false; |
356 | 0 | } |
357 | |
|
358 | 0 | end: |
359 | 0 | xmlFreeDoc(doc); |
360 | 0 | return status; |
361 | 0 | } |
362 | | |
363 | | /* Read from fh and store into buffer, until buffer contains needle. |
364 | | * Returns location of needle once found, or NULL if it's never found |
365 | | * (due to either EOF or read error). |
366 | | */ |
367 | | static uint8_t* |
368 | | read_until(GByteArray* buffer, const unsigned char* needle, FILE_T fh, int* err, char** err_info) |
369 | 0 | { |
370 | 0 | uint8_t read_buffer[RINGBUFFER_CHUNK_SIZE]; |
371 | 0 | uint8_t* found_it; |
372 | 0 | int bytes_read = 0; |
373 | |
|
374 | 0 | while (NULL == (found_it = (uint8_t*)g_strstr_len((const char*)buffer->data, buffer->len, (const char*)needle))) { |
375 | 0 | bytes_read = file_read(read_buffer, RINGBUFFER_CHUNK_SIZE, fh); |
376 | 0 | if (bytes_read < 0) { |
377 | 0 | *err = file_error(fh, err_info); |
378 | 0 | break; |
379 | 0 | } |
380 | 0 | if (bytes_read == 0) { |
381 | 0 | break; |
382 | 0 | } |
383 | 0 | g_byte_array_append(buffer, read_buffer, bytes_read); |
384 | 0 | } |
385 | 0 | return found_it; |
386 | 0 | } |
387 | | |
388 | | /* Find the next packet and parse it; called from wtap_read(). */ |
389 | | static bool dct3trace_read(wtap *wth, wtap_rec *rec, |
390 | | int *err, char **err_info, int64_t *data_offset) |
391 | 0 | { |
392 | 0 | dct3trace_file_info_t* file_info = (dct3trace_file_info_t*)wth->priv; |
393 | 0 | uint8_t* buf_start; |
394 | 0 | uint8_t* msg_start, * msg_end; |
395 | 0 | unsigned msg_offset = 0; |
396 | 0 | size_t msg_len = 0; |
397 | 0 | bool status = false; |
398 | | |
399 | | /* Make sure we have a start and end of message in our buffer -- end first */ |
400 | 0 | msg_end = read_until(file_info->buffer, (const uint8_t*)dct3trace_magic_record_end, wth->fh, err, err_info); |
401 | 0 | if (msg_end == NULL) { |
402 | 0 | goto end; |
403 | 0 | } |
404 | | |
405 | 0 | buf_start = file_info->buffer->data; |
406 | | /* Now search backwards for the message start |
407 | | * (doing it this way should skip over any empty "<msg ... />" tags we have) |
408 | | */ |
409 | 0 | msg_start = (uint8_t*)g_strrstr_len((const char*)buf_start, (unsigned)(msg_end - buf_start), dct3trace_magic_record_start); |
410 | 0 | if (msg_start == NULL || msg_start > msg_end) { |
411 | 0 | *err_info = ws_strdup_printf("dct3trace: Found \"%s\" without matching \"%s\"", dct3trace_magic_record_end, dct3trace_magic_record_start); |
412 | 0 | *err = WTAP_ERR_BAD_FILE; |
413 | 0 | goto end; |
414 | 0 | } |
415 | | |
416 | | /* We know we have a message, what's its offset from the buffer start? */ |
417 | 0 | msg_offset = (unsigned)(msg_start - buf_start); |
418 | 0 | msg_end += CLEN(dct3trace_magic_record_end); |
419 | 0 | msg_len = (unsigned)(msg_end - msg_start); |
420 | | |
421 | | /* Tell Wireshark to put us at the start of the "<msg" for seek_read later */ |
422 | 0 | *data_offset = file_info->start_offset + msg_offset; |
423 | | |
424 | | /* pass all of <l1....</l1> to dct3trace_get_packet() */ |
425 | 0 | status = dct3trace_get_packet(wth, rec, (const char*)msg_start, msg_len, err, err_info); |
426 | | |
427 | | /* Finally, shift our buffer to the end of this message to get ready for the next one. |
428 | | * Re-use msg_len to get the length of the data we're done with. |
429 | | */ |
430 | 0 | msg_len = msg_end - file_info->buffer->data; |
431 | 0 | while (G_UNLIKELY(msg_len > UINT_MAX)) { |
432 | 0 | g_byte_array_remove_range(file_info->buffer, 0, UINT_MAX); |
433 | 0 | msg_len -= UINT_MAX; |
434 | 0 | } |
435 | 0 | g_byte_array_remove_range(file_info->buffer, 0, (unsigned)msg_len); |
436 | 0 | file_info->start_offset += msg_len; |
437 | |
|
438 | 0 | end: |
439 | 0 | if (status == false) { |
440 | | /* There's no more to read. Empty out the buffer */ |
441 | 0 | g_byte_array_set_size(file_info->buffer, 0); |
442 | 0 | } |
443 | |
|
444 | 0 | return status; |
445 | 0 | } |
446 | | |
447 | | |
448 | | /* Used to read packets in random-access fashion */ |
449 | | static bool dct3trace_seek_read(wtap *wth, int64_t seek_off, |
450 | | wtap_rec *rec, int *err, char **err_info) |
451 | 0 | { |
452 | 0 | dct3trace_file_info_t* file_info = (dct3trace_file_info_t*)wth->priv; |
453 | 0 | bool status = false; |
454 | 0 | uint8_t* msg_end; |
455 | 0 | unsigned msg_len = 0; |
456 | | |
457 | | /* We stored the offset of the "<msg" for this packet */ |
458 | 0 | if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1) |
459 | 0 | return false; |
460 | | |
461 | 0 | msg_end = read_until(file_info->buffer, (const uint8_t*)dct3trace_magic_record_end, wth->random_fh, err, err_info); |
462 | 0 | if (msg_end == NULL) { |
463 | 0 | return false; |
464 | 0 | } |
465 | 0 | msg_end += CLEN(dct3trace_magic_record_end); |
466 | 0 | msg_len = (unsigned)(msg_end - file_info->buffer->data); |
467 | |
|
468 | 0 | status = dct3trace_get_packet(wth, rec, (const char*)file_info->buffer->data, msg_len, err, err_info); |
469 | 0 | g_byte_array_set_size(file_info->buffer, 0); |
470 | 0 | return status; |
471 | 0 | } |
472 | | |
473 | | /* Clean up any memory we allocated for dealing with this file. |
474 | | * Set as the subtype_close function in the file_open function below. |
475 | | * (wiretap frees wth->priv itself) |
476 | | */ |
477 | | static void |
478 | | dct3trace_close(wtap* wth) |
479 | 0 | { |
480 | 0 | dct3trace_file_info_t* file_info = (dct3trace_file_info_t*)wth->priv; |
481 | |
|
482 | 0 | if (file_info != NULL && file_info->buffer != NULL) { |
483 | 0 | g_byte_array_free(file_info->buffer, true); |
484 | 0 | file_info->buffer = NULL; |
485 | 0 | } |
486 | 0 | } |
487 | | |
488 | | wtap_open_return_val dct3trace_open(wtap* wth, int* err, char** err_info) |
489 | 0 | { |
490 | 0 | char magic_buf[MAGIC_BUF_SIZE + 1]; |
491 | 0 | int bytes_read; |
492 | 0 | const char* curr_pos; |
493 | 0 | dct3trace_file_info_t* file_info; |
494 | 0 | int64_t start_offset; |
495 | |
|
496 | 0 | start_offset = file_tell(wth->fh); // Most likely 0 but doesn't hurt to check |
497 | 0 | bytes_read = file_read(magic_buf, MAGIC_BUF_SIZE, wth->fh); |
498 | |
|
499 | 0 | if (bytes_read < 0) { |
500 | 0 | *err = file_error(wth->fh, err_info); |
501 | 0 | return WTAP_OPEN_ERROR; |
502 | 0 | } |
503 | 0 | if (bytes_read == 0) { |
504 | 0 | return WTAP_OPEN_NOT_MINE; |
505 | 0 | } |
506 | | |
507 | 0 | if (memcmp(magic_buf, c_xml_magic, CLEN(c_xml_magic)) != 0) { |
508 | 0 | return WTAP_OPEN_NOT_MINE; |
509 | 0 | } |
510 | | |
511 | 0 | curr_pos = g_strstr_len(magic_buf, bytes_read, dct3trace_magic_line2); |
512 | 0 | if (!curr_pos) { |
513 | 0 | return WTAP_OPEN_NOT_MINE; |
514 | 0 | } |
515 | 0 | curr_pos += CLEN(dct3trace_magic_line2); |
516 | | |
517 | | /* Ok it's our file. From here we'll need to free memory */ |
518 | 0 | file_info = g_new0(dct3trace_file_info_t, 1); |
519 | 0 | file_info->start_offset = start_offset + (curr_pos - magic_buf); |
520 | 0 | file_info->buffer = g_byte_array_sized_new(RINGBUFFER_START_SIZE); |
521 | 0 | g_byte_array_append(file_info->buffer, (const uint8_t*)curr_pos, (unsigned)(bytes_read - (curr_pos - magic_buf))); |
522 | |
|
523 | 0 | wth->file_type_subtype = dct3trace_file_type_subtype; |
524 | 0 | wth->file_encap = WTAP_ENCAP_GSM_UM; |
525 | 0 | wth->file_tsprec = WTAP_TSPREC_SEC; |
526 | 0 | wth->subtype_read = dct3trace_read; |
527 | 0 | wth->subtype_seek_read = dct3trace_seek_read; |
528 | 0 | wth->subtype_close = dct3trace_close; |
529 | 0 | wth->snapshot_length = 0;/* not known */ |
530 | 0 | wth->priv = (void*)file_info; |
531 | | |
532 | | /* |
533 | | * Add an IDB; we don't know how many interfaces were |
534 | | * involved, so we just say one interface, about which |
535 | | * we only know the link-layer type, snapshot length, |
536 | | * and time stamp resolution. |
537 | | */ |
538 | 0 | wtap_add_generated_idb(wth); |
539 | |
|
540 | 0 | return WTAP_OPEN_MINE; |
541 | 0 | } |
542 | | |
543 | | static const struct supported_block_type dct3trace_blocks_supported[] = { |
544 | | /* |
545 | | * We support packet blocks, with no comments or other options. |
546 | | */ |
547 | | { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED } |
548 | | }; |
549 | | |
550 | | static const struct file_type_subtype_info dct3trace_info = { |
551 | | "Gammu DCT3 trace", "dct3trace", "xml", NULL, |
552 | | false, BLOCKS_SUPPORTED(dct3trace_blocks_supported), |
553 | | NULL, NULL, NULL |
554 | | }; |
555 | | |
556 | | void register_dct3trace(void) |
557 | 16 | { |
558 | 16 | dct3trace_file_type_subtype = wtap_register_file_type_subtype(&dct3trace_info); |
559 | | |
560 | | /* |
561 | | * Register name for backwards compatibility with the |
562 | | * wtap_filetypes table in Lua. |
563 | | */ |
564 | 16 | wtap_register_backwards_compatibility_lua_name("DCT3TRACE", |
565 | 16 | dct3trace_file_type_subtype); |
566 | 16 | } |
567 | | |
568 | | /* |
569 | | * Editor modelines - https://www.wireshark.org/tools/modelines.html |
570 | | * |
571 | | * Local variables: |
572 | | * c-basic-offset: 8 |
573 | | * tab-width: 8 |
574 | | * indent-tabs-mode: t |
575 | | * End: |
576 | | * |
577 | | * vi: set shiftwidth=8 tabstop=8 noexpandtab: |
578 | | * :indentSize=8:tabSize=8:noTabs=false: |
579 | | */ |