Have the Wiretap open, read, and seek-and-read routines return, in
[metze/wireshark/wip.git] / wiretap / network_instruments.c
1 /*
2  * $Id: network_instruments.c,v 1.7 2004/01/25 21:55:16 guy Exp $
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 #define OBSERVER_UNDEFINED 0xFF
46 #define OBSERVER_ETHERNET  0x00
47 #define OBSERVER_TOKENRING 0x01
48 #define OBSERVER_FDDI      0x02
49 static const int from_wtap_encap[] = {
50         OBSERVER_UNDEFINED,
51         OBSERVER_ETHERNET,
52         OBSERVER_TOKENRING,
53 };
54 #define NUM_FROM_WTAP_ENCAPS (sizeof from_wtap_encap / sizeof observer_encap[0])
55
56 #define CAPTUREFILE_HEADER_SIZE sizeof(capture_file_header)
57
58 #define INFORMATION_TYPE_ALIAS_LIST 0x01
59 #define INFORMATION_TYPE_COMMENT    0x02
60
61 /*
62  * The time in Observer files is in nanoseconds since midnight, January 1,
63  * 2000, 00:00:00 local time.
64  *
65  * We want the seconds portion to be seconds since midnight, January 1,
66  * 1970, 00:00:00 GMT.
67  *
68  * To do that, we add the number of seconds between midnight, January 1,
69  * 2000, 00:00:00 local time and midnight, January 1, 1970, 00:00:00 GMT.
70  * (That gets the wrong answer if the time zone is being read in a different
71  * time zone, but there's not much we can do about that.)
72  */
73 static gboolean have_time_offset;
74 static time_t seconds1970to2000;
75
76 static void init_time_offset(void)
77 {
78         if (!have_time_offset) {
79                 struct tm midnight_2000_01_01;
80
81                 /*
82                  * Get the number of seconds between midnight, January 1,
83                  * 2000, 00:00:00 local time - that's just the UNIX
84                  * time stamp for 2000-01-01 00:00:00 local time.
85                  */
86                 midnight_2000_01_01.tm_year = 2000 - 1900;
87                 midnight_2000_01_01.tm_mon = 0;
88                 midnight_2000_01_01.tm_mday = 1;
89                 midnight_2000_01_01.tm_hour = 0;
90                 midnight_2000_01_01.tm_min = 0;
91                 midnight_2000_01_01.tm_sec = 0;
92                 midnight_2000_01_01.tm_isdst = -1;
93                 seconds1970to2000 = mktime(&midnight_2000_01_01);
94                 have_time_offset = TRUE;
95         }
96 }
97
98 static gboolean fill_time_struct(guint64 ns_since2000, observer_time* time_conversion);
99 static gboolean observer_read(wtap *wth, int *err, gchar **err_info,
100     long *data_offset);
101 static gboolean observer_seek_read(wtap *wth, long seek_off,
102     union wtap_pseudo_header *pseudo_header, guchar *pd, int length,
103     int *err, gchar **err_info);
104 static gboolean observer_dump_close(wtap_dumper *wdh, int *err);
105 static gboolean observer_dump(wtap_dumper *wdh, const struct wtap_pkthdr *phdr,
106     const union wtap_pseudo_header *pseudo_header, const guchar *pd, int *err);
107
108 int network_instruments_open(wtap *wth, int *err, gchar **err_info)
109 {
110         int bytes_read;
111
112         capture_file_header file_header;
113         packet_entry_header packet_header;
114
115         errno = WTAP_ERR_CANT_READ;
116
117         /* Read in the buffer file header */
118         bytes_read = file_read(&file_header, sizeof file_header, 1, wth->fh);
119         if (bytes_read != sizeof file_header) {
120                 *err = file_error(wth->fh);
121                 if (*err != 0)
122                         return -1;
123                 return 0;
124         }
125
126         /* check the magic number */
127         if (memcmp(file_header.observer_version, network_instruments_magic, true_magic_length)!=0) {
128                 return 0;
129         }
130
131         /* check the version */
132         if (strncmp(network_instruments_magic, file_header.observer_version, 30)!=0) {
133                 *err = WTAP_ERR_UNSUPPORTED_ENCAP;
134                 *err_info = g_strdup_printf("Observer: unsupported file version %s", file_header.observer_version);
135                 return -1;
136         }
137
138         /* get to the first packet */
139         file_header.offset_to_first_packet =
140             GUINT16_FROM_LE(file_header.offset_to_first_packet);
141         if (file_seek(wth->fh, file_header.offset_to_first_packet, SEEK_SET,
142             err) == -1) {
143                 if (*err != 0)
144                         return -1;
145                 return 0;
146         }
147
148         /* pull off the packet header */
149         bytes_read = file_read(&packet_header, sizeof packet_header, 1, wth->fh);
150         if (bytes_read != sizeof packet_header) {
151                 *err = file_error(wth->fh);
152                 if (*err != 0)
153                         return -1;
154                 return 0;
155         }
156
157         /* check the packet's magic number; the magic number is all 8's,
158            so the byte order doesn't matter */
159         if (packet_header.packet_magic != observer_packet_magic) {
160                 *err = WTAP_ERR_UNSUPPORTED_ENCAP;
161                 *err_info = g_strdup_printf("Observer: unsupported packet version %ul", packet_header.packet_magic);
162                 return -1;
163         }
164
165         /* Check the data link type. */
166         if (packet_header.network_type >= NUM_OBSERVER_ENCAPS) {
167                 *err = WTAP_ERR_UNSUPPORTED_ENCAP;
168                 *err_info = g_strdup_printf("observer: network type %u unknown or unsupported", packet_header.network_type);
169                 return -1;
170         }
171         wth->file_encap = observer_encap[packet_header.network_type];
172
173         wth->file_type = WTAP_FILE_NETWORK_INSTRUMENTS_V9;
174
175         /* set up the rest of the capture parameters */
176         wth->subtype_read = observer_read;
177         wth->subtype_seek_read = observer_seek_read;
178         wth->subtype_close = NULL;
179         wth->subtype_sequential_close = NULL;
180         wth->snapshot_length = 0;
181
182         /* reset the pointer to the first packet */
183         if (file_seek(wth->fh, file_header.offset_to_first_packet, SEEK_SET,
184             err) == -1) {
185                 if (*err != 0)
186                         return -1;
187                 return 0;
188         }
189         wth->data_offset = file_header.offset_to_first_packet;
190
191         init_time_offset();
192
193         return 1;
194 }
195
196 /* reads the next packet */
197 static gboolean observer_read(wtap *wth, int *err, gchar **err_info,
198     long *data_offset)
199 {
200         int bytes_read;
201         long seek_increment;
202         long seconds, useconds;
203
204         packet_entry_header packet_header;
205         
206         observer_time packet_time;
207
208         *data_offset = wth->data_offset;
209
210         /* pull off the packet header */
211         bytes_read = file_read(&packet_header, sizeof packet_header, 1, wth->fh);
212         if (bytes_read != sizeof packet_header) {
213                 *err = file_error(wth->fh);
214                 if (*err != 0)
215                         return -1;
216                 return 0;
217         }
218         wth->data_offset += bytes_read;
219
220         /* check the packet's magic number; the magic number is all 8's,
221            so the byte order doesn't matter */
222         if (packet_header.packet_magic != observer_packet_magic) {
223                 *err = WTAP_ERR_BAD_RECORD;
224                 *err_info = g_strdup("Observer: bad record");
225                 return FALSE;
226         }
227
228         /* convert from observer time to wiretap time */
229         packet_header.nano_seconds_since_2000 =
230             GUINT64_FROM_LE(packet_header.nano_seconds_since_2000);
231         fill_time_struct(packet_header.nano_seconds_since_2000, &packet_time);
232         useconds = (long)(packet_time.useconds_from_1970 - ((guint64)packet_time.seconds_from_1970)*1000000);
233         seconds = (long)packet_time.seconds_from_1970;
234
235         /* set-up the packet header */
236         packet_header.network_size =
237             GUINT16_FROM_LE(packet_header.network_size);
238         packet_header.captured_size =
239             GUINT16_FROM_LE(packet_header.captured_size);
240         wth->phdr.pkt_encap = observer_encap[packet_header.network_type];
241         wth->phdr.len    = packet_header.network_size-4; /* neglect frame markers for wiretap */
242         wth->phdr.caplen = MIN(packet_header.captured_size, wth->phdr.len);
243         wth->phdr.ts.tv_sec  = seconds;
244         wth->phdr.ts.tv_usec = useconds;
245
246         /* get to the frame data */
247         packet_header.offset_to_frame =
248             GUINT16_FROM_LE(packet_header.offset_to_frame);
249         if (packet_header.offset_to_frame < sizeof(packet_header)) {
250                 *err = WTAP_ERR_BAD_RECORD;
251                 *err_info = g_strdup_printf("Observer: bad record (offset to frame %u < %lu)",
252                     packet_header.offset_to_frame,
253                     (unsigned long)sizeof(packet_header));
254                 return FALSE;
255         }
256         seek_increment = packet_header.offset_to_frame - sizeof(packet_header);
257         if(seek_increment>0) {
258                 if (file_seek(wth->fh, seek_increment, SEEK_CUR, err) == -1)
259                         return FALSE;
260         }
261         wth->data_offset += seek_increment;
262
263         /* set-up the packet buffer */
264         buffer_assure_space(wth->frame_buffer, packet_header.captured_size);
265         wtap_file_read_expected_bytes(buffer_start_ptr(wth->frame_buffer), packet_header.captured_size, wth->fh, err);
266         wth->data_offset += packet_header.captured_size;
267
268         /* update the pseudo header */
269         switch (wth->file_encap) {
270
271         case WTAP_ENCAP_ETHERNET:
272                 /* There is no FCS in the frame */
273                 wth->pseudo_header.eth.fcs_len = 0;
274                 break;
275         }
276
277         return TRUE;
278 }
279
280 /* reads a packet at an offset */
281 static gboolean observer_seek_read(wtap *wth, long seek_off,
282     union wtap_pseudo_header *pseudo_header, guchar *pd, int length,
283     int *err, gchar **err_info)
284 {
285         packet_entry_header packet_header;
286
287         int bytes_read;
288
289         if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
290                 return FALSE;
291
292         /* pull off the packet header */
293         bytes_read = file_read(&packet_header, sizeof packet_header, 1, wth->random_fh);
294         if (bytes_read != sizeof packet_header) {
295                 *err = file_error(wth->fh);
296                 if (*err != 0)
297                         return -1;
298                 return 0;
299         }
300
301         /* check the packets magic number */
302         if (packet_header.packet_magic != observer_packet_magic) {
303                 *err = WTAP_ERR_BAD_RECORD;
304                 *err_info = g_strdup("Observer: bad magic number for record in observer_seek_read");
305                 return FALSE;
306         }
307
308         /* read in the packet */
309         bytes_read = file_read(pd, 1, length, wth->random_fh);
310         if (bytes_read != length) {
311                 *err = file_error(wth->fh);
312                 return FALSE;
313         }
314
315         /* update the pseudo header */
316         switch (wth->file_encap) {
317
318         case WTAP_ENCAP_ETHERNET:
319                 /* There is no FCS in the frame */
320                 pseudo_header->eth.fcs_len = 0;
321                 break;
322         }
323
324         return TRUE;
325 }
326
327 gboolean fill_time_struct(guint64 ns_since2000, observer_time* time_conversion)
328 {
329         time_conversion->ns_since2000 = ns_since2000;
330         time_conversion->us_since2000 = ns_since2000/1000;
331         time_conversion->sec_since2000 = ns_since2000/1000000000;
332
333         time_conversion->seconds_from_1970 = (time_t) (seconds1970to2000 + time_conversion->sec_since2000);
334         time_conversion->useconds_from_1970 = ((guint64)seconds1970to2000*1000000)+time_conversion->us_since2000;
335
336         return TRUE;
337 }
338
339 /* Returns 0 if we could write the specified encapsulation type,
340    an error indication otherwise. */
341 int network_instruments_dump_can_write_encap(int encap)
342 {
343         /* Per-packet encapsulations aren't supported. */
344         if (encap == WTAP_ENCAP_PER_PACKET)
345                 return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
346
347         if (encap < 0 || (unsigned) encap > NUM_FROM_WTAP_ENCAPS || from_wtap_encap[encap] == OBSERVER_UNDEFINED)
348                 return WTAP_ERR_UNSUPPORTED_ENCAP;
349
350         return 0;
351 }
352
353 /* Returns TRUE on success, FALSE on failure; sets "*err" to an error code on
354    failure */
355 gboolean network_instruments_dump_open(wtap_dumper *wdh, gboolean cant_seek, int *err)
356 {
357         capture_file_header file_header;
358         tlv_header comment_header;
359         char comment[64];
360         struct tm *current_time;
361         time_t system_time;
362
363         if (cant_seek) {
364                 *err = WTAP_ERR_CANT_WRITE_TO_PIPE;
365                 return FALSE;
366         }
367
368         wdh->subtype_write = observer_dump;
369         wdh->subtype_close = observer_dump_close;
370
371         wdh->dump.niobserver = g_malloc(sizeof(niobserver_dump_t));
372         wdh->dump.niobserver->packet_count = 0;
373         wdh->dump.niobserver->network_type = from_wtap_encap[wdh->encap];
374
375         /* create the file comment */
376         time(&system_time);
377         current_time = localtime(&system_time);
378         memset(&comment, 0x00, sizeof(comment));
379         sprintf(comment, "This capture was saved from Ethereal on %s", asctime(current_time));
380
381         /* create the file header */
382         if (fseek(wdh->fh, 0, SEEK_SET) == -1) {
383                 *err = errno;
384                 return FALSE;
385         }
386         memset(&file_header, 0x00, sizeof(capture_file_header));
387         strcpy(file_header.observer_version, network_instruments_magic);
388         file_header.offset_to_first_packet = sizeof(capture_file_header) + sizeof(tlv_header) + strlen(comment);
389         file_header.extra_information_present = 0x01; /* actually the number of information elements */
390         if(!fwrite(&file_header, sizeof(capture_file_header), 1, wdh->fh)) {
391                 *err = errno;
392                 return FALSE;
393         }
394
395         /* create the comment entry */
396         comment_header.type = INFORMATION_TYPE_COMMENT;
397         comment_header.length = sizeof(tlv_header) + strlen(comment);
398         if(!fwrite(&comment_header, sizeof(tlv_header), 1, wdh->fh)) {
399                 *err = errno;
400                 return FALSE;
401         }
402         if(!fwrite(&comment, sizeof(char), strlen(comment), wdh->fh)) {
403                 *err = errno;
404                 return FALSE;
405         }
406
407         init_time_offset();
408
409         return TRUE;
410 }
411
412 /* Write a record for a packet to a dump file.
413    Returns TRUE on success, FALSE on failure. */
414 static gboolean observer_dump(wtap_dumper *wdh, const struct wtap_pkthdr *phdr,
415     const union wtap_pseudo_header *pseudo_header _U_, const guchar *pd,
416     int *err)
417 {
418         niobserver_dump_t *niobserver = wdh->dump.niobserver;
419         packet_entry_header packet_header;
420         size_t nwritten;
421         guint64 capture_nanoseconds = 0;
422
423         if(phdr->ts.tv_sec<(long)seconds1970to2000)
424                 if(phdr->ts.tv_sec<0)
425                         capture_nanoseconds = 0;
426                 else
427                         capture_nanoseconds = phdr->ts.tv_sec;
428         else
429                 capture_nanoseconds = phdr->ts.tv_sec - seconds1970to2000;
430         capture_nanoseconds = ((capture_nanoseconds*1000000) + (guint64)phdr->ts.tv_usec)*1000;
431
432         memset(&packet_header, 0x00, sizeof(packet_entry_header));
433         packet_header.packet_magic = GUINT32_TO_LE(observer_packet_magic);
434         packet_header.network_speed = GUINT32_TO_LE(1000000);
435         packet_header.captured_size = GUINT16_TO_LE((guint16)phdr->caplen);
436         packet_header.network_size = GUINT16_TO_LE((guint16)(phdr->len+4));
437         packet_header.offset_to_frame = GUINT16_TO_LE(sizeof(packet_entry_header));
438         packet_header.offset_to_next_packet = GUINT16_TO_LE(sizeof(packet_entry_header) + phdr->caplen);
439         packet_header.network_type = niobserver->network_type;
440         packet_header.flags = 0x00;
441         packet_header.extra_information = 0x00;
442         packet_header.packet_type = 0x00;
443         packet_header.packet_number = GUINT64_TO_LE(niobserver->packet_count);
444         packet_header.original_packet_number = GUINT64_TO_LE(niobserver->packet_count);
445         niobserver->packet_count++;
446         packet_header.nano_seconds_since_2000 = GUINT64_TO_LE(capture_nanoseconds);
447
448         nwritten = fwrite(&packet_header, sizeof(packet_header), 1, wdh->fh);
449         if (nwritten != 1) {
450                 if (nwritten == 0 && ferror(wdh->fh))
451                         *err = errno;
452                 else
453                         *err = WTAP_ERR_SHORT_WRITE;
454                 return FALSE;
455         }
456
457         nwritten = fwrite(pd, 1, phdr->caplen, wdh->fh);
458         if (nwritten != phdr->caplen) {
459                 if (nwritten == 0 && ferror(wdh->fh))
460                         *err = errno;
461                 else
462                         *err = WTAP_ERR_SHORT_WRITE;
463                 return FALSE;
464         }
465
466         return TRUE;
467 }
468
469 /* just returns TRUE, there is no clean up needed */
470 static gboolean observer_dump_close(wtap_dumper *wdh _U_, int *err _U_)
471 {
472         return TRUE;
473 }