change all file offsets from long to gint64 so we can - theoretically - handle files...
[metze/wireshark/wip.git] / wiretap / network_instruments.c
1 /*
2  * $Id$
3  */
4
5 /***************************************************************************
6                           NetworkInstruments.c  -  description
7                              -------------------
8     begin                : Wed Oct 29 2003
9     copyright            : (C) 2003 by root
10     email                : scotte[AT}netinst.com
11  ***************************************************************************/
12
13 /***************************************************************************
14  *                                                                         *
15  *   This program is free software; you can redistribute it and/or modify  *
16  *   it under the terms of the GNU General Public License as published by  *
17  *   the Free Software Foundation; either version 2 of the License, or     *
18  *   (at your option) any later version.                                   *
19  *                                                                         *
20  ***************************************************************************/
21  
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25
26 #include <stdlib.h>
27 #include <errno.h>
28 #include <string.h>
29 #include "wtap-int.h"
30 #include "file_wrappers.h"
31 #include "buffer.h"
32 #include "network_instruments.h"
33
34 static const char network_instruments_magic[] = {"ObserverPktBufferVersion=09.00"};
35 static const int true_magic_length = 17;
36
37 static const guint32 observer_packet_magic = 0x88888888;
38
39 static const int observer_encap[] = {
40         WTAP_ENCAP_ETHERNET,
41         WTAP_ENCAP_TOKEN_RING
42 };
43 #define NUM_OBSERVER_ENCAPS (sizeof observer_encap / sizeof observer_encap[0])
44
45 static const int from_wtap_encap[] = {
46         OBSERVER_UNDEFINED,
47         OBSERVER_ETHERNET,
48         OBSERVER_TOKENRING,
49 };
50 #define NUM_FROM_WTAP_ENCAPS (sizeof from_wtap_encap / sizeof observer_encap[0])
51
52 #define CAPTUREFILE_HEADER_SIZE sizeof(capture_file_header)
53
54 /*
55  * The time in Observer files is in nanoseconds since midnight, January 1,
56  * 2000, 00:00:00 local time.
57  *
58  * We want the seconds portion to be seconds since midnight, January 1,
59  * 1970, 00:00:00 GMT.
60  *
61  * To do that, we add the number of seconds between midnight, January 1,
62  * 2000, 00:00:00 local time and midnight, January 1, 1970, 00:00:00 GMT.
63  * (That gets the wrong answer if the time zone is being read in a different
64  * time zone, but there's not much we can do about that.)
65  */
66 static gboolean have_time_offset;
67 static time_t seconds1970to2000;
68
69 static void init_time_offset(void)
70 {
71         if (!have_time_offset) {
72                 struct tm midnight_2000_01_01;
73
74                 /*
75                  * Get the number of seconds between midnight, January 1,
76                  * 2000, 00:00:00 local time - that's just the UNIX
77                  * time stamp for 2000-01-01 00:00:00 local time.
78                  */
79                 midnight_2000_01_01.tm_year = 2000 - 1900;
80                 midnight_2000_01_01.tm_mon = 0;
81                 midnight_2000_01_01.tm_mday = 1;
82                 midnight_2000_01_01.tm_hour = 0;
83                 midnight_2000_01_01.tm_min = 0;
84                 midnight_2000_01_01.tm_sec = 0;
85                 midnight_2000_01_01.tm_isdst = -1;
86                 seconds1970to2000 = mktime(&midnight_2000_01_01);
87                 have_time_offset = TRUE;
88         }
89 }
90
91 static gboolean observer_read(wtap *wth, int *err, gchar **err_info,
92     gint64 *data_offset);
93 static gboolean observer_seek_read(wtap *wth, gint64 seek_off,
94     union wtap_pseudo_header *pseudo_header, guchar *pd, int length,
95     int *err, gchar **err_info);
96 static int read_packet_header(FILE_T fh, packet_entry_header *packet_header,
97     int *err, gchar **err_info);
98 static gboolean read_packet_data(FILE_T fh, int offset_to_frame, int offset,
99     guint8 *pd, int length, int *err, char **err_info);
100 static gboolean skip_to_next_packet(wtap *wth, int offset,
101     int offset_to_next_packet, int *err, char **err_info);
102 static gboolean observer_dump_close(wtap_dumper *wdh, int *err);
103 static gboolean observer_dump(wtap_dumper *wdh, const struct wtap_pkthdr *phdr,
104     const union wtap_pseudo_header *pseudo_header, const guchar *pd, int *err);
105
106 int network_instruments_open(wtap *wth, int *err, gchar **err_info)
107 {
108         int bytes_read;
109         int offset;
110         capture_file_header file_header;
111         guint i;
112         tlv_header tlvh;
113         int seek_increment;
114         packet_entry_header packet_header;
115
116         errno = WTAP_ERR_CANT_READ;
117         offset = 0;
118
119         /* Read in the buffer file header */
120         bytes_read = file_read(&file_header, sizeof file_header, 1, wth->fh);
121         if (bytes_read != sizeof file_header) {
122                 *err = file_error(wth->fh);
123                 if (*err != 0)
124                         return -1;
125                 return 0;
126         }
127         offset += bytes_read;
128
129         /* check the magic number */
130         if (memcmp(file_header.observer_version, network_instruments_magic, true_magic_length)!=0) {
131                 return 0;
132         }
133
134         /* check the version */
135         if (strncmp(network_instruments_magic, file_header.observer_version, 30)!=0) {
136                 *err = WTAP_ERR_UNSUPPORTED_ENCAP;
137                 *err_info = g_strdup_printf("Observer: unsupported file version %s", file_header.observer_version);
138                 return -1;
139         }
140
141         /* process extra information */
142         for (i = 0; i < file_header.number_of_information_elements; i++) {
143                 /* read the TLV header */
144                 bytes_read = file_read(&tlvh, sizeof tlvh, 1, wth->fh);
145                 if (bytes_read != sizeof tlvh) {
146                         *err = file_error(wth->fh);
147                         if (*err == 0)
148                                 *err = WTAP_ERR_SHORT_READ;
149                         return -1;
150                 }
151                 offset += bytes_read;
152
153                 tlvh.length = GUINT16_FROM_LE(tlvh.length);
154                 if (tlvh.length < sizeof tlvh) {
155                         *err = WTAP_ERR_BAD_RECORD;
156                         *err_info = g_strdup_printf("Observer: bad record (TLV length %u < %lu)",
157                             tlvh.length, (unsigned long)sizeof tlvh);
158                         return -1;
159                 }
160
161                 /* skip the TLV data */
162                 seek_increment = tlvh.length - sizeof tlvh;
163                 if (seek_increment > 0) {
164                         if (file_seek(wth->fh, seek_increment, SEEK_CUR, err) == -1)
165                                 return -1;
166                 }
167                 offset += seek_increment;
168         }
169
170         /* get to the first packet */
171         file_header.offset_to_first_packet =
172             GUINT16_FROM_LE(file_header.offset_to_first_packet);
173         if (file_header.offset_to_first_packet < offset) {
174                 *err = WTAP_ERR_BAD_RECORD;
175                 *err_info = g_strdup_printf("Observer: bad record (offset to first packet %d < %d)",
176                     file_header.offset_to_first_packet, offset);
177                 return FALSE;
178         }
179         seek_increment = file_header.offset_to_first_packet - offset;
180         if (seek_increment > 0) {
181                 if (file_seek(wth->fh, seek_increment, SEEK_CUR, err) == -1)
182                         return -1;
183         }
184
185         /* pull off the packet header */
186         bytes_read = file_read(&packet_header, sizeof packet_header, 1, wth->fh);
187         if (bytes_read != sizeof packet_header) {
188                 *err = file_error(wth->fh);
189                 if (*err != 0)
190                         return -1;
191                 return 0;
192         }
193
194         /* check the packet's magic number; the magic number is all 8's,
195            so the byte order doesn't matter */
196         if (packet_header.packet_magic != observer_packet_magic) {
197                 *err = WTAP_ERR_UNSUPPORTED_ENCAP;
198                 *err_info = g_strdup_printf("Observer: unsupported packet version %ul", packet_header.packet_magic);
199                 return -1;
200         }
201
202         /* Check the data link type. */
203         if (packet_header.network_type >= NUM_OBSERVER_ENCAPS) {
204                 *err = WTAP_ERR_UNSUPPORTED_ENCAP;
205                 *err_info = g_strdup_printf("Observer: network type %u unknown or unsupported", packet_header.network_type);
206                 return -1;
207         }
208         wth->file_encap = observer_encap[packet_header.network_type];
209
210         wth->file_type = WTAP_FILE_NETWORK_INSTRUMENTS_V9;
211
212         /* set up the rest of the capture parameters */
213         wth->subtype_read = observer_read;
214         wth->subtype_seek_read = observer_seek_read;
215         wth->subtype_close = NULL;
216         wth->subtype_sequential_close = NULL;
217         wth->snapshot_length = 0;       /* not available in header */
218         wth->tsprecision = WTAP_FILE_TSPREC_NSEC;
219
220         /* reset the pointer to the first packet */
221         if (file_seek(wth->fh, file_header.offset_to_first_packet, SEEK_SET,
222             err) == -1)
223                 return -1;
224         wth->data_offset = file_header.offset_to_first_packet;
225
226         init_time_offset();
227
228         return 1;
229 }
230
231 /* reads the next packet */
232 static gboolean observer_read(wtap *wth, int *err, gchar **err_info,
233     gint64 *data_offset)
234 {
235         int offset;
236         packet_entry_header packet_header;
237
238         /*
239          * Skip records other than data records.
240          */
241         for (;;) {
242                 *data_offset = wth->data_offset;
243
244                 /* process the packet header, including TLVs */
245                 offset = read_packet_header(wth->fh, &packet_header, err,
246                     err_info);
247                 if (offset <= 0)
248                         return FALSE;   /* EOF or error */
249
250                 wth->data_offset += offset;
251
252                 if (packet_header.packet_type == PACKET_TYPE_DATA_PACKET)
253                         break;
254
255                 /* skip to next packet */
256                 packet_header.offset_to_next_packet =
257                     GUINT16_FROM_LE(packet_header.offset_to_next_packet);
258                 if (!skip_to_next_packet(wth, offset,
259                     packet_header.offset_to_next_packet, err, err_info))
260                         return FALSE;   /* EOF or error */
261         }
262
263         /* set-up the packet header */
264         packet_header.network_size =
265             GUINT16_FROM_LE(packet_header.network_size);
266         /* neglect frame markers for wiretap */
267         if (packet_header.network_size < 4) {
268                 *err = WTAP_ERR_BAD_RECORD;
269                 *err_info = g_strdup_printf("Observer: bad record: Packet length %u < 4",
270                     packet_header.network_size);
271                 return FALSE;
272         }
273         packet_header.network_size -= 4;
274         packet_header.captured_size =
275             GUINT16_FROM_LE(packet_header.captured_size);
276         wth->phdr.pkt_encap = observer_encap[packet_header.network_type];
277         wth->phdr.len    = packet_header.network_size;
278         wth->phdr.caplen = MIN(packet_header.captured_size, wth->phdr.len);
279         packet_header.nano_seconds_since_2000 =
280             GUINT64_FROM_LE(packet_header.nano_seconds_since_2000);
281         wth->phdr.ts.secs =
282             (time_t) (packet_header.nano_seconds_since_2000/1000000000 + seconds1970to2000);
283         wth->phdr.ts.nsecs = (int) (packet_header.nano_seconds_since_2000%1000000000);
284
285         /* set-up the packet buffer */
286         buffer_assure_space(wth->frame_buffer, packet_header.captured_size);
287
288         /* read data */
289         if (!read_packet_data(wth->fh, packet_header.offset_to_frame, offset,
290             buffer_start_ptr(wth->frame_buffer), packet_header.captured_size,
291             err, err_info))
292                 return FALSE;
293         wth->data_offset += packet_header.captured_size;
294         offset += packet_header.captured_size;
295
296         /* update the pseudo header */
297         switch (wth->file_encap) {
298
299         case WTAP_ENCAP_ETHERNET:
300                 /* There is no FCS in the frame */
301                 wth->pseudo_header.eth.fcs_len = 0;
302                 break;
303         }
304
305         return TRUE;
306 }
307
308 /* reads a packet at an offset */
309 static gboolean observer_seek_read(wtap *wth, gint64 seek_off,
310     union wtap_pseudo_header *pseudo_header, guchar *pd, int length,
311     int *err, gchar **err_info)
312 {
313         packet_entry_header packet_header;
314         int offset;
315
316         if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
317                 return FALSE;
318
319         /* process the packet header, including TLVs */
320         offset = read_packet_header(wth->random_fh, &packet_header, err,
321             err_info);
322         if (offset <= 0)
323                 return FALSE;   /* EOF or error */
324
325         /* read data */
326         if (!read_packet_data(wth->random_fh, packet_header.offset_to_frame,
327             offset, pd, length, err, err_info))
328                 return FALSE;
329
330         /* update the pseudo header */
331         switch (wth->file_encap) {
332
333         case WTAP_ENCAP_ETHERNET:
334                 /* There is no FCS in the frame */
335                 pseudo_header->eth.fcs_len = 0;
336                 break;
337         }
338
339         return TRUE;
340 }
341
342 static int
343 read_packet_header(FILE_T fh, packet_entry_header *packet_header, int *err,
344     gchar **err_info)
345 {
346         int offset;
347         int bytes_read;
348         guint i;
349         tlv_header tlvh;
350         int seek_increment;
351
352         offset = 0;
353
354         /* pull off the packet header */
355         bytes_read = file_read(packet_header, sizeof *packet_header, 1, fh);
356         if (bytes_read != sizeof *packet_header) {
357                 *err = file_error(fh);
358                 if (*err != 0)
359                         return -1;
360                 return 0;       /* EOF */
361         }
362         offset += bytes_read;
363
364         /* check the packet's magic number; the magic number is all 8's,
365            so the byte order doesn't matter */
366         if (packet_header->packet_magic != observer_packet_magic) {
367                 *err = WTAP_ERR_BAD_RECORD;
368                 *err_info = g_strdup_printf("Observer: bad record: Invalid magic number 0x%08x",
369                     GUINT32_FROM_LE(packet_header->packet_magic));
370                 return -1;
371         }
372
373         /* process extra information */
374         for (i = 0; i < packet_header->number_of_information_elements; i++) {
375                 /* read the TLV header */
376                 bytes_read = file_read(&tlvh, sizeof tlvh, 1, fh);
377                 if (bytes_read != sizeof tlvh) {
378                         *err = file_error(fh);
379                         if (*err == 0)
380                                 *err = WTAP_ERR_SHORT_READ;
381                         return -1;
382                 }
383                 offset += bytes_read;
384
385                 tlvh.length = GUINT16_FROM_LE(tlvh.length);
386                 if (tlvh.length < sizeof tlvh) {
387                         *err = WTAP_ERR_BAD_RECORD;
388                         *err_info = g_strdup_printf("Observer: bad record (TLV length %u < %lu)",
389                             tlvh.length, (unsigned long)sizeof tlvh);
390                         return -1;
391                 }
392
393                 /* skip the TLV data */
394                 seek_increment = tlvh.length - sizeof tlvh;
395                 if (seek_increment > 0) {
396                         if (file_seek(fh, seek_increment, SEEK_CUR, err) == -1)
397                                 return -1;
398                 }
399                 offset += seek_increment;
400         }
401
402         packet_header->offset_to_frame =
403             GUINT16_FROM_LE(packet_header->offset_to_frame);
404
405         return offset;
406 }
407
408 static gboolean
409 read_packet_data(FILE_T fh, int offset_to_frame, int offset, guint8 *pd,
410     int length, int *err, char **err_info)
411 {
412         int seek_increment;
413
414         /* get to the packet data */
415         if (offset_to_frame < offset) {
416                 *err = WTAP_ERR_BAD_RECORD;
417                 *err_info = g_strdup_printf("Observer: bad record (offset to packet data %d < %d)",
418                     offset_to_frame, offset);
419                 return FALSE;
420         }
421         seek_increment = offset_to_frame - offset;
422         if (seek_increment > 0) {
423                 if (file_seek(fh, seek_increment, SEEK_CUR, err) == -1)
424                         return FALSE;
425         }
426
427         /* read in the packet */
428         wtap_file_read_expected_bytes(pd, length, fh, err);
429         return TRUE;
430 }
431
432 static gboolean
433 skip_to_next_packet(wtap *wth, int offset, int offset_to_next_packet, int *err,
434     char **err_info)
435 {
436         int seek_increment;
437
438         if (offset_to_next_packet < offset) {
439                 *err = WTAP_ERR_BAD_RECORD;
440                 *err_info = g_strdup_printf("Observer: bad record (offset to next packet %d < %d)",
441                     offset_to_next_packet, offset);
442                 return FALSE;
443         }
444         seek_increment = offset_to_next_packet - offset;
445         if (seek_increment > 0) {
446                 if (file_seek(wth->fh, seek_increment, SEEK_CUR, err) == -1)
447                         return FALSE;
448         }
449         wth->data_offset += seek_increment;
450         return TRUE;
451 }
452
453 /* Returns 0 if we could write the specified encapsulation type,
454    an error indication otherwise. */
455 int network_instruments_dump_can_write_encap(int encap)
456 {
457         /* Per-packet encapsulations aren't supported. */
458         if (encap == WTAP_ENCAP_PER_PACKET)
459                 return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
460         
461         if (encap < 0 || (unsigned) encap >= NUM_FROM_WTAP_ENCAPS || from_wtap_encap[encap] == OBSERVER_UNDEFINED)
462                 return WTAP_ERR_UNSUPPORTED_ENCAP;
463
464         return 0;
465 }
466
467 /* Returns TRUE on success, FALSE on failure; sets "*err" to an error code on
468    failure */
469 gboolean network_instruments_dump_open(wtap_dumper *wdh, gboolean cant_seek, int *err)
470 {
471         capture_file_header file_header;
472         tlv_header comment_header;
473         char comment[64];
474         struct tm *current_time;
475         time_t system_time;
476
477         if (cant_seek) {
478                 *err = WTAP_ERR_CANT_WRITE_TO_PIPE;
479                 return FALSE;
480         }
481
482         wdh->subtype_write = observer_dump;
483         wdh->subtype_close = observer_dump_close;
484
485         wdh->dump.niobserver = g_malloc(sizeof(niobserver_dump_t));
486         wdh->dump.niobserver->packet_count = 0;
487         wdh->dump.niobserver->network_type = from_wtap_encap[wdh->encap];
488
489         /* create the file comment */
490         time(&system_time);
491         current_time = localtime(&system_time);
492         memset(&comment, 0x00, sizeof(comment));
493         sprintf(comment, "This capture was saved from Wireshark on %s", asctime(current_time));
494
495         /* create the file header */
496         if (fseek(wdh->fh, 0, SEEK_SET) == -1) {
497                 *err = errno;
498                 return FALSE;
499         }
500         memset(&file_header, 0x00, sizeof(capture_file_header));
501         strcpy(file_header.observer_version, network_instruments_magic);
502         file_header.offset_to_first_packet = sizeof(capture_file_header) + sizeof(tlv_header) + strlen(comment);
503         file_header.offset_to_first_packet = GUINT16_TO_LE(file_header.offset_to_first_packet);
504         file_header.number_of_information_elements = 1;
505         if(!fwrite(&file_header, sizeof(capture_file_header), 1, wdh->fh)) {
506                 *err = errno;
507                 return FALSE;
508         }
509
510         /* create the comment entry */
511         comment_header.type = GUINT16_TO_LE(INFORMATION_TYPE_COMMENT);
512         comment_header.length = sizeof(tlv_header) + strlen(comment);
513         comment_header.length = GUINT16_TO_LE(comment_header.length);
514         if(!fwrite(&comment_header, sizeof(tlv_header), 1, wdh->fh)) {
515                 *err = errno;
516                 return FALSE;
517         }
518         if(!fwrite(&comment, sizeof(char), strlen(comment), wdh->fh)) {
519                 *err = errno;
520                 return FALSE;
521         }
522
523         init_time_offset();
524
525         return TRUE;
526 }
527
528 /* Write a record for a packet to a dump file.
529    Returns TRUE on success, FALSE on failure. */
530 static gboolean observer_dump(wtap_dumper *wdh, const struct wtap_pkthdr *phdr,
531     const union wtap_pseudo_header *pseudo_header _U_, const guchar *pd,
532     int *err)
533 {
534         niobserver_dump_t *niobserver = wdh->dump.niobserver;
535         packet_entry_header packet_header;
536         size_t nwritten;
537         guint64 capture_nanoseconds;
538
539         if (phdr->ts.secs < seconds1970to2000) {
540                 if (phdr->ts.secs < 0)
541                         capture_nanoseconds = 0;
542                 else
543                         capture_nanoseconds = phdr->ts.secs;
544         } else
545                 capture_nanoseconds = phdr->ts.secs - seconds1970to2000;
546         capture_nanoseconds = capture_nanoseconds*1000000000 + phdr->ts.nsecs;
547
548         memset(&packet_header, 0x00, sizeof(packet_entry_header));
549         packet_header.packet_magic = GUINT32_TO_LE(observer_packet_magic);
550         packet_header.network_speed = GUINT32_TO_LE(1000000);
551         packet_header.captured_size = GUINT16_TO_LE((guint16)phdr->caplen);
552         packet_header.network_size = GUINT16_TO_LE((guint16)(phdr->len+4));
553         packet_header.offset_to_frame = GUINT16_TO_LE(sizeof(packet_entry_header));
554         packet_header.offset_to_next_packet = GUINT16_TO_LE(sizeof(packet_entry_header) + phdr->caplen);
555         packet_header.network_type = niobserver->network_type;
556         packet_header.flags = 0x00;
557         packet_header.number_of_information_elements = 0;
558         packet_header.packet_type = PACKET_TYPE_DATA_PACKET;
559         packet_header.packet_number = GUINT64_TO_LE(niobserver->packet_count);
560         packet_header.original_packet_number = GUINT64_TO_LE(niobserver->packet_count);
561         niobserver->packet_count++;
562         packet_header.nano_seconds_since_2000 = GUINT64_TO_LE(capture_nanoseconds);
563
564         nwritten = fwrite(&packet_header, sizeof(packet_header), 1, wdh->fh);
565         if (nwritten != 1) {
566                 if (nwritten == 0 && ferror(wdh->fh))
567                         *err = errno;
568                 else
569                         *err = WTAP_ERR_SHORT_WRITE;
570                 return FALSE;
571         }
572
573         nwritten = fwrite(pd, 1, phdr->caplen, wdh->fh);
574         if (nwritten != phdr->caplen) {
575                 if (nwritten == 0 && ferror(wdh->fh))
576                         *err = errno;
577                 else
578                         *err = WTAP_ERR_SHORT_WRITE;
579                 return FALSE;
580         }
581
582         return TRUE;
583 }
584
585 /* just returns TRUE, there is no clean up needed */
586 static gboolean observer_dump_close(wtap_dumper *wdh _U_, int *err _U_)
587 {
588         return TRUE;
589 }