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