Update minimum version requirement for GTK to 2.12.
[obnox/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_hex_char 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 #ifdef HAVE_CONFIG_H
50 #include "config.h"
51 #endif
52
53 #include <glib.h>
54 #include <stdio.h>
55 #include <stdlib.h>
56 #include <errno.h>
57 #include <string.h>
58 #include <ctype.h>
59
60 #include "wtap.h"
61 #include "wtap-int.h"
62 #include "buffer.h"
63 #include "file_wrappers.h"
64 #include "daintree-sna.h"
65
66 typedef struct daintree_sna_header {
67         guint32 len;
68         guint64 ts;
69 } daintree_sna_header_t;
70
71 #define DAINTREE_SNA_HEADER_SIZE 2
72 #define FCS_LENGTH 2
73
74 static const char daintree_magic_text[] =
75 { '#', 'F', 'o', 'r', 'm', 'a', 't', '=' };
76
77 #define DAINTREE_MAGIC_TEXT_SIZE (sizeof daintree_magic_text)
78 #define DAINTREE_MAX_LINE_SIZE 512
79 #define READDATA_BUF_SIZE (DAINTREE_MAX_LINE_SIZE/2)
80 #define SEEKDATA_BUF_SIZE (DAINTREE_MAX_LINE_SIZE/2)
81 #define READDATA_MAX_FIELD_SIZE "255"  /* DAINTREE_MAX_LINE_SIZE/2 -1 */
82 #define SEEKDATA_MAX_FIELD_SIZE "255"  /* DAINTREE_MAX_LINE_SIZE/2 -1 */
83
84 #define COMMENT_LINE daintree_magic_text[0]
85
86 static char readLine[DAINTREE_MAX_LINE_SIZE];
87 static char seekLine[DAINTREE_MAX_LINE_SIZE];
88
89 static char readData[READDATA_BUF_SIZE];
90 static char seekData[SEEKDATA_BUF_SIZE];
91
92 static gboolean daintree_sna_read(wtap *wth, int *err, gchar **err_info,
93         gint64 *data_offset);
94
95 static gboolean daintree_sna_seek_read(wtap *wth, gint64 seek_off,
96         union wtap_pseudo_header *pseudo_header _U_,
97         guint8 *pd, int len, int *err,
98         gchar **err_info);
99
100 static guint daintree_sna_hex_char(guchar *str, int *err);
101
102 /* Open a file and determine if it's a Daintree file */
103 int daintree_sna_open(wtap *wth, int *err _U_, gchar **err_info _U_)
104 {
105         guint i; 
106
107         /* get first line of file header */
108         if (file_gets(readLine, DAINTREE_MAX_LINE_SIZE, wth->fh)==NULL) return 0;
109         wth->data_offset += strlen(readLine);
110
111         /* check magic text */
112         i = 0;
113         while (i < DAINTREE_MAGIC_TEXT_SIZE) {
114                 if (readLine[i] != daintree_magic_text[i]) return 0; /* not daintree format */
115                 i++;
116         } 
117
118         /* read second header line */
119         if (file_gets(readLine, DAINTREE_MAX_LINE_SIZE, wth->fh)==NULL) return 0;
120         wth->data_offset += strlen(readLine); 
121         if (readLine[0] != COMMENT_LINE) return 0; /* daintree files have a two line header */
122
123         /* set up the pointers to the handlers for this file type */
124         wth->subtype_read = daintree_sna_read;
125         wth->subtype_seek_read = daintree_sna_seek_read;
126
127         /* set up for file type */
128         wth->file_type = WTAP_FILE_DAINTREE_SNA;
129         wth->file_encap = WTAP_ENCAP_IEEE802_15_4_NOFCS;
130         wth->tsprecision = WTAP_FILE_TSPREC_USEC;
131         wth->snapshot_length = 0; /* not available in header */
132
133         return 1; /* it's a Daintree file */
134 }
135
136 /* Read the capture file sequentially
137  * Wireshark scans the file with sequential reads during preview and initial display. */
138 static gboolean
139 daintree_sna_read(wtap *wth, int *err, gchar **err_info, gint64 *data_offset)
140 {
141         guint64 seconds;
142
143         *data_offset = wth->data_offset;
144
145         /* we've only seen file header lines starting with '#', but
146          * if others appear in the file, they are tossed */
147         do {
148                 if (file_gets(readLine, DAINTREE_MAX_LINE_SIZE, wth->fh) == NULL) {
149                         *err = file_error(wth->fh, err_info);
150                         return FALSE; /* all done */
151                 }
152                 wth->data_offset += strlen(readLine);
153         } while (readLine[0] == COMMENT_LINE);
154
155         /* parse one line of capture data */
156         if (sscanf(readLine, "%*s %18" G_GINT64_MODIFIER "u.%9d %9u %" READDATA_MAX_FIELD_SIZE "s",
157             &seconds, &wth->phdr.ts.nsecs, &wth->phdr.len, readData) != 4) {
158                 *err = WTAP_ERR_BAD_RECORD;
159                 *err_info = g_strdup("daintree_sna: invalid read record");
160                 return FALSE;
161         }
162
163         /* Daintree doesn't store the FCS, but pads end of packet with 0xffff, which we toss */
164         if (wth->phdr.len <= FCS_LENGTH) {
165                 *err = WTAP_ERR_BAD_RECORD;
166                 *err_info = g_strdup_printf("daintree_sna: packet length <= %u bytes, no frame data present",
167                     FCS_LENGTH);
168                 return FALSE;
169         }
170         wth->phdr.len -= FCS_LENGTH;
171
172         wth->phdr.ts.secs = (time_t) seconds;
173         wth->phdr.ts.nsecs *= 1000; /* convert mS to nS */
174
175         /* convert packet data from ASCII string to hex, sanity-check its length against what we assume is the
176          * packet length field, write data to frame buffer */
177         if ((wth->phdr.caplen = daintree_sna_hex_char(readData, err)) > FCS_LENGTH) {
178                 /* Daintree doesn't store the FCS, but pads end of packet with 0xffff, which we toss */
179                 wth->phdr.caplen -= FCS_LENGTH;
180                 if (wth->phdr.caplen <= wth->phdr.len) {
181                         buffer_assure_space(wth->frame_buffer, wth->phdr.caplen);
182                         memcpy(buffer_start_ptr(wth->frame_buffer), readData, wth->phdr.caplen);
183                 } else {
184                         *err = WTAP_ERR_BAD_RECORD;
185                         *err_info = g_strdup_printf("daintree_sna: capture length (%u) > packet length (%u)",
186                                 wth->phdr.caplen, wth->phdr.len);
187                         return FALSE;
188                 }
189         } else {
190                 *err = WTAP_ERR_BAD_RECORD;
191                 *err_info = g_strdup("daintree_sna: invalid packet data");
192                 return FALSE;
193         }
194
195         return TRUE;
196 }
197
198 /* Read the capture file randomly 
199  * Wireshark opens the capture file for random access when displaying user-selected packets */
200 static gboolean
201 daintree_sna_seek_read(wtap *wth, gint64 seek_off, union wtap_pseudo_header
202         *pseudo_header _U_, guint8 *pd, int len, int *err,
203         gchar **err_info)
204 {
205         guint pkt_len;
206
207         if(file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
208                 return FALSE;
209
210         /* It appears only file header lines start with '#', but
211          * if we find any others, we toss them */
212         do {
213                 if (file_gets(seekLine, DAINTREE_MAX_LINE_SIZE, wth->random_fh) == NULL) {
214                         *err = file_error(wth->random_fh, err_info);
215                         return FALSE; /* all done */
216                 }
217         } while (seekLine[0] == COMMENT_LINE);
218
219         /* ignore all but packet data, since the sequential read pass stored everything else */
220         if (sscanf(seekLine, "%*s %*u.%*u %*u %" SEEKDATA_MAX_FIELD_SIZE "s", seekData) != 1) {
221                 *err = WTAP_ERR_BAD_RECORD;
222                 *err_info = g_strdup("daintree_sna: corrupted seek record");
223                 return FALSE;
224         }
225
226         /* convert packet data from ASCII hex string to guchar */
227         if ((pkt_len = daintree_sna_hex_char(seekData, err)) <= FCS_LENGTH) {
228                 *err = WTAP_ERR_BAD_RECORD;
229                 *err_info = g_strdup("daintree_sna: corrupted packet data");
230                 return FALSE;
231         }
232
233         pkt_len -= FCS_LENGTH; /* remove padded bytes that Daintree stores instead of FCS */
234
235         if (pkt_len == (guint) len) {
236                 /* move to frame buffer for dissection */
237                 memcpy(pd, seekData, pkt_len);
238         } else {
239                 *err = WTAP_ERR_BAD_RECORD;
240                 *err_info = g_strdup("daintree-sna: corrupted frame");
241                 return FALSE;
242         } 
243
244         return TRUE;
245 }
246
247 /* Convert an ASCII hex string to guint8 */
248 static guint
249 daintree_sna_hex_char(guchar *str, int *err _U_) {
250         guint bytes;
251         guint8 *p;
252
253         p = str; /* overlay source buffer */
254         bytes = 0;
255         /* convert hex string to guint8 */
256         while(*str) {
257                 if (!isxdigit((guchar)*str)) return 0;
258                 /* most significant nibble */
259                 if(isdigit((guchar)*str)) {
260                         *p = (*str - '0') << 4;
261                 } else {
262                         *p = ((tolower(*str) - 'a') + 10) << 4;
263                 }
264                 str++;
265
266                 if (!isxdigit((guchar)*str)) return 0;
267                 /* least significant nibble */
268                 if(isdigit((guchar)*str)) {
269                         *p += *str - '0';
270                 } else {
271                         *p += (tolower(*str) - 'a') + 10;
272                 }
273                 str++;
274
275                 /* next byte in buffer */
276                 p++;
277                 bytes++;
278         }
279
280         return bytes;
281 }