The "file types" we have are actually combinations of types and
[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  * $Id$
10  *
11  * Wiretap Library
12  * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
13  *
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.
18  *
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.
23  *
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
27  */
28
29 #include "config.h"
30 #include "wtap-int.h"
31 #include "buffer.h"
32 #include "dct3trace.h"
33 #include "file_wrappers.h"
34
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <ctype.h>
39
40
41 /*
42    Example downlink data:
43
44 <?xml version="1.0"?>
45 <dump>
46 <l1 direction="down" logicalchannel="96" physicalchannel="19" sequence="268116" error="0" timeshift="2992" bsic="22" data="31063F100DD0297A53E1020103C802398E0B2B2B2B2B2B" >
47 <l2 data="063F100DD0297A53E1020103" rest="C802398E0B2B2B2B2B2B" >
48 </l2>
49 </l1>
50 </dump>
51
52    Example uplink data (no raw L1):
53
54 <?xml version="1.0"?>
55 <dump>
56 <l1 direction="up" logicalchannel="112" >
57 <l2 type="U" subtype="Unknown" p="0" data="061500400000000000000000000000000000" >
58 </l2>
59 </l1>
60 </dump>
61
62  */
63
64
65 /* Magic text to check */
66 static const char dct3trace_magic_line1[] = "<?xml version=\"1.0\"?>";
67 static const char dct3trace_magic_line2[] = "<dump>";
68 static const char dct3trace_magic_record_start[]  = "<l1 ";
69 static const char dct3trace_magic_record_end[]  = "</l1>";
70 static const char dct3trace_magic_l2_start[]  = "<l2 ";
71 #if 0 /* Not used ?? */
72 static const char dct3trace_magic_l2_end[]  = "</l2>";
73 #endif
74 static const char dct3trace_magic_end[]  = "</dump>";
75
76 #define MAX_PACKET_LEN 23
77
78 static gboolean dct3trace_read(wtap *wth, int *err, gchar **err_info,
79         gint64 *data_offset);
80 static gboolean dct3trace_seek_read(wtap *wth, gint64 seek_off,
81         struct wtap_pkthdr *phdr, Buffer *buf, int len,
82         int *err, gchar **err_info);
83
84 /*
85  * Following 3 functions taken from gsmdecode-0.7bis, with permission - http://wiki.thc.org/gsm
86  */
87
88 static int
89 hc2b(unsigned char hex)
90 {
91         hex = tolower(hex);
92         if ((hex >= '0') && (hex <= '9'))
93                 return hex - '0';
94         if ((hex >= 'a') && (hex <= 'f'))
95                 return hex - 'a' + 10;
96         return -1;
97 }
98
99 static int
100 hex2bin(guint8 *out, guint8 *out_end, char *in)
101 {
102         guint8 *out_start = out;
103         int is_low = 0;
104         int c;
105
106         while (*in != '\0')
107         {
108                 c = hc2b(*(unsigned char *)in);
109                 if (c < 0)
110                 {
111                         in++;
112                         continue;
113                 }
114                 if (out == out_end)
115                 {
116                         /* Too much data */
117                         return -1;
118                 }
119                 if (is_low == 0)
120                 {
121                         *out = c << 4;
122                         is_low = 1;
123                 } else {
124                         *out |= (c & 0x0f);
125                         is_low = 0;
126                         out++;
127                 }
128                 in++;
129         }
130
131         return (int)(out - out_start);
132 }
133
134 static int
135 xml_get_int(int *val, const char *str, const char *pattern)
136 {
137         const char *ptr;
138         char *start, *end;
139         char buf[32];
140
141         ptr = strstr(str, pattern);
142         if (ptr == NULL)
143                 return -1;
144         start = strchr(ptr, '"');
145         if (start == NULL)
146                 return -2;
147         start++;
148         end = strchr(start, '"');
149         if (end == NULL)
150                 return -3;
151         if (end - start > 31)
152                 return -4;
153
154         memcpy(buf, start, end - start);
155         buf[end - start] = '\0';
156         *val = atoi(buf);
157         return 0;
158 }
159
160
161 int dct3trace_open(wtap *wth, int *err, gchar **err_info)
162 {
163         char line1[64], line2[64];
164
165         /* Look for Gammu DCT3 trace header */
166         if (file_gets(line1, sizeof(line1), wth->fh) == NULL ||
167                 file_gets(line2, sizeof(line2), wth->fh) == NULL)
168         {
169                 *err = file_error(wth->fh, err_info);
170                 if (*err != 0 && *err != WTAP_ERR_SHORT_READ)
171                         return -1;
172                 return 0;
173         }
174
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)
178         {
179                 return 0;
180         }
181
182         wth->file_encap = WTAP_ENCAP_GSM_UM;
183         wth->file_type_subtype = WTAP_FILE_TYPE_SUBTYPE_DCT3TRACE;
184         wth->snapshot_length = 0; /* not known */
185         wth->subtype_read = dct3trace_read;
186         wth->subtype_seek_read = dct3trace_seek_read;
187         wth->tsprecision = WTAP_FILE_TSPREC_SEC;
188
189         return 1;
190 }
191
192
193 static gboolean dct3trace_get_packet(FILE_T fh, struct wtap_pkthdr *phdr,
194         Buffer *buf, int *err, gchar **err_info)
195 {
196         char line[1024];
197         guint8 databuf[MAX_PACKET_LEN], *bufp;
198         gboolean have_data = FALSE;
199         int len = 0;
200
201         bufp = &databuf[0];
202         while (file_gets(line, sizeof(line), fh) != NULL)
203         {
204                 if( memcmp(dct3trace_magic_end, line, strlen(dct3trace_magic_end)) == 0 )
205                 {
206                         /* Return on end of file </dump> */
207                         *err = 0;
208                         return FALSE;
209                 }
210                 else if( memcmp(dct3trace_magic_record_end, line, strlen(dct3trace_magic_record_end)) == 0 )
211                 {
212                         /* Return on end of record </l1> */
213                         if( have_data )
214                         {
215                                 /* We've got a full packet! */
216                                 phdr->presence_flags = 0; /* no time stamp, no separate "on the wire" length */
217                                 phdr->ts.secs = 0;
218                                 phdr->ts.nsecs = 0;
219                                 phdr->caplen = len;
220                                 phdr->len = len;
221
222                                 *err = 0;
223
224                                 /* Make sure we have enough room for the packet */
225                                 buffer_assure_space(buf, phdr->caplen);
226                                 memcpy( buffer_start_ptr(buf), databuf, phdr->caplen );
227
228                                 return TRUE;
229                         }
230                         else
231                         {
232                                 /* If not got any data return error */
233                                 *err = WTAP_ERR_BAD_FILE;
234                                 *err_info = g_strdup_printf("dct3trace: record without data");
235                                 return FALSE;
236                         }
237                 }
238                 else if( memcmp(dct3trace_magic_record_start, line, strlen(dct3trace_magic_record_start)) == 0 )
239                 {
240                         /* Parse L1 header <l1 ...>*/
241                         int channel, tmp;
242                         char *ptr;
243
244                         phdr->pseudo_header.gsm_um.uplink = !strstr(line, "direction=\"down\"");
245                         if (xml_get_int(&channel, line, "logicalchannel") != 0)
246                                 goto baddata;
247
248                         /* Parse downlink only fields */
249                         if( !phdr->pseudo_header.gsm_um.uplink )
250                         {
251                                 if (xml_get_int(&tmp, line, "physicalchannel") != 0)
252                                         goto baddata;
253                                 phdr->pseudo_header.gsm_um.arfcn = tmp;
254                                 if (xml_get_int(&tmp, line, "sequence") != 0)
255                                         goto baddata;
256                                 phdr->pseudo_header.gsm_um.tdma_frame = tmp;
257                                 if (xml_get_int(&tmp, line, "bsic") != 0)
258                                         goto baddata;
259                                 phdr->pseudo_header.gsm_um.bsic = tmp;
260                                 if (xml_get_int(&tmp, line, "error") != 0)
261                                         goto baddata;
262                                 phdr->pseudo_header.gsm_um.error = tmp;
263                                 if (xml_get_int(&tmp, line, "timeshift") != 0)
264                                         goto baddata;
265                                 phdr->pseudo_header.gsm_um.timeshift = tmp;
266                         }
267
268                         switch( channel )
269                         {
270                                 case 128: phdr->pseudo_header.gsm_um.channel = GSM_UM_CHANNEL_SDCCH; break;
271                                 case 112: phdr->pseudo_header.gsm_um.channel = GSM_UM_CHANNEL_SACCH; break;
272                                 case 176: phdr->pseudo_header.gsm_um.channel = GSM_UM_CHANNEL_FACCH; break;
273                                 case 96: phdr->pseudo_header.gsm_um.channel = GSM_UM_CHANNEL_CCCH; break;
274                                 case 80: phdr->pseudo_header.gsm_um.channel = GSM_UM_CHANNEL_BCCH; break;
275                                 default: phdr->pseudo_header.gsm_um.channel = GSM_UM_CHANNEL_UNKNOWN; break;
276                         }
277
278                         /* Read data (if have it) into databuf */
279                         ptr = strstr(line, "data=\"");
280                         if( ptr )
281                         {
282                                 have_data = TRUE; /* If has data... */
283                                 len = hex2bin(bufp, &databuf[MAX_PACKET_LEN], ptr+6);
284                                 if (len == -1)
285                                 {
286                                         *err = WTAP_ERR_BAD_FILE;
287                                         *err_info = g_strdup_printf("dct3trace: record length %d too long", phdr->caplen);
288                                         return FALSE;
289                                 }
290                         }
291                 }
292                 else if( !have_data && memcmp(dct3trace_magic_l2_start, line, strlen(dct3trace_magic_l2_start)) == 0 )
293                 {
294                         /* For uplink packets we might not get the raw L1, so have to recreate it from the L2 */
295                         /* Parse L2 header if didn't get data from L1 <l2 ...> */
296                         int data_len;
297                         char *ptr = strstr(line, "data=\"");
298
299                         if( !ptr )
300                         {
301                                 continue;
302                         }
303
304                         have_data = TRUE;
305
306                         /*
307                          * We know we have no data already, so we know
308                          * we have enough room for the header.
309                          */
310                         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 )
311                         {
312                                 /* Add LAPDm B header */
313                                 memset(bufp, 0x1, 2);
314                                 len = 3;
315                         }
316                         else
317                         {
318                                 /* Add LAPDm Bbis header */
319                                 len = 1;
320                         }
321                         bufp += len;
322
323                         data_len = hex2bin(bufp, &databuf[MAX_PACKET_LEN], ptr+6);
324                         if (data_len == -1)
325                         {
326                                 *err = WTAP_ERR_BAD_FILE;
327                                 *err_info = g_strdup_printf("dct3trace: record length %d too long", phdr->caplen);
328                                 return FALSE;
329                         }
330                         len += data_len;
331
332                         /* Add LAPDm length byte */
333                         *(bufp - 1) = data_len << 2 | 0x1;
334                 }
335         }
336
337         *err = file_error(fh, err_info);
338         if (*err == 0)
339         {
340                 *err = WTAP_ERR_SHORT_READ;
341         }
342         return FALSE;
343
344 baddata:
345         *err = WTAP_ERR_BAD_FILE;
346         *err_info = g_strdup_printf("dct3trace: record missing mandatory attributes");
347         return FALSE;
348 }
349
350
351 /* Find the next packet and parse it; called from wtap_read(). */
352 static gboolean dct3trace_read(wtap *wth, int *err, gchar **err_info,
353     gint64 *data_offset)
354 {
355         *data_offset = file_tell(wth->fh);
356
357         return dct3trace_get_packet(wth->fh, &wth->phdr, wth->frame_buffer,
358             err, err_info);
359 }
360
361
362 /* Used to read packets in random-access fashion */
363 static gboolean dct3trace_seek_read(wtap *wth, gint64 seek_off,
364         struct wtap_pkthdr *phdr, Buffer *buf, int len _U_,
365         int *err, gchar **err_info)
366 {
367         if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
368         {
369                 return FALSE;
370         }
371
372         return dct3trace_get_packet(wth->random_fh, phdr, buf, err, err_info);
373 }