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>
12 * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation; either version 2
17 * of the License, or (at your option) any later version.
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
24 * You should have received a copy of the GNU General Public License
25 * along with this program; if not, write to the Free Software
26 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
34 #include "dct3trace.h"
35 #include "file_wrappers.h"
44 Example downlink data:
48 <l1 direction="down" logicalchannel="96" physicalchannel="19" sequence="268116" error="0" timeshift="2992" bsic="22" data="31063F100DD0297A53E1020103C802398E0B2B2B2B2B2B" >
49 <l2 data="063F100DD0297A53E1020103" rest="C802398E0B2B2B2B2B2B" >
54 Example uplink data (no raw L1):
58 <l1 direction="up" logicalchannel="112" >
59 <l2 type="U" subtype="Unknown" p="0" data="061500400000000000000000000000000000" >
67 /* Magic text to check */
68 static const char dct3trace_magic_line1[] = "<?xml version=\"1.0\"?>";
69 static const char dct3trace_magic_line2[] = "<dump>";
70 static const char dct3trace_magic_record_start[] = "<l1 ";
71 static const char dct3trace_magic_record_end[] = "</l1>";
72 static const char dct3trace_magic_l2_start[] = "<l2 ";
73 static const char dct3trace_magic_l2_end[] = "</l2>";
74 static const char dct3trace_magic_end[] = "</dump>";
76 #define MAX_PACKET_LEN 23
78 static gboolean dct3trace_read(wtap *wth, int *err, gchar **err_info,
80 static gboolean dct3trace_seek_read(wtap *wth, gint64 seek_off,
81 union wtap_pseudo_header *pseudo_header, guint8 *pd, int len,
82 int *err, gchar **err_info);
85 * Following 3 functions taken from gsmdecode-0.7bis, with permission - http://wiki.thc.org/gsm
89 hc2b(unsigned char hex)
92 if ((hex >= '0') && (hex <= '9'))
94 if ((hex >= 'a') && (hex <= 'f'))
95 return hex - 'a' + 10;
100 hex2bin(unsigned char *out, unsigned char *in)
102 unsigned char *out_start = out;
103 unsigned char *end = in + strlen((char *)in);
107 /* Clamp to maximum packet size */
108 if (end - in > MAX_PACKET_LEN*2) /* As we're reading nibbles */
109 end = in + MAX_PACKET_LEN*2;
124 out[0] |= (c & 0x0f);
131 return (int)(out - out_start);
135 xml_get_int(int *val, const unsigned char *str, const unsigned char *pattern)
141 ptr = strstr((const char *)str, (const char *)pattern);
144 start = strchr(ptr, '"');
148 end = strchr(start, '"');
151 if (end - start > 31)
154 memcpy(buf, start, end - start);
155 buf[end - start] = '\0';
161 /* Look through the first part of a file to see if this is
164 * Returns TRUE if it is, FALSE if it isn't or if we get an I/O error;
165 * if we get an I/O error, "*err" will be set to a non-zero value
166 * and "*err_info" will be set to null or an additional error string.
168 static gboolean dct3trace_check_file_type(wtap *wth, int *err, gchar **err_info)
170 char line1[64], line2[64];
172 if (file_gets(line1, sizeof(line1), wth->fh) != NULL &&
173 file_gets(line2, sizeof(line2), wth->fh) != NULL)
175 /* Don't compare line endings */
176 if( strncmp(dct3trace_magic_line1, line1, strlen(dct3trace_magic_line1)) == 0 &&
177 strncmp(dct3trace_magic_line2, line2, strlen(dct3trace_magic_line2)) == 0)
185 if (file_eof(wth->fh))
188 *err = file_error(wth->fh, err_info);
195 int dct3trace_open(wtap *wth, int *err, gchar **err_info)
197 /* Look for Gammu DCT3 trace header */
198 if (!dct3trace_check_file_type(wth, err, err_info))
206 wth->data_offset = 0;
207 wth->file_encap = WTAP_ENCAP_GSM_UM;
208 wth->file_type = WTAP_FILE_DCT3TRACE;
209 wth->snapshot_length = 0; /* not known */
210 wth->subtype_read = dct3trace_read;
211 wth->subtype_seek_read = dct3trace_seek_read;
212 wth->tsprecision = WTAP_FILE_TSPREC_SEC;
218 static gboolean dct3trace_get_packet(FILE_T fh, union wtap_pseudo_header *pseudo_header,
219 unsigned char *buf, int *len, int *err, gchar **err_info)
221 unsigned char line[1024];
222 gboolean have_data = FALSE;
224 while (file_gets(line, sizeof(line), fh) != NULL)
226 if( memcmp(dct3trace_magic_end, line, strlen(dct3trace_magic_end)) == 0 )
228 /* Return on end of file </dump> */
232 else if( memcmp(dct3trace_magic_record_end, line, strlen(dct3trace_magic_record_end)) == 0 )
234 /* Return on end of record </l1> */
242 /* If not got any data return error */
243 *err = WTAP_ERR_BAD_RECORD;
244 *err_info = g_strdup_printf("dct3trace: record without data");
248 else if( memcmp(dct3trace_magic_record_start, line, strlen(dct3trace_magic_record_start)) == 0 )
250 /* Parse L1 header <l1 ...>*/
251 int channel, tmp, ret = 0;
254 pseudo_header->gsm_um.uplink = !strstr(line, "direction=\"down\"");
255 ret |= xml_get_int(&channel, line, "logicalchannel");
257 /* Parse downlink only fields */
258 if( !pseudo_header->gsm_um.uplink )
260 ret |= xml_get_int(&tmp, line, "physicalchannel");
261 pseudo_header->gsm_um.arfcn = tmp;
262 ret |= xml_get_int(&tmp, line, "sequence");
263 pseudo_header->gsm_um.tdma_frame = tmp;
264 ret |= xml_get_int(&tmp, line, "bsic");
265 pseudo_header->gsm_um.bsic = tmp;
266 ret |= xml_get_int(&tmp, line, "error");
267 pseudo_header->gsm_um.error = tmp;
268 ret |= xml_get_int(&tmp, line, "timeshift");
269 pseudo_header->gsm_um.timeshift = tmp;
274 *err = WTAP_ERR_BAD_RECORD;
275 *err_info = g_strdup_printf("dct3trace: record missing mandatory attributes");
281 case 128: pseudo_header->gsm_um.channel = GSM_UM_CHANNEL_SDCCH; break;
282 case 112: pseudo_header->gsm_um.channel = GSM_UM_CHANNEL_SACCH; break;
283 case 176: pseudo_header->gsm_um.channel = GSM_UM_CHANNEL_FACCH; break;
284 case 96: pseudo_header->gsm_um.channel = GSM_UM_CHANNEL_CCCH; break;
285 case 80: pseudo_header->gsm_um.channel = GSM_UM_CHANNEL_BCCH; break;
286 default: pseudo_header->gsm_um.channel = GSM_UM_CHANNEL_UNKNOWN; break;
289 /* Read data (if have it) into buf */
290 ptr = strstr(line, "data=\"");
293 have_data = TRUE; /* If has data... */
294 *len = hex2bin(buf, ptr+6);
297 else if( !have_data && memcmp(dct3trace_magic_l2_start, line, strlen(dct3trace_magic_l2_start)) == 0 )
299 /* For uplink packets we might not get the raw L1, so have to recreate it from the L2 */
300 /* Parse L2 header if didn't get data from L1 <l2 ...> */
302 char *ptr = strstr(line, "data=\"");
311 if( pseudo_header->gsm_um.channel == GSM_UM_CHANNEL_SACCH || pseudo_header->gsm_um.channel == GSM_UM_CHANNEL_FACCH || pseudo_header->gsm_um.channel == GSM_UM_CHANNEL_SDCCH )
313 /* Add LAPDm B header */
319 /* Add LAPDm Bbis header */
324 data_len = hex2bin(buf, ptr+6);
327 /* Add LAPDm length byte */
328 *(buf - 1) = data_len << 2 | 0x1;
332 *err = file_error(fh, err_info);
335 *err = WTAP_ERR_SHORT_READ;
341 /* Find the next packet and parse it; called from wtap_read(). */
342 static gboolean dct3trace_read(wtap *wth, int *err, gchar **err_info,
345 guint64 offset = file_tell(wth->fh);
347 unsigned char buf[MAX_PACKET_LEN];
349 if( !dct3trace_get_packet(wth->fh, &wth->pseudo_header, buf, &buf_len, err, err_info) )
354 /* We've got a full packet! */
355 wth->phdr.ts.secs = 0;
356 wth->phdr.ts.nsecs = 0;
357 wth->phdr.caplen = buf_len;
358 wth->phdr.len = buf_len;
360 /* Make sure we have enough room for the packet */
361 buffer_assure_space(wth->frame_buffer, buf_len);
362 memcpy( buffer_start_ptr(wth->frame_buffer), buf, buf_len );
364 wth->data_offset = *data_offset = offset;
370 /* Used to read packets in random-access fashion */
371 static gboolean dct3trace_seek_read (wtap *wth, gint64 seek_off,
372 union wtap_pseudo_header *pseudo_header, guint8 *pd, int len,
373 int *err, gchar **err_info)
376 unsigned char buf[MAX_PACKET_LEN];
378 if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
383 if( !dct3trace_get_packet(wth->random_fh, pseudo_header, buf, &buf_len, err, err_info) )
388 if( len != buf_len && len != -1 )
390 *err = WTAP_ERR_BAD_RECORD;
391 *err_info = g_strdup_printf("dct3trace: requested length %d doesn't match record length %d",
396 if( buf_len > MAX_PACKET_LEN)
398 *err = WTAP_ERR_BAD_RECORD;
399 *err_info = g_strdup_printf("dct3trace: record length %d too long", buf_len);
403 memcpy( pd, buf, buf_len );