7ffa513cd77157abab5b4322cdbbfa774eb213d1
[metze/wireshark/wip.git] / wiretap / network_instruments.c
1 /*
2  * $Id$
3  */
4
5 /***************************************************************************
6                           network_instruments.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=15.00"};
35 static const int true_magic_length = 17;
36
37 static const guint32 observer_packet_magic = 0x88888888;
38
39 /*
40  * This structure is used to keep state when writing files. An instance is
41  * allocated for each file, and its address is stored in the wtap_dumper.priv
42  * pointer field.
43  */
44 typedef struct {
45     guint64 packet_count;
46     guint8  network_type;
47     guint32 time_format;
48 } observer_dump_private_state;
49
50 /*
51  * Some time offsets are calculated in advance here, when the first Observer
52  * file is opened for reading or writing, and are then used to adjust frame
53  * timestamps as they are read or written.
54  *
55  * The Wiretap API expects timestamps in nanoseconds relative to
56  * January 1, 1970, 00:00:00 GMT (the Wiretap epoch).
57  *
58  * Observer versions before 13.10 encode frame timestamps in nanoseconds
59  * relative to January 1, 2000, 00:00:00 local time (the Observer epoch).
60  * Versions 13.10 and later switch over to GMT encoding. Which encoding was used
61  * when saving the file is identified via the time format TLV following
62  * the file header.
63  *
64  * Unfortunately, even though Observer versions before 13.10 saved in local
65  * time, they didn't include the timezone from which the frames were captured,
66  * so converting to GMT correctly from all timezones is impossible. So an
67  * assumption is made that the file is being read from within the same timezone
68  * that it was written.
69  *
70  * All code herein is normalized to versions 13.10 and later, special casing for
71  * versions earlier. In other words, timestamps are worked with as if
72  * they are GMT-encoded, and adjustments from local time are made only if
73  * the source file warrants it.
74  *
75  * All destination files are saved in GMT format.
76  */
77 static const time_t ansi_to_observer_epoch_offset = 946684800;
78 static time_t gmt_to_localtime_offset = (time_t) -1;
79
80 static void init_gmt_to_localtime_offset(void)
81 {
82     if (gmt_to_localtime_offset == (time_t) -1) {
83         time_t ansi_epoch_plus_one_day = 86400;
84         struct tm gmt_tm;
85         struct tm local_tm;
86
87         /*
88          * Compute the local time zone offset as the number of seconds west
89          * of GMT. There's no obvious cross-platform API for querying this
90          * directly. As a workaround, GMT and local tm structures are populated
91          * relative to the ANSI time_t epoch (plus one day to ensure that
92          * local time stays after 1970/1/1 00:00:00). They are then converted
93          * back to time_t as if they were both local times, resulting in the
94          * time zone offset being the difference between them.
95          */
96         gmt_tm = *gmtime(&ansi_epoch_plus_one_day);
97         local_tm = *localtime(&ansi_epoch_plus_one_day);
98         local_tm.tm_isdst = 0;
99         gmt_to_localtime_offset = mktime(&gmt_tm) - mktime(&local_tm);
100     }
101 }
102
103 static gboolean observer_read(wtap *wth, int *err, gchar **err_info,
104     gint64 *data_offset);
105 static gboolean observer_seek_read(wtap *wth, gint64 seek_off,
106     union wtap_pseudo_header *pseudo_header, guint8 *pd, int length,
107     int *err, gchar **err_info);
108 static int read_packet_header(FILE_T fh, union wtap_pseudo_header *pseudo_header, 
109     packet_entry_header *packet_header, int *err, gchar **err_info);
110 static int read_packet_data(FILE_T fh, int offset_to_frame, int current_offset_from_packet_header,
111     guint8 *pd, int length, int *err, char **err_info);
112 static gboolean skip_to_next_packet(wtap *wth, int offset_to_next_packet,
113     int current_offset_from_packet_header, int *err, char **err_info);
114 static gboolean observer_dump(wtap_dumper *wdh, const struct wtap_pkthdr *phdr,
115     const union wtap_pseudo_header *pseudo_header, const guint8 *pd, int *err);
116 static gint observer_to_wtap_encap(int observer_encap);
117 static gint wtap_to_observer_encap(int wtap_encap);
118
119 int network_instruments_open(wtap *wth, int *err, gchar **err_info)
120 {
121     int bytes_read;
122     int offset;
123     capture_file_header file_header;
124     guint i;
125     tlv_header tlvh;
126     int seek_increment;
127     int header_offset;
128     packet_entry_header packet_header;
129     observer_dump_private_state * private_state = NULL;
130
131     errno = WTAP_ERR_CANT_READ;
132     offset = 0;
133
134     /* read in the buffer file header */
135     bytes_read = file_read(&file_header, sizeof file_header, wth->fh);
136     if (bytes_read != sizeof file_header) {
137         *err = file_error(wth->fh, err_info);
138         if (*err != 0)
139             return -1;
140         return 0;
141     }
142     offset += bytes_read;
143     CAPTURE_FILE_HEADER_FROM_LE_IN_PLACE(file_header);
144
145     /* check if version info is present */
146     if (memcmp(file_header.observer_version, network_instruments_magic, true_magic_length)!=0) {
147         return 0;
148     }
149
150     /* initialize the private state */
151     private_state = (observer_dump_private_state *) g_malloc(sizeof(observer_dump_private_state));
152     private_state->time_format = TIME_INFO_LOCAL;
153     wth->priv = (void *) private_state;
154
155     /* get the location of the first packet */
156     /* v15 and newer uses high byte offset, in previous versions it will be 0 */
157     header_offset = file_header.offset_to_first_packet + ((int)(file_header.offset_to_first_packet_high_byte)<<16);
158
159     /* process extra information */
160     for (i = 0; i < file_header.number_of_information_elements; i++) {
161         /* for safety break if we've reached the first packet */
162         if (offset >= header_offset)
163             break;
164
165         /* read the TLV header */
166         bytes_read = file_read(&tlvh, sizeof tlvh, wth->fh);
167         if (bytes_read != sizeof tlvh) {
168             *err = file_error(wth->fh, err_info);
169             if (*err == 0)
170                 *err = WTAP_ERR_SHORT_READ;
171             return -1;
172         }
173         offset += bytes_read;
174         TLV_HEADER_FROM_LE_IN_PLACE(tlvh);
175
176         if (tlvh.length < sizeof tlvh) {
177             *err = WTAP_ERR_BAD_FILE;
178             *err_info = g_strdup_printf("Observer: bad record (TLV length %u < %lu)",
179                 tlvh.length, (unsigned long)sizeof tlvh);
180             return -1;
181         }
182
183         /* process (or skip over) the current TLV */
184         switch (tlvh.type) {
185         case INFORMATION_TYPE_TIME_INFO:
186             bytes_read = file_read(&private_state->time_format, sizeof private_state->time_format, wth->fh);
187             if (bytes_read != sizeof private_state->time_format) {
188                 *err = file_error(wth->fh, err_info);
189                 if(*err == 0)
190                     *err = WTAP_ERR_SHORT_READ;
191                 return -1;
192             }
193             private_state->time_format = GUINT32_FROM_LE(private_state->time_format);
194             offset += bytes_read;
195             break;
196         default:
197             seek_increment = tlvh.length - (int)sizeof tlvh;
198             if (seek_increment > 0) {
199                 if (file_seek(wth->fh, seek_increment, SEEK_CUR, err) == -1)
200                     return -1;
201             }
202             offset += seek_increment;
203         }
204     }
205
206     /* get to the first packet */
207     if (header_offset < offset) {
208         *err = WTAP_ERR_BAD_FILE;
209         *err_info = g_strdup_printf("Observer: bad record (offset to first packet %d < %d)",
210             header_offset, offset);
211         return FALSE;
212     }
213     seek_increment = header_offset - offset;
214     if (seek_increment > 0) {
215         if (file_seek(wth->fh, seek_increment, SEEK_CUR, err) == -1)
216             return -1;
217     }
218
219     /* pull off the packet header */
220     bytes_read = file_read(&packet_header, sizeof packet_header, wth->fh);
221     if (bytes_read != sizeof packet_header) {
222         *err = file_error(wth->fh, err_info);
223         if (*err != 0)
224             return -1;
225         return 0;
226     }
227     PACKET_ENTRY_HEADER_FROM_LE_IN_PLACE(packet_header);
228
229     /* check the packet's magic number */
230     if (packet_header.packet_magic != observer_packet_magic) {
231         *err = WTAP_ERR_UNSUPPORTED_ENCAP;
232         *err_info = g_strdup_printf("Observer: unsupported packet version %ul", packet_header.packet_magic);
233         return -1;
234     }
235
236     /* check the data link type */
237     if (observer_to_wtap_encap(packet_header.network_type) == WTAP_ENCAP_UNKNOWN) {
238         *err = WTAP_ERR_UNSUPPORTED_ENCAP;
239         *err_info = g_strdup_printf("Observer: network type %u unknown or unsupported", packet_header.network_type);
240         return -1;
241     }
242     wth->file_encap = observer_to_wtap_encap(packet_header.network_type);
243
244     /* set up the rest of the capture parameters */
245     private_state->packet_count = 0;
246     private_state->network_type = wtap_to_observer_encap(wth->file_encap);
247     wth->subtype_read = observer_read;
248     wth->subtype_seek_read = observer_seek_read;
249     wth->subtype_close = NULL;
250     wth->subtype_sequential_close = NULL;
251     wth->snapshot_length = 0;    /* not available in header */
252     wth->tsprecision = WTAP_FILE_TSPREC_NSEC;
253     wth->file_type = WTAP_FILE_NETWORK_INSTRUMENTS;
254
255     /* reset the pointer to the first packet */
256     if (file_seek(wth->fh, header_offset, SEEK_SET,
257         err) == -1)
258         return -1;
259     wth->data_offset = header_offset;
260
261     init_gmt_to_localtime_offset();
262
263     return 1;
264 }
265
266 /* Reads the next packet. */
267 static gboolean observer_read(wtap *wth, int *err, gchar **err_info,
268     gint64 *data_offset)
269 {
270     int bytes_consumed;
271     int offset_from_packet_header = 0;
272     packet_entry_header packet_header;
273
274     /* skip records other than data records */
275     for (;;) {
276         *data_offset = wth->data_offset;
277
278         /* process the packet header, including TLVs */
279         bytes_consumed = read_packet_header(wth->fh, &wth->pseudo_header, &packet_header, err,
280             err_info);
281         if (bytes_consumed <= 0)
282             return FALSE;    /* EOF or error */
283
284         wth->data_offset += bytes_consumed;
285
286         if (packet_header.packet_type == PACKET_TYPE_DATA_PACKET)
287             break;
288
289         /* skip to next packet */
290         offset_from_packet_header = (int) (wth->data_offset - *data_offset);
291         if (!skip_to_next_packet(wth, packet_header.offset_to_next_packet,
292                 offset_from_packet_header, err, err_info)) {
293             return FALSE;    /* EOF or error */
294         }
295     }
296
297     /* neglect frame markers for wiretap */
298     if (packet_header.network_size < 4) {
299         *err = WTAP_ERR_BAD_FILE;
300         *err_info = g_strdup_printf("Observer: bad record: Packet length %u < 4",
301             packet_header.network_size);
302         return FALSE;
303     }
304
305     /* set the wiretap packet header fields */
306     wth->phdr.presence_flags = WTAP_HAS_TS|WTAP_HAS_CAP_LEN;
307     wth->phdr.pkt_encap = observer_to_wtap_encap(packet_header.network_type);
308     if(wth->file_encap == WTAP_ENCAP_FIBRE_CHANNEL_FC2_WITH_FRAME_DELIMS) {
309         wth->phdr.len = packet_header.network_size;
310         wth->phdr.caplen = packet_header.captured_size;
311     } else {
312         wth->phdr.len = packet_header.network_size - 4;
313         wth->phdr.caplen = MIN(packet_header.captured_size, wth->phdr.len);
314     }
315
316     /* set the wiretap timestamp, assuming for the moment that Observer encoded it in GMT */
317     wth->phdr.ts.secs = (time_t) ((packet_header.nano_seconds_since_2000 / 1000000000) + ansi_to_observer_epoch_offset);
318     wth->phdr.ts.nsecs = (int) (packet_header.nano_seconds_since_2000 % 1000000000);
319
320     /* adjust to local time, if necessary, also accounting for DST if the frame
321        was captured while it was in effect */
322     if (((observer_dump_private_state*)wth->priv)->time_format == TIME_INFO_LOCAL)
323     {
324         struct tm daylight_tm;
325         struct tm standard_tm;
326         time_t    dst_offset;
327
328         /* the Observer timestamp was encoded as local time, so add a
329            correction from local time to GMT */
330         wth->phdr.ts.secs += gmt_to_localtime_offset;
331
332         /* perform a DST adjustment if necessary */
333         standard_tm = *localtime(&wth->phdr.ts.secs);
334         if (standard_tm.tm_isdst > 0) {
335             daylight_tm = standard_tm;
336             standard_tm.tm_isdst = 0;
337             dst_offset = mktime(&standard_tm) - mktime(&daylight_tm);
338             wth->phdr.ts.secs -= dst_offset;
339         }
340     }
341
342     /* update the pseudo header */
343     switch (wth->file_encap) {
344     case WTAP_ENCAP_ETHERNET:
345         /* There is no FCS in the frame */
346         wth->pseudo_header.eth.fcs_len = 0;
347         break;
348     case WTAP_ENCAP_IEEE_802_11_WITH_RADIO:
349         /* Updated in read_packet_header */
350         break;
351     }
352
353     /* set-up the packet buffer */
354     buffer_assure_space(wth->frame_buffer, packet_header.captured_size);
355
356     /* read the frame data */
357     offset_from_packet_header = (int) (wth->data_offset - *data_offset);
358     bytes_consumed = read_packet_data(wth->fh, packet_header.offset_to_frame,
359             offset_from_packet_header, buffer_start_ptr(wth->frame_buffer),
360             packet_header.captured_size, err, err_info);
361     if (bytes_consumed < 0) {
362         return FALSE;
363     }
364     wth->data_offset += bytes_consumed;
365
366     /* skip over any extra bytes following the frame data */
367     offset_from_packet_header = (int) (wth->data_offset - *data_offset);
368     if (!skip_to_next_packet(wth, packet_header.offset_to_next_packet,
369             offset_from_packet_header, err, err_info)) {
370         return FALSE;
371     }
372
373     return TRUE;
374 }
375
376 /* Reads a packet at an offset. */
377 static gboolean observer_seek_read(wtap *wth, gint64 seek_off,
378     union wtap_pseudo_header *pseudo_header, guint8 *pd, int length,
379     int *err, gchar **err_info)
380 {
381     packet_entry_header packet_header;
382     int offset;
383
384     if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
385         return FALSE;
386
387     /* process the packet header, including TLVs */
388     offset = read_packet_header(wth->random_fh, pseudo_header, &packet_header, err,
389         err_info);
390     if (offset <= 0)
391         return FALSE;    /* EOF or error */
392
393     /* update the pseudo header */
394     switch (wth->file_encap) {
395
396     case WTAP_ENCAP_ETHERNET:
397         /* There is no FCS in the frame */
398         pseudo_header->eth.fcs_len = 0;
399         break;
400     case WTAP_ENCAP_IEEE_802_11_WITH_RADIO:
401         /* Updated in read_packet_header */
402         break;
403     }
404
405     /* read the frame data */
406     if (!read_packet_data(wth->random_fh, packet_header.offset_to_frame,
407         offset, pd, length, err, err_info))
408         return FALSE;
409
410     return TRUE;
411 }
412
413 static int
414 read_packet_header(FILE_T fh, union wtap_pseudo_header *pseudo_header, 
415     packet_entry_header *packet_header, int *err, gchar **err_info)
416 {
417     int offset;
418     int bytes_read;
419     guint i;
420     tlv_header tlvh;
421     int seek_increment;
422     tlv_wireless_info wireless_header;
423
424     offset = 0;
425
426     /* pull off the packet header */
427     bytes_read = file_read(packet_header, sizeof *packet_header, fh);
428     if (bytes_read != sizeof *packet_header) {
429         *err = file_error(fh, err_info);
430         if (*err != 0)
431             return -1;
432         return 0;    /* EOF */
433     }
434     offset += bytes_read;
435     PACKET_ENTRY_HEADER_FROM_LE_IN_PLACE(*packet_header);
436
437     /* check the packet's magic number */
438     if (packet_header->packet_magic != observer_packet_magic) {
439
440         /*
441          * Some files are zero-padded at the end. There is no warning of this
442          * in the previous packet header information, such as setting
443          * offset_to_next_packet to zero. So detect this situation by treating
444          * an all-zero header as a sentinel. Return EOF when it is encountered,
445          * rather than treat it as a bad record.
446          */
447         for (i = 0; i < sizeof *packet_header; i++) {
448             if (((guint8*) packet_header)[i] != 0)
449                 break;
450         }
451         if (i == sizeof *packet_header) {
452             *err = 0;
453             return 0;    /* EOF */
454         }
455
456         *err = WTAP_ERR_BAD_FILE;
457         *err_info = g_strdup_printf("Observer: bad record: Invalid magic number 0x%08x",
458             packet_header->packet_magic);
459         return -1;
460     }
461
462     /* process extra information */
463     for (i = 0; i < packet_header->number_of_information_elements; i++) {
464         /* read the TLV header */
465         bytes_read = file_read(&tlvh, sizeof tlvh, fh);
466         if (bytes_read != sizeof tlvh) {
467             *err = file_error(fh, err_info);
468             if (*err == 0)
469                 *err = WTAP_ERR_SHORT_READ;
470             return -1;
471         }
472         offset += bytes_read;
473         TLV_HEADER_FROM_LE_IN_PLACE(tlvh);
474
475         if (tlvh.length < sizeof tlvh) {
476             *err = WTAP_ERR_BAD_FILE;
477             *err_info = g_strdup_printf("Observer: bad record (TLV length %u < %lu)",
478                 tlvh.length, (unsigned long)sizeof tlvh);
479             return -1;
480         }
481
482         /* process (or skip over) the current TLV */
483         switch (tlvh.type) {
484         case INFORMATION_TYPE_WIRELESS:
485             bytes_read = file_read(&wireless_header, sizeof wireless_header, fh);
486             if (bytes_read != sizeof wireless_header) {
487                 *err = file_error(fh, err_info);
488                 if(*err == 0)
489                     *err = WTAP_ERR_SHORT_READ;
490                 return -1;
491             }
492             /* update the pseudo header */
493             pseudo_header->ieee_802_11.fcs_len = 0;
494             pseudo_header->ieee_802_11.channel = wireless_header.frequency;
495             pseudo_header->ieee_802_11.data_rate = wireless_header.rate;
496             pseudo_header->ieee_802_11.signal_level = wireless_header.strengthPercent;
497             offset += bytes_read;
498             break;
499         default:
500             /* skip the TLV data */
501             seek_increment = tlvh.length - (int)sizeof tlvh;
502             if (seek_increment > 0) {
503                 if (file_seek(fh, seek_increment, SEEK_CUR, err) == -1)
504                     return -1;
505             }
506             offset += seek_increment;
507         }
508     }
509
510     return offset;
511 }
512
513 static int
514 read_packet_data(FILE_T fh, int offset_to_frame, int current_offset_from_packet_header, guint8 *pd,
515     int length, int *err, char **err_info)
516 {
517     int seek_increment;
518     int bytes_consumed = 0;
519
520     /* validate offsets */
521     if (offset_to_frame < current_offset_from_packet_header) {
522         *err = WTAP_ERR_BAD_FILE;
523         *err_info = g_strdup_printf("Observer: bad record (offset to packet data %d < %d)",
524             offset_to_frame, current_offset_from_packet_header);
525         return -1;
526     }
527
528     /* skip to the packet data */
529     seek_increment = offset_to_frame - current_offset_from_packet_header;
530     if (seek_increment > 0) {
531         if (file_seek(fh, seek_increment, SEEK_CUR, err) == -1) {
532             return -1;
533         }
534         bytes_consumed += seek_increment;
535     }
536
537     /* read in the packet data */
538     wtap_file_read_expected_bytes(pd, length, fh, err, err_info);
539     bytes_consumed += length;
540
541     return bytes_consumed;
542 }
543
544 static gboolean
545 skip_to_next_packet(wtap *wth, int offset_to_next_packet, int current_offset_from_packet_header, int *err,
546     char **err_info)
547 {
548     int seek_increment;
549
550     /* validate offsets */
551     if (offset_to_next_packet < current_offset_from_packet_header) {
552         *err = WTAP_ERR_BAD_FILE;
553         *err_info = g_strdup_printf("Observer: bad record (offset to next packet %d < %d)",
554             offset_to_next_packet, current_offset_from_packet_header);
555         return FALSE;
556     }
557
558     /* skip to the next packet header */
559     seek_increment = offset_to_next_packet - current_offset_from_packet_header;
560     if (seek_increment > 0) {
561         if (file_seek(wth->fh, seek_increment, SEEK_CUR, err) == -1)
562             return FALSE;
563         wth->data_offset += seek_increment;
564     }
565
566     return TRUE;
567 }
568
569 /* Returns 0 if we could write the specified encapsulation type,
570    an error indication otherwise. */
571 int network_instruments_dump_can_write_encap(int encap)
572 {
573     /* per-packet encapsulations aren't supported */
574     if (encap == WTAP_ENCAP_PER_PACKET)
575         return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
576
577     if (encap < 0 || (wtap_to_observer_encap(encap) == OBSERVER_UNDEFINED))
578         return WTAP_ERR_UNSUPPORTED_ENCAP;
579
580     return 0;
581 }
582
583 /* Returns TRUE on success, FALSE on failure; sets "*err" to an error code on
584    failure. */
585 gboolean network_instruments_dump_open(wtap_dumper *wdh, int *err)
586 {
587     observer_dump_private_state * private_state = NULL;
588     capture_file_header file_header;
589
590     tlv_header comment_header;
591     tlv_time_info time_header;
592     char comment[64];
593     size_t comment_length;
594     struct tm * current_time;
595     time_t system_time;
596
597     /* initialize the private state */
598     private_state = (observer_dump_private_state *) g_malloc(sizeof(observer_dump_private_state));
599     private_state->packet_count = 0;
600     private_state->network_type = wtap_to_observer_encap(wdh->encap);
601     private_state->time_format = TIME_INFO_GMT;
602
603     /* populate the fields of wdh */
604     wdh->priv = (void *) private_state;
605     wdh->subtype_write = observer_dump;
606
607     /* initialize the file header */
608     memset(&file_header, 0x00, sizeof(file_header));
609     g_strlcpy(file_header.observer_version, network_instruments_magic, 31);
610     file_header.offset_to_first_packet = (guint16)sizeof(file_header);
611     file_header.offset_to_first_packet_high_byte = 0;
612
613     /* create the file comment TLV */
614     {
615         time(&system_time);
616         current_time = localtime(&system_time);
617         memset(&comment, 0x00, sizeof(comment));
618         g_snprintf(comment, 64, "This capture was saved from Wireshark on %s", asctime(current_time));
619         comment_length = strlen(comment);
620
621         comment_header.type = INFORMATION_TYPE_COMMENT;
622         comment_header.length = (guint16) (sizeof(comment_header) + comment_length);
623
624         /* update the file header to account for the comment TLV */
625         file_header.number_of_information_elements++;
626         file_header.offset_to_first_packet += comment_header.length;
627     }
628
629     /* create the timestamp encoding TLV */
630     {
631         time_header.type = INFORMATION_TYPE_TIME_INFO;
632         time_header.length = (guint16) (sizeof(time_header));
633         time_header.time_format = TIME_INFO_GMT;
634
635         /* update the file header to account for the timestamp encoding TLV */
636         file_header.number_of_information_elements++;
637         file_header.offset_to_first_packet += time_header.length;
638     }
639
640     /* write the file header, swapping any multibyte fields first */
641     CAPTURE_FILE_HEADER_TO_LE_IN_PLACE(file_header);
642     if (!wtap_dump_file_write(wdh, &file_header, sizeof(file_header), err)) {
643         return FALSE;
644     }
645     wdh->bytes_dumped += sizeof(file_header);
646
647     /* write the comment TLV */
648     {
649         TLV_HEADER_TO_LE_IN_PLACE(comment_header);
650         if (!wtap_dump_file_write(wdh, &comment_header, sizeof(comment_header), err)) {
651             return FALSE;
652         }
653         wdh->bytes_dumped += sizeof(comment_header);
654
655         if (!wtap_dump_file_write(wdh, &comment, comment_length, err)) {
656             return FALSE;
657         }
658         wdh->bytes_dumped += comment_length;
659     }
660
661     /* write the time info TLV */
662     {
663         TLV_TIME_INFO_TO_LE_IN_PLACE(time_header);
664         if (!wtap_dump_file_write(wdh, &time_header, sizeof(time_header), err)) {
665             return FALSE;
666         }
667         wdh->bytes_dumped += sizeof(time_header);
668     }
669
670     init_gmt_to_localtime_offset();
671
672     return TRUE;
673 }
674
675 /* Write a record for a packet to a dump file.
676    Returns TRUE on success, FALSE on failure. */
677 static gboolean observer_dump(wtap_dumper *wdh, const struct wtap_pkthdr *phdr,
678     const union wtap_pseudo_header *pseudo_header _U_, const guint8 *pd,
679     int *err)
680 {
681     observer_dump_private_state * private_state = NULL;
682     packet_entry_header           packet_header;
683     guint64                       seconds_since_2000;
684
685     /* convert the number of seconds since epoch from ANSI-relative to
686        Observer-relative */
687     if (phdr->ts.secs < ansi_to_observer_epoch_offset) {
688         if(phdr->ts.secs > (time_t) 0) {
689             seconds_since_2000 = phdr->ts.secs;
690         } else {
691             seconds_since_2000 = (time_t) 0;
692         }
693     } else {
694         seconds_since_2000 = phdr->ts.secs - ansi_to_observer_epoch_offset;
695     }
696
697     /* populate the fields of the packet header */
698     private_state = (observer_dump_private_state *) wdh->priv;
699
700     memset(&packet_header, 0x00, sizeof(packet_header));
701     packet_header.packet_magic = observer_packet_magic;
702     packet_header.network_speed = 1000000;
703     packet_header.captured_size = (guint16) phdr->caplen;
704     packet_header.network_size = (guint16) (phdr->len + 4);
705     packet_header.offset_to_frame = sizeof(packet_header);
706     /* XXX - what if this doesn't fit in 16 bits?  It's not guaranteed to... */
707     packet_header.offset_to_next_packet = (guint16)sizeof(packet_header) + phdr->caplen;
708     packet_header.network_type = private_state->network_type;
709     packet_header.flags = 0x00;
710     packet_header.number_of_information_elements = 0;
711     packet_header.packet_type = PACKET_TYPE_DATA_PACKET;
712     packet_header.packet_number = private_state->packet_count;
713     packet_header.original_packet_number = packet_header.packet_number;
714     packet_header.nano_seconds_since_2000 = seconds_since_2000 * 1000000000 + phdr->ts.nsecs;
715
716     private_state->packet_count++;
717
718     /* write the packet header */
719     PACKET_ENTRY_HEADER_TO_LE_IN_PLACE(packet_header);
720     if (!wtap_dump_file_write(wdh, &packet_header, sizeof(packet_header), err)) {
721         return FALSE;
722     }
723     wdh->bytes_dumped += sizeof(packet_header);
724
725     /* write the packet data */
726     if (!wtap_dump_file_write(wdh, pd, phdr->caplen, err)) {
727         return FALSE;
728     }
729     wdh->bytes_dumped += phdr->caplen;
730
731     return TRUE;
732 }
733
734 static gint observer_to_wtap_encap(int observer_encap)
735 {
736     switch(observer_encap) {
737     case OBSERVER_ETHERNET:
738         return WTAP_ENCAP_ETHERNET;
739     case OBSERVER_TOKENRING:
740         return WTAP_ENCAP_TOKEN_RING;
741     case OBSERVER_FIBRE_CHANNEL:
742         return WTAP_ENCAP_FIBRE_CHANNEL_FC2_WITH_FRAME_DELIMS;
743     case OBSERVER_WIRELESS_802_11:
744         return WTAP_ENCAP_IEEE_802_11_WITH_RADIO;
745     case OBSERVER_UNDEFINED:
746         return WTAP_ENCAP_UNKNOWN;
747     }
748     return WTAP_ENCAP_UNKNOWN;
749 }
750
751 static gint wtap_to_observer_encap(int wtap_encap)
752 {
753     switch(wtap_encap) {
754     case WTAP_ENCAP_ETHERNET:
755         return OBSERVER_ETHERNET;
756     case WTAP_ENCAP_TOKEN_RING:
757         return OBSERVER_TOKENRING;
758     case WTAP_ENCAP_FIBRE_CHANNEL_FC2_WITH_FRAME_DELIMS:
759         return OBSERVER_FIBRE_CHANNEL;
760     case WTAP_ENCAP_UNKNOWN:
761         return OBSERVER_UNDEFINED;
762     }
763     return OBSERVER_UNDEFINED;
764 }