7cc5bfa841eae4932cdd854f2ef2b07fad938b46
[metze/wireshark/wip.git] / wiretap / daintree-sna.c
1 /* daintree_sna.c
2  * Routines for opening .dcf capture files created by Daintree's
3  * Sensor Network Analyzer for 802.15.4 radios
4  * Copyright 2009, Exegin Technologies Limited <fff@exegin.com>
5  *
6  * $Id$
7  *
8  * Wireshark - Network traffic analyzer
9  * By Gerald Combs <gerald@wireshark.org>
10  * Copyright 1998 Gerald Combs
11  *
12  * Started with packetlogger.c as a template, but little packetlogger code 
13  * remains. Borrowed many snippets from dbs-etherwatch.c, the 
14  * daintree_sna_process_hex_data function having the largest chunk.
15  * 
16  * This program is free software; you can redistribute it and/or
17  * modify it under the terms of the GNU General Public License
18  * as published by the Free Software Foundation; either version 2
19  * of the License, or (at your option) any later version.
20  *
21  * This program is distributed in the hope that it will be useful,
22  * but WITHOUT ANY WARRANTY; without even the implied warranty of
23  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24  * GNU General Public License for more details.
25  *
26  * You should have received a copy of the GNU General Public License
27  * along with this program; if not, write to the Free Software
28  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
29  * USA.
30  */
31
32 /* This module reads capture files saved by Daintree's Sensor Network Analyzer. 
33  * Daintree captures are plain text files with a two line header,
34  * followed by packet records, one per line, with whitespace separated fields
35  * consisting of: packet number, time, bytes of capture data, capture data,
36  * unknown, unknown, signal strength?, unknown, etc, and terminated with CRLF.
37  */
38
39 /* Example capture file:
40  
41 #Format=4
42 # SNA v2.2.0.4 SUS:20090709 ACT:819705
43 1 1233783799.326400 10 030809ffffffff07ffff 42 1 -69 25 2 0 1 32767
44 2 1233783799.477440 5 02000bffff 110 1 -44 25 6 0 1 32767
45 3 1233783799.809920 5 020013ffff 107 1 -45 25 43 0 1 3276
46
47 */
48
49 #include "config.h"
50
51 #include <glib.h>
52 #include <stdio.h>
53 #include <stdlib.h>
54 #include <errno.h>
55 #include <string.h>
56 #include <ctype.h>
57
58 #include "wtap.h"
59 #include "wtap-int.h"
60 #include "buffer.h"
61 #include "file_wrappers.h"
62 #include "daintree-sna.h"
63
64 typedef struct daintree_sna_header {
65         guint32 len;
66         guint64 ts;
67 } daintree_sna_header_t;
68
69 #define DAINTREE_SNA_HEADER_SIZE 2
70 #define FCS_LENGTH 2
71
72 static const char daintree_magic_text[] =
73 { '#', 'F', 'o', 'r', 'm', 'a', 't', '=' };
74
75 #define DAINTREE_MAGIC_TEXT_SIZE (sizeof daintree_magic_text)
76 #define DAINTREE_MAX_LINE_SIZE 512
77 #define READDATA_BUF_SIZE (DAINTREE_MAX_LINE_SIZE/2)
78 #define READDATA_MAX_FIELD_SIZE "255"  /* DAINTREE_MAX_LINE_SIZE/2 -1 */
79
80 #define COMMENT_LINE daintree_magic_text[0]
81
82 static gboolean daintree_sna_read(wtap *wth, int *err, gchar **err_info,
83         gint64 *data_offset);
84
85 static gboolean daintree_sna_seek_read(wtap *wth, gint64 seek_off,
86         struct wtap_pkthdr *phdr, Buffer *buf, int len, int *err,
87         gchar **err_info);
88
89 static gboolean daintree_sna_scan_header(struct wtap_pkthdr *phdr,
90         char *readLine, char *readData, int *err, gchar **err_info);
91
92 static gboolean daintree_sna_process_hex_data(struct wtap_pkthdr *phdr,
93         Buffer *buf, char *readData, int *err, gchar **err_info);
94
95 /* Open a file and determine if it's a Daintree file */
96 int daintree_sna_open(wtap *wth, int *err, gchar **err_info)
97 {
98         char readLine[DAINTREE_MAX_LINE_SIZE];
99         guint i; 
100
101         /* get first line of file header */
102         if (file_gets(readLine, DAINTREE_MAX_LINE_SIZE, wth->fh)==NULL) {
103                 *err = file_error(wth->fh, err_info);
104                 if (*err != 0 && *err != WTAP_ERR_SHORT_READ)
105                         return -1;
106                 return 0;
107         }
108
109         /* check magic text */
110         i = 0;
111         while (i < DAINTREE_MAGIC_TEXT_SIZE) {
112                 if (readLine[i] != daintree_magic_text[i]) return 0; /* not daintree format */
113                 i++;
114         } 
115
116         /* read second header line */
117         if (file_gets(readLine, DAINTREE_MAX_LINE_SIZE, wth->fh)==NULL) {
118                 *err = file_error(wth->fh, err_info);
119                 if (*err != 0 && *err != WTAP_ERR_SHORT_READ)
120                         return -1;
121                 return 0;
122         }
123         if (readLine[0] != COMMENT_LINE) return 0; /* daintree files have a two line header */
124
125         /* set up the pointers to the handlers for this file type */
126         wth->subtype_read = daintree_sna_read;
127         wth->subtype_seek_read = daintree_sna_seek_read;
128
129         /* set up for file type */
130         wth->file_type = WTAP_FILE_DAINTREE_SNA;
131         wth->file_encap = WTAP_ENCAP_IEEE802_15_4_NOFCS;
132         wth->tsprecision = WTAP_FILE_TSPREC_USEC;
133         wth->snapshot_length = 0; /* not available in header */
134
135         return 1; /* it's a Daintree file */
136 }
137
138 /* Read the capture file sequentially
139  * Wireshark scans the file with sequential reads during preview and initial display. */
140 static gboolean
141 daintree_sna_read(wtap *wth, int *err, gchar **err_info, gint64 *data_offset)
142 {
143         char readLine[DAINTREE_MAX_LINE_SIZE];
144         char readData[READDATA_BUF_SIZE];
145
146         *data_offset = file_tell(wth->fh);
147
148         /* we've only seen file header lines starting with '#', but
149          * if others appear in the file, they are tossed */
150         do {
151                 if (file_gets(readLine, DAINTREE_MAX_LINE_SIZE, wth->fh) == NULL) {
152                         *err = file_error(wth->fh, err_info);
153                         return FALSE; /* all done */
154                 }
155         } while (readLine[0] == COMMENT_LINE);
156
157         /* parse one line of capture data */
158         if (!daintree_sna_scan_header(&wth->phdr, readLine, readData,
159             err, err_info))
160                 return FALSE;
161
162         /* process packet data */
163         return daintree_sna_process_hex_data(&wth->phdr, wth->frame_buffer,
164             readData, err, err_info);
165 }
166
167 /* Read the capture file randomly 
168  * Wireshark opens the capture file for random access when displaying user-selected packets */
169 static gboolean
170 daintree_sna_seek_read(wtap *wth, gint64 seek_off, struct wtap_pkthdr *phdr,
171         Buffer *buf, int len _U_, int *err, gchar **err_info)
172 {
173         char readLine[DAINTREE_MAX_LINE_SIZE];
174         char readData[READDATA_BUF_SIZE];
175
176         if(file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
177                 return FALSE;
178
179         /* It appears only file header lines start with '#', but
180          * if we find any others, we toss them */
181         do {
182                 if (file_gets(readLine, DAINTREE_MAX_LINE_SIZE, wth->random_fh) == NULL) {
183                         *err = file_error(wth->random_fh, err_info);
184                         return FALSE; /* all done */
185                 }
186         } while (readLine[0] == COMMENT_LINE);
187
188         /* parse one line of capture data */
189         if (!daintree_sna_scan_header(phdr, readLine, readData, err, err_info))
190                 return FALSE;
191
192         /* process packet data */
193         return daintree_sna_process_hex_data(phdr, buf, readData, err,
194             err_info);
195 }
196
197 /* Scan a header line and fill in a struct wtap_pkthdr */
198 static gboolean
199 daintree_sna_scan_header(struct wtap_pkthdr *phdr, char *readLine,
200     char *readData, int *err, gchar **err_info)
201 {
202         guint64 seconds;
203         int useconds;
204
205         phdr->presence_flags = WTAP_HAS_TS|WTAP_HAS_CAP_LEN;
206
207         if (sscanf(readLine, "%*s %18" G_GINT64_MODIFIER "u.%9d %9u %" READDATA_MAX_FIELD_SIZE "s",
208             &seconds, &useconds, &phdr->len, readData) != 4) {
209                 *err = WTAP_ERR_BAD_FILE;
210                 *err_info = g_strdup("daintree_sna: invalid read record");
211                 return FALSE;
212         }
213
214         /* Daintree doesn't store the FCS, but pads end of packet with 0xffff, which we toss */
215         if (phdr->len <= FCS_LENGTH) {
216                 *err = WTAP_ERR_BAD_FILE;
217                 *err_info = g_strdup_printf("daintree_sna: packet length <= %u bytes, no frame data present",
218                     FCS_LENGTH);
219                 return FALSE;
220         }
221         phdr->len -= FCS_LENGTH;
222
223         phdr->ts.secs = (time_t) seconds;
224         phdr->ts.nsecs = useconds * 1000; /* convert mS to nS */
225
226         return TRUE;
227 }
228
229 /* Convert packet data from ASCII hex string to binary in place,
230  * sanity-check its length against what we assume is the packet length field,
231  * and copy it into a Buffer */
232 static gboolean
233 daintree_sna_process_hex_data(struct wtap_pkthdr *phdr, Buffer *buf,
234     char *readData, int *err, gchar **err_info)
235 {
236         guchar *str = (guchar *)readData;
237         guint bytes;
238         guint8 *p;
239
240         p = str; /* overlay source buffer */
241         bytes = 0;
242         /* convert hex string to guint8 */
243         while(*str) {
244                 /* most significant nibble */
245                 if (!isxdigit((guchar)*str)) {
246                         *err = WTAP_ERR_BAD_FILE;
247                         *err_info = g_strdup("daintree_sna: non-hex digit in hex data");
248                         return FALSE;
249                 }
250                 if(isdigit((guchar)*str)) {
251                         *p = (*str - '0') << 4;
252                 } else {
253                         *p = ((tolower(*str) - 'a') + 10) << 4;
254                 }
255                 str++;
256
257                 /* least significant nibble */
258                 if (!isxdigit((guchar)*str)) {
259                         *err = WTAP_ERR_BAD_FILE;
260                         *err_info = g_strdup("daintree_sna: non-hex digit in hex data");
261                         return FALSE;
262                 }
263                 if(isdigit((guchar)*str)) {
264                         *p += *str - '0';
265                 } else {
266                         *p += (tolower(*str) - 'a') + 10;
267                 }
268                 str++;
269
270                 /* next byte in buffer */
271                 p++;
272                 bytes++;
273         }
274
275         /* Daintree doesn't store the FCS, but pads end of packet with 0xffff, which we toss */
276         if (bytes <= FCS_LENGTH) {
277                 *err = WTAP_ERR_BAD_FILE;
278                 *err_info = g_strdup_printf("daintree_sna: Only %u bytes of packet data",
279                     bytes);
280                 return FALSE;
281         }
282         bytes -= FCS_LENGTH;
283         if (bytes > phdr->len) {
284                 *err = WTAP_ERR_BAD_FILE;
285                 *err_info = g_strdup_printf("daintree_sna: capture length (%u) > packet length (%u)",
286                     bytes, phdr->len);
287                 return FALSE;
288         }
289
290         phdr->caplen = bytes;
291
292         buffer_assure_space(buf, bytes);
293         memcpy(buffer_start_ptr(buf), readData, bytes);
294         return TRUE;
295 }