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