From Ivan Sy (with minor modifications):
[obnox/wireshark/wip.git] / wiretap / commview.c
1 /* commview.c
2  * Routines for opening CommView file format packet captures
3  * Copyright 2007, Stephen Fisher (see AUTHORS file)
4  *
5  * $Id$
6  *
7  * Wireshark - Network traffic analyzer
8  * By Gerald Combs <gerald@wireshark.org>
9  * Copyright 1998 Gerald Combs
10  *
11  * Based on csids.c and nettl.c
12  *
13  * This program is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU General Public License
15  * as published by the Free Software Foundation; either version 2
16  * of the License, or (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software
25  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
26  * USA.
27  */
28
29 /* A brief description of this file format is available at:
30  *    http://www.tamos.com/htmlhelp/commview/logformat.htm
31  */
32
33 #ifdef HAVE_CONFIG_H
34 #include "config.h"
35 #endif
36
37 #include <glib.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <errno.h>
41 #include <string.h>
42
43 #include "wtap.h"
44 #include "wtap-int.h"
45 #include "buffer.h"
46 #include "file_wrappers.h"
47 #include "commview.h"
48
49 typedef struct commview_header {
50         guint16         data_len;
51         guint16         source_data_len;
52         guint8          version;
53         guint16         year;
54         guint8          month;
55         guint8          day;
56         guint8          hours;
57         guint8          minutes;
58         guint8          seconds;
59         guint32         usecs;
60         guint8          flags;          /* Bit-field positions defined below */
61         guint8          signal_level;
62         guint8          rate;
63         guint8          band;
64         guint8          channel;
65         guint8          direction;      /* Not applicable to WiFi packets */
66         guint16         reserved;       /* Unused bytes */
67 } commview_header_t;
68
69 #define COMMVIEW_HEADER_SIZE 24
70
71 /* Bit-field positions for various fields in the flags variable of the header */
72 #define FLAGS_MEDIUM            0x0F
73 #define FLAGS_DECRYPTED         0x10
74 #define FLAGS_BROKEN            0x20
75 #define FLAGS_COMPRESSED        0x40
76 #define FLAGS_RESERVED          0x80
77
78 /* Capture mediums as defined by the commview file format */
79 #define MEDIUM_ETHERNET         0
80 #define MEDIUM_WIFI             1
81 #define MEDIUM_TOKEN_RING       2
82
83 static gboolean commview_read(wtap *wth, int *err, gchar **err_info _U_,
84                               gint64 *data_offset);
85 static gboolean commview_seek_read(wtap *wth, gint64 seek_off,
86                                    union wtap_pseudo_header *pseudo_header,
87                                    guchar *pd, int length, int *err,
88                                    gchar **err_info _U_);
89 static gboolean  commview_read_header(commview_header_t *cv_hdr, FILE_T fh,
90                                       int *err);
91 static gboolean commview_dump(wtap_dumper *wdh, const struct wtap_pkthdr *phdr,
92                               const union wtap_pseudo_header *pseudo_header _U_,
93                               const guchar *pd, int *err);
94
95 int commview_open(wtap *wth, int *err, gchar **err_info _U_)
96 {
97         commview_header_t cv_hdr;
98
99         if(!commview_read_header(&cv_hdr, wth->fh, err))
100                 return -1;
101
102         /* If any of these fields do not match what we expect, bail out. */
103         if(cv_hdr.version != 0 ||
104            cv_hdr.year < 1970 || cv_hdr.year >= 2038 ||
105            cv_hdr.month < 1 || cv_hdr.month > 12 ||
106            cv_hdr.day < 1 || cv_hdr.day > 31 ||
107            cv_hdr.hours > 23 ||
108            cv_hdr.minutes > 59 ||
109            cv_hdr.seconds > 60 ||
110            cv_hdr.signal_level > 100 ||
111            (cv_hdr.direction != 0x00 && cv_hdr.direction != 0x01 &&
112             cv_hdr.direction != 0x02) ||
113            (cv_hdr.flags & FLAGS_RESERVED) != 0 ||
114            ((cv_hdr.flags & FLAGS_MEDIUM) != MEDIUM_ETHERNET &&
115             (cv_hdr.flags & FLAGS_MEDIUM) != MEDIUM_WIFI &&
116             (cv_hdr.flags & FLAGS_MEDIUM) != MEDIUM_TOKEN_RING) ||
117            cv_hdr.reserved != 0)
118                 return 0; /* Not our kind of file */
119
120         /* No file header. Reset the fh to 0 so we can read the first packet */
121         if (file_seek(wth->fh, 0, SEEK_SET, err) == -1)
122                 return -1;
123
124         /* Set up the pointers to the handlers for this file type */
125         wth->subtype_read = commview_read;
126         wth->subtype_seek_read = commview_seek_read;
127
128         wth->data_offset = 0;
129         wth->file_type = WTAP_FILE_COMMVIEW;
130         wth->file_encap = WTAP_ENCAP_PER_PACKET;
131         wth->tsprecision = WTAP_FILE_TSPREC_USEC;
132
133         return 1; /* Our kind of file */
134 }
135
136 static gboolean
137 commview_read(wtap *wth, int *err, gchar **err_info _U_, gint64 *data_offset)
138 {
139         commview_header_t cv_hdr;
140         struct tm tm;
141         int bytes_read;
142
143         *data_offset = wth->data_offset;
144
145         if(!commview_read_header(&cv_hdr, wth->fh, err))
146                 return FALSE;
147
148         wth->data_offset += COMMVIEW_HEADER_SIZE;
149
150         switch(cv_hdr.flags & FLAGS_MEDIUM) {
151
152         case MEDIUM_ETHERNET :
153                 wth->phdr.pkt_encap = WTAP_ENCAP_ETHERNET;
154                 break;
155
156         case MEDIUM_WIFI :
157                 wth->phdr.pkt_encap = WTAP_ENCAP_IEEE_802_11_WITH_RADIO;
158                 break;
159
160         case MEDIUM_TOKEN_RING :
161                 wth->phdr.pkt_encap = WTAP_ENCAP_TOKEN_RING;
162                 break;
163         default:
164                 *err = WTAP_ERR_BAD_RECORD;
165                 *err_info = g_strdup_printf("commview: unsupported encap: %u",
166                                             cv_hdr.flags & FLAGS_MEDIUM);
167                 return FALSE;
168         }
169
170         buffer_assure_space(wth->frame_buffer, cv_hdr.data_len);
171         bytes_read = file_read(buffer_start_ptr(wth->frame_buffer), 1,
172                                cv_hdr.data_len, wth->fh);
173         if(bytes_read != cv_hdr.data_len) {
174                 *err = file_error(wth->fh);
175                 if(*err == 0)
176                         *err = WTAP_ERR_SHORT_READ;
177                 return FALSE;
178         }
179
180         tm.tm_year = cv_hdr.year - 1900;
181         tm.tm_mon = cv_hdr.month - 1;
182         tm.tm_mday = cv_hdr.day;
183         tm.tm_hour = cv_hdr.hours;
184         tm.tm_min = cv_hdr.minutes;
185         tm.tm_sec = cv_hdr.seconds;
186         tm.tm_isdst = -1;
187
188         wth->data_offset += cv_hdr.data_len;
189
190         wth->phdr.len = cv_hdr.data_len;
191         wth->phdr.caplen = cv_hdr.data_len;
192
193         wth->phdr.ts.secs = mktime(&tm);
194         wth->phdr.ts.nsecs = cv_hdr.usecs * 1000;
195
196         return TRUE;
197 }
198
199 static gboolean
200 commview_seek_read(wtap *wth, gint64 seek_off, union wtap_pseudo_header
201                    *pseudo_header, guchar *pd, int length, int *err,
202                    gchar **err_info _U_)
203 {
204         commview_header_t cv_hdr;
205         int bytes_read;
206
207         if(file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
208                 return FALSE;
209
210         if(!commview_read_header(&cv_hdr, wth->random_fh, err)) {
211                 if(*err == 0)
212                         *err = WTAP_ERR_SHORT_READ;
213
214                 return FALSE;
215         }
216
217         if(length != cv_hdr.data_len) {
218                 *err = WTAP_ERR_BAD_RECORD;
219                 *err_info = g_strdup_printf("commview: record length %u doesn't match requested length %d", cv_hdr.data_len, length);
220                 return FALSE;
221         }
222
223         /* Pass some data to the 802.11 dissector if this is a WiFi packet */
224         if((cv_hdr.flags & FLAGS_MEDIUM) == MEDIUM_WIFI) {
225                 pseudo_header->ieee_802_11.fcs_len = -1; /* Unknown */
226                 pseudo_header->ieee_802_11.channel = cv_hdr.channel;
227                 pseudo_header->ieee_802_11.data_rate = cv_hdr.rate;
228                 pseudo_header->ieee_802_11.signal_level = cv_hdr.signal_level;
229         }
230
231         bytes_read = file_read(pd, 1, cv_hdr.data_len, wth->random_fh);
232         if(bytes_read != cv_hdr.data_len) {
233                 *err = file_error(wth->random_fh);
234                 if(*err == 0)
235                         *err = WTAP_ERR_SHORT_READ;
236
237                 return FALSE;
238         }
239
240         return TRUE;
241 }
242
243 static gboolean
244 commview_read_header(commview_header_t *cv_hdr, FILE_T fh, int *err)
245 {
246         int bytes_read = 0;
247
248         bytes_read += file_read(&cv_hdr->data_len, 2, 1, fh);
249         bytes_read += file_read(&cv_hdr->source_data_len, 2, 1, fh);
250         bytes_read += file_read(&cv_hdr->version, 1, 1, fh);
251         bytes_read += file_read(&cv_hdr->year, 2, 1, fh);
252         bytes_read += file_read(&cv_hdr->month, 1, 1, fh);
253         bytes_read += file_read(&cv_hdr->day, 1, 1, fh);
254         bytes_read += file_read(&cv_hdr->hours, 1, 1, fh);
255         bytes_read += file_read(&cv_hdr->minutes, 1, 1, fh);
256         bytes_read += file_read(&cv_hdr->seconds, 1, 1, fh);
257         bytes_read += file_read(&cv_hdr->usecs, 4, 1, fh);
258         bytes_read += file_read(&cv_hdr->flags, 1, 1, fh);
259         bytes_read += file_read(&cv_hdr->signal_level, 1, 1, fh);
260         bytes_read += file_read(&cv_hdr->rate, 1, 1, fh);
261         bytes_read += file_read(&cv_hdr->band, 1, 1, fh);
262         bytes_read += file_read(&cv_hdr->channel, 1, 1, fh);
263         bytes_read += file_read(&cv_hdr->direction, 1, 1, fh);
264         bytes_read += file_read(&cv_hdr->reserved, 2, 1, fh);
265
266         /* Convert multi-byte values from little endian to host endian format */
267         cv_hdr->data_len = GUINT16_FROM_LE(cv_hdr->data_len);
268         cv_hdr->source_data_len = GUINT16_FROM_LE(cv_hdr->source_data_len);
269         cv_hdr->year = GUINT16_FROM_LE(cv_hdr->year);
270         cv_hdr->usecs = GUINT32_FROM_LE(cv_hdr->usecs);
271
272         if(bytes_read < COMMVIEW_HEADER_SIZE) {
273                 *err = file_error(fh);
274                 if(*err == 0 && bytes_read > 0)
275                         *err = WTAP_ERR_SHORT_READ;
276
277                 return FALSE;
278         }
279
280         return TRUE;
281 }
282
283 /* Returns 0 if we can write out the specified encapsulation type
284  * into a CommView format file. */
285 int commview_dump_can_write_encap(int encap)
286 {
287         switch (encap) {
288
289         case WTAP_ENCAP_ETHERNET :
290         case WTAP_ENCAP_IEEE_802_11 :
291         case WTAP_ENCAP_IEEE_802_11_WITH_RADIO :
292         case WTAP_ENCAP_TOKEN_RING :
293         case WTAP_ENCAP_PER_PACKET :
294                 return 0;
295
296         default:
297                 return WTAP_ERR_UNSUPPORTED_ENCAP;
298         }
299 }
300
301 /* Returns TRUE on success, FALSE on failure;
302    sets "*err" to an error code on failure */
303 gboolean commview_dump_open(wtap_dumper *wdh, gboolean cant_seek _U_,
304                             int *err _U_)
305 {
306         wdh->subtype_write = commview_dump;
307         wdh->subtype_close = NULL;
308
309         /* There is no file header to write out */
310         wdh->bytes_dumped = 0;
311
312         return TRUE;
313 }
314
315 /* Write a record for a packet to a dump file.
316  * Returns TRUE on success, FALSE on failure. */
317 static gboolean commview_dump(wtap_dumper *wdh,
318                               const struct wtap_pkthdr *phdr,
319                               const union wtap_pseudo_header *pseudo_header,
320                               const guchar *pd, int *err)
321 {
322         commview_header_t cv_hdr;
323         size_t bytes_written = 0;
324         char date_time[5];
325
326         memset(&cv_hdr, 0, sizeof(cv_hdr));
327
328         cv_hdr.data_len = GUINT16_TO_LE((guint16)phdr->caplen);
329         cv_hdr.source_data_len = GUINT16_TO_LE((guint16)phdr->caplen);
330         cv_hdr.version = 0;
331
332         strftime(date_time, 5, "%Y", localtime(&phdr->ts.secs));
333         cv_hdr.year = GUINT16_TO_LE((guint16)strtol(date_time, NULL, 10));
334
335         strftime(date_time, 5, "%m", localtime(&phdr->ts.secs));
336         cv_hdr.month = (guint8)strtol(date_time, NULL, 10);
337
338         strftime(date_time, 5, "%d", localtime(&phdr->ts.secs));
339         cv_hdr.day = (guint8)strtol(date_time, NULL, 10);
340
341         strftime(date_time, 5, "%H", localtime(&phdr->ts.secs));
342         cv_hdr.hours = (guint8)strtol(date_time, NULL, 10);
343
344         strftime(date_time, 5, "%M", localtime(&phdr->ts.secs));
345         cv_hdr.minutes = (guint8)strtol(date_time, NULL, 10);
346
347         strftime(date_time, 5, "%S", localtime(&phdr->ts.secs));
348         cv_hdr.seconds = (guint8)strtol(date_time, NULL, 10);
349
350         cv_hdr.usecs = GUINT32_TO_LE(phdr->ts.nsecs / 1000);
351
352         switch(phdr->pkt_encap) {
353
354         case WTAP_ENCAP_ETHERNET :
355                 cv_hdr.flags |= MEDIUM_ETHERNET;
356                 break;
357
358         case WTAP_ENCAP_IEEE_802_11 :
359                 cv_hdr.flags |=  MEDIUM_WIFI;
360                 break;
361
362         case WTAP_ENCAP_IEEE_802_11_WITH_RADIO :
363                 cv_hdr.flags |=  MEDIUM_WIFI;
364
365                 cv_hdr.channel = pseudo_header->ieee_802_11.channel;
366                 cv_hdr.rate = pseudo_header->ieee_802_11.data_rate;
367                 cv_hdr.signal_level = pseudo_header->ieee_802_11.signal_level;
368
369                 break;
370
371         case WTAP_ENCAP_TOKEN_RING :
372                 cv_hdr.flags |= MEDIUM_TOKEN_RING;
373
374         default :
375                 *err = WTAP_ERR_UNSUPPORTED_ENCAP;
376                 return FALSE;
377         }
378
379         bytes_written += fwrite(&cv_hdr.data_len, 2, 1, wdh->fh);
380         bytes_written += fwrite(&cv_hdr.source_data_len, 2, 1, wdh->fh);
381         bytes_written += fwrite(&cv_hdr.version, 1, 1, wdh->fh);
382         bytes_written += fwrite(&cv_hdr.year, 2, 1, wdh->fh);
383         bytes_written += fwrite(&cv_hdr.month, 1, 1, wdh->fh);
384         bytes_written += fwrite(&cv_hdr.day, 1, 1, wdh->fh);
385         bytes_written += fwrite(&cv_hdr.hours, 1, 1, wdh->fh);
386         bytes_written += fwrite(&cv_hdr.minutes, 1, 1, wdh->fh);
387         bytes_written += fwrite(&cv_hdr.seconds, 1, 1, wdh->fh);
388         bytes_written += fwrite(&cv_hdr.usecs, 4, 1, wdh->fh);
389         bytes_written += fwrite(&cv_hdr.flags, 1, 1, wdh->fh);
390         bytes_written += fwrite(&cv_hdr.signal_level, 1, 1, wdh->fh);
391         bytes_written += fwrite(&cv_hdr.rate, 1, 1, wdh->fh);
392         bytes_written += fwrite(&cv_hdr.band, 1, 1, wdh->fh);
393         bytes_written += fwrite(&cv_hdr.channel, 1, 1, wdh->fh);
394         bytes_written += fwrite(&cv_hdr.direction, 1, 1, wdh->fh);
395         bytes_written += fwrite(&cv_hdr.reserved, 2, 1, wdh->fh);
396
397         if(bytes_written != 17) { /* 17 units of data should have been written
398                                    * above. */
399                 if(bytes_written == 0 && ferror(wdh->fh))
400                         *err = errno;
401                 else
402                         *err = WTAP_ERR_SHORT_WRITE;
403
404                 return FALSE;
405         }
406
407         wdh->bytes_dumped += COMMVIEW_HEADER_SIZE;
408
409         bytes_written = fwrite(pd, 1, phdr->caplen, wdh->fh);
410
411         if(bytes_written != phdr->caplen) {
412                 if(bytes_written == 0 && ferror(wdh->fh))
413                         *err = errno;
414                 else
415                         *err = WTAP_ERR_SHORT_WRITE;
416
417                 return FALSE;
418         }
419
420         wdh->bytes_dumped += phdr->caplen;
421
422         return TRUE;
423 }