Fix cut-and-pasteo that happened not to cause a problem on *P64
[obnox/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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
27  */
28
29 #ifdef HAVE_CONFIG_H
30 #include "config.h"
31 #endif
32 #include "wtap-int.h"
33 #include "buffer.h"
34 #include "dct3trace.h"
35 #include "file_wrappers.h"
36
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <ctype.h>
41
42
43 /*
44    Example downlink data:
45
46 <?xml version="1.0"?>
47 <dump>
48 <l1 direction="down" logicalchannel="96" physicalchannel="19" sequence="268116" error="0" timeshift="2992" bsic="22" data="31063F100DD0297A53E1020103C802398E0B2B2B2B2B2B" >
49 <l2 data="063F100DD0297A53E1020103" rest="C802398E0B2B2B2B2B2B" >
50 </l2>
51 </l1>
52 </dump>
53
54    Example uplink data (no raw L1):
55
56 <?xml version="1.0"?>
57 <dump>
58 <l1 direction="up" logicalchannel="112" >
59 <l2 type="U" subtype="Unknown" p="0" data="061500400000000000000000000000000000" >
60 </l2>
61 </l1>
62 </dump>
63
64  */
65
66
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>";
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         union wtap_pseudo_header *pseudo_header, guint8 *pd, 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(unsigned char *out, unsigned char *in)
101 {
102         unsigned char *out_start = out;
103         unsigned char *end = in + strlen((char *)in);
104         int is_low = 0;
105         int c;
106
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;
110
111         while (in < end)
112         {
113                 c = hc2b(in[0]);
114                 if (c < 0)
115                 {
116                         in++;
117                         continue;
118                 }
119                 if (is_low == 0)
120                 {
121                         out[0] = c << 4;
122                         is_low = 1;
123                 } else {
124                         out[0] |= (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 unsigned char *str, const unsigned char *pattern)
136 {
137         const char *ptr;
138         char *start, *end;
139         char buf[32];
140
141         ptr = strstr((const char *)str, (const char *)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 /* Look through the first part of a file to see if this is
162  * a DCT3 trace file.
163  *
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.
167  */
168 static gboolean dct3trace_check_file_type(wtap *wth, int *err, gchar **err_info)
169 {
170         char line1[64], line2[64];
171
172         if (file_gets(line1, sizeof(line1), wth->fh) != NULL &&
173                 file_gets(line2, sizeof(line2), wth->fh) != NULL)
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 TRUE;
180                 }
181         }
182         /* EOF or error. */
183         else
184         {
185                 if (file_eof(wth->fh))
186                         *err = 0;
187                 else
188                         *err = file_error(wth->fh, err_info);
189         }
190
191         return FALSE;
192 }
193
194
195 int dct3trace_open(wtap *wth, int *err, gchar **err_info)
196 {
197         /* Look for Gammu DCT3 trace header */
198         if (!dct3trace_check_file_type(wth, err, err_info))
199         {
200                 if (*err == 0)
201                         return 0;
202                 else
203                         return -1;
204         }
205
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;
213
214         return 1;
215 }
216
217
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)
220 {
221         unsigned char line[1024];
222         gboolean have_data = FALSE;
223
224         while (file_gets(line, sizeof(line), fh) != NULL)
225         {
226                 if( memcmp(dct3trace_magic_end, line, strlen(dct3trace_magic_end)) == 0 )
227                 {
228                         /* Return on end of file </dump> */
229                         *err = 0;
230                         return FALSE;
231                 }
232                 else if( memcmp(dct3trace_magic_record_end, line, strlen(dct3trace_magic_record_end)) == 0 )
233                 {
234                         /* Return on end of record </l1> */
235                         if( have_data )
236                         {
237                                 *err = 0;
238                                 return TRUE;
239                         }
240                         else
241                         {
242                                 /* If not got any data return error */
243                                 *err = WTAP_ERR_BAD_RECORD;
244                                 *err_info = g_strdup_printf("dct3trace: record without data");
245                                 return FALSE;
246                         }
247                 }
248                 else if( memcmp(dct3trace_magic_record_start, line, strlen(dct3trace_magic_record_start)) == 0 )
249                 {
250                         /* Parse L1 header <l1 ...>*/
251                         int channel, tmp, ret = 0;
252                         char *ptr;
253
254                         pseudo_header->gsm_um.uplink = !strstr(line, "direction=\"down\"");
255                         ret |= xml_get_int(&channel, line, "logicalchannel");
256
257                         /* Parse downlink only fields */
258                         if( !pseudo_header->gsm_um.uplink )
259                         {
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;
270                         }
271
272                         if( ret != 0 )
273                         {
274                                 *err = WTAP_ERR_BAD_RECORD;
275                                 *err_info = g_strdup_printf("dct3trace: record missing mandatory attributes");
276                                 return FALSE;
277                         }
278
279                         switch( channel )
280                         {
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;
287                         }
288
289                         /* Read data (if have it) into buf */
290                         ptr = strstr(line, "data=\"");
291                         if( ptr )
292                         {
293                                 have_data = TRUE; /* If has data... */
294                                 *len = hex2bin(buf, ptr+6);
295                         }
296                 }
297                 else if( !have_data && memcmp(dct3trace_magic_l2_start, line, strlen(dct3trace_magic_l2_start)) == 0 )
298                 {
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 ...> */
301                         int data_len = 0;
302                         char *ptr = strstr(line, "data=\"");
303
304                         if( !ptr )
305                         {
306                                 continue;
307                         }
308
309                         have_data = TRUE;
310
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 )
312                         {
313                                 /* Add LAPDm B header */
314                                 memset(buf, 0x1, 2);
315                                 *len = 3;
316                         }
317                         else
318                         {
319                                 /* Add LAPDm Bbis header */
320                                 *len = 1;
321                         }
322                         buf += *len;
323
324                         data_len = hex2bin(buf, ptr+6);
325                         *len += data_len;
326
327                         /* Add LAPDm length byte */
328                         *(buf - 1) = data_len << 2 | 0x1;
329                 }
330         }
331
332         *err = file_error(fh, err_info);
333         if (*err == 0)
334         {
335                 *err = WTAP_ERR_SHORT_READ;
336         }
337         return FALSE;
338 }
339
340
341 /* Find the next packet and parse it; called from wtap_read(). */
342 static gboolean dct3trace_read(wtap *wth, int *err, gchar **err_info,
343     gint64 *data_offset)
344 {
345         guint64 offset = file_tell(wth->fh);
346         int buf_len;
347         unsigned char buf[MAX_PACKET_LEN];
348
349         if( !dct3trace_get_packet(wth->fh, &wth->pseudo_header, buf, &buf_len, err, err_info) )
350         {
351                 return FALSE;
352         }
353
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;
359
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 );
363
364         wth->data_offset = *data_offset = offset;
365
366         return TRUE;
367 }
368
369
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)
374 {
375         int buf_len;
376         unsigned char buf[MAX_PACKET_LEN];
377
378         if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
379         {
380                 return FALSE;
381         }
382
383         if( !dct3trace_get_packet(wth->random_fh, pseudo_header, buf, &buf_len, err, err_info) )
384         {
385                 return FALSE;
386         }
387
388         if( len != buf_len && len != -1 )
389         {
390                 *err = WTAP_ERR_BAD_RECORD;
391                 *err_info = g_strdup_printf("dct3trace: requested length %d doesn't match record length %d",
392                     len, buf_len);
393                 return FALSE;
394         }
395
396         if( buf_len > MAX_PACKET_LEN)
397         {
398                 *err = WTAP_ERR_BAD_RECORD;
399                 *err_info = g_strdup_printf("dct3trace: record length %d too long", buf_len);
400                 return FALSE;
401         }
402
403         memcpy( pd, buf, buf_len );
404         return TRUE;
405 }