2 * Routines for reading signalling traces generated by Gammu (www.gammu.org)
3 * from Nokia DCT3 phones in Netmonitor mode.
5 * gammu --nokiadebug nhm5_587.txt v18-19
7 * Duncan Salerno <duncan.salerno@googlemail.com>
10 * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
12 * SPDX-License-Identifier: GPL-2.0-or-later
16 #include "dct3trace.h"
18 #include "file_wrappers.h"
24 #include <wsutil/strtoi.h>
27 Example downlink data:
31 <l1 direction="down" logicalchannel="96" physicalchannel="19" sequence="268116" error="0" timeshift="2992" bsic="22" data="31063F100DD0297A53E1020103C802398E0B2B2B2B2B2B" >
32 <l2 data="063F100DD0297A53E1020103" rest="C802398E0B2B2B2B2B2B" >
37 Example uplink data (no raw L1):
41 <l1 direction="up" logicalchannel="112" >
42 <l2 type="U" subtype="Unknown" p="0" data="061500400000000000000000000000000000" >
50 /* Magic text to check */
51 static const char dct3trace_magic_line1
[] = "<?xml version=\"1.0\"?>";
52 static const char dct3trace_magic_line2
[] = "<dump>";
53 static const char dct3trace_magic_record_start
[] = "<l1 ";
54 static const char dct3trace_magic_record_end
[] = "</l1>";
55 static const char dct3trace_magic_l2_start
[] = "<l2 ";
56 #if 0 /* Not used ?? */
57 static const char dct3trace_magic_l2_end
[] = "</l2>";
59 static const char dct3trace_magic_end
[] = "</dump>";
61 #define MAX_PACKET_LEN 23
63 static bool dct3trace_read(wtap
*wth
, wtap_rec
*rec
,
64 Buffer
*buf
, int *err
, char **err_info
, int64_t *data_offset
);
65 static bool dct3trace_seek_read(wtap
*wth
, int64_t seek_off
,
66 wtap_rec
*rec
, Buffer
*buf
, int *err
, char **err_info
);
68 static int dct3trace_file_type_subtype
= -1;
70 void register_dct3trace(void);
73 * Following 3 functions taken from gsmdecode-0.7bis, with permission:
75 * https://web.archive.org/web/20091218112927/http://wiki.thc.org/gsm
79 hc2b(unsigned char hex
)
81 hex
= g_ascii_tolower(hex
);
82 if ((hex
>= '0') && (hex
<= '9'))
84 if ((hex
>= 'a') && (hex
<= 'f'))
85 return hex
- 'a' + 10;
90 hex2bin(uint8_t *out
, uint8_t *out_end
, char *in
)
92 uint8_t *out_start
= out
;
98 c
= hc2b(*(unsigned char *)in
);
121 return (int)(out
- out_start
);
125 xml_get_int(int *val
, const char *str
, const char *pattern
, int *err
, char **err_info
)
127 const char *ptr
, *endptr
;
131 ptr
= strstr(str
, pattern
);
133 *err
= WTAP_ERR_BAD_FILE
;
134 *err_info
= ws_strdup_printf("dct3trace: %s not found", pattern
);
138 * XXX - should we just skip past the pattern and check for ="?
140 start
= strchr(ptr
, '"');
142 *err
= WTAP_ERR_BAD_FILE
;
143 *err_info
= ws_strdup_printf("dct3trace: opening quote for %s not found", pattern
);
148 * XXX - should we just use ws_strtoi32() and check whether
149 * the character following the number is a "?
151 end
= strchr(start
, '"');
153 *err
= WTAP_ERR_BAD_FILE
;
154 *err_info
= ws_strdup_printf("dct3trace: closing quote for %s not found", pattern
);
157 if (end
- start
> 31) {
158 *err
= WTAP_ERR_BAD_FILE
;
159 *err_info
= ws_strdup_printf("dct3trace: %s value is too long", pattern
);
163 memcpy(buf
, start
, end
- start
);
164 buf
[end
- start
] = '\0';
166 * XXX - should we allow negative numbers in all cases? Or are
167 * there cases where the number is unsigned?
169 if (!ws_strtoi32(buf
, &endptr
, val
)) {
170 *err
= WTAP_ERR_BAD_FILE
;
171 if (errno
== ERANGE
) {
173 *err_info
= ws_strdup_printf("dct3trace: %s value is too small, minimum is %d", pattern
, *val
);
175 *err_info
= ws_strdup_printf("dct3trace: %s value is too large, maximum is %d", pattern
, *val
);
177 *err_info
= ws_strdup_printf("dct3trace: %s value \"%s\" not a number", pattern
, buf
);
180 if (*endptr
!= '\0') {
181 *err
= WTAP_ERR_BAD_FILE
;
182 *err_info
= ws_strdup_printf("dct3trace: %s value \"%s\" not a number", pattern
, buf
);
189 wtap_open_return_val
dct3trace_open(wtap
*wth
, int *err
, char **err_info
)
191 char line1
[64], line2
[64];
193 /* Look for Gammu DCT3 trace header */
194 if (file_gets(line1
, sizeof(line1
), wth
->fh
) == NULL
||
195 file_gets(line2
, sizeof(line2
), wth
->fh
) == NULL
)
197 *err
= file_error(wth
->fh
, err_info
);
198 if (*err
!= 0 && *err
!= WTAP_ERR_SHORT_READ
)
199 return WTAP_OPEN_ERROR
;
200 return WTAP_OPEN_NOT_MINE
;
203 /* Don't compare line endings */
204 if( strncmp(dct3trace_magic_line1
, line1
, strlen(dct3trace_magic_line1
)) != 0 ||
205 strncmp(dct3trace_magic_line2
, line2
, strlen(dct3trace_magic_line2
)) != 0)
207 return WTAP_OPEN_NOT_MINE
;
210 wth
->file_encap
= WTAP_ENCAP_GSM_UM
;
211 wth
->file_type_subtype
= dct3trace_file_type_subtype
;
212 wth
->snapshot_length
= 0; /* not known */
213 wth
->subtype_read
= dct3trace_read
;
214 wth
->subtype_seek_read
= dct3trace_seek_read
;
215 wth
->file_tsprec
= WTAP_TSPREC_SEC
;
218 * Add an IDB; we don't know how many interfaces were
219 * involved, so we just say one interface, about which
220 * we only know the link-layer type, snapshot length,
221 * and time stamp resolution.
223 wtap_add_generated_idb(wth
);
225 return WTAP_OPEN_MINE
;
229 static bool dct3trace_get_packet(FILE_T fh
, wtap_rec
*rec
,
230 Buffer
*buf
, int *err
, char **err_info
)
233 uint8_t databuf
[MAX_PACKET_LEN
], *bufp
;
234 bool have_data
= false;
238 while (file_gets(line
, sizeof(line
), fh
) != NULL
)
240 if( memcmp(dct3trace_magic_end
, line
, strlen(dct3trace_magic_end
)) == 0 )
242 /* Return on end of file </dump> */
246 else if( memcmp(dct3trace_magic_record_end
, line
, strlen(dct3trace_magic_record_end
)) == 0 )
248 /* Return on end of record </l1> */
251 /* We've got a full packet! */
252 rec
->rec_type
= REC_TYPE_PACKET
;
253 rec
->block
= wtap_block_create(WTAP_BLOCK_PACKET
);
254 rec
->presence_flags
= 0; /* no time stamp, no separate "on the wire" length */
257 rec
->rec_header
.packet_header
.caplen
= len
;
258 rec
->rec_header
.packet_header
.len
= len
;
262 /* Make sure we have enough room for the packet */
263 ws_buffer_assure_space(buf
, rec
->rec_header
.packet_header
.caplen
);
264 memcpy( ws_buffer_start_ptr(buf
), databuf
, rec
->rec_header
.packet_header
.caplen
);
270 /* If not got any data return error */
271 *err
= WTAP_ERR_BAD_FILE
;
272 *err_info
= g_strdup("dct3trace: record without data");
276 else if( memcmp(dct3trace_magic_record_start
, line
, strlen(dct3trace_magic_record_start
)) == 0 )
278 /* Parse L1 header <l1 ...>*/
282 rec
->rec_header
.packet_header
.pseudo_header
.gsm_um
.uplink
= !strstr(line
, "direction=\"down\"");
283 if (!xml_get_int(&channel
, line
, "logicalchannel", err
, err_info
))
286 /* Parse downlink only fields */
287 if( !rec
->rec_header
.packet_header
.pseudo_header
.gsm_um
.uplink
)
289 if (!xml_get_int(&tmp
, line
, "physicalchannel", err
, err_info
))
291 rec
->rec_header
.packet_header
.pseudo_header
.gsm_um
.arfcn
= tmp
;
292 if (!xml_get_int(&tmp
, line
, "sequence", err
, err_info
))
294 rec
->rec_header
.packet_header
.pseudo_header
.gsm_um
.tdma_frame
= tmp
;
295 if (!xml_get_int(&tmp
, line
, "bsic", err
, err_info
))
297 rec
->rec_header
.packet_header
.pseudo_header
.gsm_um
.bsic
= tmp
;
298 if (!xml_get_int(&tmp
, line
, "error", err
, err_info
))
300 rec
->rec_header
.packet_header
.pseudo_header
.gsm_um
.error
= tmp
;
301 if (!xml_get_int(&tmp
, line
, "timeshift", err
, err_info
))
303 rec
->rec_header
.packet_header
.pseudo_header
.gsm_um
.timeshift
= tmp
;
308 case 128: rec
->rec_header
.packet_header
.pseudo_header
.gsm_um
.channel
= GSM_UM_CHANNEL_SDCCH
; break;
309 case 112: rec
->rec_header
.packet_header
.pseudo_header
.gsm_um
.channel
= GSM_UM_CHANNEL_SACCH
; break;
310 case 176: rec
->rec_header
.packet_header
.pseudo_header
.gsm_um
.channel
= GSM_UM_CHANNEL_FACCH
; break;
311 case 96: rec
->rec_header
.packet_header
.pseudo_header
.gsm_um
.channel
= GSM_UM_CHANNEL_CCCH
; break;
312 case 80: rec
->rec_header
.packet_header
.pseudo_header
.gsm_um
.channel
= GSM_UM_CHANNEL_BCCH
; break;
313 default: rec
->rec_header
.packet_header
.pseudo_header
.gsm_um
.channel
= GSM_UM_CHANNEL_UNKNOWN
; break;
316 /* Read data (if have it) into databuf */
317 ptr
= strstr(line
, "data=\"");
320 have_data
= true; /* If has data... */
321 len
= hex2bin(bufp
, &databuf
[MAX_PACKET_LEN
], ptr
+6);
324 *err
= WTAP_ERR_BAD_FILE
;
325 *err_info
= ws_strdup_printf("dct3trace: record length %d too long", rec
->rec_header
.packet_header
.caplen
);
330 else if( !have_data
&& memcmp(dct3trace_magic_l2_start
, line
, strlen(dct3trace_magic_l2_start
)) == 0 )
332 /* For uplink packets we might not get the raw L1, so have to recreate it from the L2 */
333 /* Parse L2 header if didn't get data from L1 <l2 ...> */
335 char *ptr
= strstr(line
, "data=\"");
345 * We know we have no data already, so we know
346 * we have enough room for the header.
348 if( rec
->rec_header
.packet_header
.pseudo_header
.gsm_um
.channel
== GSM_UM_CHANNEL_SACCH
|| rec
->rec_header
.packet_header
.pseudo_header
.gsm_um
.channel
== GSM_UM_CHANNEL_FACCH
|| rec
->rec_header
.packet_header
.pseudo_header
.gsm_um
.channel
== GSM_UM_CHANNEL_SDCCH
)
350 /* Add LAPDm B header */
351 memset(bufp
, 0x1, 2);
356 /* Add LAPDm Bbis header */
361 data_len
= hex2bin(bufp
, &databuf
[MAX_PACKET_LEN
], ptr
+6);
364 *err
= WTAP_ERR_BAD_FILE
;
365 *err_info
= ws_strdup_printf("dct3trace: record length %d too long", rec
->rec_header
.packet_header
.caplen
);
370 /* Add LAPDm length byte */
371 *(bufp
- 1) = data_len
<< 2 | 0x1;
375 *err
= file_error(fh
, err_info
);
378 *err
= WTAP_ERR_SHORT_READ
;
384 /* Find the next packet and parse it; called from wtap_read(). */
385 static bool dct3trace_read(wtap
*wth
, wtap_rec
*rec
, Buffer
*buf
,
386 int *err
, char **err_info
, int64_t *data_offset
)
388 *data_offset
= file_tell(wth
->fh
);
390 return dct3trace_get_packet(wth
->fh
, rec
, buf
, err
, err_info
);
394 /* Used to read packets in random-access fashion */
395 static bool dct3trace_seek_read(wtap
*wth
, int64_t seek_off
,
396 wtap_rec
*rec
, Buffer
*buf
, int *err
, char **err_info
)
398 if (file_seek(wth
->random_fh
, seek_off
, SEEK_SET
, err
) == -1)
403 return dct3trace_get_packet(wth
->random_fh
, rec
, buf
, err
, err_info
);
406 static const struct supported_block_type dct3trace_blocks_supported
[] = {
408 * We support packet blocks, with no comments or other options.
410 { WTAP_BLOCK_PACKET
, MULTIPLE_BLOCKS_SUPPORTED
, NO_OPTIONS_SUPPORTED
}
413 static const struct file_type_subtype_info dct3trace_info
= {
414 "Gammu DCT3 trace", "dct3trace", "xml", NULL
,
415 false, BLOCKS_SUPPORTED(dct3trace_blocks_supported
),
419 void register_dct3trace(void)
421 dct3trace_file_type_subtype
= wtap_register_file_type_subtype(&dct3trace_info
);
424 * Register name for backwards compatibility with the
425 * wtap_filetypes table in Lua.
427 wtap_register_backwards_compatibility_lua_name("DCT3TRACE",
428 dct3trace_file_type_subtype
);
432 * Editor modelines - https://www.wireshark.org/tools/modelines.html
437 * indent-tabs-mode: t
440 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
441 * :indentSize=8:tabSize=8:noTabs=false: