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