Have "All Capture Files" match only capture files.
[metze/wireshark/wip.git] / wiretap / dct3trace.c
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  * This program is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU General Public License
14  * as published by the Free Software Foundation; either version 2
15  * of the License, or (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25  */
26
27 #include "config.h"
28 #include "wtap-int.h"
29 #include "dct3trace.h"
30 #include "file_wrappers.h"
31
32 #include <stdlib.h>
33 #include <string.h>
34 #include <errno.h>
35
36 #include <wsutil/strtoi.h>
37
38 /*
39    Example downlink data:
40
41 <?xml version="1.0"?>
42 <dump>
43 <l1 direction="down" logicalchannel="96" physicalchannel="19" sequence="268116" error="0" timeshift="2992" bsic="22" data="31063F100DD0297A53E1020103C802398E0B2B2B2B2B2B" >
44 <l2 data="063F100DD0297A53E1020103" rest="C802398E0B2B2B2B2B2B" >
45 </l2>
46 </l1>
47 </dump>
48
49    Example uplink data (no raw L1):
50
51 <?xml version="1.0"?>
52 <dump>
53 <l1 direction="up" logicalchannel="112" >
54 <l2 type="U" subtype="Unknown" p="0" data="061500400000000000000000000000000000" >
55 </l2>
56 </l1>
57 </dump>
58
59  */
60
61
62 /* Magic text to check */
63 static const char dct3trace_magic_line1[] = "<?xml version=\"1.0\"?>";
64 static const char dct3trace_magic_line2[] = "<dump>";
65 static const char dct3trace_magic_record_start[]  = "<l1 ";
66 static const char dct3trace_magic_record_end[]  = "</l1>";
67 static const char dct3trace_magic_l2_start[]  = "<l2 ";
68 #if 0 /* Not used ?? */
69 static const char dct3trace_magic_l2_end[]  = "</l2>";
70 #endif
71 static const char dct3trace_magic_end[]  = "</dump>";
72
73 #define MAX_PACKET_LEN 23
74
75 static gboolean dct3trace_read(wtap *wth, int *err, gchar **err_info,
76         gint64 *data_offset);
77 static gboolean dct3trace_seek_read(wtap *wth, gint64 seek_off,
78         struct wtap_pkthdr *phdr, Buffer *buf, int *err, gchar **err_info);
79
80 /*
81  * Following 3 functions taken from gsmdecode-0.7bis, with permission - http://wiki.thc.org/gsm
82  */
83
84 static int
85 hc2b(unsigned char hex)
86 {
87         hex = g_ascii_tolower(hex);
88         if ((hex >= '0') && (hex <= '9'))
89                 return hex - '0';
90         if ((hex >= 'a') && (hex <= 'f'))
91                 return hex - 'a' + 10;
92         return -1;
93 }
94
95 static int
96 hex2bin(guint8 *out, guint8 *out_end, char *in)
97 {
98         guint8 *out_start = out;
99         int is_low = 0;
100         int c;
101
102         while (*in != '\0')
103         {
104                 c = hc2b(*(unsigned char *)in);
105                 if (c < 0)
106                 {
107                         in++;
108                         continue;
109                 }
110                 if (out == out_end)
111                 {
112                         /* Too much data */
113                         return -1;
114                 }
115                 if (is_low == 0)
116                 {
117                         *out = c << 4;
118                         is_low = 1;
119                 } else {
120                         *out |= (c & 0x0f);
121                         is_low = 0;
122                         out++;
123                 }
124                 in++;
125         }
126
127         return (int)(out - out_start);
128 }
129
130 static gboolean
131 xml_get_int(int *val, const char *str, const char *pattern, int *err, gchar **err_info)
132 {
133         const char *ptr, *endptr;
134         char *start, *end;
135         char buf[32];
136
137         ptr = strstr(str, pattern);
138         if (ptr == NULL) {
139                 *err = WTAP_ERR_BAD_FILE;
140                 *err_info = g_strdup_printf("dct3trace: %s not found", pattern);
141                 return FALSE;
142         }
143         /*
144          * XXX - should we just skip past the pattern and check for ="?
145          */
146         start = strchr(ptr, '"');
147         if (start == NULL) {
148                 *err = WTAP_ERR_BAD_FILE;
149                 *err_info = g_strdup_printf("dct3trace: opening quote for %s not found", pattern);
150                 return FALSE;
151         }
152         start++;
153         /*
154          * XXX - should we just use ws_strtoi32() and check whether
155          * the character following the number is a "?
156          */
157         end = strchr(start, '"');
158         if (end == NULL) {
159                 *err = WTAP_ERR_BAD_FILE;
160                 *err_info = g_strdup_printf("dct3trace: closing quote for %s not found", pattern);
161                 return FALSE;
162         }
163         if (end - start > 31) {
164                 *err = WTAP_ERR_BAD_FILE;
165                 *err_info = g_strdup_printf("dct3trace: %s value is too long", pattern);
166                 return FALSE;
167         }
168
169         memcpy(buf, start, end - start);
170         buf[end - start] = '\0';
171         /*
172          * XXX - should we allow negative numbers in all cases?  Or are
173          * there cases where the number is unsigned?
174          */
175         if (!ws_strtoi32(buf, &endptr, val)) {
176                 *err = WTAP_ERR_BAD_FILE;
177                 if (errno == ERANGE) {
178                         if (*val < 0)
179                                 *err_info = g_strdup_printf("dct3trace: %s value is too small, minimum is %d", pattern, *val);
180                         else
181                                 *err_info = g_strdup_printf("dct3trace: %s value is too large, maximum is %d", pattern, *val);
182                 } else
183                         *err_info = g_strdup_printf("dct3trace: %s value \"%s\" not a number", pattern, buf);
184                 return FALSE;
185         }
186         if (*endptr != '\0') {
187                 *err = WTAP_ERR_BAD_FILE;
188                 *err_info = g_strdup_printf("dct3trace: %s value \"%s\" not a number", pattern, buf);
189                 return FALSE;
190         }
191         return TRUE;
192 }
193
194
195 wtap_open_return_val dct3trace_open(wtap *wth, int *err, gchar **err_info)
196 {
197         char line1[64], line2[64];
198
199         /* Look for Gammu DCT3 trace header */
200         if (file_gets(line1, sizeof(line1), wth->fh) == NULL ||
201                 file_gets(line2, sizeof(line2), wth->fh) == NULL)
202         {
203                 *err = file_error(wth->fh, err_info);
204                 if (*err != 0 && *err != WTAP_ERR_SHORT_READ)
205                         return WTAP_OPEN_ERROR;
206                 return WTAP_OPEN_NOT_MINE;
207         }
208
209         /* Don't compare line endings */
210         if( strncmp(dct3trace_magic_line1, line1, strlen(dct3trace_magic_line1)) != 0 ||
211                 strncmp(dct3trace_magic_line2, line2, strlen(dct3trace_magic_line2)) != 0)
212         {
213                 return WTAP_OPEN_NOT_MINE;
214         }
215
216         wth->file_encap = WTAP_ENCAP_GSM_UM;
217         wth->file_type_subtype = WTAP_FILE_TYPE_SUBTYPE_DCT3TRACE;
218         wth->snapshot_length = 0; /* not known */
219         wth->subtype_read = dct3trace_read;
220         wth->subtype_seek_read = dct3trace_seek_read;
221         wth->file_tsprec = WTAP_TSPREC_SEC;
222
223         return WTAP_OPEN_MINE;
224 }
225
226
227 static gboolean dct3trace_get_packet(FILE_T fh, struct wtap_pkthdr *phdr,
228         Buffer *buf, int *err, gchar **err_info)
229 {
230         char line[1024];
231         guint8 databuf[MAX_PACKET_LEN], *bufp;
232         gboolean have_data = FALSE;
233         int len = 0;
234
235         bufp = &databuf[0];
236         while (file_gets(line, sizeof(line), fh) != NULL)
237         {
238                 if( memcmp(dct3trace_magic_end, line, strlen(dct3trace_magic_end)) == 0 )
239                 {
240                         /* Return on end of file </dump> */
241                         *err = 0;
242                         return FALSE;
243                 }
244                 else if( memcmp(dct3trace_magic_record_end, line, strlen(dct3trace_magic_record_end)) == 0 )
245                 {
246                         /* Return on end of record </l1> */
247                         if( have_data )
248                         {
249                                 /* We've got a full packet! */
250                                 phdr->rec_type = REC_TYPE_PACKET;
251                                 phdr->presence_flags = 0; /* no time stamp, no separate "on the wire" length */
252                                 phdr->ts.secs = 0;
253                                 phdr->ts.nsecs = 0;
254                                 phdr->caplen = len;
255                                 phdr->len = len;
256
257                                 *err = 0;
258
259                                 /* Make sure we have enough room for the packet */
260                                 ws_buffer_assure_space(buf, phdr->caplen);
261                                 memcpy( ws_buffer_start_ptr(buf), databuf, phdr->caplen );
262
263                                 return TRUE;
264                         }
265                         else
266                         {
267                                 /* If not got any data return error */
268                                 *err = WTAP_ERR_BAD_FILE;
269                                 *err_info = g_strdup("dct3trace: record without data");
270                                 return FALSE;
271                         }
272                 }
273                 else if( memcmp(dct3trace_magic_record_start, line, strlen(dct3trace_magic_record_start)) == 0 )
274                 {
275                         /* Parse L1 header <l1 ...>*/
276                         int channel, tmp;
277                         char *ptr;
278
279                         phdr->pseudo_header.gsm_um.uplink = !strstr(line, "direction=\"down\"");
280                         if (!xml_get_int(&channel, line, "logicalchannel", err, err_info))
281                                 return FALSE;
282
283                         /* Parse downlink only fields */
284                         if( !phdr->pseudo_header.gsm_um.uplink )
285                         {
286                                 if (!xml_get_int(&tmp, line, "physicalchannel", err, err_info))
287                                         return FALSE;
288                                 phdr->pseudo_header.gsm_um.arfcn = tmp;
289                                 if (!xml_get_int(&tmp, line, "sequence", err, err_info))
290                                         return FALSE;
291                                 phdr->pseudo_header.gsm_um.tdma_frame = tmp;
292                                 if (!xml_get_int(&tmp, line, "bsic", err, err_info))
293                                         return FALSE;
294                                 phdr->pseudo_header.gsm_um.bsic = tmp;
295                                 if (!xml_get_int(&tmp, line, "error", err, err_info))
296                                         return FALSE;
297                                 phdr->pseudo_header.gsm_um.error = tmp;
298                                 if (!xml_get_int(&tmp, line, "timeshift", err, err_info))
299                                         return FALSE;
300                                 phdr->pseudo_header.gsm_um.timeshift = tmp;
301                         }
302
303                         switch( channel )
304                         {
305                                 case 128: phdr->pseudo_header.gsm_um.channel = GSM_UM_CHANNEL_SDCCH; break;
306                                 case 112: phdr->pseudo_header.gsm_um.channel = GSM_UM_CHANNEL_SACCH; break;
307                                 case 176: phdr->pseudo_header.gsm_um.channel = GSM_UM_CHANNEL_FACCH; break;
308                                 case 96: phdr->pseudo_header.gsm_um.channel = GSM_UM_CHANNEL_CCCH; break;
309                                 case 80: phdr->pseudo_header.gsm_um.channel = GSM_UM_CHANNEL_BCCH; break;
310                                 default: phdr->pseudo_header.gsm_um.channel = GSM_UM_CHANNEL_UNKNOWN; break;
311                         }
312
313                         /* Read data (if have it) into databuf */
314                         ptr = strstr(line, "data=\"");
315                         if( ptr )
316                         {
317                                 have_data = TRUE; /* If has data... */
318                                 len = hex2bin(bufp, &databuf[MAX_PACKET_LEN], ptr+6);
319                                 if (len == -1)
320                                 {
321                                         *err = WTAP_ERR_BAD_FILE;
322                                         *err_info = g_strdup_printf("dct3trace: record length %d too long", phdr->caplen);
323                                         return FALSE;
324                                 }
325                         }
326                 }
327                 else if( !have_data && memcmp(dct3trace_magic_l2_start, line, strlen(dct3trace_magic_l2_start)) == 0 )
328                 {
329                         /* For uplink packets we might not get the raw L1, so have to recreate it from the L2 */
330                         /* Parse L2 header if didn't get data from L1 <l2 ...> */
331                         int data_len;
332                         char *ptr = strstr(line, "data=\"");
333
334                         if( !ptr )
335                         {
336                                 continue;
337                         }
338
339                         have_data = TRUE;
340
341                         /*
342                          * We know we have no data already, so we know
343                          * we have enough room for the header.
344                          */
345                         if( phdr->pseudo_header.gsm_um.channel == GSM_UM_CHANNEL_SACCH || phdr->pseudo_header.gsm_um.channel == GSM_UM_CHANNEL_FACCH || phdr->pseudo_header.gsm_um.channel == GSM_UM_CHANNEL_SDCCH )
346                         {
347                                 /* Add LAPDm B header */
348                                 memset(bufp, 0x1, 2);
349                                 len = 3;
350                         }
351                         else
352                         {
353                                 /* Add LAPDm Bbis header */
354                                 len = 1;
355                         }
356                         bufp += len;
357
358                         data_len = hex2bin(bufp, &databuf[MAX_PACKET_LEN], ptr+6);
359                         if (data_len == -1)
360                         {
361                                 *err = WTAP_ERR_BAD_FILE;
362                                 *err_info = g_strdup_printf("dct3trace: record length %d too long", phdr->caplen);
363                                 return FALSE;
364                         }
365                         len += data_len;
366
367                         /* Add LAPDm length byte */
368                         *(bufp - 1) = data_len << 2 | 0x1;
369                 }
370         }
371
372         *err = file_error(fh, err_info);
373         if (*err == 0)
374         {
375                 *err = WTAP_ERR_SHORT_READ;
376         }
377         return FALSE;
378 }
379
380
381 /* Find the next packet and parse it; called from wtap_read(). */
382 static gboolean dct3trace_read(wtap *wth, int *err, gchar **err_info,
383     gint64 *data_offset)
384 {
385         *data_offset = file_tell(wth->fh);
386
387         return dct3trace_get_packet(wth->fh, &wth->phdr, wth->frame_buffer,
388             err, err_info);
389 }
390
391
392 /* Used to read packets in random-access fashion */
393 static gboolean dct3trace_seek_read(wtap *wth, gint64 seek_off,
394         struct wtap_pkthdr *phdr, Buffer *buf, int *err, gchar **err_info)
395 {
396         if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
397         {
398                 return FALSE;
399         }
400
401         return dct3trace_get_packet(wth->random_fh, phdr, buf, err, err_info);
402 }
403
404 /*
405  * Editor modelines  -  http://www.wireshark.org/tools/modelines.html
406  *
407  * Local variables:
408  * c-basic-offset: 8
409  * tab-width: 8
410  * indent-tabs-mode: t
411  * End:
412  *
413  * vi: set shiftwidth=8 tabstop=8 noexpandtab:
414  * :indentSize=8:tabSize=8:noTabs=false:
415  */