summaryrefslogtreecommitdiffstats
path: root/wiretap
diff options
context:
space:
mode:
Diffstat (limited to 'wiretap')
-rw-r--r--wiretap/.editorconfig142
-rw-r--r--wiretap/5views.c501
-rw-r--r--wiretap/5views.h16
-rw-r--r--wiretap/CMakeLists.txt242
-rw-r--r--wiretap/README186
-rw-r--r--wiretap/README.airmagnet295
-rw-r--r--wiretap/README.developer88
-rw-r--r--wiretap/aethra.c380
-rw-r--r--wiretap/aethra.h17
-rw-r--r--wiretap/ascend-int.h57
-rw-r--r--wiretap/ascend_parser.lemon515
-rw-r--r--wiretap/ascend_scanner.l409
-rw-r--r--wiretap/ascendtext.c483
-rw-r--r--wiretap/ascendtext.h24
-rw-r--r--wiretap/atm.c134
-rw-r--r--wiretap/atm.h26
-rw-r--r--wiretap/autosar_dlt.c359
-rw-r--r--wiretap/autosar_dlt.h39
-rw-r--r--wiretap/ber.c167
-rw-r--r--wiretap/ber.h16
-rw-r--r--wiretap/blf.c2549
-rw-r--r--wiretap/blf.h650
-rw-r--r--wiretap/btsnoop.c455
-rw-r--r--wiretap/btsnoop.h16
-rw-r--r--wiretap/busmaster.c460
-rw-r--r--wiretap/busmaster.h20
-rw-r--r--wiretap/busmaster_parser.lemon455
-rw-r--r--wiretap/busmaster_priv.h136
-rw-r--r--wiretap/busmaster_scanner.l199
-rw-r--r--wiretap/camins.c501
-rw-r--r--wiretap/camins.h21
-rw-r--r--wiretap/candump.c271
-rw-r--r--wiretap/candump.h20
-rw-r--r--wiretap/candump_parser.lemon355
-rw-r--r--wiretap/candump_priv.h69
-rw-r--r--wiretap/candump_scanner.l144
-rw-r--r--wiretap/capsa.c490
-rw-r--r--wiretap/capsa.h18
-rw-r--r--wiretap/catapult_dct2000.c1701
-rw-r--r--wiretap/catapult_dct2000.h23
-rw-r--r--wiretap/commview.c1390
-rw-r--r--wiretap/commview.h20
-rw-r--r--wiretap/cosine.c528
-rw-r--r--wiretap/cosine.h21
-rw-r--r--wiretap/csids.c249
-rw-r--r--wiretap/csids.h18
-rw-r--r--wiretap/daintree-sna.c293
-rw-r--r--wiretap/daintree-sna.h19
-rw-r--r--wiretap/dbs-etherwatch.c670
-rw-r--r--wiretap/dbs-etherwatch.h18
-rw-r--r--wiretap/dct3trace.c442
-rw-r--r--wiretap/dct3trace.h17
-rw-r--r--wiretap/dpa400.c289
-rw-r--r--wiretap/dpa400.h30
-rw-r--r--wiretap/erf-common.h41
-rw-r--r--wiretap/erf.c3640
-rw-r--r--wiretap/erf.h58
-rw-r--r--wiretap/erf_record.h429
-rw-r--r--wiretap/eri_enb_log.c168
-rw-r--r--wiretap/eri_enb_log.h16
-rw-r--r--wiretap/eyesdn.c514
-rw-r--r--wiretap/eyesdn.h31
-rw-r--r--wiretap/file_access.c2994
-rw-r--r--wiretap/file_wrappers.c2236
-rw-r--r--wiretap/file_wrappers.h47
-rw-r--r--wiretap/hcidump.c161
-rw-r--r--wiretap/hcidump.h18
-rw-r--r--wiretap/i4b_trace.h67
-rw-r--r--wiretap/i4btrace.c336
-rw-r--r--wiretap/i4btrace.h19
-rw-r--r--wiretap/introspection-enums.c549
-rw-r--r--wiretap/introspection.c31
-rw-r--r--wiretap/introspection.h36
-rw-r--r--wiretap/ipfix.c364
-rw-r--r--wiretap/ipfix.h18
-rw-r--r--wiretap/iptrace.c909
-rw-r--r--wiretap/iptrace.h18
-rw-r--r--wiretap/iseries.c1108
-rw-r--r--wiretap/iseries.h17
-rw-r--r--wiretap/json.c108
-rw-r--r--wiretap/json.h31
-rw-r--r--wiretap/k12.c1440
-rw-r--r--wiretap/k12.h19
-rw-r--r--wiretap/k12text.l604
-rw-r--r--wiretap/lanalyzer.c1010
-rw-r--r--wiretap/lanalyzer.h17
-rw-r--r--wiretap/libpcap.c1590
-rw-r--r--wiretap/libpcap.h101
-rw-r--r--wiretap/log3gpp.c858
-rw-r--r--wiretap/log3gpp.h16
-rw-r--r--wiretap/logcat.c401
-rw-r--r--wiretap/logcat.h64
-rw-r--r--wiretap/logcat_text.c762
-rw-r--r--wiretap/logcat_text.h40
-rw-r--r--wiretap/merge.c1485
-rw-r--r--wiretap/merge.h212
-rw-r--r--wiretap/mime_file.c229
-rw-r--r--wiretap/mime_file.h17
-rw-r--r--wiretap/mp2t.c434
-rw-r--r--wiretap/mp2t.h19
-rw-r--r--wiretap/mp4.c97
-rw-r--r--wiretap/mp4.h16
-rw-r--r--wiretap/mpeg.c415
-rw-r--r--wiretap/mpeg.h19
-rw-r--r--wiretap/mplog.c294
-rw-r--r--wiretap/mplog.h21
-rw-r--r--wiretap/netmon.c2038
-rw-r--r--wiretap/netmon.h17
-rw-r--r--wiretap/netscaler.c2555
-rw-r--r--wiretap/netscaler.h112
-rw-r--r--wiretap/netscreen.c553
-rw-r--r--wiretap/netscreen.h39
-rw-r--r--wiretap/nettl.c840
-rw-r--r--wiretap/nettl.h121
-rw-r--r--wiretap/nettrace_3gpp_32_423.c789
-rw-r--r--wiretap/nettrace_3gpp_32_423.h17
-rw-r--r--wiretap/netxray.c2223
-rw-r--r--wiretap/netxray.h17
-rw-r--r--wiretap/ngsniffer.c2935
-rw-r--r--wiretap/ngsniffer.h17
-rw-r--r--wiretap/observer.c957
-rw-r--r--wiretap/observer.h255
-rw-r--r--wiretap/packetlogger.c420
-rw-r--r--wiretap/packetlogger.h19
-rw-r--r--wiretap/pcap-common.c2852
-rw-r--r--wiretap/pcap-common.h36
-rw-r--r--wiretap/pcap-encap.h31
-rw-r--r--wiretap/pcapng.c6876
-rw-r--r--wiretap/pcapng.h77
-rw-r--r--wiretap/pcapng_module.h211
-rw-r--r--wiretap/peekclassic.c746
-rw-r--r--wiretap/peekclassic.h19
-rw-r--r--wiretap/peektagged.c968
-rw-r--r--wiretap/peektagged.h16
-rw-r--r--wiretap/pppdump.c838
-rw-r--r--wiretap/pppdump.h18
-rw-r--r--wiretap/radcom.c411
-rw-r--r--wiretap/radcom.h19
-rw-r--r--wiretap/required_file_handlers.h34
-rw-r--r--wiretap/rfc7468.c221
-rw-r--r--wiretap/rfc7468.h27
-rw-r--r--wiretap/rtpdump.c356
-rw-r--r--wiretap/rtpdump.h20
-rw-r--r--wiretap/ruby_marshal.c123
-rw-r--r--wiretap/ruby_marshal.h35
-rw-r--r--wiretap/secrets-types.h24
-rw-r--r--wiretap/snoop.c1013
-rw-r--r--wiretap/snoop.h18
-rw-r--r--wiretap/socketcan.h38
-rw-r--r--wiretap/stanag4607.c246
-rw-r--r--wiretap/stanag4607.h18
-rw-r--r--wiretap/systemd_journal.c276
-rw-r--r--wiretap/systemd_journal.h19
-rw-r--r--wiretap/tnef.c83
-rw-r--r--wiretap/tnef.h20
-rw-r--r--wiretap/toshiba.c469
-rw-r--r--wiretap/toshiba.h18
-rw-r--r--wiretap/visual.c910
-rw-r--r--wiretap/visual.h23
-rw-r--r--wiretap/vms.c575
-rw-r--r--wiretap/vms.h19
-rw-r--r--wiretap/vwr.c3443
-rw-r--r--wiretap/vwr.h17
-rw-r--r--wiretap/wtap-int.h467
-rw-r--r--wiretap/wtap.c2142
-rw-r--r--wiretap/wtap.h2646
-rw-r--r--wiretap/wtap_modules.h50
-rw-r--r--wiretap/wtap_opttypes.c2269
-rw-r--r--wiretap/wtap_opttypes.h1269
169 files changed, 84727 insertions, 0 deletions
diff --git a/wiretap/.editorconfig b/wiretap/.editorconfig
new file mode 100644
index 00000000..004cf195
--- /dev/null
+++ b/wiretap/.editorconfig
@@ -0,0 +1,142 @@
+#
+# Editor configuration
+#
+# https://editorconfig.org/
+#
+
+[5views.[ch]]
+indent_style = tab
+indent_size = tab
+
+[aethra.[ch]]
+indent_style = tab
+indent_size = tab
+
+[atm.[ch]]
+indent_style = tab
+indent_size = tab
+
+[ber.[ch]]
+indent_size = 2
+
+[capsa.[ch]]
+indent_style = tab
+indent_size = tab
+
+[commview.[ch]]
+indent_style = tab
+indent_size = tab
+
+[cosine.[ch]]
+indent_style = tab
+indent_size = tab
+
+[csids.[ch]]
+indent_size = 2
+
+[daintree-sna.[ch]]
+indent_style = tab
+indent_size = tab
+
+[dct3trace.[ch]]
+indent_style = tab
+indent_size = tab
+
+[erf.[ch]]
+indent_size = 2
+
+[eyesdn.[ch]]
+indent_style = tab
+indent_size = tab
+
+[file_access.[ch]]
+indent_style = tab
+indent_size = tab
+
+[hcidump.[ch]]
+indent_style = tab
+indent_size = tab
+
+[i4btrace.[ch]]
+indent_style = tab
+indent_size = tab
+
+[iptrace.[ch]]
+indent_style = tab
+indent_size = tab
+
+[iseries.[ch]]
+indent_size = 2
+
+[lanalyzer.[ch]]
+indent_size = 6
+
+[libpcap.[ch]]
+indent_style = tab
+indent_size = tab
+
+[mime_file.[ch]]
+indent_style = tab
+indent_size = tab
+
+[mpeg.[ch]]
+indent_style = tab
+indent_size = tab
+
+[netmon.[ch]]
+indent_style = tab
+indent_size = tab
+
+[netscreen.[ch]]
+indent_style = tab
+indent_size = tab
+
+[nettrace_3gpp_32_423.[ch]]
+indent_style = tab
+indent_size = tab
+
+[netxray.[ch]]
+indent_style = tab
+indent_size = tab
+
+[ngsniffer.[ch]]
+indent_style = tab
+indent_size = tab
+
+[packetlogger.[ch]]
+indent_style = tab
+indent_size = tab
+
+[pcap-common.[ch]]
+indent_style = tab
+indent_size = tab
+
+[peekclassic.[ch]]
+indent_style = tab
+indent_size = tab
+
+[pppdump.[ch]]
+indent_style = tab
+indent_size = tab
+
+[radcom.[ch]]
+indent_style = tab
+indent_size = tab
+
+[snoop.[ch]]
+indent_style = tab
+indent_size = tab
+
+[stanag4607.[ch]]
+indent_size = 2
+
+[tnef.[ch]]
+indent_size = 2
+
+[toshiba.[ch]]
+indent_style = tab
+indent_size = tab
+
+[wtap.[ch]]
+indent_style = tab
+indent_size = tab
diff --git a/wiretap/5views.c b/wiretap/5views.c
new file mode 100644
index 00000000..c474804a
--- /dev/null
+++ b/wiretap/5views.c
@@ -0,0 +1,501 @@
+/* 5views.c
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "config.h"
+#include <string.h>
+
+#include "wtap-int.h"
+#include "file_wrappers.h"
+#include "5views.h"
+
+
+typedef struct
+{
+ guint32 Signature;
+ guint32 Size; /* Total size of Header in bytes (included Signature) */
+ guint32 Version; /* Identify version and so the format of this record */
+ guint32 DataSize; /* Total size of data included in the Info Record (except the header size) */
+ guint32 FileType; /* Type of the file */
+ guint32 Reserved[3]; /* Reserved for future use */
+}t_5VW_Info_Header;
+
+typedef struct
+{
+ guint32 Type; /* Id of the attribute */
+ guint16 Size; /* Size of the data part of the attribute (not including header size) */
+ guint16 Nb; /* Number of elements */
+}t_5VW_Attributes_Header;
+
+
+#define CST_5VW_INFO_HEADER_KEY 0xAAAAAAAAU /* signature */
+
+#define CST_5VW_INFO_RECORD_VERSION 0x00010000U /* version */
+
+#define CST_5VW_DECALE_FILE_TYPE 24
+#define CST_5VW_SECTION_CAPTURES 0x08U
+#define CST_5VW_CAPTURES_FILE (CST_5VW_SECTION_CAPTURES << CST_5VW_DECALE_FILE_TYPE) /* 0x08000000 */
+#define CST_5VW_FLAT_FILE 0x10000000U
+#define CST_5VW_CAPTURE_FILEID (CST_5VW_FLAT_FILE | CST_5VW_CAPTURES_FILE)
+#define CST_5VW_FAMILY_CAP_ETH 0x01U
+#define CST_5VW_FAMILY_CAP_WAN 0x02U
+#define CST_5VW_DECALE_FILE_FAMILY 12
+#define CST_5VW_CAP_ETH (CST_5VW_FAMILY_CAP_ETH << CST_5VW_DECALE_FILE_FAMILY) /* 0x00001000 */
+#define CST_5VW_CAP_WAN (CST_5VW_FAMILY_CAP_WAN << CST_5VW_DECALE_FILE_FAMILY) /* 0x00002000 */
+#define CST_5VW_CAPTURE_ETH_FILEID (CST_5VW_CAPTURE_FILEID | CST_5VW_CAP_ETH)
+#define CST_5VW_CAPTURE_WAN_FILEID (CST_5VW_CAPTURE_FILEID | CST_5VW_CAP_WAN)
+
+#define CST_5VW_CAPTURE_FILE_TYPE_MASK 0xFF000000U
+
+#define CST_5VW_FRAME_RECORD 0x00000000U
+#define CST_5VW_RECORDS_HEADER_KEY 0x3333EEEEU
+
+typedef struct
+{
+ t_5VW_Info_Header Info_Header;
+ t_5VW_Attributes_Header HeaderDateCreation;
+ guint32 Time;
+ t_5VW_Attributes_Header HeaderNbFrames;
+ guint32 TramesStockeesInFile;
+}t_5VW_Capture_Header;
+
+typedef struct
+{
+ guint32 Key; /* 0x3333EEEE */
+ guint16 HeaderSize; /* Actual size of this header in bytes (32) */
+ guint16 HeaderType; /* Exact type of this header (0x4000) */
+ guint32 RecType; /* Type of record */
+ guint32 RecSubType; /* Subtype of record */
+ guint32 RecSize; /* Size of one record */
+ guint32 RecNb; /* Number of records */
+ guint32 Utc;
+ guint32 NanoSecondes;
+ guint32 RecInfo; /* Info about Alarm / Event / Frame captured */
+}t_5VW_TimeStamped_Header;
+
+
+#define CST_5VW_IA_CAP_INF_NB_TRAMES_STOCKEES 0x20000000U
+#define CST_5VW_IA_DATE_CREATION 0x80000007U /* Struct t_Attrib_Date_Create */
+#define CST_5VW_TIMESTAMPED_HEADER_TYPE 0x4000U
+#define CST_5VW_CAPTURES_RECORD (CST_5VW_SECTION_CAPTURES << 28) /* 0x80000000 */
+#define CST_5VW_SYSTEM_RECORD 0x00000000U
+
+static gboolean _5views_read(wtap *wth, wtap_rec *rec, Buffer *buf, int *err,
+ gchar **err_info, gint64 *data_offset);
+static gboolean _5views_seek_read(wtap *wth, gint64 seek_off, wtap_rec *rec,
+ Buffer *buf, int *err, gchar **err_info);
+static int _5views_read_header(wtap *wth, FILE_T fh, t_5VW_TimeStamped_Header *hdr,
+ wtap_rec *rec, int *err, gchar **err_info);
+
+static gboolean _5views_dump(wtap_dumper *wdh, const wtap_rec *rec, const guint8 *pd, int *err, gchar **err_info);
+static gboolean _5views_dump_finish(wtap_dumper *wdh, int *err, gchar **err_info);
+
+static int _5views_file_type_subtype = -1;
+
+void register_5views(void);
+
+wtap_open_return_val
+_5views_open(wtap *wth, int *err, gchar **err_info)
+{
+ t_5VW_Capture_Header Capture_Header;
+ int encap = WTAP_ENCAP_UNKNOWN;
+
+ if (!wtap_read_bytes(wth->fh, &Capture_Header.Info_Header,
+ sizeof(t_5VW_Info_Header), err, err_info)) {
+ if (*err != WTAP_ERR_SHORT_READ)
+ return WTAP_OPEN_ERROR;
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ /* Check whether that's 5Views format or not */
+ if(Capture_Header.Info_Header.Signature != CST_5VW_INFO_HEADER_KEY)
+ {
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ /* Check Version */
+ Capture_Header.Info_Header.Version =
+ pletoh32(&Capture_Header.Info_Header.Version);
+ switch (Capture_Header.Info_Header.Version) {
+
+ case CST_5VW_INFO_RECORD_VERSION:
+ break;
+
+ default:
+ *err = WTAP_ERR_UNSUPPORTED;
+ *err_info = ws_strdup_printf("5views: header version %u unsupported", Capture_Header.Info_Header.Version);
+ return WTAP_OPEN_ERROR;
+ }
+
+ /* Check File Type */
+ Capture_Header.Info_Header.FileType =
+ pletoh32(&Capture_Header.Info_Header.FileType);
+ if((Capture_Header.Info_Header.FileType & CST_5VW_CAPTURE_FILE_TYPE_MASK) != CST_5VW_CAPTURE_FILEID)
+ {
+ *err = WTAP_ERR_UNSUPPORTED;
+ *err_info = ws_strdup_printf("5views: file is not a capture file (filetype is %u)", Capture_Header.Info_Header.Version);
+ return WTAP_OPEN_ERROR;
+ }
+
+ /* Check possible Encap */
+ switch (Capture_Header.Info_Header.FileType) {
+ case CST_5VW_CAPTURE_ETH_FILEID:
+ encap = WTAP_ENCAP_ETHERNET;
+ break;
+/* case CST_5VW_CAPTURE_WAN_FILEID:
+ break;
+*/
+ default:
+ *err = WTAP_ERR_UNSUPPORTED;
+ *err_info = ws_strdup_printf("5views: network type %u unknown or unsupported",
+ Capture_Header.Info_Header.FileType);
+ return WTAP_OPEN_ERROR;
+ }
+
+ /* read the remaining header information */
+ if (!wtap_read_bytes(wth->fh, &Capture_Header.HeaderDateCreation,
+ sizeof (t_5VW_Capture_Header) - sizeof(t_5VW_Info_Header), err, err_info))
+ return WTAP_OPEN_ERROR;
+
+ /* This is a 5views capture file */
+ wth->file_type_subtype = _5views_file_type_subtype;
+ wth->subtype_read = _5views_read;
+ wth->subtype_seek_read = _5views_seek_read;
+ wth->file_encap = encap;
+ wth->snapshot_length = 0; /* not available in header */
+ wth->file_tsprec = WTAP_TSPREC_NSEC;
+
+ /*
+ * Add an IDB; we don't know how many interfaces were
+ * involved, so we just say one interface, about which
+ * we only know the link-layer type, snapshot length,
+ * and time stamp resolution.
+ */
+ wtap_add_generated_idb(wth);
+
+ return WTAP_OPEN_MINE;
+}
+
+/* Read the next packet */
+static gboolean
+_5views_read(wtap *wth, wtap_rec *rec, Buffer *buf, int *err,
+ gchar **err_info, gint64 *data_offset)
+{
+ t_5VW_TimeStamped_Header TimeStamped_Header;
+
+ /*
+ * Keep reading until we see a record with a subtype of
+ * CST_5VW_FRAME_RECORD.
+ */
+ do
+ {
+ *data_offset = file_tell(wth->fh);
+
+ /* Read record header. */
+ if (!_5views_read_header(wth, wth->fh, &TimeStamped_Header,
+ rec, err, err_info))
+ return FALSE;
+
+ if (TimeStamped_Header.RecSubType == CST_5VW_FRAME_RECORD) {
+ /*
+ * OK, this is a packet.
+ */
+ break;
+ }
+
+ /*
+ * Not a packet - skip to the next record.
+ */
+ if (!wtap_read_bytes(wth->fh, NULL, TimeStamped_Header.RecSize, err, err_info))
+ return FALSE;
+ } while (1);
+
+ if (rec->rec_header.packet_header.caplen > WTAP_MAX_PACKET_SIZE_STANDARD) {
+ /*
+ * Probably a corrupt capture file; don't blow up trying
+ * to allocate space for an immensely-large packet.
+ */
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("5views: File has %u-byte packet, bigger than maximum of %u",
+ rec->rec_header.packet_header.caplen, WTAP_MAX_PACKET_SIZE_STANDARD);
+ return FALSE;
+ }
+
+ return wtap_read_packet_bytes(wth->fh, buf,
+ rec->rec_header.packet_header.caplen, err, err_info);
+}
+
+static gboolean
+_5views_seek_read(wtap *wth, gint64 seek_off, wtap_rec *rec,
+ Buffer *buf, int *err, gchar **err_info)
+{
+ t_5VW_TimeStamped_Header TimeStamped_Header;
+
+ if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
+ return FALSE;
+
+ /*
+ * Read the header.
+ */
+ if (!_5views_read_header(wth, wth->random_fh, &TimeStamped_Header,
+ rec, err, err_info)) {
+ if (*err == 0)
+ *err = WTAP_ERR_SHORT_READ;
+ return FALSE;
+ }
+
+ /*
+ * Read the packet data.
+ */
+ return wtap_read_packet_bytes(wth->random_fh, buf, rec->rec_header.packet_header.caplen,
+ err, err_info);
+}
+
+/* Read the header of the next packet. Return TRUE on success, FALSE
+ on error. */
+static gboolean
+_5views_read_header(wtap *wth, FILE_T fh, t_5VW_TimeStamped_Header *hdr,
+ wtap_rec *rec, int *err, gchar **err_info)
+{
+ /* Read record header. */
+ if (!wtap_read_bytes_or_eof(fh, hdr, (unsigned int)sizeof(t_5VW_TimeStamped_Header),
+ err, err_info))
+ return FALSE;
+
+ hdr->Key = pletoh32(&hdr->Key);
+ if (hdr->Key != CST_5VW_RECORDS_HEADER_KEY) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("5views: Time-stamped header has bad key value 0x%08X",
+ hdr->Key);
+ return FALSE;
+ }
+
+ hdr->RecSubType = pletoh32(&hdr->RecSubType);
+ hdr->RecSize = pletoh32(&hdr->RecSize);
+ hdr->Utc = pletoh32(&hdr->Utc);
+ hdr->NanoSecondes = pletoh32(&hdr->NanoSecondes);
+
+ rec->rec_type = REC_TYPE_PACKET;
+ rec->block = wtap_block_create(WTAP_BLOCK_PACKET);
+ rec->presence_flags = WTAP_HAS_TS;
+ rec->ts.secs = hdr->Utc;
+ rec->ts.nsecs = hdr->NanoSecondes;
+ rec->rec_header.packet_header.caplen = hdr->RecSize;
+ rec->rec_header.packet_header.len = hdr->RecSize;
+
+ switch (wth->file_encap) {
+
+ case WTAP_ENCAP_ETHERNET:
+ /* We assume there's no FCS in this frame. */
+ rec->rec_header.packet_header.pseudo_header.eth.fcs_len = 0;
+ break;
+ }
+
+ return TRUE;
+}
+
+typedef struct {
+ guint32 nframes;
+} _5views_dump_t;
+
+static const int wtap_encap[] = {
+ -1, /* WTAP_ENCAP_UNKNOWN -> unsupported */
+ CST_5VW_CAPTURE_ETH_FILEID, /* WTAP_ENCAP_ETHERNET -> Ethernet */
+};
+#define NUM_WTAP_ENCAPS (sizeof wtap_encap / sizeof wtap_encap[0])
+
+/* Returns 0 if we could write the specified encapsulation type,
+ an error indication otherwise. */
+static int _5views_dump_can_write_encap(int encap)
+{
+ /* Per-packet encapsulations aren't supported. */
+ if (encap == WTAP_ENCAP_PER_PACKET)
+ return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
+
+ if (encap < 0 || (unsigned int) encap >= NUM_WTAP_ENCAPS || wtap_encap[encap] == -1)
+ return WTAP_ERR_UNWRITABLE_ENCAP;
+
+ return 0;
+}
+
+/* Returns TRUE on success, FALSE on failure; sets "*err" to an error code on
+ failure */
+static gboolean _5views_dump_open(wtap_dumper *wdh, int *err, gchar **err_info _U_)
+{
+ _5views_dump_t *_5views;
+
+ /* We can't fill in all the fields in the file header, as we
+ haven't yet written any packets. As we'll have to rewrite
+ the header when we've written out all the packets, we just
+ skip over the header for now. */
+ if (wtap_dump_file_seek(wdh, sizeof(t_5VW_Capture_Header), SEEK_SET, err) == -1)
+ return FALSE;
+
+ /* This is a 5Views file */
+ wdh->subtype_write = _5views_dump;
+ wdh->subtype_finish = _5views_dump_finish;
+ _5views = g_new(_5views_dump_t, 1);
+ wdh->priv = (void *)_5views;
+ _5views->nframes = 0;
+
+ return TRUE;
+}
+
+/* Write a record for a packet to a dump file.
+ Returns TRUE on success, FALSE on failure. */
+static gboolean _5views_dump(wtap_dumper *wdh,
+ const wtap_rec *rec,
+ const guint8 *pd, int *err, gchar **err_info _U_)
+{
+ _5views_dump_t *_5views = (_5views_dump_t *)wdh->priv;
+ t_5VW_TimeStamped_Header HeaderFrame;
+
+ /* We can only write packet records. */
+ if (rec->rec_type != REC_TYPE_PACKET) {
+ *err = WTAP_ERR_UNWRITABLE_REC_TYPE;
+ return FALSE;
+ }
+
+ /*
+ * Make sure this packet doesn't have a link-layer type that
+ * differs from the one for the file.
+ */
+ if (wdh->file_encap != rec->rec_header.packet_header.pkt_encap) {
+ *err = WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
+ return FALSE;
+ }
+
+ /* Don't write out something bigger than we can read. */
+ if (rec->rec_header.packet_header.caplen > WTAP_MAX_PACKET_SIZE_STANDARD) {
+ *err = WTAP_ERR_PACKET_TOO_LARGE;
+ return FALSE;
+ }
+
+ /* Frame Header */
+ /* constant fields */
+ HeaderFrame.Key = GUINT32_TO_LE(CST_5VW_RECORDS_HEADER_KEY);
+ HeaderFrame.HeaderSize = GUINT16_TO_LE(sizeof(t_5VW_TimeStamped_Header));
+ HeaderFrame.HeaderType = GUINT16_TO_LE(CST_5VW_TIMESTAMPED_HEADER_TYPE);
+ HeaderFrame.RecType = GUINT32_TO_LE(CST_5VW_CAPTURES_RECORD | CST_5VW_SYSTEM_RECORD);
+ HeaderFrame.RecSubType = GUINT32_TO_LE(CST_5VW_FRAME_RECORD);
+ HeaderFrame.RecNb = GUINT32_TO_LE(1);
+
+ /* record-dependent fields */
+ /*
+ * XXX - is the frame time signed, or unsigned? If it's signed,
+ * we should check against G_MININT32 and G_MAXINT32 and make
+ * Utc a gint32.
+ */
+ if (rec->ts.secs < 0 || rec->ts.secs > WTAP_NSTIME_32BIT_SECS_MAX) {
+ *err = WTAP_ERR_TIME_STAMP_NOT_SUPPORTED;
+ return FALSE;
+ }
+ HeaderFrame.Utc = GUINT32_TO_LE((guint32)rec->ts.secs);
+ HeaderFrame.NanoSecondes = GUINT32_TO_LE(rec->ts.nsecs);
+ HeaderFrame.RecSize = GUINT32_TO_LE(rec->rec_header.packet_header.len);
+ HeaderFrame.RecInfo = GUINT32_TO_LE(0);
+
+ /* write the record header */
+ if (!wtap_dump_file_write(wdh, &HeaderFrame,
+ sizeof(t_5VW_TimeStamped_Header), err))
+ return FALSE;
+
+ /* write the data */
+ if (!wtap_dump_file_write(wdh, pd, rec->rec_header.packet_header.caplen, err))
+ return FALSE;
+
+ _5views->nframes ++;
+
+ return TRUE;
+}
+
+static gboolean _5views_dump_finish(wtap_dumper *wdh, int *err, gchar **err_info _U_)
+{
+ _5views_dump_t *_5views = (_5views_dump_t *)wdh->priv;
+ t_5VW_Capture_Header file_hdr;
+
+ if (wtap_dump_file_seek(wdh, 0, SEEK_SET, err) == -1)
+ return FALSE;
+
+ /* fill in the Info_Header */
+ file_hdr.Info_Header.Signature = GUINT32_TO_LE(CST_5VW_INFO_HEADER_KEY);
+ file_hdr.Info_Header.Size = GUINT32_TO_LE(sizeof(t_5VW_Info_Header)); /* Total size of Header in bytes (included Signature) */
+ file_hdr.Info_Header.Version = GUINT32_TO_LE(CST_5VW_INFO_RECORD_VERSION); /* Identify version and so the format of this record */
+ file_hdr.Info_Header.DataSize = GUINT32_TO_LE(sizeof(t_5VW_Attributes_Header)
+ + sizeof(guint32)
+ + sizeof(t_5VW_Attributes_Header)
+ + sizeof(guint32));
+ /* Total size of data included in the Info Record (except the header size) */
+ file_hdr.Info_Header.FileType = GUINT32_TO_LE(wtap_encap[wdh->file_encap]); /* Type of the file */
+ file_hdr.Info_Header.Reserved[0] = 0; /* Reserved for future use */
+ file_hdr.Info_Header.Reserved[1] = 0; /* Reserved for future use */
+ file_hdr.Info_Header.Reserved[2] = 0; /* Reserved for future use */
+
+ /* fill in the HeaderDateCreation */
+ file_hdr.HeaderDateCreation.Type = GUINT32_TO_LE(CST_5VW_IA_DATE_CREATION); /* Id of the attribute */
+ file_hdr.HeaderDateCreation.Size = GUINT16_TO_LE(sizeof(guint32)); /* Size of the data part of the attribute (not including header size) */
+ file_hdr.HeaderDateCreation.Nb = GUINT16_TO_LE(1); /* Number of elements */
+
+ /* fill in the Time field */
+#ifdef _WIN32
+ _tzset();
+#endif
+ file_hdr.Time = GUINT32_TO_LE(time(NULL));
+
+ /* fill in the Time field */
+ file_hdr.HeaderNbFrames.Type = GUINT32_TO_LE(CST_5VW_IA_CAP_INF_NB_TRAMES_STOCKEES); /* Id of the attribute */
+ file_hdr.HeaderNbFrames.Size = GUINT16_TO_LE(sizeof(guint32)); /* Size of the data part of the attribute (not including header size) */
+ file_hdr.HeaderNbFrames.Nb = GUINT16_TO_LE(1); /* Number of elements */
+
+ /* fill in the number of frames saved */
+ file_hdr.TramesStockeesInFile = GUINT32_TO_LE(_5views->nframes);
+
+ /* Write the file header. */
+ if (!wtap_dump_file_write(wdh, &file_hdr, sizeof(t_5VW_Capture_Header),
+ err))
+ return FALSE;
+
+ return TRUE;
+}
+
+static const struct supported_block_type _5views_blocks_supported[] = {
+ /*
+ * We support packet blocks, with no comments or other options.
+ */
+ { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
+};
+
+static const struct file_type_subtype_info _5views_info = {
+ "InfoVista 5View capture", "5views", "5vw", NULL,
+ TRUE, BLOCKS_SUPPORTED(_5views_blocks_supported),
+ _5views_dump_can_write_encap, _5views_dump_open, NULL
+};
+
+void register_5views(void)
+{
+ _5views_file_type_subtype = wtap_register_file_type_subtype(&_5views_info);
+
+ /*
+ * Register name for backwards compatibility with the
+ * wtap_filetypes table in Lua.
+ */
+ wtap_register_backwards_compatibility_lua_name("5VIEWS",
+ _5views_file_type_subtype);
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/wiretap/5views.h b/wiretap/5views.h
new file mode 100644
index 00000000..24a069d8
--- /dev/null
+++ b/wiretap/5views.h
@@ -0,0 +1,16 @@
+/** @file
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __5VIEWS_H__
+#define __5VIEWS_H__
+#include <glib.h>
+#include "wtap.h"
+
+wtap_open_return_val _5views_open(wtap *wth, int *err, gchar **err_info);
+
+#endif
diff --git a/wiretap/CMakeLists.txt b/wiretap/CMakeLists.txt
new file mode 100644
index 00000000..7a7b51e1
--- /dev/null
+++ b/wiretap/CMakeLists.txt
@@ -0,0 +1,242 @@
+# CMakeLists.txt
+#
+# Wireshark - Network traffic analyzer
+# By Gerald Combs <gerald@wireshark.org>
+# Copyright 1998 Gerald Combs
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+
+set(WIRETAP_PUBLIC_HEADERS
+ file_wrappers.h
+ introspection.h
+ merge.h
+ pcap-encap.h
+ pcapng_module.h
+ secrets-types.h
+ wtap.h
+ wtap_modules.h
+ wtap_opttypes.h
+)
+
+#
+# Files that implement reading and possibly writing one or more
+# file types. (For cases where more than one source file is
+# used, this should be the one that contains a registration routine.)
+#
+# This does not include libpcap.c and pcapng.c; those are listed below,
+# and we don't scan them to see whether they have registration
+# routines, we *require* them to have registration routines named
+# register_pcap() and register_pcapng(), and directly call those routines.
+#
+set(WIRETAP_C_MODULE_FILES
+ ${CMAKE_CURRENT_SOURCE_DIR}/5views.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/aethra.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/ascendtext.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/atm.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/autosar_dlt.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/ber.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/blf.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/btsnoop.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/busmaster.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/camins.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/candump.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/capsa.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/catapult_dct2000.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/commview.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/cosine.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/csids.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/daintree-sna.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/dbs-etherwatch.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/dpa400.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/dct3trace.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/erf.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/eri_enb_log.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/eyesdn.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/hcidump.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/i4btrace.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/ipfix.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/iptrace.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/iseries.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/json.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/k12.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/lanalyzer.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/log3gpp.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/logcat.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/logcat_text.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/mp4.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/mpeg.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/mplog.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/mime_file.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/mp2t.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/netmon.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/netscaler.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/netscreen.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/nettl.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/nettrace_3gpp_32_423.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/netxray.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/ngsniffer.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/observer.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/packetlogger.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/pcap-common.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/peekclassic.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/peektagged.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/pppdump.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/radcom.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/rfc7468.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/rtpdump.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/ruby_marshal.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/snoop.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/stanag4607.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/systemd_journal.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/tnef.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/toshiba.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/visual.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/vms.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/vwr.c
+)
+
+set(WIRETAP_LEX_MODULE_FILES
+ ${CMAKE_CURRENT_SOURCE_DIR}/k12text.l
+)
+
+#
+# Files to scan for registration routines.
+#
+set(WIRETAP_MODULE_FILES
+ ${WIRETAP_C_MODULE_FILES}
+ ${WIRETAP_LEX_MODULE_FILES}
+)
+
+#
+# C source files that aren't generated.
+#
+set(WIRETAP_NONGENERATED_C_FILES
+ ${WIRETAP_C_MODULE_FILES}
+ ${CMAKE_CURRENT_SOURCE_DIR}/pcapng.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/introspection.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/libpcap.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/file_access.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/file_wrappers.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/merge.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/wtap.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/wtap_opttypes.c
+)
+
+#
+# All C files to compile.
+#
+set(WIRETAP_FILES ${WIRETAP_NONGENERATED_C_FILES} wtap_modules.c)
+
+add_lex_files(LEX_FILES WIRETAP_FILES
+ ${WIRETAP_LEX_MODULE_FILES}
+ ${CMAKE_CURRENT_SOURCE_DIR}/ascend_scanner.l
+ ${CMAKE_CURRENT_SOURCE_DIR}/busmaster_scanner.l
+ ${CMAKE_CURRENT_SOURCE_DIR}/candump_scanner.l
+)
+
+add_lemon_files(LEMON_FILES WIRETAP_FILES
+ ${CMAKE_CURRENT_SOURCE_DIR}/ascend_parser.lemon
+ ${CMAKE_CURRENT_SOURCE_DIR}/busmaster_parser.lemon
+ ${CMAKE_CURRENT_SOURCE_DIR}/candump_parser.lemon
+)
+
+#
+# We pass the arguments to make-regs.py in a file to avoid limitations
+# with the number of arguments handled by main().
+#
+file(GENERATE
+ OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/wtap_modules.in.txt"
+ CONTENT "$<JOIN:${WIRETAP_MODULE_FILES},\n>\n"
+)
+add_custom_command(
+ OUTPUT wtap_modules.c
+ COMMAND ${Python3_EXECUTABLE} ${CMAKE_SOURCE_DIR}/tools/make-regs.py wtap_modules wtap_modules.c @wtap_modules.in.txt
+ DEPENDS ${CMAKE_SOURCE_DIR}/tools/make-regs.py ${WIRETAP_MODULE_FILES}
+ "${CMAKE_CURRENT_BINARY_DIR}/wtap_modules.in.txt"
+ COMMENT "Making wtap_modules.c"
+)
+
+#
+# All files are warning-clean. (Let's keep it that way.)
+#
+set_source_files_properties(
+ ${WIRETAP_NONGENERATED_C_FILES}
+ PROPERTIES
+ COMPILE_FLAGS "${WERROR_COMMON_FLAGS}"
+)
+
+add_library(wiretap
+ ${WIRETAP_FILES}
+ ${CMAKE_BINARY_DIR}/resources/libwiretap.rc
+)
+
+set_target_properties(wiretap PROPERTIES
+ PREFIX "lib"
+ COMPILE_DEFINITIONS "WS_BUILD_DLL"
+ LINK_FLAGS "${WS_LINK_FLAGS}"
+ VERSION "14.1.0" SOVERSION 14
+ FOLDER "DLLs"
+ INSTALL_RPATH "${LIBRARY_INSTALL_RPATH}"
+)
+if(MSVC)
+ set_target_properties(wiretap PROPERTIES LINK_FLAGS_DEBUG "${WS_MSVC_DEBUG_LINK_FLAGS}")
+endif()
+
+target_link_libraries(wiretap
+ PUBLIC
+ wsutil
+ ${GLIB2_LIBRARIES}
+ PRIVATE
+ ${ZLIB_LIBRARIES}
+ ${ZSTD_LIBRARIES}
+ ${LZ4_LIBRARIES}
+)
+
+target_include_directories(wiretap SYSTEM
+ PRIVATE
+ ${ZLIB_INCLUDE_DIRS}
+ ${ZSTD_INCLUDE_DIRS}
+ ${LZ4_INCLUDE_DIRS}
+)
+
+target_include_directories(wiretap PUBLIC
+ $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
+)
+
+install(TARGETS wiretap
+ EXPORT WiresharkTargets
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+install(FILES ${WIRETAP_PUBLIC_HEADERS}
+ DESTINATION "${PROJECT_INSTALL_INCLUDEDIR}/wiretap"
+ COMPONENT "Development"
+ EXCLUDE_FROM_ALL
+)
+
+CHECKAPI(
+ NAME
+ wiretap
+ SWITCHES
+ SOURCES
+ ${WIRETAP_NONGENERATED_FILES}
+# LEX files commented out due to use of malloc, free etc.
+# ${LEX_FILES}
+ ${LEMON_FILES}
+)
+
+#
+# Editor modelines - https://www.wireshark.org/tools/modelines.html
+#
+# Local variables:
+# c-basic-offset: 8
+# tab-width: 8
+# indent-tabs-mode: t
+# End:
+#
+# vi: set shiftwidth=8 tabstop=8 noexpandtab:
+# :indentSize=8:tabSize=8:noTabs=false:
+#
diff --git a/wiretap/README b/wiretap/README
new file mode 100644
index 00000000..7b393ad4
--- /dev/null
+++ b/wiretap/README
@@ -0,0 +1,186 @@
+NOTE: this documents the original intent behind libwiretap. Currently,
+it is being developed solely as a library for reading capture files,
+rather than packet capture. The list of file formats is also
+out-of-date.
+
+Wiretap is a library that is being developed as a future replacement for
+libpcap, the current standard Unix library for packet capturing. Libpcap
+is great in that it is very platform independent and has a wonderful
+BPF optimizing engine. But it has some shortcomings as well. These
+shortcomings came to a head during the development of Wireshark
+(https://www.wireshark.org/), a packet analyzer. As such, I began developing
+wiretap so that:
+
+1. The library can easily be amended with new packet filtering objects.
+Libpcap is very TCP/IP-oriented. I want to filter on IPX objects, SNA objects,
+etc. I also want any decent programmer to be able to add new filters to the
+library.
+
+2. The library can read file formats from many packet-capturing utilities.
+Libpcap only reads Libpcap files.
+
+3. The library can capture on more than one network interface at a time, and
+save this trace in one file.
+
+4. Network names can be resolved immediately after a trace and saved in the
+trace file. That way, I can ship a trace of my firewall-protected network to a
+colleague, and he'll see the proper hostnames for the IP addresses in the
+packet capture, even though he doesn't have access to the DNS server behind my
+LAN's firewall.
+
+5. I want to look into the possibility of compressing packet data when saved
+to a file, like Sniffer.
+
+6. The packet-filter can be optimized for the host OS. Not all OSes have BPF;
+SunOS has NIT and Solaris has DLPI, which both use the CMU/Stanford
+packet-filter pseudomachine. RMON has another type of packet-filter syntax
+which we could support.
+
+Wiretap is very good at reading many file formats, as per #2
+above. Wiretap has no filter capability at present; it currently doesn't
+support packet capture, so it wouldn't be useful there, and filtering
+when reading a capture file is done by Wireshark, using a more powerful
+filtering mechanism than that provided by BPF.
+
+
+File Formats
+============
+
+Libpcap
+-------
+The "libpcap" file format was determined by reading the "libpcap" code;
+wiretap reads the "libpcap" file format with its own code, rather than
+using the "libpcap" library's code to read it.
+
+Sniffer (compressed and uncompressed)
+-------
+The uncompressed Sniffer format is documented in the Sniffer manual.
+Unfortunately, Sniffer manuals tend to document only the format for
+the Sniffer model they document. Token-Ring and ethernet seems to work
+well, though. If you have an ATM Sniffer file, both Guy and Gilbert
+would be *very* interested in receiving a sample. (see 'AUTHORS' file
+for our e-mail addresses).
+
+LANalyzer
+---------
+The LANalyzer format is available from http://www.novell.com. Search
+their knowledge base for "Trace File Format".
+
+Network Monitor
+---------------
+Microsoft's Network Monitor file format is supported, at least under
+Ethernet and token-ring. If you have capture files of other datalink
+types, please send them to Guy.
+
+"snoop"
+-------
+The Solaris 2.x "snoop" program's format is documented in RFC 1761.
+
+"iptrace"
+---------
+This is the capture program that comes with AIX 3.x and 4.x. AIX 3 uses
+the iptrace 1.0 file format, while AIX4 uses iptrace 2.0. iptrace has
+an undocumented, yet very simple, file format. The interesting thing
+about iptrace is that it will record packets coming in from all network
+interfaces; a single iptrace file can contain multiple datalink types.
+
+Sniffer Basic (NetXRay)/Windows Sniffer Pro
+-------------------------------------------
+Network Associates' Sniffer Basic (formerly NetXRay from Cinco Networks)
+file format is now supported, at least for Ethernet and token-ring.
+Network Associates' Windows Sniffer Pro appears to use a variant of that
+format; it's supported to the same extent.
+
+RADCOM WAN/LAN Analyzers
+------------------------
+Olivier Abad has added code to read Ethernet and LAPB captures from
+RADCOM WAN/LAN Analyzers (see https://web.archive.org/web/20031231213434/http://www.radcom-inc.com/).
+
+Lucent/Ascend access products
+-----------------------------
+Gerald
+
+HP-UX nettl
+-----------
+nettl is used on HP-UX to trace various streams based subsystems. Wiretap
+can read nettl files containing IP frames (NS_LS_IP subsystem) and LAPB
+frames (SX25L2 subsystem). It has been tested with files generated on
+HP-UX 9.04 and 10.20.
+Use the following commands to generate a trace :
+# IP capture. 0x30000000 means PDU in and PDU out :
+nettl -tn 0x30000000 -e NS_LS_IP -f tracefile
+# X25 capture. You must specify an interface :
+nettl -tn 0x30000000 -e SX25l2 -d /dev/x25_0 -f tracefile
+# stop capture. subsystem is NS_LS_IP or SX25L2 :
+nettl -tf -e subsystem
+
+One may be able to specify "-tn pduin pduout" rather than
+"-tn 0x30000000"; the nettl man page for HP-UX 10.30 implies that it
+should work.
+
+There is also basic support for nettl files containing NS_LS_DRIVER,
+NS_LS_TCP, NS_LS_UDP, NS_LS_LOOPBACK, unknown type 0xb9, and NS_LS_ICMP.
+However, NS_LS_ICMP will not be decoded since WTAP lacks a raw ICMP
+encapsulation type.
+
+
+Toshiba ISDN Router
+-------------------
+An under-documented command that the router supports in a telnet session
+is "snoop" (not related to the Solaris "snoop" command). If you give
+it the "dump" option (either by letting "snoop" query you for its next
+argument, or typing "snoop dump" on the command line), you'll get a hex
+dump of all packets across the router (except of your own telnet session
+-- good thinking Toshiba!). You can select a certain channel to sniff
+(LAN, B1, B2, D), but the default is all channels. You save this hex
+dump to disk with 'script' or by 'telnet | tee'. Wiretap will read the
+ASCII hex dump and convert it to binary data.
+
+ISDN4BSD "i4btrace" utility
+---------------------------
+Bert Driehuis
+
+Cisco Secure Intrusion Detection System iplogging facility
+-----------------------------------------------------------
+Mike Hall
+
+pppd logs (pppdump-format files)
+--------------------------------
+Gilbert
+
+VMS TCPTRACE
+------------
+Compaq VMS's TCPIPTRACE format is supported. This is the capture program
+that comes with TCP/IP or UCX as supplied by Compaq or Digital Equipment
+Corporation.
+
+Under UCX 4.x, it is invoked as TCPIPTRACE. Under TCPIP 5.x, it is invoked
+as TCPTRACE.
+
+TCPTRACE produces an ascii text based format, that has changed slightly over
+time.
+
+DBS Etherwatch (text format)
+----------------------------
+Text output from DBS Etherwatch is supported. DBS Etherwatch is available
+from: https://web.archive.org/web/20070612033348/http://www.users.bigpond.com/dbsneddon/software.htm.
+
+Catapult DCT2000 (.out files)
+-----------------------------
+DCT2000 test systems produce ascii text-based .out files for ports
+that have logging enabled. When being read, the data part of the message is
+prefixed with a short header that provides some context (context+port,
+direction, original timestamp, etc).
+
+You can choose to suppress the reading of non-standard protocols
+(i.e. messages between layers rather than the well-known link-level protocols
+usually found on board ports).
+
+
+Gilbert Ramirez <gram@alumni.rice.edu>
+Guy Harris <guy@alum.mit.edu>
+
+STANAG 4607
+-----------
+Initial support for the STANAG 4607 protocol. Documentation at:
+https://web.archive.org/web/20130223054955/http://www.nato.int/structur/AC/224/standard/4607/4607.htm
diff --git a/wiretap/README.airmagnet b/wiretap/README.airmagnet
new file mode 100644
index 00000000..084ad24f
--- /dev/null
+++ b/wiretap/README.airmagnet
@@ -0,0 +1,295 @@
+AMC: Wireless Analyzer Captured Data (AirMagnet)
+------------------------------------------------
+This is just a braindump from looking at some Airmagnet capture files,
+in one case having a look at a decoded display in Airmagnet itself.
+Lots of things are still unknown.
+This is NOT the intention to write a file importer in the foreseeable
+future.
+
+Exact timestamp decoding still unknown:
+From a different decoded example file:
+06 51 49 1b Timestamp (105990427) = 03:30:27.497544
+06 51 4a cd Timestamp (105990861) = 03:30:27.497978 (+434)
+06 51 4b ce Timestamp (105991118) = 03:30:27.498235 (+257)
+
+Timestamps this file:
+Frame1: 15a5 2fb4 363147188 -
+Frame2: 15a5 3a8e 363149966 + 1778
+Frame3: 15a5 3c50 363150416 + 550
+Frame4: 15a5 487d 363153533
+Frame5: 15a5 49d9 363153881
+Frame6: 15a5 4bcf 363154383
+Frame7: 15a5 53da 363156442
+Frame8: 15a5 59e4 363157988
+Frame9: 15cc 9da8 365731240
+FrameA: 15db dd60 366730592
+FrameB: 15dc 04f6 366740726
+FrameC: 15df 5d29 366959913
+
+Unknown stuff:
+Header: 0000 0000 0002 | 3b93 d886
+Frame1: da9d | 0000 0000 0002
+Frame2: bf9c | 0000 0000 0002
+Frame3: d59c | 0000 0000 0002
+Frame4: c09c | 0000 0000 0002
+Frame5: c09c | 0000 0000 0002
+Frame6: d69c | 0000 0000 0002
+Frame7: d79c | 0000 0000 0002
+Frame8: c09c | 0000 0000 0002
+Frame9: d79c | 0000 0000 0002
+FrameA: d89c | 0000 0000 0002
+FrameB: d69d | 0000 0000 0002
+FrameC: d79c | 0000 0000 0002
+
+Headerstructure:
+ 6 Bytes: 'N2GINC'
+44 Bytes: unknown (0x0)
+ 6 Bytes: 'N2GINC'
+ 2 Bytes: unknown (0x0)
+ 2 Bytes: #Frames (BE)
+ 2 Bytes: unknown (0x0)
+ 2 Bytes: unknown (0x0002)
+ 4 Bytes: Timestamp
+ 4 Bytes: unknown (Timestamp date part ?)
+28 Bytes: unknown (0x0)
+==================
+Total: 100 Bytes
+
+Recordstructure:
+ 2 Bytes: unknown
+ 2 Bytes: Bytes "on wire" (BE)
+ 2 Bytes: LL Bytes in capturefile (BE)
+ 4 Bytes: unknown (0x0) (02 00 00 00 = Frame displayed in RED)
+ 2 Bytes: unknown (0x0002)
+ 4 Bytes: Timestamp
+ 1 Byte: DataRate ((AND 7f) / 2 in Mbit/s)
+ 1 Byte: Channel (1-13: Chan 1-13, 15: Chan 36, 16: Chan 40, ..., 19: Chan 52, ...)
+ 1 Byte: SignalStrength (%)
+ 1 Byte: NoiseLevel (%)
+LL Bytes: Capturedata
+ 6 Bytes: unknown
+ 4 Bytes: 0x33333333
+(1 Byte: 0x33) in case LL is an odd number
+
+Filelength: 0x57e Bytes
+12 Frames
+============== Header ===================
+0000000: 4e32 4749 4e43 0000 0000 0000 0000 0000
+0000010: 0000 0000 0000 0000 0000 0000 0000 0000
+0000020: 0000 0000 0000 0000 0000 0000 0000 0000
+0000030: 0000 4e32 4749 4e43 0000 000c 0000 0002
+0000040: 1545 5198 3b93 d886 0000 0000 0000 0000
+0000050: 0000 0000 0000 0000 0000 0000 0000 0000
+0000060: 0000 0000
+============== Frame 1 ==================
+Length: 0x36
+-------- ???
+0000064: da9d
+0000066: 0026 0026
+000006a: 0000 0000 0002
+0000070: 15a5 2fb4 Timestamp
+0000074: 02 DataRate
+0000075: 06 Channel
+0000076: 64 SignalStrength (%)
+0000077: 01 NoiseLevel (%)
+-------- ieee 802.11
+0000078: b000 Type/Subtype/Frame Control
+000007a: 3a01 Duration
+000007c: 0040 9647 71a3 Destaddr
+0000082: 0040 9631 d395 Sourceaddr
+0000088: 0040 9647 71a3 BSS Id
+000008e: b000 Fragment nr/Seq nr
+-------- ???
+0000090: 0000 0100 0000 3333 3333
+============== Frame 2===================
+Length: 0x36
+-------- ???
+000009a: bf9c 0026 0026
+00000a0: 0000 0000 0002 15a5 3a8e 1606 3c00
+-------- ieee 802.11
+00000ae: b000 Type/Subtype/Frame Control
+00000b0: 7500
+00000b2: 0040 9631 d395
+00000b8: 0040 9647 71a3
+00000be: 0040 9647 71a3
+00000c4: 806d
+-------- ???
+00000c6: 0000 0200 0000 3333 3333
+============== Frame 3 ==================
+Length: 0x62
+-------- ???
+00000d0: d59c 0051 0051
+00000d6: 0000 0000 0002 15a5 3c50 0206 6400
+-------- ieee 802.11
+00000e4: 0000
+00000e6: 3a01
+00000e8: 0040 9647 71a3
+00000ee: 0040 9631 d395
+00000f4: 0040 9647 71a3
+00000fa: c000
+-------- ???
+00000fc: 2100 c800
+0000100: 0005 616c 616d 6f01 0402 040b 1685 1e00
+0000110: 016d 0d1f 00ff 0318 0049 6e73 7472 7563
+0000120: 746f 7200 0000 00
+-------- ???
+0000127: 00 0000 0000 2133 3333 3333
+================= Frame 4 ===============
+Length: 0x68
+-------- ???
+0000132: c09c 0058 0058
+0000138: 0000 0000 0002 15a5 487d 1606 3e00
+-------- ieee 802.11
+000014a: 1000
+000014c: 7500
+000014e: 0040 9631 d395
+0000154: 0040 9647 71a3
+000015a: 0040 9647 71a3
+000015c: 906d
+-------- ???
+000015e: 2100
+0000160: 0000 1d00 0104 8284 8b96 851c 0000 4c0d
+0000170: 0000 0000 0100 416c 616d 6f20 436c 6173
+0000180: 7372 6f6f 6d20 0002 880c 80f3 0300 8137
+-------- ???
+0000190: 0300 0000 0000 3333 3333
+=================== Frame 5 =============
+-------- ???
+000019a: c09c 005a 005a
+00001a0: 0000 0000 0002 15a5 49d9 1606 3e00
+-------- ieee 802.11
+00001ae: 0802
+00001b0: 7500 0040 9631 d395 0040 9647 71a3 0040
+00001c0: 9647 71a3 a06d aaaa 0300 4096 0000 0032
+00001d0: 4001 0040 9631 d395 0040 9647 71a3 0100
+00001e0: 0000 0000 0000 0000 0000 0000 0000 0000
+00001f0: 0000 0000 0000 0000 0000
+-------- ???
+00001fa: 0000 0000 0000 3333 3333
+=================== Frame 6 =============
+-------- ???
+0000204: d69c 0072 0072
+000020a: 0000 0000 0002 15a5 4bcf 0206 6400
+-------- ieee 802.11
+0000218: 0801 3a01 0040 9647
+0000220: 71a3 0040 9631 d395 0040 9647 71a3 d000
+0000230: aaaa 0300 4096 0000 004a 4081 0040 9647
+0000240: 71a3 0040 9631 d395 016d 0004 1900 0040
+0000250: 9647 71a3 0000 0000 0000 0000 0000 0000
+0000260: 0000 001e 496e 7374 7275 6374 6f72 0000
+0000270: 0000 0000 0000 0000 0000 0000
+-------- ???
+000027c: 0000 0000 0000 3333 3333
+=================== Frame 7 =============
+-------- ???
+0000286: d79c 0039 0039
+000028c: 0000 0000 0002 15a5 53da 0206 6400
+-------- ieee 802.11
+000029a: 0801 3a01 0040
+00002a0: 9647 71a3 0040 9631 d395 0040 9647 71a3
+00002b0: e000 aaaa 0300 4096 0000 0011 4001 0040
+00002c0: 9647 71a3 00
+00002c5: 40 9631 d395 0133 3333 3333
+====================== Frame 8 ==========
+-------- ???
+00002d0: c09c 0072 0072
+00002d6: 0000 0000 0002 15a5 59e4 1606 3e00
+-------- ieee 802.11
+00002e4: 0802 7500 0040 9631 d395 0040
+00002f0: 9647 71a3 0040 9647 71a3 b06d aaaa 0300
+0000300: 4096 0000 004a 4081 0040 9631 d395 0040
+0000310: 9647 71a3 014c 030b 1500 0000 0000 0000
+0000320: 0000 0000 0000 0002 0000 0a00 0001 0000
+0000330: 416c 616d 6f20 436c 6173 7372 6f6f 6d00
+0000340: 0000 0000 0000 0000
+0000348: 0000 0000 2200 3333 3333
+=================== Frame 9 =============
+-------- ???
+0000352: d79c 0170 00a0
+0000358: 0020 0000 0002 15cc 9da8 1606 6400
+-------- ieee 802.11
+0000366: 0801 7500 0040 9647 71a3
+0000370: 0040 9631 d395 ffff ffff ffff f000 aaaa
+0000380: 0300 0000 0800 4500 0148 42a1 0000 8011
+0000390: f704 0000 0000 ffff ffff 0044 0043 0134
+00003a0: d991 0101 0600 7728 b62a 0000 0000 0000
+00003b0: 0000 0000 0000 0000 0000 0000 0000 0040
+00003c0: 9631 d395 0000 0000 0000 0000 0000 0000
+00003d0: 0000 0000 0000 0000 0000 0000 0000 0000
+00003e0: 0000 0000 0000 0000 0000 0000 0000 0000
+00003f0: 0000 0000 0000 0000
+-------- ???
+00003f8: 0000 0000 0000 3333 3333
+=================== Frame A =============
+-------- ???
+0000402: d89c 0182 00a0
+0000408: 0020 0000 0002 15db dd60 1606 6400
+-------- ieee 802.11
+0000416: 0801 7500
+000041a: 0040 9647 71a3
+0000420: 0040 9631 d395
+0000426: ffff ffff ffff
+000042c: 0001
+-------- LLC
+000042e: aaaa 0300 0000 0800
+-------- IP
+0000436: 4500 015a 42a3 0000 8011 f6f0 0000 0000 ffff ffff
+-------- UDP
+000044a: 0044 0043 0146 57bc
+-------- DHCP
+0000452: 0101 0600 7728 b62a 0000 0000 0000
+0000460: 0000 0000 0000 0000 0000 0000 0000 0040
+0000470: 9631 d395 0000 0000 0000 0000 0000 0000
+0000480: 0000 0000 0000 0000 0000 0000 0000 0000
+0000490: 0000 0000 0000 0000 0000 0000 0000 0000
+00004a0: 0000 0000 0000 0000
+-------- ???
+00004a8: 0000 0000 0000 3333 3333
+=================== Frame B =============
+-------- ???
+00004b2: d69d 0056 0056
+00004b8: 0000 0000 0002 15dc 04f6 1606 6401
+-------- ieee 802.11
+00004c6: 0801
+00004c8: 7500
+00004ca: 0040 9647 71a3
+00004d0: 0040 9631 d395
+00004d6: ffff ffff ffff
+0000edc: 1001
+-------- LLC
+00004de: aaaa 0300 0000 0806
+-------- ARP
+00004e6: 0001 0800 0604 0001
+00004ee: 0040 9631 d395 0a00 0065
+00004f8: 0000 0000 0000 0a00 0065
+-------- ???
+0000502: 7b00 e097 7b00 e097 7b00 e097
+000050e: 7b00 e097 7b00 3333 3333
+=================== Frame C =============
+-------- ???
+0000518: d79c 0056 0056
+000051e: 0000 0000 0002 15df 5d29 1606 6400
+-------- ieee 802.11
+000052c: 0801
+000052e: 7500
+0000530: 0040 9647 71a3
+0000536: 0040 9631 d395
+000053a: ffff ffff ffff
+0000540: 2001
+-------- LLC
+0000542: aaaa 0300 0000 0806
+-------- ARP
+000054a: 0001 Hw
+000054c: 0800 Protocol
+0000550: 06 Hw-Size
+0000551: 04 Protocolsize
+0000552: 0001 Opcode
+0000554: 0040 9631 d395 Sender MAC
+000055a: 0a00 0065 Sender IP
+000055e: 0000 0000 0000 Destination MAC
+0000564: 0a00 0065 Destination IP
+-------- ???
+0000568: 0000 8602 0000 ffff ffff 0cc3
+0000574: 4b82 58a1 1d82 3333 3333
+=========================================
+000057e: EOF
diff --git a/wiretap/README.developer b/wiretap/README.developer
new file mode 100644
index 00000000..6552ad18
--- /dev/null
+++ b/wiretap/README.developer
@@ -0,0 +1,88 @@
+This is a very quick and very dirty guide to adding support for new
+capture file formats. If you see any errors or have any improvements,
+submit patches - free software is a community effort....
+
+To add the ability to read a new capture file format, you have to:
+
+ write an "open" routine that can read the beginning of the
+ capture file and figure out if it's in that format or not,
+ either by looking at a magic number at the beginning or by using
+ some form of heuristic to determine if it's a file of that type
+ (if the file format has a magic number, that's what should be
+ used);
+
+ write a "read" routine that can read a packet from the file and
+ supply the packet length, captured data length, time stamp, and
+ packet pseudo-header (if any) and data, and have the "open"
+ routine set the "subtype_read" member of the "wtap" structure
+ supplied to it to point to that routine;
+
+ write a "seek and read" routine that can seek to a specified
+ location in the file for a packet and supply the packet
+ pseudo-header (if any) and data, and have the "open" routine set
+ the "subtype_seek_read" member of the "wtap" structure to point
+ to that routine;
+
+ write a "close" routine, if necessary (if, for example, the
+ "open" routine allocates any memory), and set the
+ "subtype_close" member of the "wtap" structure to point to it,
+ otherwise leave it set to NULL;
+
+ add an entry for that file type in the "open_info_base[]" table
+ in "wiretap/file_access.c", giving a pointer to the "open" routine,
+ a name to use in file open dialogs, whether the file type uses a
+ magic number or a heuristic, and if it uses a heuristic, file
+ extensions for which the heuristic should be tried first. More
+ discriminating and faster heuristics should be put before less
+ accurate and slower heuristics;
+
+ add a registration routine passing a "file_type_subtype_info" struct
+ to wtap_register_file_type_subtypes(), giving a descriptive name, a
+ short name that's convenient to type on a command line (no blanks or
+ capital letters, please), common file extensions to open and save,
+ any block types supported, and pointers to the "can_write_encap" and
+ "dump_open" routines if writing that file is supported (see below),
+ otherwise just null pointers.
+
+Wiretap applications typically first perform sequential reads through
+the capture file and may later do "seek and read" for individual frames.
+The "read" routine should set the variable data_offset to the byte
+offset within the capture file from which the "seek and read" routine
+will read. If the capture records consist of:
+
+ capture record header
+ pseudo-header (e.g., for ATM)
+ frame data
+
+then data_offset should point to the pseudo-header. The first
+sequential read pass will process and store the capture record header
+data, but it will not store the pseudo-header. Note that the
+seek_and_read routine should work with the "random_fh" file handle
+of the passed in wtap struct, instead of the "fh" file handle used
+in the normal read routine.
+
+To add the ability to write a new capture file format, you have to:
+
+ add a "can_write_encap" routine that returns an indication of
+ whether a given packet encapsulation format is supported by the
+ new capture file format;
+
+ add a "dump_open" routine that starts writing a file (writing
+ headers, allocating data structures, etc.);
+
+ add a "dump" routine to write a packet to a file, and have the
+ "dump_open" routine set the "subtype_write" member of the
+ "wtap_dumper" structure passed to it to point to it;
+
+ add a "dump_close" routine, if necessary (if, for example, the
+ "dump_open" routine allocates any memory, or if some of the file
+ header can be written only after all the packets have been
+ written), and have the "dump_open" routine set the
+ "subtype_close" member of the "wtap_dumper" structure to point
+ to it;
+
+ put pointers to the "can_write_encap" and "dump_open" routines
+ in the "file_type_subtype_info" struct passed to
+ wtap_register_file_type_subtypes().
+
+In the wiretap directory, add your source file to CMakelists.txt.
diff --git a/wiretap/aethra.c b/wiretap/aethra.c
new file mode 100644
index 00000000..3216e74e
--- /dev/null
+++ b/wiretap/aethra.c
@@ -0,0 +1,380 @@
+/* aethra.c
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "config.h"
+#include <string.h>
+#include "wtap-int.h"
+#include "file_wrappers.h"
+#include "aethra.h"
+
+/* Magic number in Aethra PC108 files. */
+#define MAGIC_SIZE 5
+
+static const guchar aethra_magic[MAGIC_SIZE] = {
+ 'V', '0', '2', '0', '8'
+};
+
+/* Aethra file header. */
+struct aethra_hdr {
+ guchar magic[MAGIC_SIZE];
+ guint8 unknown1[39]; /* 5-43 */
+ guchar sw_vers[60]; /* 44-103 - software version string, not null-terminated */
+ guint8 unknown2[118]; /* 104-221 */
+ guint8 start_sec; /* 222 - seconds of capture start time */
+ guint8 start_min; /* 223 - minutes of capture start time */
+ guint8 start_hour; /* 224 - hour of capture start time */
+ guint8 unknown3[462]; /* 225-686 */
+ guchar xxx_string[37]; /* 687-723 - null-terminated short comment string? */
+ guint8 unknown3_5[4]; /* 724-727 */
+ guchar yyy_string[4504];/* 728-5231 - null-terminated long comment string? */
+ guint8 start_year[2]; /* 5232-5233 - year of capture start date */
+ guint8 start_month[2]; /* 5234-5235 - month of capture start date */
+ guint8 unknown4[2]; /* 5236-5237 */
+ guint8 start_day[2]; /* 5238-5239 - day of capture start date */
+ guint8 unknown5[8]; /* 5240-5247 */
+ guchar com_info[16]; /* 5248-5263 - COM port and speed, null-padded(?) */
+ guint8 unknown6[107]; /* 5264-5370 */
+ guchar xxx_vers[41]; /* 5371-5411 - unknown version string (longer, null-padded?) */
+};
+
+/* Aethra record header. Yes, the alignment is weird.
+ All multi-byte fields are little-endian. */
+struct aethrarec_hdr {
+ guint8 rec_size[2]; /* record length, not counting the length itself */
+ guint8 rec_type; /* record type */
+ guint8 timestamp[4]; /* milliseconds since start of capture */
+ guint8 flags; /* low-order bit: 0 = N->U, 1 = U->N */
+};
+
+/*
+ * Record types.
+ *
+ * As the indications from the device and signalling messages appear not
+ * to have the 8th bit set, and at least some B-channel records do, we
+ * assume, for now, that the 8th bit indicates bearer information.
+ *
+ * 0x9F is the record type seen for B31 channel records; that might be
+ * 0x80|31, so, for now, we assume that if the 8th bit is set, the B
+ * channel number is in the low 7 bits.
+ */
+#define AETHRA_BEARER 0x80 /* bearer information */
+
+#define AETHRA_DEVICE 0x00 /* indication from the monitoring device */
+#define AETHRA_ISDN_LINK 0x01 /* information from the ISDN link */
+
+/*
+ * In AETHRA_DEVICE records, the flags field has what appears to
+ * be a record subtype.
+ */
+#define AETHRA_DEVICE_STOP_MONITOR 0x00 /* Stop Monitor */
+#define AETHRA_DEVICE_START_MONITOR 0x04 /* Start Monitor */
+#define AETHRA_DEVICE_ACTIVATION 0x05 /* Activation */
+#define AETHRA_DEVICE_START_CAPTURE 0x5F /* Start Capture */
+
+/*
+ * In AETHRA_ISDN_LINK and bearer channel records, the flags field has
+ * a direction flag and possibly some other bits.
+ *
+ * In AETHRA_ISDN_LINK records, at least some of the other bits are
+ * a subtype.
+ *
+ * In bearer channel records, there are records with data and
+ * "Constant Value" records with a single byte. Data has a
+ * flags value of 0x14 ORed with the direction flag, and Constant Value
+ * records have a flags value of 0x16 ORed with the direction flag.
+ * There are also records of an unknown type with 0x02, probably
+ * ORed with the direction flag.
+ */
+#define AETHRA_U_TO_N 0x01 /* set for TE->NT */
+
+#define AETHRA_ISDN_LINK_SUBTYPE 0xFE
+#define AETHRA_ISDN_LINK_LAPD 0x00 /* LAPD frame */
+#define AETHRA_ISDN_LINK_SA_BITS 0x2E /* 2048K PRI Sa bits (G.704 section 2.3.2) */
+#define AETHRA_ISDN_LINK_ALL_ALARMS_CLEARED 0x30 /* All Alarms Cleared */
+
+typedef struct {
+ time_t start;
+} aethra_t;
+
+static gboolean aethra_read(wtap *wth, wtap_rec *rec, Buffer *buf, int *err,
+ gchar **err_info, gint64 *data_offset);
+static gboolean aethra_seek_read(wtap *wth, gint64 seek_off,
+ wtap_rec *rec, Buffer *buf, int *err, gchar **err_info);
+static gboolean aethra_read_rec_header(wtap *wth, FILE_T fh, struct aethrarec_hdr *hdr,
+ wtap_rec *rec, int *err, gchar **err_info);
+
+static int aethra_file_type_subtype = -1;
+
+void register_aethra(void);
+
+wtap_open_return_val aethra_open(wtap *wth, int *err, gchar **err_info)
+{
+ struct aethra_hdr hdr;
+ struct tm tm;
+ aethra_t *aethra;
+
+ /* Read in the string that should be at the start of a "aethra" file */
+ if (!wtap_read_bytes(wth->fh, hdr.magic, sizeof hdr.magic, err,
+ err_info)) {
+ if (*err != WTAP_ERR_SHORT_READ)
+ return WTAP_OPEN_ERROR;
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ if (memcmp(hdr.magic, aethra_magic, sizeof aethra_magic) != 0)
+ return WTAP_OPEN_NOT_MINE;
+
+ /* Read the rest of the header. */
+ if (!wtap_read_bytes(wth->fh, (char *)&hdr + sizeof hdr.magic,
+ sizeof hdr - sizeof hdr.magic, err, err_info))
+ return WTAP_OPEN_ERROR;
+ wth->file_type_subtype = aethra_file_type_subtype;
+ aethra = g_new(aethra_t, 1);
+ wth->priv = (void *)aethra;
+ wth->subtype_read = aethra_read;
+ wth->subtype_seek_read = aethra_seek_read;
+
+ /*
+ * Convert the time stamp to a "time_t".
+ */
+ tm.tm_year = pletoh16(&hdr.start_year) - 1900;
+ tm.tm_mon = pletoh16(&hdr.start_month) - 1;
+ tm.tm_mday = pletoh16(&hdr.start_day);
+ tm.tm_hour = hdr.start_hour;
+ tm.tm_min = hdr.start_min;
+ tm.tm_sec = hdr.start_sec;
+ tm.tm_isdst = -1;
+ aethra->start = mktime(&tm);
+
+ /*
+ * We've only seen ISDN files, so, for now, we treat all
+ * files as ISDN.
+ */
+ wth->file_encap = WTAP_ENCAP_ISDN;
+ wth->snapshot_length = 0; /* not available in header */
+ wth->file_tsprec = WTAP_TSPREC_MSEC;
+
+ /*
+ * Add an IDB; we don't know how many interfaces were
+ * involved, so we just say one interface, about which
+ * we only know the link-layer type, snapshot length,
+ * and time stamp resolution.
+ */
+ wtap_add_generated_idb(wth);
+
+ return WTAP_OPEN_MINE;
+}
+
+#if 0
+static guint packet = 0;
+#endif
+
+/* Read the next packet */
+static gboolean aethra_read(wtap *wth, wtap_rec *rec, Buffer *buf, int *err,
+ gchar **err_info, gint64 *data_offset)
+{
+ struct aethrarec_hdr hdr;
+
+ /*
+ * Keep reading until we see an AETHRA_ISDN_LINK with a subtype
+ * of AETHRA_ISDN_LINK_LAPD record or get an end-of-file.
+ */
+ for (;;) {
+ *data_offset = file_tell(wth->fh);
+
+ /* Read record header. */
+ if (!aethra_read_rec_header(wth, wth->fh, &hdr, rec, err, err_info))
+ return FALSE;
+
+ /*
+ * XXX - if this is big, we might waste memory by
+ * growing the buffer to handle it.
+ */
+ if (rec->rec_header.packet_header.caplen != 0) {
+ if (!wtap_read_packet_bytes(wth->fh, buf,
+ rec->rec_header.packet_header.caplen, err, err_info))
+ return FALSE; /* Read error */
+ }
+#if 0
+packet++;
+#endif
+ switch (hdr.rec_type) {
+
+ case AETHRA_ISDN_LINK:
+#if 0
+fprintf(stderr, "Packet %u: type 0x%02x (AETHRA_ISDN_LINK)\n",
+packet, hdr.rec_type);
+#endif
+ switch (hdr.flags & AETHRA_ISDN_LINK_SUBTYPE) {
+
+ case AETHRA_ISDN_LINK_LAPD:
+ /*
+ * The data is a LAPD frame.
+ */
+#if 0
+fprintf(stderr, " subtype 0x%02x (AETHRA_ISDN_LINK_LAPD)\n", hdr.flags & AETHRA_ISDN_LINK_SUBTYPE);
+#endif
+ goto found;
+
+ case AETHRA_ISDN_LINK_SA_BITS:
+ /*
+ * These records have one data byte, which
+ * has the Sa bits in the lower 5 bits.
+ *
+ * XXX - what about stuff other than 2048K
+ * PRI lines?
+ */
+#if 0
+fprintf(stderr, " subtype 0x%02x (AETHRA_ISDN_LINK_SA_BITS)\n", hdr.flags & AETHRA_ISDN_LINK_SUBTYPE);
+#endif
+ break;
+
+ case AETHRA_ISDN_LINK_ALL_ALARMS_CLEARED:
+ /*
+ * No data, just an "all alarms cleared"
+ * indication.
+ */
+#if 0
+fprintf(stderr, " subtype 0x%02x (AETHRA_ISDN_LINK_ALL_ALARMS_CLEARED)\n", hdr.flags & AETHRA_ISDN_LINK_SUBTYPE);
+#endif
+ break;
+
+ default:
+#if 0
+fprintf(stderr, " subtype 0x%02x, packet_size %u, direction 0x%02x\n",
+hdr.flags & AETHRA_ISDN_LINK_SUBTYPE, rec->rec_header.packet_header.caplen, hdr.flags & AETHRA_U_TO_N);
+#endif
+ break;
+ }
+ break;
+
+ default:
+#if 0
+fprintf(stderr, "Packet %u: type 0x%02x, packet_size %u, flags 0x%02x\n",
+packet, hdr.rec_type, rec->rec_header.packet_header.caplen, hdr.flags);
+#endif
+ break;
+ }
+ }
+
+found:
+ return TRUE;
+}
+
+static gboolean
+aethra_seek_read(wtap *wth, gint64 seek_off, wtap_rec *rec,
+ Buffer *buf, int *err, gchar **err_info)
+{
+ struct aethrarec_hdr hdr;
+
+ if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
+ return FALSE;
+
+ if (!aethra_read_rec_header(wth, wth->random_fh, &hdr, rec, err,
+ err_info)) {
+ if (*err == 0)
+ *err = WTAP_ERR_SHORT_READ;
+ return FALSE;
+ }
+
+ /*
+ * Read the packet data.
+ */
+ if (!wtap_read_packet_bytes(wth->random_fh, buf, rec->rec_header.packet_header.caplen, err, err_info))
+ return FALSE; /* failed */
+
+ return TRUE;
+}
+
+static gboolean
+aethra_read_rec_header(wtap *wth, FILE_T fh, struct aethrarec_hdr *hdr,
+ wtap_rec *rec, int *err, gchar **err_info)
+{
+ aethra_t *aethra = (aethra_t *)wth->priv;
+ guint32 rec_size;
+ guint32 packet_size;
+ guint32 msecs;
+
+ /* Read record header. */
+ if (!wtap_read_bytes_or_eof(fh, hdr, sizeof *hdr, err, err_info))
+ return FALSE;
+
+ rec_size = pletoh16(hdr->rec_size);
+ if (rec_size < (sizeof *hdr - sizeof hdr->rec_size)) {
+ /* The record is shorter than a record header. */
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("aethra: File has %u-byte record, less than minimum of %u",
+ rec_size,
+ (unsigned int)(sizeof *hdr - sizeof hdr->rec_size));
+ return FALSE;
+ }
+ if (rec_size > WTAP_MAX_PACKET_SIZE_STANDARD) {
+ /*
+ * Probably a corrupt capture file; return an error,
+ * so that our caller doesn't blow up trying to allocate
+ * space for an immensely-large packet.
+ */
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("aethra: File has %u-byte packet, bigger than maximum of %u",
+ rec_size, WTAP_MAX_PACKET_SIZE_STANDARD);
+ return FALSE;
+ }
+
+ packet_size = rec_size - (guint32)(sizeof *hdr - sizeof hdr->rec_size);
+
+ msecs = pletoh32(hdr->timestamp);
+ rec->rec_type = REC_TYPE_PACKET;
+ rec->block = wtap_block_create(WTAP_BLOCK_PACKET);
+ rec->presence_flags = WTAP_HAS_TS;
+ rec->ts.secs = aethra->start + (msecs / 1000);
+ rec->ts.nsecs = (msecs % 1000) * 1000000;
+ rec->rec_header.packet_header.caplen = packet_size;
+ rec->rec_header.packet_header.len = packet_size;
+ rec->rec_header.packet_header.pseudo_header.isdn.uton = (hdr->flags & AETHRA_U_TO_N);
+ rec->rec_header.packet_header.pseudo_header.isdn.channel = 0; /* XXX - D channel */
+
+ return TRUE;
+}
+
+static const struct supported_block_type aethra_blocks_supported[] = {
+ /*
+ * We support packet blocks, with no comments or other options.
+ */
+ { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
+};
+
+static const struct file_type_subtype_info aethra_info = {
+ "Aethra .aps file", "aethra", "aps", NULL,
+ FALSE, BLOCKS_SUPPORTED(aethra_blocks_supported),
+ NULL, NULL, NULL
+};
+
+void register_aethra(void)
+{
+ aethra_file_type_subtype = wtap_register_file_type_subtype(&aethra_info);
+
+ /*
+ * Register name for backwards compatibility with the
+ * wtap_filetypes table in Lua.
+ */
+ wtap_register_backwards_compatibility_lua_name("AETHRA",
+ aethra_file_type_subtype);
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/wiretap/aethra.h b/wiretap/aethra.h
new file mode 100644
index 00000000..2cb4fe7d
--- /dev/null
+++ b/wiretap/aethra.h
@@ -0,0 +1,17 @@
+/** @file
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __W_AETHRA_H__
+#define __W_AETHRA_H__
+
+#include <glib.h>
+#include "wtap.h"
+
+wtap_open_return_val aethra_open(wtap *wth, int *err, gchar **err_info);
+
+#endif
diff --git a/wiretap/ascend-int.h b/wiretap/ascend-int.h
new file mode 100644
index 00000000..6c5760ff
--- /dev/null
+++ b/wiretap/ascend-int.h
@@ -0,0 +1,57 @@
+/** @file
+ *
+ * Definitions for routines common to multiple modules in the Lucent/Ascend
+ * capture file reading code, but not used outside that code.
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __ASCEND_INT_H__
+#define __ASCEND_INT_H__
+
+#include <glib.h>
+#include <stdbool.h>
+#include "ws_symbol_export.h"
+
+typedef struct {
+ time_t inittime;
+ gboolean adjusted;
+ gint64 next_packet_seek_start;
+} ascend_t;
+
+typedef struct {
+ int length;
+ guint32 u32_val;
+ guint16 u16_val;
+ guint8 u8_val;
+ char str_val[ASCEND_MAX_STR_LEN];
+} ascend_token_t;
+
+typedef struct {
+ FILE_T fh;
+ const gchar *ascend_parse_error;
+ int err;
+ gchar *err_info;
+ struct ascend_phdr *pseudo_header;
+ guint8 *pkt_data;
+
+ gboolean saw_timestamp;
+ time_t timestamp;
+
+ gint64 first_hexbyte;
+ guint32 wirelen;
+ guint32 caplen;
+ time_t secs;
+ guint32 usecs;
+
+ ascend_token_t token;
+} ascend_state_t;
+
+extern bool
+run_ascend_parser(guint8 *pd, ascend_state_t *parser_state, int *err, gchar **err_info);
+
+#endif /* ! __ASCEND_INT_H__ */
diff --git a/wiretap/ascend_parser.lemon b/wiretap/ascend_parser.lemon
new file mode 100644
index 00000000..89df9268
--- /dev/null
+++ b/wiretap/ascend_parser.lemon
@@ -0,0 +1,515 @@
+%include {
+/* ascend_parser.lemon
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+/*
+ Example 'pridisp' output data - one paragraph/frame:
+
+PRI-XMIT-27: (task "l1Task" at 0x10216fe0, time: 560194.01) 4 octets @ 0x1027c5b0
+ [0000]: 00 01 01 a9 ....
+PRI-RCV-27: (task "idle task" at 0x10123570, time: 560194.01) 4 octets @ 0x1027fb00
+ [0000]: 00 01 01 dd
+
+ Example 'pridisp' output data - two paragraphs/frame for XMIT case only:
+
+PRI-XMIT-19/1: (task "l1Task" at 0x10216840, time: 274759.98) 4 octets @ 0x1027f230
+ [0000]: 00 01 30 d8 ..0.
+PRI-XMIT-19/2 (task "l1Task" at 0x10216840, time: 274759.98) 11 octets @ 0x1027f234
+ [0000]: 08 02 8c bf 02 18 04 e9 82 83 8f ........ ...
+
+ Example 'ether-disp' output data:
+
+ETHER3ND RECV: (task "_sarTask" at 0x802c6eb0, time: 259848.03) 775 octets @ 0xa8fb2020
+ [0000]: 00 d0 52 04 e7 1e 08 00 20 ae 51 b5 08 00 45 00 ..R..... .Q...E.
+ [0010]: 02 f9 05 e6 40 00 3f 11 6e 39 87 fe c4 95 3c 3c ....@.?. n9....<<
+ [0020]: 3c 05 13 c4 13 c4 02 e5 ef ed 49 4e 56 49 54 45 <....... ..INVITE
+ [0030]: 20 73 69 70 3a 35 32 30 37 33 40 36 30 2e 36 30 sip:520 73@60.60
+ [0040]: 2e 36 30 2e 35 20 53 49 50 2f 32 2e 30 0d 0a 56 .60.5 SI P/2.0..V
+ [0050]: 69 61 3a 20 53 49 50 2f 32 2e 30 2f 55 44 50 20 ia: SIP/ 2.0/UDP
+ [0060]: 31 33 35 2e 135.
+
+ Example 'wandsess' output data:
+
+RECV-iguana:241:(task: B02614C0, time: 1975432.85) 49 octets @ 8003BD94
+ [0000]: FF 03 00 3D C0 06 CA 22 2F 45 00 00 28 6A 3B 40
+ [0010]: 00 3F 03 D7 37 CE 41 62 12 CF 00 FB 08 20 27 00
+ [0020]: 50 E4 08 DD D7 7C 4C 71 92 50 10 7D 78 67 C8 00
+ [0030]: 00
+XMIT-iguana:241:(task: B04E12C0, time: 1975432.85) 53 octets @ 8009EB16
+ [0000]: FF 03 00 3D C0 09 1E 31 21 45 00 00 2C 2D BD 40
+ [0010]: 00 7A 06 D8 B1 CF 00 FB 08 CE 41 62 12 00 50 20
+ [0020]: 29 7C 4C 71 9C 9A 6A 93 A4 60 12 22 38 3F 10 00
+ [0030]: 00 02 04 05 B4
+
+ Example 'wdd' output data:
+
+Date: 01/12/1990. Time: 12:22:33
+Cause an attempt to place call to 14082750382
+WD_DIALOUT_DISP: chunk 2515EE type IP.
+(task: 251790, time: 994953.28) 44 octets @ 2782B8
+ [0000]: 00 C0 7B 71 45 6C 00 60 08 16 AA 51 08 00 45 00
+ [0010]: 00 2C 66 1C 40 00 80 06 53 F6 AC 14 00 18 CC 47
+ [0020]: C8 45 0A 31 00 50 3B D9 5B 75 00 00
+
+ The following output comes from a MAX with Software 7.2.3:
+
+RECV-187:(task: B050B480, time: 18042248.03) 100 octets @ 800012C0
+ [0000]: FF 03 00 21 45 00 00 60 E3 49 00 00 7F 11 FD 7B
+ [0010]: C0 A8 F7 05 8A C8 18 51 00 89 00 89 00 4C C7 C1
+ [0020]: CC 8E 40 00 00 01 00 00 00 00 00 01 20 45 4A 45
+ [0030]: 42 45 43 45 48 43 4E 46 43 46 41 43 41 43 41 43
+ [0040]: 41 43 41 43 41 43 41 43 41 43 41 42 4E 00 00 20
+ [0050]: 00 01 C0 0C 00 20 00 01 00 04 93 E0 00 06 60 00
+ [0060]: C0 A8 F7 05
+XMIT-187:(task: B0292CA0, time: 18042248.04) 60 octets @ 800AD576
+ [0000]: FF 03 00 21 45 00 00 38 D7 EE 00 00 0F 01 11 2B
+ [0010]: 0A FF FF FE C0 A8 F7 05 03 0D 33 D3 00 00 00 00
+ [0020]: 45 00 00 60 E3 49 00 00 7E 11 FE 7B C0 A8 F7 05
+ [0030]: 8A C8 18 51 00 89 00 89 00 4C C7 C1
+RECV-187:(task: B0292CA0, time: 18042251.92) 16 octets @ 800018E8
+ [0000]: FF 03 C0 21 09 01 00 0C DE 61 96 4B 00 30 94 92
+
+ In TAOS 8.0, Lucent slightly changed the format as follows:
+
+ Example 'wandisp' output data (TAOS 8.0.3): (same format is used
+ for 'wanopen' and 'wannext' command)
+
+RECV-14: (task "idle task" at 0xb05e6e00, time: 1279.01) 29 octets @ 0x8000e0fc
+ [0000]: ff 03 c0 21 01 01 00 19 01 04 05 f4 11 04 05 f4 ...!.... ........
+ [0010]: 13 09 03 00 c0 7b 9a 9f 2d 17 04 10 00 .....{.. -....
+XMIT-14: (task "idle task" at 0xb05e6e00, time: 1279.02) 38 octets @ 0x8007fd56
+ [0000]: ff 03 c0 21 01 01 00 22 00 04 00 00 01 04 05 f4 ...!..." ........
+ [0010]: 03 05 c2 23 05 11 04 05 f4 13 09 03 00 c0 7b 80 ...#.... ......{.
+ [0020]: 7c ef 17 04 0e 00 |.....
+XMIT-14: (task "idle task" at 0xb05e6e00, time: 1279.02) 29 octets @ 0x8007fa36
+ [0000]: ff 03 c0 21 02 01 00 19 01 04 05 f4 11 04 05 f4 ...!.... ........
+ [0010]: 13 09 03 00 c0 7b 9a 9f 2d 17 04 10 00 .....{.. -....
+
+ Example 'wandsess' output data (TAOS 8.0.3):
+
+RECV-Max7:20: (task "_brouterControlTask" at 0xb094ac20, time: 1481.50) 20 octets @ 0x8000d198
+ [0000]: ff 03 00 3d c0 00 00 04 80 fd 02 01 00 0a 11 06 ...=.... ........
+ [0010]: 00 01 01 03 ....
+XMIT-Max7:20: (task "_brouterControlTask" at 0xb094ac20, time: 1481.51) 26 octets @ 0x800806b6
+ [0000]: ff 03 00 3d c0 00 00 00 80 21 01 01 00 10 02 06 ...=.... .!......
+ [0010]: 00 2d 0f 01 03 06 89 64 03 08 .-.....d ..
+XMIT-Max7:20: (task "_brouterControlTask" at 0xb094ac20, time: 1481.51) 20 octets @ 0x8007f716
+ [0000]: ff 03 00 3d c0 00 00 01 80 fd 01 01 00 0a 11 06 ...=.... ........
+ [0010]: 00 01 01 03 ....
+
+ The changes since TAOS 7.X are:
+
+ 1) White space is added before "(task".
+ 2) Task has a name, indicated by a subsequent string surrounded by a
+ double-quote.
+ 3) Address expressed in hex number has a preceding "0x".
+ 4) Hex numbers are in lower case.
+ 5) There is a character display corresponding to hex data in each line.
+
+ */
+
+#include "config.h"
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "wtap-int.h"
+#include "ascendtext.h"
+#include "ascend-int.h"
+#include "ascend_parser.h"
+#include "ascend_scanner_lex.h"
+#include "file_wrappers.h"
+#include <wsutil/wslog.h>
+
+#define NO_USER "<none>"
+
+static void *AscendParserAlloc(void *(*mallocProc)(size_t));
+static void AscendParser(void *yyp, int yymajor, ascend_token_t yyminor, ascend_state_t *state);
+static void AscendParserFree(void *p, void (*freeProc)(void*));
+
+#if 0
+#define ASCEND_PARSER_DEBUG 1
+#undef NDEBUG
+#define ascend_debug(...) ws_warning(__VA_ARGS__)
+#else
+#define ascend_debug(...)
+#endif
+
+DIAG_OFF_LEMON()
+} /* end of %include */
+
+%code {
+DIAG_ON_LEMON()
+}
+
+%name AscendParser
+
+%extra_argument { ascend_state_t *parser_state }
+
+%token_type { ascend_token_t }
+
+%token_destructor {
+ (void) parser_state;
+ (void) yypminor;
+}
+
+%type STRING { ascend_token_t* }
+%type KEYWORD { ascend_token_t* }
+%type WDD_DATE { ascend_token_t* }
+%type WDD_DECNUM { ascend_token_t* }
+%type WDD_TIME { ascend_token_t* }
+%type WDD_CAUSE { ascend_token_t* }
+%type WDD_CALLNUM { ascend_token_t* }
+%type WDD_CHUNK { ascend_token_t* }
+%type COUNTER { ascend_token_t* }
+%type SLASH_SUFFIX { ascend_token_t* }
+
+%type WDS_PREFIX { ascend_token_t* }
+%type ISDN_PREFIX { ascend_token_t* }
+%type ETHER_PREFIX { ascend_token_t* }
+%type DECNUM { ascend_token_t* }
+%type YEAR { ascend_token_t* }
+%type MONTH { ascend_token_t* }
+%type MDAY { ascend_token_t* }
+%type HEXNUM { ascend_token_t* }
+
+%type HEXBYTE { ascend_token_t* }
+
+data_packet ::= ether_hdr datagroup .
+data_packet ::= deferred_isdn_hdr datagroup deferred_isdn_hdr datagroup .
+data_packet ::= isdn_hdr datagroup .
+data_packet ::= wds_hdr datagroup .
+data_packet ::= wds8_hdr datagroup .
+data_packet ::= wdp7_hdr datagroup .
+data_packet ::= wdp8_hdr datagroup .
+data_packet ::= wdd_date wdd_hdr datagroup .
+data_packet ::= wdd_hdr datagroup .
+
+%type isdn_prefix { guint16 }
+isdn_prefix(U16) ::= ISDN_PREFIX(A_TOK) . { U16 = A_TOK.u16_val; }
+
+%type ether_prefix { guint16 }
+ether_prefix(U16) ::= ETHER_PREFIX(A_TOK) . { U16 = A_TOK.u16_val; }
+
+%type wds_prefix { guint16 }
+wds_prefix(U16) ::= WDS_PREFIX(A_TOK) . { U16 = A_TOK.u16_val; }
+
+string ::= STRING .
+
+%type decnum { guint32 }
+decnum(U32) ::= DECNUM(A_TOK) . { U32 = A_TOK.u32_val; }
+
+%type hexnum { guint32 }
+hexnum(U32) ::= HEXNUM(A_TOK) . { U32 = A_TOK.u32_val; }
+
+%type wdd_decnum { guint32 }
+wdd_decnum(U32) ::= WDD_DECNUM(A_TOK) . { U32 = A_TOK.u32_val; }
+
+/*
+ pridisp special case - I-frame header printed separately from contents,
+ one frame across two messages.
+
+PRI-XMIT-0/1: (task "l1Task" at 0x80152b20, time: 283529.65) 4 octets @
+0x80128220
+ [0000]: 00 01 ae b2 ....
+PRI-XMIT-0/2 (task "l1Task" at 0x80152b20, time: 283529.65) 10 octets @
+0x80128224
+ [0000]: 08 02 d7 e3 02 18 03 a9 83 8a ........
+
+*/
+deferred_isdn_hdr ::= isdn_prefix(TYPE) decnum(SESS) SLASH_SUFFIX KEYWORD string KEYWORD hexnum(TASK) KEYWORD decnum(SECS) decnum(USECS) decnum(WIRELEN) KEYWORD HEXNUM . {
+ parser_state->wirelen += WIRELEN;
+ parser_state->secs = SECS;
+ parser_state->usecs = USECS;
+ if (parser_state->pseudo_header != NULL) {
+ parser_state->pseudo_header->type = TYPE;
+ parser_state->pseudo_header->sess = SESS;
+ parser_state->pseudo_header->call_num[0] = '\0';
+ parser_state->pseudo_header->chunk = 0;
+ parser_state->pseudo_header->task = TASK;
+ }
+ /* because we have two data groups */
+ parser_state->first_hexbyte = 0;
+}
+
+/*
+PRI-XMIT-19: (task "l1Task" at 0x10216840, time: 274758.67) 4 octets @ 0x1027c1c0
+ ... or ...
+PRI-RCV-27: (task "idle task" at 0x10123570, time: 560194.01) 4 octets @ 0x1027fb00
+*/
+isdn_hdr ::= isdn_prefix(TYPE) decnum(SESS) KEYWORD string KEYWORD hexnum(TASK) KEYWORD decnum(SECS) decnum(USECS) decnum(WIRELEN) KEYWORD HEXNUM . {
+ parser_state->wirelen += WIRELEN;
+ parser_state->secs = SECS;
+ parser_state->usecs = USECS;
+ if (parser_state->pseudo_header != NULL) {
+ parser_state->pseudo_header->type = TYPE;
+ parser_state->pseudo_header->sess = SESS;
+ parser_state->pseudo_header->call_num[0] = '\0';
+ parser_state->pseudo_header->chunk = 0;
+ parser_state->pseudo_header->task = TASK;
+ }
+ parser_state->first_hexbyte = 0;
+}
+
+/*
+ETHER3ND XMIT: (task "_sarTask" at 0x802c6eb0, time: 259848.11) 414 octets @ 0xa
+885f80e
+*/
+ether_hdr ::= ether_prefix(TYPE) string KEYWORD string KEYWORD hexnum(TASK) KEYWORD decnum(SECS) decnum(USECS) decnum(WIRELEN) KEYWORD HEXNUM . {
+ parser_state->wirelen += WIRELEN;
+ parser_state->secs = SECS;
+ parser_state->usecs = USECS;
+ if (parser_state->pseudo_header != NULL) {
+ parser_state->pseudo_header->type = TYPE;
+ parser_state->pseudo_header->call_num[0] = '\0';
+ parser_state->pseudo_header->chunk = 0;
+ parser_state->pseudo_header->task = TASK;
+ }
+}
+
+/* RECV-iguana:241:(task: B02614C0, time: 1975432.85) 49 octets @ 8003BD94 */
+/* 1 2 3 4 5 6 7 8 9 10 11 */
+wds_hdr ::= wds_prefix(TYPE) string decnum(SESS) KEYWORD hexnum(TASK) KEYWORD decnum(SECS) decnum(USECS) decnum(WIRELEN) KEYWORD HEXNUM . {
+ parser_state->wirelen += WIRELEN;
+ parser_state->secs = SECS;
+ parser_state->usecs = USECS;
+ if (parser_state->pseudo_header != NULL) {
+ /* parser_state->pseudo_header->user is set in ascend_scanner.l */
+ parser_state->pseudo_header->type = TYPE;
+ parser_state->pseudo_header->sess = SESS;
+ parser_state->pseudo_header->call_num[0] = '\0';
+ parser_state->pseudo_header->chunk = 0;
+ parser_state->pseudo_header->task = TASK;
+ }
+}
+
+/* RECV-Max7:20: (task "_brouterControlTask" at 0xb094ac20, time: 1481.50) 20 octets @ 0x8000d198 */
+/* 1 2 3 4 5 6 7 8 9 10 11 12 13 */
+wds8_hdr ::= wds_prefix(TYPE) string decnum(SESS) KEYWORD string KEYWORD hexnum(TASK) KEYWORD decnum(SECS) decnum(USECS) decnum(WIRELEN) KEYWORD HEXNUM . {
+ parser_state->wirelen += WIRELEN;
+ parser_state->secs = SECS;
+ parser_state->usecs = USECS;
+ if (parser_state->pseudo_header != NULL) {
+ /* parser_state->pseudo_header->user is set in ascend_scanner.l */
+ parser_state->pseudo_header->type = TYPE;
+ parser_state->pseudo_header->sess = SESS;
+ parser_state->pseudo_header->call_num[0] = '\0';
+ parser_state->pseudo_header->chunk = 0;
+ parser_state->pseudo_header->task = TASK;
+ }
+}
+
+/* RECV-187:(task: B050B480, time: 18042248.03) 100 octets @ 800012C0 */
+/* 1 2 3 4 5 6 7 8 9 10 */
+wdp7_hdr ::= wds_prefix(TYPE) decnum(SESS) KEYWORD hexnum(TASK) KEYWORD decnum(SECS) decnum(USECS) decnum(WIRELEN) KEYWORD HEXNUM . {
+ parser_state->wirelen += WIRELEN;
+ parser_state->secs = SECS;
+ parser_state->usecs = USECS;
+ if (parser_state->pseudo_header != NULL) {
+ /* parser_state->pseudo_header->user is set in ascend_scanner.l */
+ parser_state->pseudo_header->type = TYPE;
+ parser_state->pseudo_header->sess = SESS;
+ parser_state->pseudo_header->call_num[0] = '\0';
+ parser_state->pseudo_header->chunk = 0;
+ parser_state->pseudo_header->task = TASK;
+ }
+}
+
+/* XMIT-44: (task "freedm_task" at 0xe051fd10, time: 6258.66) 29 octets @ 0x606d1f00 */
+/* 1 2 3 4 5 6 7 8 9 10 11 12 */
+wdp8_hdr ::= wds_prefix(TYPE) decnum(SESS) KEYWORD string KEYWORD hexnum(TASK) KEYWORD decnum(SECS) decnum(USECS) decnum(WIRELEN) KEYWORD HEXNUM . {
+ parser_state->wirelen += WIRELEN;
+ parser_state->secs = SECS;
+ parser_state->usecs = USECS;
+ if (parser_state->pseudo_header != NULL) {
+ /* parser_state->pseudo_header->user is set in ascend_scanner.l */
+ parser_state->pseudo_header->type = TYPE;
+ parser_state->pseudo_header->sess = SESS;
+ parser_state->pseudo_header->call_num[0] = '\0';
+ parser_state->pseudo_header->chunk = 0;
+ parser_state->pseudo_header->task = TASK;
+ }
+}
+
+/*
+Date: 01/12/1990. Time: 12:22:33
+Cause an attempt to place call to 14082750382
+*/
+/* 1 2 3 4 5 6 7 8 9 10*/
+wdd_date ::= WDD_DATE wdd_decnum(MONTH) wdd_decnum(MDAY) wdd_decnum(YEAR) WDD_TIME wdd_decnum(HOUR) wdd_decnum(MINUTE) wdd_decnum(SECOND) WDD_CAUSE WDD_CALLNUM(CN_T) . {
+ /*
+ * Supply the date/time value to the code above us; it will use the
+ * first date/time value supplied as the capture start date/time.
+ */
+ struct tm wddt;
+
+ wddt.tm_sec = SECOND;
+ wddt.tm_min = MINUTE;
+ wddt.tm_hour = HOUR;
+ wddt.tm_mday = MDAY;
+ wddt.tm_mon = MONTH - 1;
+ wddt.tm_year = (YEAR > 1970) ? YEAR - 1900 : 70;
+ wddt.tm_isdst = -1;
+
+ parser_state->timestamp = (guint32) mktime(&wddt);
+ parser_state->saw_timestamp = TRUE;
+
+ (void) g_strlcpy(parser_state->pseudo_header->call_num, CN_T.str_val, ASCEND_MAX_STR_LEN);
+}
+
+/*
+WD_DIALOUT_DISP: chunk 2515EE type IP.
+(task: 251790, time: 994953.28) 44 octets @ 2782B8
+*/
+/* 1 2 3 4 5 6 7 8 9 10 11*/
+wdd_hdr ::= WDD_CHUNK hexnum(CHUNK) KEYWORD KEYWORD hexnum(TASK) KEYWORD decnum(SECS) decnum(USECS) decnum(WIRELEN) KEYWORD HEXNUM . {
+ parser_state->wirelen = WIRELEN;
+ parser_state->secs = SECS;
+ parser_state->usecs = USECS;
+ if (parser_state->pseudo_header != NULL) {
+ parser_state->pseudo_header->type = ASCEND_PFX_WDD;
+ parser_state->pseudo_header->user[0] = '\0';
+ parser_state->pseudo_header->sess = 0;
+ parser_state->pseudo_header->chunk = CHUNK;
+ parser_state->pseudo_header->task = TASK;
+ }
+}
+
+byte ::= HEXBYTE(A_TOK) . {
+ /* remember the position of the data group in the trace, to tip off
+ ascend_find_next_packet() as to where to look for the next header. */
+ if (parser_state->first_hexbyte == 0) {
+ parser_state->first_hexbyte = file_tell(parser_state->fh) - A_TOK.length;
+ }
+
+ /* XXX - if this test fails, it means that we parsed more bytes than
+ the header claimed there were. */
+ if (parser_state->caplen < parser_state->wirelen) {
+ parser_state->pkt_data[parser_state->caplen] = A_TOK.u8_val;
+ parser_state->caplen++;
+ }
+}
+
+/* XXX There must be a better way to do this... */
+bytegroup ::= byte byte byte byte byte byte byte byte byte byte byte byte byte byte byte byte .
+bytegroup ::= byte byte byte byte byte byte byte byte byte byte byte byte byte byte byte .
+bytegroup ::= byte byte byte byte byte byte byte byte byte byte byte byte byte byte .
+bytegroup ::= byte byte byte byte byte byte byte byte byte byte byte byte byte .
+bytegroup ::= byte byte byte byte byte byte byte byte byte byte byte byte .
+bytegroup ::= byte byte byte byte byte byte byte byte byte byte byte .
+bytegroup ::= byte byte byte byte byte byte byte byte byte byte .
+bytegroup ::= byte byte byte byte byte byte byte byte byte .
+bytegroup ::= byte byte byte byte byte byte byte byte .
+bytegroup ::= byte byte byte byte byte byte byte .
+bytegroup ::= byte byte byte byte byte byte .
+bytegroup ::= byte byte byte byte byte .
+bytegroup ::= byte byte byte byte .
+bytegroup ::= byte byte byte .
+bytegroup ::= byte byte .
+bytegroup ::= byte .
+
+dataln ::= COUNTER bytegroup .
+
+datagroup ::= dataln dataln dataln dataln dataln dataln dataln dataln .
+datagroup ::= dataln dataln dataln dataln dataln dataln dataln .
+datagroup ::= dataln dataln dataln dataln dataln dataln .
+datagroup ::= dataln dataln dataln dataln dataln .
+datagroup ::= dataln dataln dataln dataln .
+datagroup ::= dataln dataln dataln .
+datagroup ::= dataln dataln .
+datagroup ::= dataln .
+
+%syntax_error
+{
+ /*
+ * We might be parsing output that includes console session output along
+ * with packet dumps.
+ */
+ (void)yypParser;
+ (void)yyminor;
+ static char *err = "non-packet data";
+
+ parser_state->ascend_parse_error = err;
+}
+
+%code {
+
+/* Run the parser. */
+bool
+run_ascend_parser(guint8 *pd, ascend_state_t *parser_state, int *err, gchar **err_info)
+{
+ yyscan_t scanner = NULL;
+ void *parser;
+
+ if (ascend_lex_init(&scanner) != 0) {
+ /* errno is set if this fails */
+ *err = errno;
+ *err_info = NULL;
+ return false;
+ }
+ /* Associate the parser state with the lexical analyzer state */
+ ascend_set_extra(parser_state, scanner);
+ parser_state->ascend_parse_error = NULL;
+ parser_state->err = 0;
+ parser_state->err_info = NULL;
+ parser_state->pkt_data = pd;
+
+ /*
+ * We haven't seen a time stamp yet.
+ */
+ parser_state->saw_timestamp = FALSE;
+ parser_state->timestamp = 0;
+
+ parser_state->first_hexbyte = 0;
+ parser_state->caplen = 0;
+ parser_state->wirelen = 0;
+
+ parser_state->secs = 0;
+ parser_state->usecs = 0;
+
+ /*
+ * Not all packets in a "wdd" dump necessarily have a "Cause an
+ * attempt to place call to" header (I presume this can happen if
+ * there was a call in progress when the packet was sent or
+ * received), so we won't necessarily have the phone number for
+ * the packet.
+ *
+ * XXX - we could assume, in the sequential pass, that it's the
+ * phone number from the last call, and remember that for use
+ * when doing random access.
+ */
+ parser_state->pseudo_header->call_num[0] = '\0';
+
+ parser = AscendParserAlloc(g_malloc);
+
+#ifdef ASCEND_PARSER_DEBUG
+ AscendParserTrace(stderr, "=AP ");
+#endif
+
+ int token_id;
+ do {
+ token_id = ascend_lex(scanner);
+
+ ascend_debug("Got token %d at %" PRId64, token_id, file_tell(parser_state->fh));
+
+ AscendParser(parser, token_id, parser_state->token, parser_state);
+ } while (token_id && !parser_state->err && !parser_state->ascend_parse_error && parser_state->caplen < ASCEND_MAX_PKT_LEN);
+
+ AscendParserFree(parser, g_free);
+ ascend_lex_destroy(scanner);
+
+ if (parser_state->err) {
+ *err = parser_state->err;
+ *err_info = parser_state->err_info;
+ return false;
+ }
+
+ return true;
+}
+
+} // %code
diff --git a/wiretap/ascend_scanner.l b/wiretap/ascend_scanner.l
new file mode 100644
index 00000000..f6fe4032
--- /dev/null
+++ b/wiretap/ascend_scanner.l
@@ -0,0 +1,409 @@
+%top {
+/* Include this before everything else, for various large-file definitions */
+#include "config.h"
+#include <wireshark.h>
+}
+
+/*
+ * We want a reentrant scanner.
+ */
+%option reentrant
+
+/*
+ * We don't read interactively from the terminal.
+ */
+%option never-interactive
+
+/*
+ * We want to stop processing when we get to the end of the input.
+ */
+%option noyywrap
+
+/*
+ * The type for the state we keep for the scanner (and parser).
+ */
+%option extra-type="ascend_state_t *"
+
+/*
+ * Prefix scanner routines with "ascend_" rather than "yy", so this scanner
+ * can coexist with other scanners.
+ */
+%option prefix="ascend_"
+
+/*
+ * We have to override the memory allocators so that we don't get
+ * "unused argument" warnings from the yyscanner argument (which
+ * we don't use, as we have a global memory allocator).
+ *
+ * We provide, as macros, our own versions of the routines generated by Flex,
+ * which just call malloc()/realloc()/free() (as the Flex versions do),
+ * discarding the extra argument.
+ */
+%option noyyalloc
+%option noyyrealloc
+%option noyyfree
+
+%{
+/* ascend_scanner.l
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "wtap-int.h"
+#include "ascendtext.h"
+#include "ascend-int.h"
+#include "ascend_parser.h"
+#include "file_wrappers.h"
+
+/*
+ * Disable diagnostics in the code generated by Flex.
+ */
+DIAG_OFF_FLEX()
+
+static int ascend_yyinput(void *buf, ascend_state_t *parser_state) {
+ int c = file_getc(parser_state->fh);
+ if (c == EOF) {
+ parser_state->err = file_error(parser_state->fh,
+ &parser_state->err_info);
+ if (parser_state->err == 0)
+ parser_state->err = WTAP_ERR_SHORT_READ;
+ return YY_NULL;
+ } else {
+ *(char *) buf = c;
+ return 1;
+ }
+}
+
+#define YY_INPUT(buf, result, max_size) \
+ do { (result) = ascend_yyinput((buf), yyextra); } while (0)
+
+
+/* Count bytes read. This is required in order to rewind the file
+ * to the beginning of the next packet, since flex reads more bytes
+ * before executing the action that does yyterminate(). */
+#define YY_USER_ACTION do { yyextra->token.length = yyleng; } while (0);
+
+#define NO_USER "<none>"
+
+#ifndef HAVE_UNISTD_H
+#define YY_NO_UNISTD_H
+#endif
+
+/*
+ * Sleazy hack to suppress compiler warnings in yy_fatal_error().
+ */
+#define YY_EXIT_FAILURE ((void)yyscanner, 2)
+
+/*
+ * Macros for the allocators, to discard the extra argument.
+ */
+#define ascend_alloc(size, yyscanner) (void *)malloc(size)
+#define ascend_realloc(ptr, size, yyscanner) (void *)realloc((char *)(ptr), (size))
+#define ascend_free(ptr, yyscanner) free((char *)ptr)
+
+%}
+
+D [0-9]
+H [A-Fa-f0-9]
+
+PPP_XPFX PPP-OUT
+PPP_RPFX PPP-IN
+ISDN_XPFX PRI-XMIT-
+ISDN_RPFX PRI-RCV-
+WAN_XPFX XMIT[\-:]*
+WAN_RPFX RECV[\-:]*
+ETHER_PFX ETHER
+
+WDD_DATE "Date:"
+WDD_TIME "Time:"
+WDD_CAUSE "Cause an attempt to place call to "
+WDD_CALLNUM [^\n\r\t ]+
+WDD_CHUNK "WD_DIALOUT_DISP: chunk"
+WDD_TYPE "type "[^\n\r\t ]+
+
+%s sc_gen_task
+%s sc_gen_time_s
+%s sc_gen_time_u
+%s sc_gen_octets
+%s sc_gen_counter
+%s sc_gen_byte
+
+%s sc_wds_user
+%s sc_wds_sess
+
+%s sc_wdd_date_d
+%s sc_wdd_date_m
+%s sc_wdd_date_y
+%s sc_wdd_time
+%s sc_wdd_time_h
+%s sc_wdd_time_m
+%s sc_wdd_time_s
+%s sc_wdd_cause
+%s sc_wdd_callnum
+%s sc_wdd_chunk
+%s sc_wdd_chunknum
+%s sc_wdd_type
+
+%s sc_chardisp
+
+%s sc_isdn_call
+%s sc_ether_direction
+
+%%
+
+<INITIAL,sc_gen_byte>{ETHER_PFX} {
+ BEGIN(sc_ether_direction);
+ yyextra->token.u16_val = ASCEND_PFX_ETHER;
+ return ETHER_PREFIX;
+}
+
+<INITIAL,sc_gen_byte>{ISDN_XPFX} {
+ BEGIN(sc_isdn_call);
+ yyextra->token.u16_val = ASCEND_PFX_ISDN_X;
+ return ISDN_PREFIX;
+}
+
+<INITIAL,sc_gen_byte>{ISDN_RPFX} {
+ BEGIN(sc_isdn_call);
+ yyextra->token.u16_val = ASCEND_PFX_ISDN_R;
+ return ISDN_PREFIX;
+}
+
+<INITIAL,sc_gen_byte>{WAN_XPFX} {
+ BEGIN(sc_wds_user);
+ yyextra->token.u16_val = ASCEND_PFX_WDS_X;
+ return WDS_PREFIX;
+}
+
+<INITIAL,sc_gen_byte>{WAN_RPFX} {
+ BEGIN(sc_wds_user);
+ yyextra->token.u16_val = ASCEND_PFX_WDS_R;
+ return WDS_PREFIX;
+}
+
+<INITIAL,sc_gen_byte>{PPP_XPFX} {
+ BEGIN(sc_wds_user);
+ yyextra->token.u16_val = ASCEND_PFX_WDS_X;
+ return WDS_PREFIX;
+}
+
+<INITIAL,sc_gen_byte>{PPP_RPFX} {
+ BEGIN(sc_wds_user);
+ yyextra->token.u16_val = ASCEND_PFX_WDS_R;
+ return WDS_PREFIX;
+}
+
+ /*
+ * If we allow an arbitrary non-zero number of non-left-parentheses after
+ * "ETHER", that means that some file that has ETHER followed by a lot of
+ * text (see, for example, tpncp/tpncp.dat in the source tree) can cause
+ * either an infinite loop or a loop that take forever to finish, as the
+ * scanner keeps swallowing characters. Limit it to 20 characters.
+ *
+ * XXX - any reason to require at least two of them?
+ */
+<sc_ether_direction>[^\(]{2,20} {
+ BEGIN(sc_gen_task);
+ return STRING;
+}
+
+ /*
+ * If we allow an arbitrary non-zero number of non-slash, non-left-parentheses,
+ * non-colon characters after "PRI-XMIT", that means that some file that has
+ * PRI-XMIT= followed by a lot of text can cause either an infinite loop or
+ * a loop that take forever to finish, as the scanner keeps swallowing
+ * characters. Limit it to 20 characters.
+ */
+<sc_isdn_call>[^\/\(:]{1,20} {
+ BEGIN(sc_gen_task);
+ return DECNUM;
+}
+
+<sc_wds_user>[^:]{2,20} {
+ char *atcopy = g_strdup(yytext);
+ char colon = input(yyscanner);
+ char after = input(yyscanner);
+ int retval = STRING;
+
+ unput(after); unput(colon);
+
+ if (after != '(' && after != ' ') {
+ BEGIN(sc_wds_sess);
+ if (yyextra->pseudo_header != NULL && yyextra->pseudo_header->user[0] == '\0') {
+ (void) g_strlcpy(yyextra->pseudo_header->user, atcopy, ASCEND_MAX_STR_LEN);
+ }
+ } else { /* We have a version 7 file */
+ BEGIN(sc_gen_task);
+ if (yyextra->pseudo_header != NULL && yyextra->pseudo_header->user[0] == '\0') {
+ (void) g_strlcpy(yyextra->pseudo_header->user, NO_USER, ASCEND_MAX_STR_LEN);
+ }
+ /* Are valid values ever > 2^32? If so we need to adjust YYSTYPE and a lot of */
+ /* upstream code accordingly. */
+ yyextra->token.u32_val = (guint32) strtoul(yytext, NULL, 10);
+ retval = DECNUM;
+ }
+ g_free (atcopy);
+ return retval;
+}
+
+<sc_wds_sess>{D}* {
+ BEGIN(sc_gen_task);
+ yyextra->token.u32_val = (guint32) strtoul(yytext, NULL, 10);
+ return DECNUM;
+}
+
+<sc_gen_task>(0x|0X)?{H}{2,8} {
+ BEGIN(sc_gen_time_s);
+ yyextra->token.u32_val = (guint32) strtoul(yytext, NULL, 16);
+ return HEXNUM;
+}
+
+<sc_gen_task>\"[A-Za-z0-9_ ]+\" {
+ return STRING;
+}
+
+<sc_gen_time_s>{D}{1,10} {
+ BEGIN(sc_gen_time_u);
+ yyextra->token.u32_val = (guint32) strtoul(yytext, NULL, 10);
+ return DECNUM;
+}
+
+<sc_gen_time_u>{D}{1,6} {
+ char *atcopy = g_strdup(yytext);
+ BEGIN(sc_gen_octets);
+ /* only want the most significant 2 digits. convert to usecs */
+ if (strlen(atcopy) > 2)
+ atcopy[2] = '\0';
+ yyextra->token.u32_val = (guint32) strtoul(atcopy, NULL, 10) * 10000;
+ g_free(atcopy);
+ return DECNUM;
+}
+
+<sc_gen_octets>{D}{1,10} {
+ BEGIN(sc_gen_counter);
+ yyextra->token.u32_val = (guint32) strtoul(yytext, NULL, 10);
+ return DECNUM;
+}
+
+<sc_gen_counter,sc_gen_byte>"["{H}{4}"]:" {
+ BEGIN(sc_gen_byte);
+ return COUNTER;
+}
+
+<sc_gen_byte>{H}{2} {
+ yyextra->token.u8_val = (guint8) strtoul(yytext, NULL, 16);
+ return HEXBYTE;
+}
+
+<sc_gen_byte>" "{4} {
+ BEGIN(sc_chardisp);
+}
+
+<sc_chardisp>.* {
+ BEGIN(sc_gen_byte);
+}
+
+<INITIAL,sc_gen_byte>{WDD_DATE} {
+ BEGIN(sc_wdd_date_m);
+ return WDD_DATE;
+}
+
+ /*
+ * Scan m/d/y as three separate m, /d/, and y tokens.
+ * We could alternately treat m/d/y as a single token.
+ */
+<sc_wdd_date_m>{D}{2} {
+ BEGIN(sc_wdd_date_d);
+ yyextra->token.u32_val = (guint32) strtoul(yytext, NULL, 10);
+ return WDD_DECNUM;
+}
+
+<sc_wdd_date_d>\/{D}{2}\/ {
+ BEGIN(sc_wdd_date_y);
+ yyextra->token.u32_val = (guint32) strtoul(yytext+1, NULL, 10);
+ return WDD_DECNUM;
+}
+
+<sc_wdd_date_y>{D}{4} {
+ BEGIN(sc_wdd_time);
+ yyextra->token.u32_val = (guint32) strtoul(yytext, NULL, 10);
+ return WDD_DECNUM;
+}
+
+<sc_wdd_time>{WDD_TIME} {
+ BEGIN(sc_wdd_time_h);
+ return WDD_TIME;
+}
+
+ /*
+ * Scan h:m:s as three separate h, :m:, and s tokens similar to above.
+ */
+<sc_wdd_time_h>{D}{2} {
+ BEGIN(sc_wdd_time_m);
+ yyextra->token.u32_val = (guint32) strtoul(yytext, NULL, 10);
+ return WDD_DECNUM;
+}
+
+<sc_wdd_time_m>:{D}{2}: {
+ BEGIN(sc_wdd_time_s);
+ yyextra->token.u32_val = (guint32) strtoul(yytext+1, NULL, 10);
+ return WDD_DECNUM;
+}
+
+<sc_wdd_time_s>{D}{2} {
+ BEGIN(sc_wdd_cause);
+ yyextra->token.u32_val = (guint32) strtoul(yytext, NULL, 10);
+ return WDD_DECNUM;
+}
+
+<sc_wdd_cause>{WDD_CAUSE} {
+ BEGIN(sc_wdd_callnum);
+ return WDD_CAUSE;
+}
+
+<sc_wdd_callnum>{WDD_CALLNUM} {
+ BEGIN(sc_wdd_chunk);
+ (void) g_strlcpy(yyextra->token.str_val, yytext, ASCEND_MAX_STR_LEN);
+ return WDD_CALLNUM;
+}
+
+<INITIAL,sc_wdd_chunk,sc_gen_byte>{WDD_CHUNK} {
+ BEGIN(sc_wdd_chunknum);
+ return WDD_CHUNK;
+}
+
+<sc_wdd_chunknum>{H}{1,8} {
+ BEGIN(sc_wdd_type);
+ yyextra->token.u32_val = (guint32) strtoul(yytext, NULL, 16);
+ return HEXNUM;
+}
+
+<sc_wdd_type>{WDD_TYPE} {
+ BEGIN(sc_gen_task);
+ return KEYWORD;
+}
+
+<sc_gen_task>\/{D}+ {
+ return SLASH_SUFFIX;
+}
+
+(0x|0X)?{H}+ { return HEXNUM; }
+
+task:|task|at|time:|octets { return KEYWORD; }
+
+<<EOF>> { yyterminate(); }
+
+(.|\n) ;
+
+%%
+
+/*
+ * Turn diagnostics back on, so we check the code that we've written.
+ */
+DIAG_ON_FLEX()
diff --git a/wiretap/ascendtext.c b/wiretap/ascendtext.c
new file mode 100644
index 00000000..71e72738
--- /dev/null
+++ b/wiretap/ascendtext.c
@@ -0,0 +1,483 @@
+/* ascendtext.c
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "config.h"
+#include "wtap-int.h"
+#include "ascendtext.h"
+#include "ascend-int.h"
+#include "file_wrappers.h"
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <string.h>
+
+/* Last updated: Feb 03 2005: Josh Bailey (joshbailey@lucent.com).
+
+ This module reads the text hex dump output of various TAOS
+ (Avaya/Alcatel/Lucent/Ascend Max, Max TNT, APX, etc) debug commands, including:
+
+ * pridisplay traces primary rate ISDN
+ * ether-display traces Ethernet packets (dangerous! CPU intensive)
+ * wanopening, wandisplay, wannext, wandsess
+ traces PPP or other WAN connections
+
+ Please see ascend_parser.lemon for examples.
+
+ Detailed documentation on TAOS products was at http://support.lucent.com;
+ that no longer works, and appears not to be available on the Wayback
+ Machine.
+
+ Some online manuals and other information include:
+
+ MAX Administration Guide:
+ https://downloads.avaya.com/elmodocs2/definity/def_r10_new/max/0678_002.pdf
+
+ Other MAX documentation:
+ https://support.avaya.com/products/P1192/max
+ https://web.archive.org/web/20201127014004/https://support.avaya.com/products/P1192/max#Tab4
+
+ Ascend Router Information:
+ http://maxrouter.rde.net/
+ https://web.archive.org/web/20200807215418/http://maxrouter.rde.net/
+
+ */
+
+typedef struct _ascend_magic_string {
+ guint type;
+ const gchar *strptr;
+ size_t strlength;
+} ascend_magic_string;
+
+/* these magic strings signify the headers of a supported debug commands */
+#define ASCEND_MAGIC_ENTRY(type, string) \
+ { type, string, sizeof string - 1 } /* strlen of a constant string */
+static const ascend_magic_string ascend_magic[] = {
+ ASCEND_MAGIC_ENTRY(ASCEND_PFX_ISDN_X, "PRI-XMIT-"),
+ ASCEND_MAGIC_ENTRY(ASCEND_PFX_ISDN_R, "PRI-RCV-"),
+ ASCEND_MAGIC_ENTRY(ASCEND_PFX_WDS_X, "XMIT-"),
+ ASCEND_MAGIC_ENTRY(ASCEND_PFX_WDS_R, "RECV-"),
+ ASCEND_MAGIC_ENTRY(ASCEND_PFX_WDS_X, "XMIT:"),
+ ASCEND_MAGIC_ENTRY(ASCEND_PFX_WDS_R, "RECV:"),
+ ASCEND_MAGIC_ENTRY(ASCEND_PFX_WDS_X, "PPP-OUT"),
+ ASCEND_MAGIC_ENTRY(ASCEND_PFX_WDS_R, "PPP-IN"),
+ ASCEND_MAGIC_ENTRY(ASCEND_PFX_WDD, "WD_DIALOUT_DISP:"),
+ ASCEND_MAGIC_ENTRY(ASCEND_PFX_ETHER, "ETHER"),
+};
+
+#define ASCEND_MAGIC_STRINGS G_N_ELEMENTS(ascend_magic)
+
+#define ASCEND_DATE "Date:"
+
+static gboolean ascend_read(wtap *wth, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info, gint64 *data_offset);
+static gboolean ascend_seek_read(wtap *wth, gint64 seek_off,
+ wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info);
+
+static int ascend_file_type_subtype = -1;
+
+void register_ascend(void);
+
+/* Seeks to the beginning of the next packet, and returns the
+ byte offset at which the header for that packet begins.
+ Returns -1 on failure. */
+static gint64 ascend_find_next_packet(wtap *wth, int *err, gchar **err_info)
+{
+ int byte;
+ gint64 date_off = -1, cur_off, packet_off;
+ size_t string_level[ASCEND_MAGIC_STRINGS];
+ guint string_i = 0;
+ static const gchar ascend_date[] = ASCEND_DATE;
+ size_t ascend_date_len = sizeof ascend_date - 1; /* strlen of a constant string */
+ size_t ascend_date_string_level;
+ guint excessive_read_count = 262144;
+
+ memset(&string_level, 0, sizeof(string_level));
+ ascend_date_string_level = 0;
+
+ while (((byte = file_getc(wth->fh)) != EOF)) {
+ excessive_read_count--;
+
+ if (!excessive_read_count) {
+ *err = 0;
+ return -1;
+ }
+
+ /*
+ * See whether this is the string_level[string_i]th character of
+ * Ascend magic string string_i.
+ */
+ for (string_i = 0; string_i < ASCEND_MAGIC_STRINGS; string_i++) {
+ const gchar *strptr = ascend_magic[string_i].strptr;
+ size_t len = ascend_magic[string_i].strlength;
+
+ if (byte == *(strptr + string_level[string_i])) {
+ /*
+ * Yes, it is, so we need to check for the next character of
+ * that string.
+ */
+ string_level[string_i]++;
+
+ /*
+ * Have we matched the entire string?
+ */
+ if (string_level[string_i] >= len) {
+ /*
+ * Yes.
+ */
+ cur_off = file_tell(wth->fh);
+ if (cur_off == -1) {
+ /* Error. */
+ *err = file_error(wth->fh, err_info);
+ return -1;
+ }
+
+ /* We matched some other type of header. */
+ if (date_off == -1) {
+ /* We haven't yet seen a date header, so this packet
+ doesn't have one.
+ Back up over the header we just read; that's where a read
+ of this packet should start. */
+ packet_off = cur_off - len;
+ } else {
+ /* This packet has a date/time header; a read of it should
+ start at the beginning of *that* header. */
+ packet_off = date_off;
+ }
+
+ goto found;
+ }
+ } else {
+ /*
+ * Not a match for this string, so reset the match process.
+ */
+ string_level[string_i] = 0;
+ }
+ }
+
+ /*
+ * See whether this is the date_string_level'th character of
+ * ASCEND_DATE.
+ */
+ if (byte == *(ascend_date + ascend_date_string_level)) {
+ /*
+ * Yes, it is, so we need to check for the next character of
+ * that string.
+ */
+ ascend_date_string_level++;
+
+ /*
+ * Have we matched the entire string?
+ */
+ if (ascend_date_string_level >= ascend_date_len) {
+ /* We matched a Date: header. It's a special case;
+ remember the offset, but keep looking for other
+ headers.
+
+ Reset the amount of Date: header that we've matched,
+ so that we start the process of matching a Date:
+ header all over again.
+
+ XXX - what if we match multiple Date: headers before
+ matching some other header? */
+ cur_off = file_tell(wth->fh);
+ if (cur_off == -1) {
+ /* Error. */
+ *err = file_error(wth->fh, err_info);
+ return -1;
+ }
+
+ date_off = cur_off - ascend_date_len;
+ ascend_date_string_level = 0;
+ }
+ } else {
+ /*
+ * Not a match for the Date: string, so reset the match process.
+ */
+ ascend_date_string_level = 0;
+ }
+ }
+
+ *err = file_error(wth->fh, err_info);
+ return -1;
+
+found:
+ /*
+ * Move to where the read for this packet should start, and return
+ * that seek offset.
+ */
+ if (file_seek(wth->fh, packet_off, SEEK_SET, err) == -1)
+ return -1;
+
+ return packet_off;
+}
+
+wtap_open_return_val ascend_open(wtap *wth, int *err, gchar **err_info)
+{
+ gint64 offset;
+ guint8 buf[ASCEND_MAX_PKT_LEN];
+ ascend_state_t parser_state = {0};
+ ws_statb64 statbuf;
+ ascend_t *ascend;
+ wtap_rec rec;
+
+ /* We haven't yet allocated a data structure for our private stuff;
+ set the pointer to null, so that "ascend_find_next_packet()" knows
+ not to fill it in. */
+ wth->priv = NULL;
+
+ offset = ascend_find_next_packet(wth, err, err_info);
+ if (offset == -1) {
+ if (*err != 0 && *err != WTAP_ERR_SHORT_READ)
+ return WTAP_OPEN_ERROR; /* read error */
+ return WTAP_OPEN_NOT_MINE; /* EOF */
+ }
+
+ /* Do a trial parse of the first packet just found to see if we might
+ really have an Ascend file. If it fails with an actual error,
+ fail; those will be I/O errors. */
+ parser_state.fh = wth->fh;
+ parser_state.pseudo_header = &rec.rec_header.packet_header.pseudo_header.ascend;
+ if (run_ascend_parser(buf, &parser_state, err, err_info) != 0 && *err != 0) {
+ /* An I/O error. */
+ return WTAP_OPEN_ERROR;
+ }
+
+ /* Either the parse succeeded, or it failed but didn't get an I/O
+ error.
+
+ If we got at least some data, return success even if the parser
+ reported an error. This is because the debug header gives the
+ number of bytes on the wire, not actually how many bytes are in
+ the trace. We won't know where the data ends until we run into
+ the next packet. */
+ if (parser_state.caplen == 0) {
+ /* We read no data, so this presumably isn't an Ascend file. */
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ wth->file_type_subtype = ascend_file_type_subtype;
+ wth->file_encap = WTAP_ENCAP_ASCEND;
+
+ wth->snapshot_length = ASCEND_MAX_PKT_LEN;
+ wth->subtype_read = ascend_read;
+ wth->subtype_seek_read = ascend_seek_read;
+ ascend = g_new(ascend_t, 1);
+ wth->priv = (void *)ascend;
+
+ /* The first packet we want to read is the one that
+ "ascend_find_next_packet()" just found; start searching
+ for it at the offset at which it found it. */
+ ascend->next_packet_seek_start = offset;
+
+ /* MAXen and Pipelines report the time since reboot. In order to keep
+ from reporting packet times near the epoch, we subtract the first
+ packet's timestamp from the capture file's ctime, which gives us an
+ offset that we can apply to each packet.
+ */
+ if (wtap_fstat(wth, &statbuf, err) == -1) {
+ return WTAP_OPEN_ERROR;
+ }
+ ascend->inittime = statbuf.st_ctime;
+ ascend->adjusted = FALSE;
+ wth->file_tsprec = WTAP_TSPREC_USEC;
+
+ /*
+ * Add an IDB; we don't know how many interfaces were
+ * involved, so we just say one interface, about which
+ * we only know the link-layer type, snapshot length,
+ * and time stamp resolution.
+ */
+ wtap_add_generated_idb(wth);
+
+ return WTAP_OPEN_MINE;
+}
+
+/* Parse the capture file.
+ Returns TRUE if we got a packet, FALSE otherwise. */
+static gboolean
+parse_ascend(ascend_t *ascend, FILE_T fh, wtap_rec *rec, Buffer *buf,
+ guint length, gint64 *next_packet_seek_start_ret,
+ int *err, gchar **err_info)
+{
+ ascend_state_t parser_state = {0};
+ int retval;
+
+ ws_buffer_assure_space(buf, length);
+ parser_state.fh = fh;
+ parser_state.pseudo_header = &rec->rec_header.packet_header.pseudo_header.ascend;
+
+ retval = run_ascend_parser(ws_buffer_start_ptr(buf), &parser_state, err, err_info);
+
+ /* Did we see any data (hex bytes)? */
+ if (parser_state.first_hexbyte) {
+ /* Yes. Provide the offset of the first byte so that our caller can
+ tip off ascend_find_next_packet() as to where to look for the next
+ packet, if any. */
+ if (next_packet_seek_start_ret != NULL)
+ *next_packet_seek_start_ret = parser_state.first_hexbyte;
+ } else {
+ /* No. Maybe this record was broken; sometimes, a header will be
+ printed but the data will be omitted, or worse -- two headers will
+ be printed, followed by the data for each.
+
+ Because of this, we need to be fairly tolerant of what we accept
+ here. Provide our current offset so that our caller can tell
+ ascend_find_next_packet() to skip over what we've read so far so
+ we can try reading a new packet.
+
+ . That keeps us from getting into an infinite loop reading a broken
+ trace. */
+ if (next_packet_seek_start_ret != NULL)
+ *next_packet_seek_start_ret = file_tell(fh);
+
+ /* Don't treat that as a fatal error; pretend the parse succeeded. */
+ retval = 0;
+ }
+
+ /* if we got at least some data, return success even if the parser
+ reported an error. This is because the debug header gives the number
+ of bytes on the wire, not actually how many bytes are in the trace.
+ We won't know where the data ends until we run into the next packet. */
+ if (parser_state.caplen) {
+ if (! ascend->adjusted) {
+ ascend->adjusted = TRUE;
+ if (parser_state.saw_timestamp) {
+ /*
+ * Capture file contained a date and time.
+ * We do this only if this is the very first packet we've seen -
+ * i.e., if "ascend->adjusted" is false - because
+ * if we get a date and time after the first packet, we can't
+ * go back and adjust the time stamps of the packets we've already
+ * processed, and basing the time stamps of this and following
+ * packets on the time stamp from the file text rather than the
+ * ctime of the capture file means times before this and after
+ * this can't be compared.
+ */
+ ascend->inittime = parser_state.timestamp;
+ }
+ if (ascend->inittime > parser_state.secs)
+ ascend->inittime -= parser_state.secs;
+ }
+ rec->rec_type = REC_TYPE_PACKET;
+ rec->block = wtap_block_create(WTAP_BLOCK_PACKET);
+ rec->presence_flags = WTAP_HAS_TS|WTAP_HAS_CAP_LEN;
+ rec->ts.secs = parser_state.secs + ascend->inittime;
+ rec->ts.nsecs = parser_state.usecs * 1000;
+ rec->rec_header.packet_header.caplen = parser_state.caplen;
+ rec->rec_header.packet_header.len = parser_state.wirelen;
+
+ return TRUE;
+ }
+
+ /* Didn't see any data. Still, perhaps the parser was happy. */
+ if (retval) {
+ if (*err == 0) {
+ /* Parser failed, but didn't report an I/O error, so a parse error.
+ Return WTAP_ERR_BAD_FILE, with the parse error as the error string. */
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup((parser_state.ascend_parse_error != NULL) ? parser_state.ascend_parse_error : "parse error");
+ }
+ } else {
+ if (*err == 0) {
+ /* Parser succeeded, but got no data, and didn't report an I/O error.
+ Return WTAP_ERR_BAD_FILE, with a "got no data" error string. */
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("no data returned by parse");
+ }
+ }
+ return FALSE;
+}
+
+/* Read the next packet; called from wtap_read(). */
+static gboolean ascend_read(wtap *wth, wtap_rec *rec, Buffer *buf, int *err,
+ gchar **err_info, gint64 *data_offset)
+{
+ ascend_t *ascend = (ascend_t *)wth->priv;
+ gint64 offset;
+
+ /* parse_ascend() will advance the point at which to look for the next
+ packet's header, to just after the last packet's header (ie. at the
+ start of the last packet's data). We have to get past the last
+ packet's header because we might mistake part of it for a new header. */
+ if (file_seek(wth->fh, ascend->next_packet_seek_start,
+ SEEK_SET, err) == -1)
+ return FALSE;
+
+ offset = ascend_find_next_packet(wth, err, err_info);
+ if (offset == -1) {
+ /* EOF or read error */
+ return FALSE;
+ }
+ if (!parse_ascend(ascend, wth->fh, rec, buf, wth->snapshot_length,
+ &ascend->next_packet_seek_start, err, err_info))
+ return FALSE;
+
+ /* Flex might have gotten an EOF and caused *err to be set to
+ WTAP_ERR_SHORT_READ. If so, that's not an error, as the parser
+ didn't return an error; set *err to 0, and get rid of any error
+ string. */
+ *err = 0;
+ if (*err_info != NULL) {
+ g_free(*err_info);
+ *err_info = NULL;
+ }
+ *data_offset = offset;
+ return TRUE;
+}
+
+static gboolean ascend_seek_read(wtap *wth, gint64 seek_off,
+ wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info)
+{
+ ascend_t *ascend = (ascend_t *)wth->priv;
+
+ if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
+ return FALSE;
+ if (!parse_ascend(ascend, wth->random_fh, rec, buf,
+ wth->snapshot_length, NULL, err, err_info))
+ return FALSE;
+
+ /* Flex might have gotten an EOF and caused *err to be set to
+ WTAP_ERR_SHORT_READ. If so, that's not an error, as the parser
+ didn't return an error; set *err to 0, and get rid of any error
+ string. */
+ *err = 0;
+ if (*err_info != NULL) {
+ g_free(*err_info);
+ *err_info = NULL;
+ }
+ return TRUE;
+}
+
+static const struct supported_block_type ascend_blocks_supported[] = {
+ /*
+ * We support packet blocks, with no comments or other options.
+ */
+ { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
+};
+
+static const struct file_type_subtype_info ascend_info = {
+ "Lucent/Ascend access server trace", "ascend", "txt", NULL,
+ FALSE, BLOCKS_SUPPORTED(ascend_blocks_supported),
+ NULL, NULL, NULL
+};
+
+void register_ascend(void)
+{
+ ascend_file_type_subtype = wtap_register_file_type_subtype(&ascend_info);
+
+ /*
+ * Register name for backwards compatibility with the
+ * wtap_filetypes table in Lua.
+ */
+ wtap_register_backwards_compatibility_lua_name("ASCEND",
+ ascend_file_type_subtype);
+}
diff --git a/wiretap/ascendtext.h b/wiretap/ascendtext.h
new file mode 100644
index 00000000..74ad6986
--- /dev/null
+++ b/wiretap/ascendtext.h
@@ -0,0 +1,24 @@
+/** @file
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ */
+
+#ifndef __ASCENDTEXT_H__
+#define __ASCENDTEXT_H__
+#include <glib.h>
+
+/*
+ * ASCEND_MAX_PKT_LEN is < WTAP_MAX_PACKET_SIZE_STANDARD, so we don't need to
+ * check the packet length.
+ */
+#define ASCEND_MAX_DATA_ROWS 8
+#define ASCEND_MAX_DATA_COLS 16
+#define ASCEND_MAX_PKT_LEN (ASCEND_MAX_DATA_ROWS * ASCEND_MAX_DATA_COLS)
+
+wtap_open_return_val ascend_open(wtap *wth, int *err, gchar **err_info);
+
+#endif
diff --git a/wiretap/atm.c b/wiretap/atm.c
new file mode 100644
index 00000000..47b471dd
--- /dev/null
+++ b/wiretap/atm.c
@@ -0,0 +1,134 @@
+/* atm.c
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "config.h"
+#include "wtap-int.h"
+#include "atm.h"
+
+/*
+ * Routines to use with ATM capture file types that don't include information
+ * about the *type* of ATM traffic (or, at least, where we haven't found
+ * that information).
+ *
+ * We assume the traffic is AAL5, unless it's VPI 0/VCI 5, in which case
+ * we assume it's the signalling AAL.
+ */
+
+void
+atm_guess_traffic_type(wtap_rec *rec, const guint8 *pd)
+{
+ /*
+ * Start out assuming nothing other than that it's AAL5.
+ */
+ rec->rec_header.packet_header.pseudo_header.atm.aal = AAL_5;
+ rec->rec_header.packet_header.pseudo_header.atm.type = TRAF_UNKNOWN;
+ rec->rec_header.packet_header.pseudo_header.atm.subtype = TRAF_ST_UNKNOWN;
+
+ if (rec->rec_header.packet_header.pseudo_header.atm.vpi == 0) {
+ /*
+ * Traffic on some PVCs with a VPI of 0 and certain
+ * VCIs is of particular types.
+ */
+ switch (rec->rec_header.packet_header.pseudo_header.atm.vci) {
+
+ case 5:
+ /*
+ * Signalling AAL.
+ */
+ rec->rec_header.packet_header.pseudo_header.atm.aal = AAL_SIGNALLING;
+ return;
+
+ case 16:
+ /*
+ * ILMI.
+ */
+ rec->rec_header.packet_header.pseudo_header.atm.type = TRAF_ILMI;
+ return;
+ }
+ }
+
+ /*
+ * OK, we can't tell what it is based on the VPI/VCI; try
+ * guessing based on the contents, if we have enough data
+ * to guess.
+ */
+
+ if (rec->rec_header.packet_header.caplen >= 3) {
+ if (pd[0] == 0xaa && pd[1] == 0xaa && pd[2] == 0x03) {
+ /*
+ * Looks like a SNAP header; assume it's LLC
+ * multiplexed RFC 1483 traffic.
+ */
+ rec->rec_header.packet_header.pseudo_header.atm.type = TRAF_LLCMX;
+ } else if ((rec->rec_header.packet_header.pseudo_header.atm.aal5t_len && rec->rec_header.packet_header.pseudo_header.atm.aal5t_len < 16) ||
+ rec->rec_header.packet_header.caplen < 16) {
+ /*
+ * As this cannot be a LANE Ethernet frame (less
+ * than 2 bytes of LANE header + 14 bytes of
+ * Ethernet header) we can try it as a SSCOP frame.
+ */
+ rec->rec_header.packet_header.pseudo_header.atm.aal = AAL_SIGNALLING;
+ } else if (pd[0] == 0x83 || pd[0] == 0x81) {
+ /*
+ * MTP3b headers often encapsulate
+ * a SCCP or MTN in the 3G network.
+ * This should cause 0x83 or 0x81
+ * in the first byte.
+ */
+ rec->rec_header.packet_header.pseudo_header.atm.aal = AAL_SIGNALLING;
+ } else {
+ /*
+ * Assume it's LANE.
+ */
+ rec->rec_header.packet_header.pseudo_header.atm.type = TRAF_LANE;
+ atm_guess_lane_type(rec, pd);
+ }
+ } else {
+ /*
+ * Not only VCI 5 is used for signaling. It might be
+ * one of these VCIs.
+ */
+ rec->rec_header.packet_header.pseudo_header.atm.aal = AAL_SIGNALLING;
+ }
+}
+
+void
+atm_guess_lane_type(wtap_rec *rec, const guint8 *pd)
+{
+ if (rec->rec_header.packet_header.caplen >= 2) {
+ if (pd[0] == 0xff && pd[1] == 0x00) {
+ /*
+ * Looks like LE Control traffic.
+ */
+ rec->rec_header.packet_header.pseudo_header.atm.subtype = TRAF_ST_LANE_LE_CTRL;
+ } else {
+ /*
+ * XXX - Ethernet, or Token Ring?
+ * Assume Ethernet for now; if we see earlier
+ * LANE traffic, we may be able to figure out
+ * the traffic type from that, but there may
+ * still be situations where the user has to
+ * tell us.
+ */
+ rec->rec_header.packet_header.pseudo_header.atm.subtype = TRAF_ST_LANE_802_3;
+ }
+ }
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/wiretap/atm.h b/wiretap/atm.h
new file mode 100644
index 00000000..4f560e3e
--- /dev/null
+++ b/wiretap/atm.h
@@ -0,0 +1,26 @@
+/** @file
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __ATM_H__
+#define __ATM_H__
+#include <glib.h>
+#include "ws_symbol_export.h"
+
+/*
+ * Routines to use with ATM capture file types that don't include information
+ * about the *type* of ATM traffic (or, at least, where we haven't found
+ * that information).
+ */
+
+extern void
+atm_guess_traffic_type(wtap_rec *rec, const guint8 *pd);
+
+extern void
+atm_guess_lane_type(wtap_rec *rec, const guint8 *pd);
+
+#endif /* __ATM_H__ */
diff --git a/wiretap/autosar_dlt.c b/wiretap/autosar_dlt.c
new file mode 100644
index 00000000..5310443c
--- /dev/null
+++ b/wiretap/autosar_dlt.c
@@ -0,0 +1,359 @@
+/* autosar_dlt.c
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * Support for DLT file format as defined by AUTOSAR et. al.
+ * Copyright (c) 2022-2022 by Dr. Lars Voelker <lars.voelker@technica-engineering.de>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+/*
+ * Sources for specification:
+ * https://www.autosar.org/fileadmin/user_upload/standards/classic/21-11/AUTOSAR_SWS_DiagnosticLogAndTrace.pdf
+ * https://www.autosar.org/fileadmin/user_upload/standards/foundation/21-11/AUTOSAR_PRS_LogAndTraceProtocol.pdf
+ * https://github.com/COVESA/dlt-viewer
+ */
+
+#include <config.h>
+
+#define WS_LOG_DOMAIN LOG_DOMAIN_WIRETAP
+
+#include <errno.h>
+#include "autosar_dlt.h"
+
+#include "file_wrappers.h"
+#include "wtap-int.h"
+
+static const guint8 dlt_magic[] = { 'D', 'L', 'T', 0x01 };
+
+static int autosar_dlt_file_type_subtype = -1;
+
+
+void register_autosar_dlt(void);
+static gboolean autosar_dlt_read(wtap *wth, wtap_rec *rec, Buffer *buf, int *err, gchar **err_info, gint64 *data_offset);
+static gboolean autosar_dlt_seek_read(wtap *wth, gint64 seek_off, wtap_rec* rec, Buffer *buf, int *err, gchar **err_info);
+static void autosar_dlt_close(wtap *wth);
+
+
+typedef struct autosar_dlt_blockheader {
+ guint8 magic[4];
+ guint32 timestamp_s;
+ guint32 timestamp_us;
+ guint8 ecu_id[4];
+} autosar_dlt_blockheader_t;
+
+typedef struct autosar_dlt_itemheader {
+ guint8 header_type;
+ guint8 counter;
+ guint16 length;
+} autosar_dlt_itemheader_t;
+
+
+typedef struct autosar_dlt_data {
+ GHashTable *ecu_to_iface_ht;
+ guint32 next_interface_id;
+} autosar_dlt_t;
+
+typedef struct autosar_dlt_params {
+ wtap *wth;
+ wtap_rec *rec;
+ Buffer *buf;
+ FILE_T fh;
+
+ autosar_dlt_t *dlt_data;
+} autosar_dlt_params_t;
+
+static gint
+autosar_dlt_calc_key(guint8 ecu[4]) {
+ return (gint)(ecu[0] << 24 | ecu[1] << 16 | ecu[2] << 8 | ecu[3]);
+}
+
+static guint32
+autosar_dlt_add_interface(autosar_dlt_params_t *params, guint8 ecu[4]) {
+ wtap_block_t int_data = wtap_block_create(WTAP_BLOCK_IF_ID_AND_INFO);
+ wtapng_if_descr_mandatory_t *if_descr_mand = (wtapng_if_descr_mandatory_t*)wtap_block_get_mandatory_data(int_data);
+
+ if_descr_mand->wtap_encap = WTAP_ENCAP_AUTOSAR_DLT;
+ wtap_block_add_string_option(int_data, OPT_IDB_NAME, (gchar *)ecu, 4);
+ if_descr_mand->time_units_per_second = 1000 * 1000 * 1000;
+ if_descr_mand->tsprecision = WTAP_TSPREC_NSEC;
+ wtap_block_add_uint8_option(int_data, OPT_IDB_TSRESOL, 9);
+ if_descr_mand->snap_len = WTAP_MAX_PACKET_SIZE_STANDARD;
+ if_descr_mand->num_stat_entries = 0;
+ if_descr_mand->interface_statistics = NULL;
+ wtap_add_idb(params->wth, int_data);
+
+ if (params->wth->file_encap == WTAP_ENCAP_UNKNOWN) {
+ params->wth->file_encap = if_descr_mand->wtap_encap;
+ } else {
+ if (params->wth->file_encap != if_descr_mand->wtap_encap) {
+ params->wth->file_encap = WTAP_ENCAP_PER_PACKET;
+ }
+ }
+
+ gint32 key = autosar_dlt_calc_key(ecu);
+ guint32 iface_id = params->dlt_data->next_interface_id++;
+ g_hash_table_insert(params->dlt_data->ecu_to_iface_ht, GINT_TO_POINTER(key), GUINT_TO_POINTER(iface_id));
+
+ return iface_id;
+}
+
+static guint32
+autosar_dlt_lookup_interface(autosar_dlt_params_t *params, guint8 ecu[4]) {
+ gint32 key = autosar_dlt_calc_key(ecu);
+
+ if (params->dlt_data->ecu_to_iface_ht == NULL) {
+ return 0;
+ }
+
+ gpointer iface = NULL;
+ gboolean found = g_hash_table_lookup_extended(params->dlt_data->ecu_to_iface_ht, GINT_TO_POINTER(key), NULL, &iface);
+
+ if (found) {
+ return GPOINTER_TO_UINT(iface);
+ } else {
+ return autosar_dlt_add_interface(params, ecu);
+ }
+}
+
+static void
+fix_endianness_autosar_dlt_blockheader(autosar_dlt_blockheader_t *header) {
+ header->timestamp_s = GUINT32_FROM_LE(header->timestamp_s);
+ header->timestamp_us = GUINT32_FROM_LE(header->timestamp_us);
+}
+
+static void
+fix_endianness_autosar_dlt_itemheader(autosar_dlt_itemheader_t *header) {
+ header->length = GUINT16_FROM_BE(header->length);
+}
+
+static gboolean
+autosar_dlt_read_block(autosar_dlt_params_t *params, gint64 start_pos, int *err, gchar **err_info) {
+ autosar_dlt_blockheader_t header;
+ autosar_dlt_itemheader_t item_header;
+
+ while (1) {
+ params->buf->first_free = params->buf->start;
+
+ if (!wtap_read_bytes_or_eof(params->fh, &header, sizeof header, err, err_info)) {
+ if (*err == WTAP_ERR_SHORT_READ) {
+ *err = WTAP_ERR_BAD_FILE;
+ g_free(*err_info);
+ *err_info = ws_strdup_printf("AUTOSAR DLT: Capture file cut short! Cannot find storage header at pos 0x%" PRIx64 "!", start_pos);
+ }
+ return FALSE;
+ }
+
+ fix_endianness_autosar_dlt_blockheader(&header);
+
+ if (memcmp(header.magic, dlt_magic, sizeof(dlt_magic))) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("AUTOSAR DLT: Bad capture file! Object magic is not DLT\\x01 at pos 0x%" PRIx64 "!", start_pos);
+ return FALSE;
+ }
+
+ /* Set to the byte after the magic. */
+ guint64 current_start_of_item = file_tell(params->fh) - sizeof header + 4;
+
+ if (!wtap_read_bytes_or_eof(params->fh, &item_header, sizeof item_header, err, err_info)) {
+ *err = WTAP_ERR_BAD_FILE;
+ g_free(*err_info);
+ *err_info = ws_strdup_printf("AUTOSAR DLT: Capture file cut short! Not enough bytes for item header at pos 0x%" PRIx64 "!", start_pos);
+ return FALSE;
+ }
+
+ fix_endianness_autosar_dlt_itemheader(&item_header);
+
+ if (file_seek(params->fh, current_start_of_item, SEEK_SET, err) < 0) {
+ return FALSE;
+ }
+
+ ws_buffer_assure_space(params->buf, (gsize)(item_header.length + sizeof header));
+
+ /* Creating AUTOSAR DLT Encapsulation Header:
+ * guint32 time_s
+ * guint32 time_us
+ * guint8[4] ecuname
+ * guint8[1] 0x00 (termination)
+ * guint8[3] reserved
+ */
+ void *tmpbuf = g_malloc0(sizeof header);
+ if (!wtap_read_bytes_or_eof(params->fh, tmpbuf, sizeof header - 4, err, err_info)) {
+ /* this would have been caught before ...*/
+ *err = WTAP_ERR_BAD_FILE;
+ g_free(*err_info);
+ *err_info = ws_strdup_printf("AUTOSAR DLT: Internal Error! Not enough bytes for storage header at pos 0x%" PRIx64 "!", start_pos);
+ return FALSE;
+ }
+ ws_buffer_append(params->buf, tmpbuf, (gsize)(sizeof header));
+ g_free(tmpbuf);
+
+ tmpbuf = g_try_malloc0(item_header.length);
+ if (tmpbuf == NULL) {
+ *err = ENOMEM; /* we assume we're out of memory */
+ return FALSE;
+ }
+
+ if (!wtap_read_bytes_or_eof(params->fh, tmpbuf, item_header.length, err, err_info)) {
+ *err = WTAP_ERR_BAD_FILE;
+ g_free(*err_info);
+ *err_info = ws_strdup_printf("AUTOSAR DLT: Capture file cut short! Not enough bytes for item at pos 0x%" PRIx64 "!", start_pos);
+ return FALSE;
+ }
+ ws_buffer_append(params->buf, tmpbuf, (gsize)(item_header.length));
+ g_free(tmpbuf);
+
+ params->rec->rec_type = REC_TYPE_PACKET;
+ params->rec->block = wtap_block_create(WTAP_BLOCK_PACKET);
+ params->rec->presence_flags = WTAP_HAS_TS | WTAP_HAS_CAP_LEN | WTAP_HAS_INTERFACE_ID;
+ params->rec->tsprec = WTAP_TSPREC_USEC;
+ params->rec->ts.secs = header.timestamp_s;
+ params->rec->ts.nsecs = header.timestamp_us * 1000;
+
+ params->rec->rec_header.packet_header.caplen = (guint32)(item_header.length + sizeof header);
+ params->rec->rec_header.packet_header.len = (guint32)(item_header.length + sizeof header);
+ params->rec->rec_header.packet_header.pkt_encap = WTAP_ENCAP_AUTOSAR_DLT;
+ params->rec->rec_header.packet_header.interface_id = autosar_dlt_lookup_interface(params, header.ecu_id);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean autosar_dlt_read(wtap *wth, wtap_rec *rec, Buffer *buf, int *err, gchar **err_info, gint64 *data_offset) {
+ autosar_dlt_params_t dlt_tmp;
+
+ dlt_tmp.wth = wth;
+ dlt_tmp.fh = wth->fh;
+ dlt_tmp.rec = rec;
+ dlt_tmp.buf = buf;
+ dlt_tmp.dlt_data = (autosar_dlt_t *)wth->priv;
+
+ *data_offset = file_tell(wth->fh);
+
+ if (!autosar_dlt_read_block(&dlt_tmp, *data_offset, err, err_info)) {
+ ws_debug("couldn't read packet block (data_offset is %" PRId64 ")", *data_offset);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean autosar_dlt_seek_read(wtap *wth, gint64 seek_off, wtap_rec *rec, Buffer *buf, int *err, gchar **err_info) {
+ autosar_dlt_params_t dlt_tmp;
+
+ dlt_tmp.wth = wth;
+ dlt_tmp.fh = wth->random_fh;
+ dlt_tmp.rec = rec;
+ dlt_tmp.buf = buf;
+ dlt_tmp.dlt_data = (autosar_dlt_t *)wth->priv;
+
+ if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
+ return FALSE;
+
+ if (!autosar_dlt_read_block(&dlt_tmp, seek_off, err, err_info)) {
+ ws_debug("couldn't read packet block (seek_off: %" PRId64 ") (err=%d).", seek_off, *err);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void autosar_dlt_close(wtap *wth) {
+ autosar_dlt_t *dlt = (autosar_dlt_t *)wth->priv;
+
+ if (dlt != NULL && dlt->ecu_to_iface_ht != NULL) {
+ g_hash_table_destroy(dlt->ecu_to_iface_ht);
+ dlt->ecu_to_iface_ht = NULL;
+ }
+
+ g_free(dlt);
+ wth->priv = NULL;
+
+ return;
+}
+
+wtap_open_return_val
+autosar_dlt_open(wtap *wth, int *err, gchar **err_info) {
+ guint8 magic[4];
+ autosar_dlt_t *dlt;
+
+ ws_debug("opening file");
+
+ if (!wtap_read_bytes_or_eof(wth->fh, &magic, sizeof magic, err, err_info)) {
+ ws_debug("wtap_read_bytes_or_eof() failed, err = %d.", *err);
+ if (*err == 0 || *err == WTAP_ERR_SHORT_READ) {
+ *err = 0;
+ g_free(*err_info);
+ *err_info = NULL;
+ return WTAP_OPEN_NOT_MINE;
+ }
+ return WTAP_OPEN_ERROR;
+ }
+
+ if (memcmp(magic, dlt_magic, sizeof(dlt_magic))) {
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ file_seek(wth->fh, 0, SEEK_SET, err);
+
+ dlt = g_new(autosar_dlt_t, 1);
+ dlt->ecu_to_iface_ht = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, NULL);
+ dlt->next_interface_id = 0;
+
+ wth->priv = (void *)dlt;
+ wth->file_encap = WTAP_ENCAP_UNKNOWN;
+ wth->snapshot_length = 0;
+ wth->file_tsprec = WTAP_TSPREC_UNKNOWN;
+ wth->subtype_read = autosar_dlt_read;
+ wth->subtype_seek_read = autosar_dlt_seek_read;
+ wth->subtype_close = autosar_dlt_close;
+ wth->file_type_subtype = autosar_dlt_file_type_subtype;
+
+ return WTAP_OPEN_MINE;
+}
+
+/* Options for interface blocks. */
+static const struct supported_option_type interface_block_options_supported[] = {
+ /* No comments, just an interface name. */
+ { OPT_IDB_NAME, ONE_OPTION_SUPPORTED }
+};
+
+static const struct supported_block_type dlt_blocks_supported[] = {
+ { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED },
+ { WTAP_BLOCK_IF_ID_AND_INFO, MULTIPLE_BLOCKS_SUPPORTED, OPTION_TYPES_SUPPORTED(interface_block_options_supported) },
+};
+
+static const struct file_type_subtype_info dlt_info = {
+ "AUTOSAR DLT Logfile", "dlt", "dlt", NULL,
+ FALSE, BLOCKS_SUPPORTED(dlt_blocks_supported),
+ NULL, NULL, NULL
+};
+
+void register_autosar_dlt(void)
+{
+ autosar_dlt_file_type_subtype = wtap_register_file_type_subtype(&dlt_info);
+
+ /*
+ * Register name for backwards compatibility with the
+ * wtap_filetypes table in Lua.
+ */
+ wtap_register_backwards_compatibility_lua_name("DLT", autosar_dlt_file_type_subtype);
+}
+
+
+ /*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * vi: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */
diff --git a/wiretap/autosar_dlt.h b/wiretap/autosar_dlt.h
new file mode 100644
index 00000000..382deb30
--- /dev/null
+++ b/wiretap/autosar_dlt.h
@@ -0,0 +1,39 @@
+/* autosar_dlt.h
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * Support for DLT file format as defined by AUTOSAR et. al.
+ * Copyright (c) 2022-2022 by Dr. Lars Voelker <lars.voelker@technica-engineering.de>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+ /*
+ * Sources for specification:
+ * https://www.autosar.org/fileadmin/user_upload/standards/classic/21-11/AUTOSAR_SWS_DiagnosticLogAndTrace.pdf
+ * https://www.autosar.org/fileadmin/user_upload/standards/foundation/21-11/AUTOSAR_PRS_LogAndTraceProtocol.pdf
+ * https://github.com/COVESA/dlt-viewer
+ */
+
+#ifndef __W_AUTOSAR_DLT_H__
+#define __W_AUTOSAR_DLT_H__
+
+#include "wtap.h"
+
+wtap_open_return_val autosar_dlt_open(wtap *wth, int *err, gchar **err_info);
+
+#endif
+
+ /*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * vi: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */
diff --git a/wiretap/ber.c b/wiretap/ber.c
new file mode 100644
index 00000000..ead975a4
--- /dev/null
+++ b/wiretap/ber.c
@@ -0,0 +1,167 @@
+/* ber.c
+ *
+ * Basic Encoding Rules (BER) file reading
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "config.h"
+
+#include "wtap-int.h"
+#include "file_wrappers.h"
+#include <wsutil/buffer.h>
+#include "ber.h"
+
+
+#define BER_CLASS_UNI 0
+#define BER_CLASS_APP 1
+#define BER_CLASS_CON 2
+
+#define BER_UNI_TAG_SEQ 16 /* SEQUENCE, SEQUENCE OF */
+#define BER_UNI_TAG_SET 17 /* SET, SET OF */
+
+static int ber_file_type_subtype = -1;
+
+void register_ber(void);
+
+static gboolean ber_full_file_read(wtap *wth, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info,
+ gint64 *data_offset)
+{
+ if (!wtap_full_file_read(wth, rec, buf, err, err_info, data_offset))
+ return FALSE;
+
+ /* Pass the file name. */
+ rec->rec_header.packet_header.pseudo_header.ber.pathname = wth->pathname;
+ return TRUE;
+}
+
+static gboolean ber_full_file_seek_read(wtap *wth, gint64 seek_off,
+ wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info)
+{
+ if (!wtap_full_file_seek_read(wth, seek_off, rec, buf, err, err_info))
+ return FALSE;
+
+ /* Pass the file name. */
+ rec->rec_header.packet_header.pseudo_header.ber.pathname = wth->pathname;
+ return TRUE;
+}
+
+wtap_open_return_val ber_open(wtap *wth, int *err, gchar **err_info)
+{
+#define BER_BYTES_TO_CHECK 8
+ guint8 bytes[BER_BYTES_TO_CHECK];
+ guint8 ber_id;
+ gint8 ber_class;
+ gint8 ber_tag;
+ gboolean ber_pc;
+ guint8 oct, nlb = 0;
+ int len = 0;
+ gint64 file_size;
+ int offset = 0, i;
+
+ if (!wtap_read_bytes(wth->fh, &bytes, BER_BYTES_TO_CHECK, err, err_info)) {
+ if (*err != WTAP_ERR_SHORT_READ)
+ return WTAP_OPEN_ERROR;
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ ber_id = bytes[offset++];
+
+ ber_class = (ber_id>>6) & 0x03;
+ ber_pc = (ber_id>>5) & 0x01;
+ ber_tag = ber_id & 0x1F;
+
+ /* it must be constructed and either a SET or a SEQUENCE */
+ /* or a CONTEXT/APPLICATION less than 32 (arbitrary) */
+ if(!(ber_pc &&
+ (((ber_class == BER_CLASS_UNI) && ((ber_tag == BER_UNI_TAG_SET) || (ber_tag == BER_UNI_TAG_SEQ))) ||
+ (((ber_class == BER_CLASS_CON) || (ber_class == BER_CLASS_APP)) && (ber_tag < 32)))))
+ return WTAP_OPEN_NOT_MINE;
+
+ /* now check the length */
+ oct = bytes[offset++];
+
+ if(oct != 0x80) {
+ /* not indefinite length encoded */
+
+ if(!(oct & 0x80))
+ /* length fits into a single byte */
+ len = oct;
+ else {
+ nlb = oct & 0x7F; /* number of length bytes */
+
+ if((nlb > 0) && (nlb <= (BER_BYTES_TO_CHECK - 2))) {
+ /* not indefinite length and we have read enough bytes to compute the length */
+ i = nlb;
+ while(i--) {
+ oct = bytes[offset++];
+ len = (len<<8) + oct;
+ }
+ }
+ }
+
+ len += (2 + nlb); /* add back Tag and Length bytes */
+ file_size = wtap_file_size(wth, err);
+
+ if(len != file_size) {
+ return WTAP_OPEN_NOT_MINE; /* not ASN.1 */
+ }
+ } else {
+ /* Indefinite length encoded - assume it is BER */
+ }
+
+ /* seek back to the start of the file */
+ if (file_seek(wth->fh, 0, SEEK_SET, err) == -1)
+ return WTAP_OPEN_ERROR;
+
+ wth->file_type_subtype = ber_file_type_subtype;
+ wth->file_encap = WTAP_ENCAP_BER;
+ wth->snapshot_length = 0;
+
+ wth->subtype_read = ber_full_file_read;
+ wth->subtype_seek_read = ber_full_file_seek_read;
+ wth->file_tsprec = WTAP_TSPREC_SEC;
+
+ return WTAP_OPEN_MINE;
+}
+
+static const struct supported_block_type ber_blocks_supported[] = {
+ /*
+ * These are file formats that we dissect, so we provide only one
+ * "packet" with the file's contents, and don't support any
+ * options.
+ */
+ { WTAP_BLOCK_PACKET, ONE_BLOCK_SUPPORTED, NO_OPTIONS_SUPPORTED }
+};
+
+static const struct file_type_subtype_info ber_info = {
+ "ASN.1 Basic Encoding Rules", "ber", NULL, NULL,
+ FALSE, BLOCKS_SUPPORTED(ber_blocks_supported),
+ NULL, NULL, NULL
+};
+
+void register_ber(void)
+{
+ ber_file_type_subtype = wtap_register_file_type_subtype(&ber_info);
+
+ /*
+ * Register name for backwards compatibility with the
+ * wtap_filetypes table in Lua.
+ */
+ wtap_register_backwards_compatibility_lua_name("BER", ber_file_type_subtype);
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local Variables:
+ * c-basic-offset: 2
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * vi: set shiftwidth=2 tabstop=8 expandtab:
+ * :indentSize=2:tabSize=8:noTabs=true:
+ */
diff --git a/wiretap/ber.h b/wiretap/ber.h
new file mode 100644
index 00000000..6c0c70c6
--- /dev/null
+++ b/wiretap/ber.h
@@ -0,0 +1,16 @@
+/** @file
+ *
+ * Basic Encoding Rules (BER) file reading
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ */
+
+#ifndef __BER_H__
+#define __BER_H__
+#include <glib.h>
+#include "ws_symbol_export.h"
+
+wtap_open_return_val ber_open(wtap *wth, int *err, gchar **err_info);
+
+#endif
diff --git a/wiretap/blf.c b/wiretap/blf.c
new file mode 100644
index 00000000..9c08c5ec
--- /dev/null
+++ b/wiretap/blf.c
@@ -0,0 +1,2549 @@
+/* blf.c
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * File format support for the Binary Log File (BLF) file format from
+ * Vector Informatik decoder
+ * Copyright (c) 2021-2022 by Dr. Lars Voelker <lars.voelker@technica-engineering.de>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+ /*
+ * The following was used as a reference for the file format:
+ * https://bitbucket.org/tobylorenz/vector_blf
+ * The repo above includes multiple examples files as well.
+ */
+
+#include <config.h>
+#define WS_LOG_DOMAIN LOG_DOMAIN_WIRETAP
+
+#include "blf.h"
+
+#include <epan/dissectors/packet-socketcan.h>
+#include <string.h>
+#include <errno.h>
+#include <epan/value_string.h>
+#include <wsutil/wslog.h>
+#include <wsutil/exported_pdu_tlvs.h>
+#include "file_wrappers.h"
+#include "wtap-int.h"
+
+#ifdef HAVE_ZLIB
+#define ZLIB_CONST
+#include <zlib.h>
+#endif /* HAVE_ZLIB */
+
+static const guint8 blf_magic[] = { 'L', 'O', 'G', 'G' };
+static const guint8 blf_obj_magic[] = { 'L', 'O', 'B', 'J' };
+
+static int blf_file_type_subtype = -1;
+
+void register_blf(void);
+
+static gboolean blf_read(wtap *wth, wtap_rec *rec, Buffer *buf, int *err, gchar **err_info, gint64 *data_offset);
+static gboolean blf_seek_read(wtap *wth, gint64 seek_off, wtap_rec* rec, Buffer *buf, int *err, gchar **err_info);
+static void blf_close(wtap *wth);
+
+/*
+ * The virtual buffer looks like this (skips all headers):
+ * uncompressed log container data
+ * uncompressed log container data
+ * ...
+ *
+ * The "real" positions, length, etc. reference this layout and not the file.
+ * When no compression is used the file is accessed directly.
+ */
+
+typedef struct blf_log_container {
+ gint64 infile_start_pos; /* start position of log container in file */
+ guint64 infile_length; /* length of log container in file */
+ guint64 infile_data_start; /* start position of data in log container in file */
+
+ gint64 real_start_pos; /* decompressed (virtual) start position including header */
+ guint64 real_length; /* decompressed length */
+ gint64 real_first_object_pos; /* where does the first obj start? */
+ guint64 real_leftover_bytes; /* how many bytes are left over for the next container? */
+
+ guint16 compression_method; /* 0: uncompressed, 2: zlib */
+
+ unsigned char *real_data; /* cache for decompressed data */
+} blf_log_container_t;
+
+typedef struct blf_data {
+ gint64 start_of_last_obj;
+ gint64 current_real_seek_pos;
+ guint64 start_offset_ns;
+
+ guint current_log_container;
+ GArray *log_containers;
+
+ GHashTable *channel_to_iface_ht;
+ guint32 next_interface_id;
+} blf_t;
+
+typedef struct blf_params {
+ wtap *wth;
+ wtap_rec *rec;
+ Buffer *buf;
+ FILE_T fh;
+
+ blf_t *blf_data;
+} blf_params_t;
+
+typedef struct blf_channel_to_iface_entry {
+ int pkt_encap;
+ guint16 channel;
+ guint16 hwchannel;
+ guint32 interface_id;
+} blf_channel_to_iface_entry_t;
+
+static void
+blf_free_key(gpointer key) {
+ g_free(key);
+}
+
+static void
+blf_free_channel_to_iface_entry(gpointer data) {
+ g_free(data);
+}
+
+static gint64
+blf_calc_key_value(int pkt_encap, guint16 channel, guint16 hwchannel) {
+ return (gint64)(((guint64)pkt_encap << 32) | ((guint64)hwchannel << 16) | (guint64)channel);
+}
+
+static void add_interface_name(wtap_block_t *int_data, int pkt_encap, guint16 channel, guint16 hwchannel, gchar *name) {
+ if (name != NULL) {
+ wtap_block_add_string_option_format(*int_data, OPT_IDB_NAME, "%s", name);
+ } else {
+ switch (pkt_encap) {
+ case WTAP_ENCAP_ETHERNET:
+ /* we use UINT16_MAX to encode no hwchannel */
+ if (hwchannel == UINT16_MAX) {
+ wtap_block_add_string_option_format(*int_data, OPT_IDB_NAME, "ETH-%u", channel);
+ } else {
+ wtap_block_add_string_option_format(*int_data, OPT_IDB_NAME, "ETH-%u-%u", channel, hwchannel);
+ }
+ break;
+ case WTAP_ENCAP_IEEE_802_11:
+ wtap_block_add_string_option_format(*int_data, OPT_IDB_NAME, "WLAN-%u", channel);
+ break;
+ case WTAP_ENCAP_FLEXRAY:
+ wtap_block_add_string_option_format(*int_data, OPT_IDB_NAME, "FR-%u", channel);
+ break;
+ case WTAP_ENCAP_LIN:
+ wtap_block_add_string_option_format(*int_data, OPT_IDB_NAME, "LIN-%u", channel);
+ break;
+ case WTAP_ENCAP_SOCKETCAN:
+ wtap_block_add_string_option_format(*int_data, OPT_IDB_NAME, "CAN-%u", channel);
+ break;
+ default:
+ wtap_block_add_string_option_format(*int_data, OPT_IDB_NAME, "ENCAP_%d-%u", pkt_encap, channel);
+ }
+ }
+}
+
+static guint32
+blf_add_interface(blf_params_t *params, int pkt_encap, guint32 channel, guint16 hwchannel, gchar *name) {
+ wtap_block_t int_data = wtap_block_create(WTAP_BLOCK_IF_ID_AND_INFO);
+ wtapng_if_descr_mandatory_t *if_descr_mand = (wtapng_if_descr_mandatory_t*)wtap_block_get_mandatory_data(int_data);
+ blf_channel_to_iface_entry_t *item = NULL;
+
+ if_descr_mand->wtap_encap = pkt_encap;
+ add_interface_name(&int_data, pkt_encap, channel, hwchannel, name);
+ /*
+ * The time stamp resolution in these files can be per-record;
+ * the maximum resolution is nanoseconds, so we specify that
+ * as the interface's resolution.
+ *
+ * We set the resolution for a record on a per-record basis,
+ * based on what the record specifies.
+ */
+ if_descr_mand->time_units_per_second = 1000 * 1000 * 1000;
+ if_descr_mand->tsprecision = WTAP_TSPREC_NSEC;
+ wtap_block_add_uint8_option(int_data, OPT_IDB_TSRESOL, 9);
+ if_descr_mand->snap_len = WTAP_MAX_PACKET_SIZE_STANDARD;
+ if_descr_mand->num_stat_entries = 0;
+ if_descr_mand->interface_statistics = NULL;
+ wtap_add_idb(params->wth, int_data);
+
+ if (params->wth->file_encap == WTAP_ENCAP_NONE) {
+ params->wth->file_encap = if_descr_mand->wtap_encap;
+ } else {
+ if (params->wth->file_encap != if_descr_mand->wtap_encap) {
+ params->wth->file_encap = WTAP_ENCAP_PER_PACKET;
+ }
+ }
+
+ gint64 *key = NULL;
+ key = g_new(gint64, 1);
+ *key = blf_calc_key_value(pkt_encap, channel, hwchannel);
+
+ item = g_new(blf_channel_to_iface_entry_t, 1);
+ item->channel = channel;
+ item->hwchannel = hwchannel;
+ item->pkt_encap = pkt_encap;
+ item->interface_id = params->blf_data->next_interface_id++;
+ g_hash_table_insert(params->blf_data->channel_to_iface_ht, key, item);
+
+ return item->interface_id;
+}
+
+static guint32
+blf_lookup_interface(blf_params_t *params, int pkt_encap, guint16 channel, guint16 hwchannel, gchar *name) {
+ gint64 key = blf_calc_key_value(pkt_encap, channel, hwchannel);
+ blf_channel_to_iface_entry_t *item = NULL;
+
+ if (params->blf_data->channel_to_iface_ht == NULL) {
+ return 0;
+ }
+
+ item = (blf_channel_to_iface_entry_t *)g_hash_table_lookup(params->blf_data->channel_to_iface_ht, &key);
+
+ if (item != NULL) {
+ return item->interface_id;
+ } else {
+ return blf_add_interface(params, pkt_encap, channel, hwchannel, name);
+ }
+}
+
+static void
+fix_endianness_blf_date(blf_date_t *date) {
+ date->year = GUINT16_FROM_LE(date->year);
+ date->month = GUINT16_FROM_LE(date->month);
+ date->dayofweek = GUINT16_FROM_LE(date->dayofweek);
+ date->day = GUINT16_FROM_LE(date->day);
+ date->hour = GUINT16_FROM_LE(date->hour);
+ date->mins = GUINT16_FROM_LE(date->mins);
+ date->sec = GUINT16_FROM_LE(date->sec);
+ date->ms = GUINT16_FROM_LE(date->ms);
+}
+
+static void
+fix_endianness_blf_fileheader(blf_fileheader_t *header) {
+ header->header_length = GUINT32_FROM_LE(header->header_length);
+ header->len_compressed = GUINT64_FROM_LE(header->len_compressed);
+ header->len_uncompressed = GUINT64_FROM_LE(header->len_uncompressed);
+ header->obj_count = GUINT32_FROM_LE(header->obj_count);
+ header->obj_read = GUINT32_FROM_LE(header->obj_read);
+ fix_endianness_blf_date(&(header->start_date));
+ fix_endianness_blf_date(&(header->end_date));
+ header->length3 = GUINT32_FROM_LE(header->length3);
+}
+
+static void
+fix_endianness_blf_blockheader(blf_blockheader_t *header) {
+ header->header_length = GUINT16_FROM_LE(header->header_length);
+ header->header_type = GUINT16_FROM_LE(header->header_type);
+ header->object_length = GUINT32_FROM_LE(header->object_length);
+ header->object_type = GUINT32_FROM_LE(header->object_type);
+}
+
+static void
+fix_endianness_blf_logcontainerheader(blf_logcontainerheader_t *header) {
+ header->compression_method = GUINT16_FROM_LE(header->compression_method);
+ header->res1 = GUINT16_FROM_LE(header->res1);
+ header->res2 = GUINT32_FROM_LE(header->res2);
+ header->uncompressed_size = GUINT32_FROM_LE(header->uncompressed_size);
+ header->res4 = GUINT32_FROM_LE(header->res4);
+}
+
+static void
+fix_endianness_blf_logobjectheader(blf_logobjectheader_t *header) {
+ header->flags = GUINT32_FROM_LE(header->flags);
+ header->client_index = GUINT16_FROM_LE(header->client_index);
+ header->object_version = GUINT16_FROM_LE(header->object_version);
+ header->object_timestamp = GUINT64_FROM_LE(header->object_timestamp);
+}
+
+static void
+fix_endianness_blf_logobjectheader2(blf_logobjectheader2_t *header) {
+ header->flags = GUINT32_FROM_LE(header->flags);
+ header->object_version = GUINT16_FROM_LE(header->object_version);
+ header->object_timestamp = GUINT64_FROM_LE(header->object_timestamp);
+ header->original_timestamp = GUINT64_FROM_LE(header->object_timestamp);
+}
+
+static void
+fix_endianness_blf_logobjectheader3(blf_logobjectheader3_t *header) {
+ header->flags = GUINT32_FROM_LE(header->flags);
+ header->static_size = GUINT16_FROM_LE(header->static_size);
+ header->object_version = GUINT16_FROM_LE(header->object_version);
+ header->object_timestamp = GUINT64_FROM_LE(header->object_timestamp);
+}
+
+static void
+fix_endianness_blf_ethernetframeheader(blf_ethernetframeheader_t *header) {
+ header->channel = GUINT16_FROM_LE(header->channel);
+ header->direction = GUINT16_FROM_LE(header->direction);
+ header->ethtype = GUINT16_FROM_LE(header->ethtype);
+ header->tpid = GUINT16_FROM_LE(header->tpid);
+ header->tci = GUINT16_FROM_LE(header->tci);
+ header->payloadlength = GUINT16_FROM_LE(header->payloadlength);
+}
+
+static void
+fix_endianness_blf_ethernetframeheader_ex(blf_ethernetframeheader_ex_t *header) {
+ header->struct_length = GUINT16_FROM_LE(header->struct_length);
+ header->flags = GUINT16_FROM_LE(header->flags);
+ header->channel = GUINT16_FROM_LE(header->channel);
+ header->hw_channel = GUINT16_FROM_LE(header->hw_channel);
+ header->frame_duration = GUINT64_FROM_LE(header->frame_duration);
+ header->frame_checksum = GUINT32_FROM_LE(header->frame_checksum);
+ header->direction = GUINT16_FROM_LE(header->direction);
+ header->frame_length = GUINT16_FROM_LE(header->frame_length);
+ header->frame_handle = GUINT32_FROM_LE(header->frame_handle);
+ header->error = GUINT32_FROM_LE(header->error);
+}
+
+static void
+fix_endianness_blf_wlanframeheader(blf_wlanframeheader_t* header) {
+ header->channel = GUINT16_FROM_LE(header->channel);
+ header->flags = GUINT16_FROM_LE(header->flags);
+ header->signal_strength = GUINT16_FROM_LE(header->signal_strength);
+ header->signal_quality = GUINT16_FROM_LE(header->signal_quality);
+ header->frame_length = GUINT16_FROM_LE(header->frame_length);
+}
+
+static void
+fix_endianness_blf_canmessage(blf_canmessage_t *header) {
+ header->channel = GUINT16_FROM_LE(header->channel);
+ header->id = GUINT32_FROM_LE(header->id);
+}
+
+static void
+fix_endianness_blf_canmessage2_trailer(blf_canmessage2_trailer_t *header) {
+ header->frameLength_in_ns = GUINT32_FROM_LE(header->frameLength_in_ns);
+ header->reserved2 = GUINT16_FROM_LE(header->reserved1);
+}
+
+static void
+fix_endianness_blf_canfdmessage(blf_canfdmessage_t *header) {
+ header->channel = GUINT16_FROM_LE(header->channel);
+ header->id = GUINT32_FROM_LE(header->id);
+ header->frameLength_in_ns = GUINT32_FROM_LE(header->frameLength_in_ns);
+ header->reservedCanFdMessage2 = GUINT32_FROM_LE(header->reservedCanFdMessage2);
+}
+
+static void
+fix_endianness_blf_canfdmessage64(blf_canfdmessage64_t *header) {
+ header->id = GUINT32_FROM_LE(header->id);
+ header->frameLength_in_ns = GUINT32_FROM_LE(header->frameLength_in_ns);
+ header->flags = GUINT32_FROM_LE(header->flags);
+ header->btrCfgArb = GUINT32_FROM_LE(header->btrCfgArb);
+ header->btrCfgData = GUINT32_FROM_LE(header->btrCfgData);
+ header->timeOffsetBrsNs = GUINT32_FROM_LE(header->timeOffsetBrsNs);
+ header->timeOffsetCrcDelNs = GUINT32_FROM_LE(header->timeOffsetCrcDelNs);
+ header->bitCount = GUINT16_FROM_LE(header->bitCount);
+ header->crc = GUINT32_FROM_LE(header->crc);
+}
+
+static void
+fix_endianness_blf_canerror(blf_canerror_t *header) {
+ header->channel = GUINT16_FROM_LE(header->channel);
+ header->length = GUINT16_FROM_LE(header->length);
+}
+
+static void
+fix_endianness_blf_canerrorext(blf_canerrorext_t *header) {
+ header->channel = GUINT16_FROM_LE(header->channel);
+ header->length = GUINT16_FROM_LE(header->length);
+ header->flags = GUINT32_FROM_LE(header->flags);
+ header->frameLength_in_ns = GUINT32_FROM_LE(header->frameLength_in_ns);
+ header->id = GUINT32_FROM_LE(header->id);
+ header->errorCodeExt = GUINT16_FROM_LE(header->errorCodeExt);
+}
+
+static void
+fix_endianness_blf_canfderror64(blf_canfderror64_t *header) {
+ header->flags = GUINT16_FROM_LE(header->flags);
+ header->errorCodeExt = GUINT16_FROM_LE(header->errorCodeExt);
+ header->extFlags = GUINT16_FROM_LE(header->extFlags);
+ header->id = GUINT32_FROM_LE(header->id);
+ header->frameLength_in_ns = GUINT32_FROM_LE(header->frameLength_in_ns);
+ header->btrCfgArb = GUINT32_FROM_LE(header->btrCfgArb);
+ header->btrCfgData = GUINT32_FROM_LE(header->btrCfgData);
+ header->timeOffsetBrsNs = GUINT32_FROM_LE(header->timeOffsetBrsNs);
+ header->timeOffsetCrcDelNs = GUINT32_FROM_LE(header->timeOffsetCrcDelNs);
+ header->crc = GUINT32_FROM_LE(header->crc);
+ header->errorPosition = GUINT16_FROM_LE(header->errorPosition);
+}
+
+static void
+fix_endianness_blf_flexraydata(blf_flexraydata_t *header) {
+ header->channel = GUINT16_FROM_LE(header->channel);
+ header->messageId = GUINT16_FROM_LE(header->messageId);
+ header->crc = GUINT16_FROM_LE(header->crc);
+ header->reservedFlexRayData2 = GUINT16_FROM_LE(header->reservedFlexRayData2);
+}
+
+static void
+fix_endianness_blf_flexraymessage(blf_flexraymessage_t *header) {
+ header->channel = GUINT16_FROM_LE(header->channel);
+ header->fpgaTick = GUINT32_FROM_LE(header->fpgaTick);
+ header->fpgaTickOverflow = GUINT32_FROM_LE(header->fpgaTickOverflow);
+ header->clientIndexFlexRayV6Message = GUINT32_FROM_LE(header->clientIndexFlexRayV6Message);
+ header->clusterTime = GUINT32_FROM_LE(header->clusterTime);
+ header->frameId = GUINT16_FROM_LE(header->frameId);
+ header->headerCrc = GUINT16_FROM_LE(header->headerCrc);
+ header->frameState = GUINT16_FROM_LE(header->frameState);
+ header->reservedFlexRayV6Message2 = GUINT16_FROM_LE(header->reservedFlexRayV6Message2);
+}
+
+static void
+fix_endianness_blf_flexrayrcvmessage(blf_flexrayrcvmessage_t *header) {
+ header->channel = GUINT16_FROM_LE(header->channel);
+ header->version = GUINT16_FROM_LE(header->version);
+ header->channelMask = GUINT16_FROM_LE(header->channelMask);
+ header->dir = GUINT16_FROM_LE(header->dir);
+ header->clientIndex = GUINT32_FROM_LE(header->clientIndex);
+ header->clusterNo = GUINT32_FROM_LE(header->clusterNo);
+ header->frameId = GUINT16_FROM_LE(header->frameId);
+ header->headerCrc1 = GUINT16_FROM_LE(header->headerCrc1);
+ header->headerCrc2 = GUINT16_FROM_LE(header->headerCrc2);
+ header->payloadLength = GUINT16_FROM_LE(header->payloadLength);
+ header->payloadLengthValid = GUINT16_FROM_LE(header->payloadLengthValid);
+ header->cycle = GUINT16_FROM_LE(header->cycle);
+ header->tag = GUINT32_FROM_LE(header->tag);
+ header->data = GUINT32_FROM_LE(header->data);
+ header->frameFlags = GUINT32_FROM_LE(header->frameFlags);
+ header->appParameter = GUINT32_FROM_LE(header->appParameter);
+/* this would be extra for ext format:
+ header->frameCRC = GUINT32_FROM_LE(header->frameCRC);
+ header->frameLengthInNs = GUINT32_FROM_LE(header->frameLengthInNs);
+ header->frameId1 = GUINT16_FROM_LE(header->frameId1);
+ header->pduOffset = GUINT16_FROM_LE(header->pduOffset);
+ header->blfLogMask = GUINT16_FROM_LE(header->blfLogMask);
+*/
+}
+
+static void
+fix_endianness_blf_linmessage(blf_linmessage_t *header) {
+ header->channel = GUINT16_FROM_LE(header->channel);
+}
+
+static void
+fix_endianness_blf_linmessage_trailer(blf_linmessage_trailer_t *header) {
+ header->crc = GUINT16_FROM_LE(header->crc);
+/* skip the optional part
+ header->res2 = GUINT32_FROM_LE(header->res2);
+*/
+}
+
+static void
+fix_endianness_blf_apptext_header(blf_apptext_t *header) {
+ header->source = GUINT32_FROM_LE(header->source);
+ header->reservedAppText1 = GUINT32_FROM_LE(header->reservedAppText1);
+ header->textLength = GUINT32_FROM_LE(header->textLength);
+ header->reservedAppText2 = GUINT32_FROM_LE(header->reservedAppText2);
+}
+
+static void
+fix_endianness_blf_ethernet_status_header(blf_ethernet_status_t* header) {
+ header->channel = GUINT16_FROM_LE(header->channel);
+ header->flags = GUINT16_FROM_LE(header->flags);
+ /*uint8_t linkStatus;*/
+ /*uint8_t ethernetPhy;*/
+ /*uint8_t duplex;*/
+ /*uint8_t mdi;*/
+ /*uint8_t connector;*/
+ /*uint8_t clockMode;*/
+ /*uint8_t pairs;*/
+ /*uint8_t hardwareChannel;*/
+ header->bitrate = GUINT32_FROM_LE(header->bitrate);
+}
+
+static void
+blf_init_logcontainer(blf_log_container_t *tmp) {
+ tmp->infile_start_pos = 0;
+ tmp->infile_length = 0;
+ tmp->infile_data_start = 0;
+ tmp->real_start_pos = 0;
+ tmp->real_length = 0;
+ tmp->real_first_object_pos = -1;
+ tmp->real_leftover_bytes = G_MAXUINT64;
+ tmp->real_data = NULL;
+ tmp->compression_method = 0;
+}
+
+static void
+blf_add_logcontainer(blf_t *blf_data, blf_log_container_t log_container) {
+ if (blf_data->log_containers == NULL) {
+ blf_data->log_containers = g_array_sized_new(FALSE, FALSE, sizeof(blf_log_container_t), 1);
+ blf_data->current_log_container = 0;
+ } else {
+ blf_data->current_log_container++;
+ }
+
+ g_array_append_val(blf_data->log_containers, log_container);
+}
+
+static gboolean
+blf_get_logcontainer_by_index(blf_t *blf_data, guint container_index, blf_log_container_t **ret) {
+ if (blf_data == NULL || blf_data->log_containers == NULL || container_index >= blf_data->log_containers->len) {
+ return FALSE;
+ }
+
+ *ret = &g_array_index(blf_data->log_containers, blf_log_container_t, container_index);
+ return TRUE;
+}
+
+static gboolean
+blf_find_logcontainer_for_address(blf_t *blf_data, gint64 pos, blf_log_container_t **container, gint *container_index) {
+ blf_log_container_t *tmp;
+
+ if (blf_data == NULL || blf_data->log_containers == NULL) {
+ return FALSE;
+ }
+
+ for (guint i = 0; i < blf_data->log_containers->len; i++) {
+ tmp = &g_array_index(blf_data->log_containers, blf_log_container_t, i);
+ if (tmp->real_start_pos <= pos && pos < tmp->real_start_pos + (gint64)tmp->real_length) {
+ *container = tmp;
+ *container_index = i;
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+static gboolean
+blf_pull_logcontainer_into_memory(blf_params_t *params, guint index_log_container, int *err, gchar **err_info) {
+ blf_t *blf_data = params->blf_data;
+ blf_log_container_t tmp;
+
+ if (index_log_container >= blf_data->log_containers->len) {
+ /*
+ * XXX - does this represent a bug (WTAP_ERR_INTERNAL) or a
+ * malformed file (WTAP_ERR_BAD_FILE)?
+ */
+ *err = WTAP_ERR_INTERNAL;
+ *err_info = ws_strdup_printf("blf_pull_logcontainer_into_memory: index_log_container (%u) >= blf_data->log_containers->len (%u)",
+ index_log_container, blf_data->log_containers->len);
+ return FALSE;
+ }
+
+ tmp = g_array_index(blf_data->log_containers, blf_log_container_t, index_log_container);
+
+ if (tmp.real_data != NULL) {
+ return TRUE;
+ }
+
+ if (tmp.compression_method == BLF_COMPRESSION_ZLIB) {
+#ifdef HAVE_ZLIB
+ if (file_seek(params->fh, tmp.infile_data_start, SEEK_SET, err) == -1) {
+ return FALSE;
+ }
+
+ /* pull compressed data into buffer */
+ if (tmp.infile_start_pos < 0) {
+ /*
+ * XXX - does this represent a bug (WTAP_ERR_INTERNAL) or a
+ * malformed file (WTAP_ERR_BAD_FILE)?
+ */
+ *err = WTAP_ERR_INTERNAL;
+ *err_info = ws_strdup_printf("blf_pull_logcontainer_into_memory: tmp.infile_start_pos (%" G_GINT64_FORMAT ") < 0",
+ tmp.infile_start_pos);
+ return FALSE;
+ }
+ if (tmp.infile_data_start < (guint64)tmp.infile_start_pos) {
+ /*
+ * XXX - does this represent a bug (WTAP_ERR_INTERNAL) or a
+ * malformed file (WTAP_ERR_BAD_FILE)?
+ */
+ *err = WTAP_ERR_INTERNAL;
+ *err_info = ws_strdup_printf("blf_pull_logcontainer_into_memory: tmp.infile_data_start (%" G_GUINT64_FORMAT ") < tmp.infile_start_pos (%" G_GINT64_FORMAT ")",
+ tmp.infile_data_start, tmp.infile_start_pos);
+ return FALSE;
+ }
+ if (tmp.infile_length < tmp.infile_data_start - (guint64)tmp.infile_start_pos) {
+ /*
+ * XXX - does this represent a bug (WTAP_ERR_INTERNAL) or a
+ * malformed file (WTAP_ERR_BAD_FILE)?
+ */
+ *err = WTAP_ERR_INTERNAL;
+ *err_info = ws_strdup_printf("blf_pull_logcontainer_into_memory: tmp.infile_length (%" G_GUINT64_FORMAT ") < (tmp.infile_data_start (%" G_GUINT64_FORMAT ") - tmp.infile_start_pos (%" G_GINT64_FORMAT ")) = %" G_GUINT64_FORMAT,
+ tmp.infile_length,
+ tmp.infile_data_start, tmp.infile_start_pos,
+ tmp.infile_data_start - (guint64)tmp.infile_start_pos);
+ return FALSE;
+ }
+ guint64 data_length = tmp.infile_length - (tmp.infile_data_start - (guint64)tmp.infile_start_pos);
+ if (data_length > UINT_MAX) {
+ /*
+ * XXX - does this represent a bug (WTAP_ERR_INTERNAL) or a
+ * malformed file (WTAP_ERR_BAD_FILE)?
+ */
+ *err = WTAP_ERR_INTERNAL;
+ *err_info = ws_strdup_printf("blf_pull_logcontainer_into_memory: data_length (%" G_GUINT64_FORMAT ") > UINT_MAX",
+ data_length);
+ return FALSE;
+ }
+ unsigned char *compressed_data = g_try_malloc0((gsize)tmp.infile_length);
+ if (!wtap_read_bytes_or_eof(params->fh, compressed_data, (unsigned int)data_length, err, err_info)) {
+ g_free(compressed_data);
+ if (*err == WTAP_ERR_SHORT_READ) {
+ /*
+ * XXX - our caller will turn this into an EOF.
+ * How *should* it be treated?
+ * For now, we turn it into Yet Another Internal Error,
+ * pending having better documentation of the file
+ * format.
+ */
+ *err = WTAP_ERR_INTERNAL;
+ *err_info = ws_strdup("blf_pull_logcontainer_into_memory: short read on compressed data");
+ }
+ return FALSE;
+ }
+
+ unsigned char *buf = g_try_malloc0((gsize)tmp.real_length);
+ z_stream infstream = {0};
+
+ infstream.avail_in = (unsigned int)data_length;
+ infstream.next_in = compressed_data;
+ infstream.avail_out = (unsigned int)tmp.real_length;
+ infstream.next_out = buf;
+
+ /* the actual DE-compression work. */
+ if (Z_OK != inflateInit(&infstream)) {
+ /*
+ * XXX - check the error code and handle this appropriately.
+ */
+ g_free(buf);
+ g_free(compressed_data);
+ *err = WTAP_ERR_INTERNAL;
+ if (infstream.msg != NULL) {
+ *err_info = ws_strdup_printf("blf_pull_logcontainer_into_memory: inflateInit failed for LogContainer %u, message\"%s\"",
+ index_log_container,
+ infstream.msg);
+ } else {
+ *err_info = ws_strdup_printf("blf_pull_logcontainer_into_memory: inflateInit failed for LogContainer %u",
+ index_log_container);
+ }
+ ws_debug("inflateInit failed for LogContainer %u", index_log_container);
+ if (infstream.msg != NULL) {
+ ws_debug("inflateInit returned: \"%s\"", infstream.msg);
+ }
+ return FALSE;
+ }
+
+ int ret = inflate(&infstream, Z_NO_FLUSH);
+ /* Z_OK should not happen here since we know how big the buffer should be */
+ if (Z_STREAM_END != ret) {
+ switch (ret) {
+
+ case Z_NEED_DICT:
+ *err = WTAP_ERR_DECOMPRESS;
+ *err_info = ws_strdup("preset dictionary needed");
+ break;
+
+ case Z_STREAM_ERROR:
+ *err = WTAP_ERR_DECOMPRESS;
+ *err_info = (infstream.msg != NULL) ? ws_strdup(infstream.msg) : NULL;
+ break;
+
+ case Z_MEM_ERROR:
+ /* This means "not enough memory". */
+ *err = ENOMEM;
+ *err_info = NULL;
+ break;
+
+ case Z_DATA_ERROR:
+ /* This means "deflate stream invalid" */
+ *err = WTAP_ERR_DECOMPRESS;
+ *err_info = (infstream.msg != NULL) ? ws_strdup(infstream.msg) : NULL;
+ break;
+
+ case Z_BUF_ERROR:
+ /* XXX - this is recoverable; what should we do here? */
+ *err = WTAP_ERR_INTERNAL;
+ *err_info = ws_strdup_printf("blf_pull_logcontainer_into_memory: Z_BUF_ERROR from inflate(), message \"%s\"",
+ (infstream.msg != NULL) ? infstream.msg : "(none)");
+ break;
+
+ case Z_VERSION_ERROR:
+ *err = WTAP_ERR_INTERNAL;
+ *err_info = ws_strdup_printf("blf_pull_logcontainer_into_memory: Z_VERSION_ERROR from inflate(), message \"%s\"",
+ (infstream.msg != NULL) ? infstream.msg : "(none)");
+ break;
+
+ default:
+ *err = WTAP_ERR_INTERNAL;
+ *err_info = ws_strdup_printf("blf_pull_logcontainer_into_memory: unexpected error %d from inflate(), message \"%s\"",
+ ret,
+ (infstream.msg != NULL) ? infstream.msg : "(none)");
+ break;
+ }
+ g_free(buf);
+ g_free(compressed_data);
+ ws_debug("inflate failed (return code %d) for LogContainer %u", ret, index_log_container);
+ if (infstream.msg != NULL) {
+ ws_debug("inflate returned: \"%s\"", infstream.msg);
+ }
+ /* Free up any dynamically-allocated memory in infstream */
+ inflateEnd(&infstream);
+ return FALSE;
+ }
+
+ if (Z_OK != inflateEnd(&infstream)) {
+ /*
+ * The zlib manual says this only returns Z_OK on success
+ * and Z_STREAM_ERROR if the stream state was inconsistent.
+ *
+ * It's not clear what useful information can be reported
+ * for Z_STREAM_ERROR; a look at the 1.2.11 source indicates
+ * that no string is returned to indicate what the problem
+ * was.
+ *
+ * It's also not clear what to do about infstream if this
+ * fails.
+ */
+ *err = WTAP_ERR_INTERNAL;
+ *err_info = ws_strdup_printf("blf_pull_logcontainer_into_memory: inflateEnd failed for LogContainer %u", index_log_container);
+ g_free(buf);
+ g_free(compressed_data);
+ ws_debug("inflateEnd failed for LogContainer %u", index_log_container);
+ if (infstream.msg != NULL) {
+ ws_debug("inflateEnd returned: \"%s\"", infstream.msg);
+ }
+ return FALSE;
+ }
+
+ g_free(compressed_data);
+ tmp.real_data = buf;
+ g_array_index(blf_data->log_containers, blf_log_container_t, index_log_container) = tmp;
+ return TRUE;
+#else
+ *err = WTAP_ERR_DECOMPRESSION_NOT_SUPPORTED;
+ *err_info = ws_strdup("blf_pull_logcontainer_into_memory: reading gzip-compressed containers isn't supported");
+ return FALSE;
+#endif
+ }
+
+ return FALSE;
+}
+
+static gboolean
+blf_read_bytes_or_eof(blf_params_t *params, guint64 real_pos, void *target_buffer, unsigned int count, int *err, gchar **err_info) {
+ blf_log_container_t *start_container;
+ blf_log_container_t *end_container;
+ blf_log_container_t *current_container;
+
+ gint start_container_index = -1;
+ gint end_container_index = -1;
+ gint current_container_index = -1;
+
+ unsigned int copied = 0;
+ unsigned int data_left;
+ unsigned int start_in_buf;
+
+ unsigned char *buf = (unsigned char *)target_buffer;
+
+ if (!blf_find_logcontainer_for_address(params->blf_data, real_pos, &start_container, &start_container_index)) {
+ /*
+ * XXX - why is this treated as an EOF rather than an error?
+ * *err appears to be 0, which means our caller treats it as an
+ * EOF, at least when reading the log object header.
+ */
+ ws_debug("cannot read data because start position cannot be mapped");
+ return FALSE;
+ }
+ if (!blf_find_logcontainer_for_address(params->blf_data, real_pos + count - 1, &end_container, &end_container_index)) {
+ /*
+ * XXX - why is this treated as an EOF rather than an error?
+ * *err appears to be 0, which means our caller treats it as an
+ * EOF, at least when reading the log object header.
+ */
+ ws_debug("cannot read data because end position cannot be mapped");
+ return FALSE;
+ }
+
+ current_container_index = start_container_index;
+ current_container = start_container;
+ start_in_buf = (unsigned int)real_pos - (unsigned int)start_container->real_start_pos;
+
+ switch (start_container->compression_method) {
+ case BLF_COMPRESSION_NONE:
+ while (current_container_index <= end_container_index) {
+ if (!blf_get_logcontainer_by_index(params->blf_data, current_container_index, &current_container)) {
+ /*
+ * XXX - does this represent a bug (WTAP_ERR_INTERNAL) or a
+ * malformed file (WTAP_ERR_BAD_FILE)?
+ */
+ *err = WTAP_ERR_INTERNAL;
+ *err_info = ws_strdup_printf("blf_read_bytes_or_eof: cannot refresh container");
+ ws_debug("cannot refresh container");
+ return FALSE;
+ }
+
+ data_left = (unsigned int)(current_container->real_length - start_in_buf);
+
+ if (file_seek(params->fh, current_container->infile_data_start + start_in_buf, SEEK_SET, err) < 0) {
+ ws_debug("cannot seek data");
+ return FALSE;
+ }
+
+ if (data_left < (count - copied)) {
+ if (!wtap_read_bytes_or_eof(params->fh, buf + copied, data_left, err, err_info)) {
+ ws_debug("cannot read data");
+ return FALSE;
+ }
+ copied += data_left;
+ current_container_index++;
+ start_in_buf = 0;
+ } else {
+ if (!wtap_read_bytes_or_eof(params->fh, buf + copied, count - copied, err, err_info)) {
+ ws_debug("cannot read data");
+ return FALSE;
+ }
+ return TRUE;
+ }
+ }
+ break;
+
+ case BLF_COMPRESSION_ZLIB:
+ while (current_container_index <= end_container_index) {
+ if (!blf_pull_logcontainer_into_memory(params, current_container_index, err, err_info)) {
+ return FALSE;
+ }
+
+ if (!blf_get_logcontainer_by_index(params->blf_data, current_container_index, &current_container)) {
+ /*
+ * XXX - does this represent a bug (WTAP_ERR_INTERNAL) or a
+ * malformed file (WTAP_ERR_BAD_FILE)?
+ */
+ *err = WTAP_ERR_INTERNAL;
+ *err_info = ws_strdup_printf("blf_read_bytes_or_eof: cannot refresh container");
+ ws_debug("cannot refresh container");
+ return FALSE;
+ }
+
+ if (current_container->real_data == NULL) {
+ /*
+ * XXX - does this represent a bug (WTAP_ERR_INTERNAL) or a
+ * malformed file (WTAP_ERR_BAD_FILE)?
+ */
+ *err = WTAP_ERR_INTERNAL;
+ *err_info = ws_strdup_printf("blf_read_bytes_or_eof: pulling in container failed hard");
+ ws_debug("pulling in container failed hard");
+ return FALSE;
+ }
+
+ data_left = (unsigned int)(current_container->real_length - start_in_buf);
+
+ if (data_left < (count - copied)) {
+ memcpy(buf + copied, current_container->real_data + start_in_buf, (unsigned int)data_left);
+ copied += data_left;
+ current_container_index++;
+ start_in_buf = 0;
+ } else {
+ memcpy(buf + copied, current_container->real_data + start_in_buf, count - copied);
+ return TRUE;
+ }
+ }
+ /*
+ * XXX - does this represent a bug (WTAP_ERR_INTERNAL) or a
+ * malformed file (WTAP_ERR_BAD_FILE)?
+ */
+ *err = WTAP_ERR_INTERNAL;
+ *err_info = ws_strdup_printf("blf_read_bytes_or_eof: ran out of items in container");
+ return FALSE;
+ break;
+
+ default:
+ *err = WTAP_ERR_UNSUPPORTED;
+ *err_info = ws_strdup_printf("blf: unknown compression method %u",
+ start_container->compression_method);
+ ws_debug("unknown compression method");
+ return FALSE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+blf_read_bytes(blf_params_t *params, guint64 real_pos, void *target_buffer, unsigned int count, int *err, gchar **err_info) {
+ if (!blf_read_bytes_or_eof(params, real_pos, target_buffer, count, err, err_info)) {
+ if (*err == 0) {
+ *err = WTAP_ERR_SHORT_READ;
+ }
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/* this is only called once on open to figure out the layout of the file */
+static gboolean
+blf_scan_file_for_logcontainers(blf_params_t *params) {
+ blf_blockheader_t header;
+ blf_logcontainerheader_t logcontainer_header;
+ blf_log_container_t tmp;
+
+ int err;
+ gchar *err_info;
+
+ guint64 current_start_pos;
+ guint64 current_real_start = 0;
+
+ while (1) {
+ current_start_pos = file_tell(params->fh);
+
+ /* Find Object */
+ while (1) {
+ if (!wtap_read_bytes_or_eof(params->fh, &header, sizeof header, &err, &err_info)) {
+ ws_debug("we found end of file");
+
+ /* lets ignore some bytes at the end since some implementations think it is ok to add a few zero bytes */
+ if (err == WTAP_ERR_SHORT_READ) {
+ err = 0;
+ }
+
+ return TRUE;
+ }
+
+ fix_endianness_blf_blockheader(&header);
+
+ if (memcmp(header.magic, blf_obj_magic, sizeof(blf_obj_magic))) {
+ ws_debug("object magic is not LOBJ (pos: 0x%" PRIx64 ")", current_start_pos);
+ } else {
+ break;
+ }
+
+ /* we are moving back and try again but 1 byte later */
+ /* TODO: better understand how this paddings works... */
+ current_start_pos++;
+ if (file_seek(params->fh, current_start_pos, SEEK_SET, &err) < 0) {
+ return FALSE;
+ }
+ }
+
+ if (header.header_type != BLF_HEADER_TYPE_DEFAULT) {
+ ws_debug("unknown header type, I know only BLF_HEADER_TYPE_DEFAULT (1)");
+ return FALSE;
+ }
+
+ switch (header.object_type) {
+ case BLF_OBJTYPE_LOG_CONTAINER:
+ if (header.header_length < sizeof(blf_blockheader_t)) {
+ ws_debug("log container header length too short");
+ return FALSE;
+ }
+
+ /* skip unknown header part if needed */
+ if (header.header_length - sizeof(blf_blockheader_t) > 0) {
+ /* seek over unknown header part */
+ if (file_seek(params->fh, current_start_pos + header.header_length, SEEK_SET, &err) < 0) {
+ ws_debug("cannot seek file for skipping unknown header bytes in log container");
+ return FALSE;
+ }
+ }
+
+ if (!wtap_read_bytes_or_eof(params->fh, &logcontainer_header, sizeof(blf_logcontainerheader_t), &err, &err_info)) {
+ ws_debug("not enough bytes for log container header");
+ return FALSE;
+ }
+
+ fix_endianness_blf_logcontainerheader(&logcontainer_header);
+
+ blf_init_logcontainer(&tmp);
+
+ tmp.infile_start_pos = current_start_pos;
+ tmp.infile_data_start = file_tell(params->fh);
+ tmp.infile_length = header.object_length;
+
+ tmp.real_start_pos = current_real_start;
+ tmp.real_length = logcontainer_header.uncompressed_size;
+ tmp.compression_method = logcontainer_header.compression_method;
+
+ /* set up next start position */
+ current_real_start += logcontainer_header.uncompressed_size;
+
+ if (file_seek(params->fh, current_start_pos + MAX(MAX(16, header.object_length), header.header_length), SEEK_SET, &err) < 0) {
+ ws_debug("cannot seek file for skipping log container bytes");
+ return FALSE;
+ }
+
+ blf_add_logcontainer(params->blf_data, tmp);
+
+ break;
+ default:
+ ws_debug("we found a non BLF log container on top level. this is unexpected.");
+
+ /* TODO: maybe create "fake Log Container" for this */
+ if (file_seek(params->fh, current_start_pos + MAX(MAX(16, header.object_length), header.header_length), SEEK_SET, &err) < 0) {
+ return FALSE;
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+static void
+blf_init_rec(blf_params_t *params, guint32 flags, guint64 object_timestamp, int pkt_encap, guint16 channel, guint16 hwchannel, guint caplen, guint len) {
+ params->rec->rec_type = REC_TYPE_PACKET;
+ params->rec->block = wtap_block_create(WTAP_BLOCK_PACKET);
+ params->rec->presence_flags = WTAP_HAS_TS | WTAP_HAS_CAP_LEN | WTAP_HAS_INTERFACE_ID;
+ switch (flags) {
+ case BLF_TIMESTAMP_RESOLUTION_10US:
+ params->rec->tsprec = WTAP_TSPREC_10_USEC;
+ object_timestamp *= 10000;
+ object_timestamp += params->blf_data->start_offset_ns;
+ break;
+
+ case BLF_TIMESTAMP_RESOLUTION_1NS:
+ params->rec->tsprec = WTAP_TSPREC_NSEC;
+ object_timestamp += params->blf_data->start_offset_ns;
+ break;
+
+ default:
+ if (flags == 0 && object_timestamp == 0) {
+ /* This is not an error, but is used for metadata at the beginning of the file. */
+ params->rec->tsprec = WTAP_TSPREC_NSEC;
+ object_timestamp = params->blf_data->start_offset_ns;
+ }
+ else {
+ /*
+ * XXX - report this as an error?
+ *
+ * Or provide a mechanism to allow file readers to report
+ * a warning (an error that the reader tries to work
+ * around and that the caller should report)?
+ *
+ * Set the timestamp to params->blf_data->start_offset_ns also here?
+ */
+ ws_debug("Unknown combination of flags and timestamp (0x%x, %" PRIu64 ")", flags, object_timestamp);
+ params->rec->tsprec = WTAP_TSPREC_NSEC;
+ object_timestamp = 0;
+ }
+ break;
+ }
+ params->rec->ts.secs = object_timestamp / (1000 * 1000 * 1000);
+ params->rec->ts.nsecs = object_timestamp % (1000 * 1000 * 1000);
+ params->rec->rec_header.packet_header.caplen = caplen;
+ params->rec->rec_header.packet_header.len = len;
+
+ nstime_t tmp_ts;
+ tmp_ts.secs = params->blf_data->start_offset_ns / (1000 * 1000 * 1000);
+ tmp_ts.nsecs = params->blf_data->start_offset_ns % (1000 * 1000 * 1000);
+ nstime_delta(&params->rec->ts_rel_cap, &params->rec->ts, &tmp_ts);
+ params->rec->ts_rel_cap_valid = true;
+
+ params->rec->rec_header.packet_header.pkt_encap = pkt_encap;
+ params->rec->rec_header.packet_header.interface_id = blf_lookup_interface(params, pkt_encap, channel, hwchannel, NULL);
+
+ /* TODO: before we had to remove comments and verdict here to not leak memory but APIs have changed ... */
+}
+
+static void
+blf_add_direction_option(blf_params_t *params, guint16 direction) {
+ guint32 tmp = 0; /* dont care */
+
+ switch (direction) {
+ case BLF_DIR_RX:
+ tmp = 1; /* inbound */
+ break;
+ case BLF_DIR_TX:
+ case BLF_DIR_TX_RQ:
+ tmp = 2; /* outbound */
+ break;
+ }
+
+ /* pcapng.c: #define OPT_EPB_FLAGS 0x0002 */
+ wtap_block_add_uint32_option(params->rec->block, 0x0002, tmp);
+}
+
+static gboolean
+blf_read_log_object_header(blf_params_t *params, int *err, gchar **err_info, gint64 header2_start, gint64 data_start, blf_logobjectheader_t *logheader) {
+ if (data_start - header2_start < (gint64)sizeof(blf_logobjectheader_t)) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("blf: not enough bytes for log object header");
+ ws_debug("not enough bytes for timestamp header");
+ return FALSE;
+ }
+
+ if (!blf_read_bytes_or_eof(params, header2_start, logheader, sizeof(*logheader), err, err_info)) {
+ ws_debug("not enough bytes for logheader");
+ return FALSE;
+ }
+ fix_endianness_blf_logobjectheader(logheader);
+ return TRUE;
+}
+
+static gboolean
+blf_read_log_object_header2(blf_params_t *params, int *err, gchar **err_info, gint64 header2_start, gint64 data_start, blf_logobjectheader2_t *logheader) {
+ if (data_start - header2_start < (gint64)sizeof(blf_logobjectheader2_t)) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("blf: not enough bytes for log object header");
+ ws_debug("not enough bytes for timestamp header");
+ return FALSE;
+ }
+
+ if (!blf_read_bytes_or_eof(params, header2_start, logheader, sizeof(*logheader), err, err_info)) {
+ ws_debug("not enough bytes for logheader");
+ return FALSE;
+ }
+ fix_endianness_blf_logobjectheader2(logheader);
+ return TRUE;
+}
+
+static gboolean
+blf_read_log_object_header3(blf_params_t *params, int *err, gchar **err_info, gint64 header2_start, gint64 data_start, blf_logobjectheader3_t *logheader) {
+ if (data_start - header2_start < (gint64)sizeof(blf_logobjectheader3_t)) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("blf: not enough bytes for log object header");
+ ws_debug("not enough bytes for timestamp header");
+ return FALSE;
+ }
+
+ if (!blf_read_bytes_or_eof(params, header2_start, logheader, sizeof(*logheader), err, err_info)) {
+ ws_debug("not enough bytes for logheader");
+ return FALSE;
+ }
+ fix_endianness_blf_logobjectheader3(logheader);
+ return TRUE;
+}
+
+static gboolean
+blf_read_ethernetframe(blf_params_t *params, int *err, gchar **err_info, gint64 block_start, gint64 data_start, gint64 object_length, guint32 flags, guint64 object_timestamp) {
+ blf_ethernetframeheader_t ethheader;
+ guint8 tmpbuf[18];
+ guint caplen, len;
+
+ if (object_length < (data_start - block_start) + (int) sizeof(blf_ethernetframeheader_t)) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("blf: ETHERNET_FRAME: not enough bytes for ethernet frame header in object");
+ ws_debug("not enough bytes for ethernet frame header in object");
+ return FALSE;
+ }
+
+ if (!blf_read_bytes(params, data_start, &ethheader, sizeof(ethheader), err, err_info)) {
+ ws_debug("not enough bytes for ethernet frame header in file");
+ return FALSE;
+ }
+ fix_endianness_blf_ethernetframeheader(&ethheader);
+
+ /*
+ * BLF breaks up and reorders the Ethernet header and VLAN tag fields.
+ * This is a really bad design and makes this format one of the worst.
+ * If you want a fast format that keeps your data intact, avoid this format!
+ * So, lets hope we can reconstruct the original packet successfully.
+ */
+
+ tmpbuf[0] = ethheader.dst_addr[0];
+ tmpbuf[1] = ethheader.dst_addr[1];
+ tmpbuf[2] = ethheader.dst_addr[2];
+ tmpbuf[3] = ethheader.dst_addr[3];
+ tmpbuf[4] = ethheader.dst_addr[4];
+ tmpbuf[5] = ethheader.dst_addr[5];
+ tmpbuf[6] = ethheader.src_addr[0];
+ tmpbuf[7] = ethheader.src_addr[1];
+ tmpbuf[8] = ethheader.src_addr[2];
+ tmpbuf[9] = ethheader.src_addr[3];
+ tmpbuf[10] = ethheader.src_addr[4];
+ tmpbuf[11] = ethheader.src_addr[5];
+
+ if (ethheader.tpid != 0 && ethheader.tci != 0) {
+ tmpbuf[12] = (ethheader.tpid & 0xff00) >> 8;
+ tmpbuf[13] = (ethheader.tpid & 0x00ff);
+ tmpbuf[14] = (ethheader.tci & 0xff00) >> 8;
+ tmpbuf[15] = (ethheader.tci & 0x00ff);
+ tmpbuf[16] = (ethheader.ethtype & 0xff00) >> 8;
+ tmpbuf[17] = (ethheader.ethtype & 0x00ff);
+ ws_buffer_assure_space(params->buf, (gsize)18 + ethheader.payloadlength);
+ ws_buffer_append(params->buf, tmpbuf, (gsize)18);
+ caplen = ((guint32)18 + ethheader.payloadlength);
+ len = ((guint32)18 + ethheader.payloadlength);
+ } else {
+ tmpbuf[12] = (ethheader.ethtype & 0xff00) >> 8;
+ tmpbuf[13] = (ethheader.ethtype & 0x00ff);
+ ws_buffer_assure_space(params->buf, (gsize)14 + ethheader.payloadlength);
+ ws_buffer_append(params->buf, tmpbuf, (gsize)14);
+ caplen = ((guint32)14 + ethheader.payloadlength);
+ len = ((guint32)14 + ethheader.payloadlength);
+ }
+
+ if (!blf_read_bytes(params, data_start + sizeof(blf_ethernetframeheader_t), ws_buffer_end_ptr(params->buf), ethheader.payloadlength, err, err_info)) {
+ ws_debug("copying ethernet frame failed");
+ return FALSE;
+ }
+ params->buf->first_free += ethheader.payloadlength;
+
+ blf_init_rec(params, flags, object_timestamp, WTAP_ENCAP_ETHERNET, ethheader.channel, UINT16_MAX, caplen, len);
+ blf_add_direction_option(params, ethheader.direction);
+
+ return TRUE;
+}
+
+static gboolean
+blf_read_ethernetframe_ext(blf_params_t *params, int *err, gchar **err_info, gint64 block_start, gint64 data_start, gint64 object_length, guint32 flags, guint64 object_timestamp) {
+ blf_ethernetframeheader_ex_t ethheader;
+
+ if (object_length < (data_start - block_start) + (int) sizeof(blf_ethernetframeheader_ex_t)) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("blf: ETHERNET_FRAME_EX: not enough bytes for ethernet frame header in object");
+ ws_debug("not enough bytes for ethernet frame header in object");
+ return FALSE;
+ }
+
+ if (!blf_read_bytes(params, data_start, &ethheader, sizeof(blf_ethernetframeheader_ex_t), err, err_info)) {
+ ws_debug("not enough bytes for ethernet frame header in file");
+ return FALSE;
+ }
+ fix_endianness_blf_ethernetframeheader_ex(&ethheader);
+
+ ws_buffer_assure_space(params->buf, ethheader.frame_length);
+
+ if (object_length - (data_start - block_start) - sizeof(blf_ethernetframeheader_ex_t) < ethheader.frame_length) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("blf: ETHERNET_FRAME_EX: frame too short");
+ ws_debug("frame too short");
+ return FALSE;
+ }
+
+ if (!blf_read_bytes(params, data_start + sizeof(blf_ethernetframeheader_ex_t), ws_buffer_start_ptr(params->buf), ethheader.frame_length, err, err_info)) {
+ ws_debug("copying ethernet frame failed");
+ return FALSE;
+ }
+
+ blf_init_rec(params, flags, object_timestamp, WTAP_ENCAP_ETHERNET, ethheader.channel, ethheader.hw_channel, ethheader.frame_length, ethheader.frame_length);
+ wtap_block_add_uint32_option(params->rec->block, OPT_PKT_QUEUE, ethheader.hw_channel);
+ blf_add_direction_option(params, ethheader.direction);
+
+ return TRUE;
+}
+
+/*
+ * XXX - provide radio information to our caller in the pseudo-header.
+ */
+static gboolean
+blf_read_wlanframe(blf_params_t* params, int* err, gchar** err_info, gint64 block_start, gint64 data_start, gint64 object_length, guint32 flags, guint64 object_timestamp) {
+ blf_wlanframeheader_t wlanheader;
+
+ if (object_length < (data_start - block_start) + (int)sizeof(blf_wlanframeheader_t)) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("blf: WLAN_FRAME: not enough bytes for wlan frame header in object");
+ ws_debug("not enough bytes for wlan frame header in object");
+ return FALSE;
+ }
+
+ if (!blf_read_bytes(params, data_start, &wlanheader, sizeof(blf_wlanframeheader_t), err, err_info)) {
+ ws_debug("not enough bytes for wlan frame header in file");
+ return FALSE;
+ }
+ fix_endianness_blf_wlanframeheader(&wlanheader);
+
+ ws_buffer_assure_space(params->buf, wlanheader.frame_length);
+
+ if (object_length - (data_start - block_start) - sizeof(blf_wlanframeheader_t) < wlanheader.frame_length) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("blf: WLAN_FRAME: frame too short");
+ ws_debug("frame too short");
+ return FALSE;
+ }
+
+ if (!blf_read_bytes(params, data_start + sizeof(blf_wlanframeheader_t), ws_buffer_start_ptr(params->buf), wlanheader.frame_length, err, err_info)) {
+ ws_debug("copying wlan frame failed");
+ return FALSE;
+ }
+
+ blf_init_rec(params, flags, object_timestamp, WTAP_ENCAP_IEEE_802_11, wlanheader.channel, UINT16_MAX, wlanheader.frame_length, wlanheader.frame_length);
+ blf_add_direction_option(params, wlanheader.direction);
+
+ return TRUE;
+}
+
+static guint8 can_dlc_to_length[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8 };
+static guint8 canfd_dlc_to_length[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 12, 16, 20, 24, 32, 48, 64 };
+
+static gboolean
+blf_can_fill_buf_and_rec(blf_params_t *params, int *err, gchar **err_info, guint32 canid, guint8 payload_length, guint8 payload_length_valid, guint64 start_position,
+ guint32 flags, guint64 object_timestamp, guint16 channel) {
+ guint8 tmpbuf[8];
+ guint caplen, len;
+
+ tmpbuf[0] = (canid & 0xff000000) >> 24;
+ tmpbuf[1] = (canid & 0x00ff0000) >> 16;
+ tmpbuf[2] = (canid & 0x0000ff00) >> 8;
+ tmpbuf[3] = (canid & 0x000000ff);
+ tmpbuf[4] = payload_length;
+ tmpbuf[5] = 0;
+ tmpbuf[6] = 0;
+ tmpbuf[7] = 0;
+
+ ws_buffer_assure_space(params->buf, sizeof(tmpbuf) + payload_length_valid);
+ ws_buffer_append(params->buf, tmpbuf, sizeof(tmpbuf));
+ caplen = sizeof(tmpbuf) + payload_length_valid;
+ len = sizeof(tmpbuf) + payload_length;
+
+ if (payload_length_valid > 0 && !blf_read_bytes(params, start_position, ws_buffer_end_ptr(params->buf), payload_length_valid, err, err_info)) {
+ ws_debug("copying can payload failed");
+ return FALSE;
+ }
+ params->buf->first_free += payload_length_valid;
+
+ blf_init_rec(params, flags, object_timestamp, WTAP_ENCAP_SOCKETCAN, channel, UINT16_MAX, caplen, len);
+
+ return TRUE;
+}
+
+static gboolean
+blf_read_canmessage(blf_params_t *params, int *err, gchar **err_info, gint64 block_start, gint64 data_start, gint64 object_length, guint32 flags, guint64 object_timestamp, gboolean can_message2) {
+ blf_canmessage_t canheader;
+ blf_canmessage2_trailer_t can2trailer;
+
+ guint32 canid;
+ guint8 payload_length;
+
+ if (object_length < (data_start - block_start) + (int) sizeof(canheader)) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("blf: %s: not enough bytes for can header in object",
+ can_message2 ? "CAN_MESSAGE2" : "CAN_MESSAGE");
+ ws_debug("not enough bytes for can header in object");
+ return FALSE;
+ }
+
+ if (!blf_read_bytes(params, data_start, &canheader, sizeof(canheader), err, err_info)) {
+ ws_debug("not enough bytes for can header in file");
+ return FALSE;
+ }
+ fix_endianness_blf_canmessage(&canheader);
+
+ canheader.dlc &= 0x0f;
+
+ payload_length = canheader.dlc;
+ if (payload_length > 8) {
+ ws_debug("regular CAN tries more than 8 bytes? Cutting to 8!");
+ payload_length = 8;
+ }
+
+ canid = canheader.id;
+
+ if ((canheader.flags & BLF_CANMESSAGE_FLAG_RTR) == BLF_CANMESSAGE_FLAG_RTR) {
+ canid |= CAN_RTR_FLAG;
+ payload_length = 0;
+ }
+
+ if (!blf_can_fill_buf_and_rec(params, err, err_info, canid, payload_length, payload_length, data_start + sizeof(canheader), flags, object_timestamp, canheader.channel)) {
+ return FALSE;
+ }
+
+ /* actually, we do not really need the data, right now.... */
+ if (can_message2) {
+ if (object_length < (data_start - block_start) + (int) sizeof(canheader) + 8 + (int) sizeof(can2trailer)) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("blf: CAN_MESSAGE2: not enough bytes for can message 2 trailer");
+ ws_debug("not enough bytes for can message 2 trailer");
+ return FALSE;
+ }
+ if (!blf_read_bytes(params, data_start + sizeof(canheader) + 8, &can2trailer, sizeof(can2trailer), err, err_info)) {
+ ws_debug("not enough bytes for can message 2 trailer in file");
+ return FALSE;
+ }
+ fix_endianness_blf_canmessage2_trailer(&can2trailer);
+ }
+
+ blf_add_direction_option(params, (canheader.flags & BLF_CANMESSAGE_FLAG_TX) == BLF_CANMESSAGE_FLAG_TX ? BLF_DIR_TX: BLF_DIR_RX);
+
+ return TRUE;
+}
+
+static gboolean
+blf_read_canfdmessage(blf_params_t *params, int *err, gchar **err_info, gint64 block_start, gint64 data_start, gint64 object_length, guint32 flags, guint64 object_timestamp) {
+ blf_canfdmessage_t canheader;
+
+ gboolean canfd;
+ guint32 canid;
+ guint8 payload_length;
+ guint8 payload_length_valid;
+
+ if (object_length < (data_start - block_start) + (int) sizeof(canheader)) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("blf: CAN_FD_MESSAGE: not enough bytes for canfd header in object");
+ ws_debug("not enough bytes for canfd header in object");
+ return FALSE;
+ }
+
+ if (!blf_read_bytes(params, data_start, &canheader, sizeof(canheader), err, err_info)) {
+ ws_debug("not enough bytes for canfd header in file");
+ return FALSE;
+ }
+ fix_endianness_blf_canfdmessage(&canheader);
+
+ canheader.dlc &= 0x0f;
+
+ canfd = (canheader.canfdflags & BLF_CANFDMESSAGE_CANFDFLAG_EDL) == BLF_CANFDMESSAGE_CANFDFLAG_EDL;
+ if (canfd) {
+ payload_length = canfd_dlc_to_length[canheader.dlc];
+ } else {
+ if (canheader.dlc > 8) {
+ ws_debug("regular CAN tries more than 8 bytes?");
+ }
+ payload_length = can_dlc_to_length[canheader.dlc];
+ }
+
+ if (payload_length > canheader.validDataBytes) {
+ ws_debug("shortening canfd payload because valid data bytes shorter!");
+ payload_length = canheader.validDataBytes;
+ }
+
+ canid = canheader.id;
+
+ if (!canfd && (canheader.flags & BLF_CANMESSAGE_FLAG_RTR) == BLF_CANMESSAGE_FLAG_RTR) {
+ canid |= CAN_RTR_FLAG;
+ payload_length = 0; /* Should already be zero from validDataBytes */
+ }
+
+ payload_length_valid = payload_length;
+
+ if (payload_length_valid > object_length - (data_start - block_start) + sizeof(canheader)) {
+ ws_debug("shortening can payload because buffer is too short!");
+ payload_length_valid = (guint8)(object_length - (data_start - block_start));
+ }
+
+ if (!blf_can_fill_buf_and_rec(params, err, err_info, canid, payload_length, payload_length_valid, data_start + sizeof(canheader), flags, object_timestamp, canheader.channel)) {
+ return FALSE;
+ }
+
+ blf_add_direction_option(params, (canheader.flags & BLF_CANMESSAGE_FLAG_TX) == BLF_CANMESSAGE_FLAG_TX ? BLF_DIR_TX : BLF_DIR_RX);
+
+ return TRUE;
+}
+
+static gboolean
+blf_read_canfdmessage64(blf_params_t *params, int *err, gchar **err_info, gint64 block_start, gint64 data_start, gint64 object_length, guint32 flags, guint64 object_timestamp) {
+ blf_canfdmessage64_t canheader;
+
+ gboolean canfd;
+ guint32 canid;
+ guint8 payload_length;
+ guint8 payload_length_valid;
+
+ if (object_length < (data_start - block_start) + (int) sizeof(canheader)) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("blf: CAN_FD_MESSAGE_64: not enough bytes for canfd header in object");
+ ws_debug("not enough bytes for canfd header in object");
+ return FALSE;
+ }
+
+ if (!blf_read_bytes(params, data_start, &canheader, sizeof(canheader), err, err_info)) {
+ ws_debug("not enough bytes for canfd header in file");
+ return FALSE;
+ }
+ fix_endianness_blf_canfdmessage64(&canheader);
+
+ canheader.dlc &= 0x0f;
+
+ canfd = (canheader.flags & BLF_CANFDMESSAGE64_FLAG_EDL) == BLF_CANFDMESSAGE64_FLAG_EDL;
+ if (canfd) {
+ payload_length = canfd_dlc_to_length[canheader.dlc];
+ } else {
+ if (canheader.dlc > 8) {
+ ws_debug("regular CAN tries more than 8 bytes?");
+ }
+ payload_length = can_dlc_to_length[canheader.dlc];
+ }
+
+ if (payload_length > canheader.validDataBytes) {
+ ws_debug("shortening canfd payload because valid data bytes shorter!");
+ payload_length = canheader.validDataBytes;
+ }
+
+ canid = canheader.id;
+
+ if (!canfd && (canheader.flags & BLF_CANFDMESSAGE64_FLAG_REMOTE_FRAME) == BLF_CANFDMESSAGE64_FLAG_REMOTE_FRAME) {
+ canid |= CAN_RTR_FLAG;
+ payload_length = 0; /* Should already be zero from validDataBytes */
+ }
+
+ payload_length_valid = payload_length;
+
+ if (payload_length_valid > object_length - (data_start - block_start)) {
+ ws_debug("shortening can payload because buffer is too short!");
+ payload_length_valid = (guint8)(object_length - (data_start - block_start));
+ }
+
+ if (!blf_can_fill_buf_and_rec(params, err, err_info, canid, payload_length, payload_length_valid, data_start + sizeof(canheader), flags, object_timestamp, canheader.channel)) {
+ return FALSE;
+ }
+
+ blf_add_direction_option(params, canheader.dir);
+
+ return TRUE;
+}
+
+static gboolean
+blf_read_canerror(blf_params_t *params, int *err, gchar **err_info, gint64 block_start, gint64 data_start, gint64 object_length, guint32 flags, guint64 object_timestamp) {
+ blf_canerror_t canheader;
+ guint32 canid;
+ guint8 payload_length;
+ guint8 tmpbuf[16] = {0};
+
+ if (object_length < (data_start - block_start) + (int) sizeof(canheader)) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("blf: CAN_ERROR: not enough bytes for canerror header in object");
+ ws_debug("not enough bytes for canerror header in object");
+ return FALSE;
+ }
+
+ if (!blf_read_bytes(params, data_start, &canheader, sizeof(canheader), err, err_info)) {
+ ws_debug("not enough bytes for canerror header in file");
+ return FALSE;
+ }
+ fix_endianness_blf_canerror(&canheader);
+
+ // Set CAN_ERR_FLAG in unused bits of Can ID to indicate error in socketcan
+ canid = CAN_ERR_FLAG;
+
+ // Fixed packet data length for socketcan error messages
+ payload_length = CAN_ERR_DLC;
+
+ tmpbuf[0] = (canid & 0xff000000) >> 24;
+ tmpbuf[1] = (canid & 0x00ff0000) >> 16;
+ tmpbuf[2] = (canid & 0x0000ff00) >> 8;
+ tmpbuf[3] = (canid & 0x000000ff);
+ tmpbuf[4] = payload_length;
+
+ ws_buffer_assure_space(params->buf, sizeof(tmpbuf));
+ ws_buffer_append(params->buf, tmpbuf, sizeof(tmpbuf));
+
+ blf_init_rec(params, flags, object_timestamp, WTAP_ENCAP_SOCKETCAN, canheader.channel, UINT16_MAX, sizeof(tmpbuf), sizeof(tmpbuf));
+ return TRUE;
+}
+
+static gboolean
+blf_read_canerrorext(blf_params_t *params, int *err, gchar **err_info, gint64 block_start, gint64 data_start, gint64 object_length, guint32 flags, guint64 object_timestamp) {
+ blf_canerrorext_t canheader;
+
+ gboolean err_ack = false;
+ gboolean err_prot = false;
+ gboolean direction_tx;
+ guint32 canid;
+ guint8 payload_length;
+ guint8 tmpbuf[16] = {0};
+
+ if (object_length < (data_start - block_start) + (int) sizeof(canheader)) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("blf: CAN_ERROR_EXT: not enough bytes for canerrorext header in object");
+ ws_debug("not enough bytes for canerrorext header in object");
+ return FALSE;
+ }
+
+ if (!blf_read_bytes(params, data_start, &canheader, sizeof(canheader), err, err_info)) {
+ ws_debug("not enough bytes for canerrorext header in file");
+ return FALSE;
+ }
+ fix_endianness_blf_canerrorext(&canheader);
+
+ if (canheader.flags & BLF_CANERROREXT_FLAG_CANCORE) {
+ // Map Vector Can Core error codes to compareable socketcan errors
+ switch ((canheader.errorCodeExt >> 6) & 0x3f) {
+ case BLF_CANERROREXT_ECC_MEANING_BIT_ERROR:
+ err_prot = true;
+ tmpbuf[10] = CAN_ERR_PROT_BIT;
+ break;
+ case BLF_CANERROREXT_ECC_MEANING_FORM_ERROR:
+ err_prot = true;
+ tmpbuf[10] = CAN_ERR_PROT_FORM;
+ break;
+ case BLF_CANERROREXT_ECC_MEANING_STUFF_ERROR:
+ err_prot = true;
+ tmpbuf[10] = CAN_ERR_PROT_STUFF;
+ break;
+ case BLF_CANERROREXT_ECC_MEANING_CRC_ERROR:
+ err_prot = true;
+ tmpbuf[11] = CAN_ERR_PROT_LOC_CRC_SEQ;
+ break;
+ case BLF_CANERROREXT_ECC_MEANING_NACK_ERROR:
+ err_ack = true;
+ tmpbuf[11] = CAN_ERR_PROT_LOC_ACK;
+ break;
+ case BLF_CANERROREXT_ECC_MEANING_OVERLOAD:
+ err_prot = true;
+ tmpbuf[10] = CAN_ERR_PROT_OVERLOAD;
+ break;
+ default:
+ err_prot = true;
+ tmpbuf[10] = CAN_ERR_PROT_UNSPEC;
+ break;
+ }
+ err_ack = err_ack || (canheader.errorCodeExt & BLF_CANERROREXT_EXTECC_NOT_ACK) == 0x0;
+ if (err_ack) {
+ // Don't set protocol error on ack errors
+ err_prot = false;
+ }
+ }
+
+ // CanID contains error class in socketcan
+ canid = CAN_ERR_FLAG;
+ canid |= err_prot ? CAN_ERR_PROT : 0;
+ canid |= err_ack ? CAN_ERR_ACK : 0;
+
+ // Fixed packet data length for socketcan error messages
+ payload_length = CAN_ERR_DLC;
+ canheader.dlc = payload_length;
+
+ tmpbuf[0] = (canid & 0xff000000) >> 24;
+ tmpbuf[1] = (canid & 0x00ff0000) >> 16;
+ tmpbuf[2] = (canid & 0x0000ff00) >> 8;
+ tmpbuf[3] = (canid & 0x000000ff);
+ tmpbuf[4] = payload_length;
+
+ ws_buffer_assure_space(params->buf, sizeof(tmpbuf));
+ ws_buffer_append(params->buf, tmpbuf, sizeof(tmpbuf));
+
+ blf_init_rec(params, flags, object_timestamp, WTAP_ENCAP_SOCKETCAN, canheader.channel, UINT16_MAX, sizeof(tmpbuf), sizeof(tmpbuf));
+ if (canheader.flags & BLF_CANERROREXT_FLAG_CANCORE) {
+ direction_tx = (canheader.errorCodeExt & BLF_CANERROREXT_EXTECC_TX) == BLF_CANERROREXT_EXTECC_TX;
+ blf_add_direction_option(params, direction_tx ? BLF_DIR_TX: BLF_DIR_RX);
+ }
+ return TRUE;
+}
+
+static gboolean
+blf_read_canfderror64(blf_params_t *params, int *err, gchar **err_info, gint64 block_start, gint64 data_start, gint64 object_length, guint32 flags, guint64 object_timestamp) {
+ blf_canfderror64_t canheader;
+
+ gboolean err_ack = false;
+ gboolean err_prot = false;
+ gboolean direction_tx;
+ guint32 canid;
+ guint8 payload_length;
+ guint8 tmpbuf[16] = {0};
+
+ if (object_length < (data_start - block_start) + (int) sizeof(canheader)) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("blf: CAN_FD_ERROR_64: not enough bytes for canfderror header in object");
+ ws_debug("not enough bytes for canfderror header in object");
+ return FALSE;
+ }
+
+ if (!blf_read_bytes(params, data_start, &canheader, sizeof(canheader), err, err_info)) {
+ ws_debug("not enough bytes for canfderror header in file");
+ return FALSE;
+ }
+ fix_endianness_blf_canfderror64(&canheader);
+
+ if (canheader.flags & BLF_CANERROREXT_FLAG_CANCORE) {
+ // Map Vector Can Core error codes to compareable socketcan errors
+ switch ((canheader.errorCodeExt >> 6) & 0x3f) {
+ case BLF_CANERROREXT_ECC_MEANING_BIT_ERROR:
+ err_prot = true;
+ tmpbuf[10] = CAN_ERR_PROT_BIT;
+ break;
+ case BLF_CANERROREXT_ECC_MEANING_FORM_ERROR:
+ err_prot = true;
+ tmpbuf[10] = CAN_ERR_PROT_FORM;
+ break;
+ case BLF_CANERROREXT_ECC_MEANING_STUFF_ERROR:
+ err_prot = true;
+ tmpbuf[10] = CAN_ERR_PROT_STUFF;
+ break;
+ case BLF_CANERROREXT_ECC_MEANING_CRC_ERROR:
+ err_prot = true;
+ tmpbuf[11] = CAN_ERR_PROT_LOC_CRC_SEQ;
+ break;
+ case BLF_CANERROREXT_ECC_MEANING_NACK_ERROR:
+ err_ack = true;
+ tmpbuf[11] = CAN_ERR_PROT_LOC_ACK;
+ break;
+ case BLF_CANERROREXT_ECC_MEANING_OVERLOAD:
+ err_prot = true;
+ tmpbuf[10] = CAN_ERR_PROT_OVERLOAD;
+ break;
+ default:
+ err_prot = true;
+ tmpbuf[10] = CAN_ERR_PROT_UNSPEC;
+ break;
+ }
+ err_ack = err_ack || (canheader.errorCodeExt & BLF_CANERROREXT_EXTECC_NOT_ACK) == 0x0;
+ if (err_ack) {
+ // Don't set protocol error on ack errors
+ err_prot = false;
+ }
+ }
+
+ // CanID contains error class in socketcan
+ canid = CAN_ERR_FLAG;
+ canid |= err_prot ? CAN_ERR_PROT : 0;
+ canid |= err_ack ? CAN_ERR_ACK : 0;
+
+ // Fixed packet data length for socketcan error messages
+ payload_length = CAN_ERR_DLC;
+ canheader.dlc = payload_length;
+
+ tmpbuf[0] = (canid & 0xff000000) >> 24;
+ tmpbuf[1] = (canid & 0x00ff0000) >> 16;
+ tmpbuf[2] = (canid & 0x0000ff00) >> 8;
+ tmpbuf[3] = (canid & 0x000000ff);
+ tmpbuf[4] = payload_length;
+
+ ws_buffer_assure_space(params->buf, sizeof(tmpbuf));
+ ws_buffer_append(params->buf, tmpbuf, sizeof(tmpbuf));
+
+ blf_init_rec(params, flags, object_timestamp, WTAP_ENCAP_SOCKETCAN, canheader.channel, UINT16_MAX, sizeof(tmpbuf), sizeof(tmpbuf));
+ if (canheader.flags & BLF_CANERROREXT_FLAG_CANCORE) {
+ direction_tx = (canheader.errorCodeExt & BLF_CANERROREXT_EXTECC_TX) == BLF_CANERROREXT_EXTECC_TX;
+ blf_add_direction_option(params, direction_tx ? BLF_DIR_TX: BLF_DIR_RX);
+ }
+ return TRUE;
+}
+
+static gboolean
+blf_read_flexraydata(blf_params_t *params, int *err, gchar **err_info, gint64 block_start, gint64 data_start, gint64 object_length, guint32 flags, guint64 object_timestamp) {
+ blf_flexraydata_t frheader;
+
+ guint8 payload_length;
+ guint8 payload_length_valid;
+ guint8 tmpbuf[7];
+ guint caplen, len;
+
+ if (object_length < (data_start - block_start) + (int) sizeof(frheader)) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("blf: FLEXRAY_DATA: not enough bytes for flexrayheader in object");
+ ws_debug("not enough bytes for flexrayheader in object");
+ return FALSE;
+ }
+
+ if (!blf_read_bytes(params, data_start, &frheader, sizeof(frheader), err, err_info)) {
+ ws_debug("not enough bytes for flexrayheader header in file");
+ return FALSE;
+ }
+ fix_endianness_blf_flexraydata(&frheader);
+
+ payload_length = frheader.len;
+ payload_length_valid = payload_length;
+
+ if ((frheader.len & 0x01) == 0x01) {
+ ws_debug("reading odd length in FlexRay!?");
+ }
+
+ if (payload_length_valid > object_length - (data_start - block_start) - sizeof(frheader)) {
+ ws_debug("shortening FlexRay payload because buffer is too short!");
+ payload_length_valid = (guint8)(object_length - (data_start - block_start) - sizeof(frheader));
+ }
+
+ if (frheader.channel != 0 && frheader.channel != 1) {
+ ws_debug("FlexRay supports only two channels.");
+ }
+
+ /* Measurement Header */
+ if (frheader.channel == 0) {
+ tmpbuf[0] = BLF_FLEXRAYDATA_FRAME;
+ } else {
+ tmpbuf[0] = BLF_FLEXRAYDATA_FRAME | BLF_FLEXRAYDATA_CHANNEL_B;
+ }
+
+ /* Error Flags */
+ tmpbuf[1] = 0;
+
+ /* Frame Header */
+ tmpbuf[2] = 0x20 | ((0x0700 & frheader.messageId) >> 8);
+ tmpbuf[3] = 0x00ff & frheader.messageId;
+ tmpbuf[4] = (0xfe & frheader.len) | ((frheader.crc & 0x0400) >> 10);
+ tmpbuf[5] = (0x03fc & frheader.crc) >> 2;
+ tmpbuf[6] = ((0x0003 & frheader.crc) << 6) | (0x3f & frheader.mux);
+
+ ws_buffer_assure_space(params->buf, sizeof(tmpbuf) + payload_length_valid);
+ ws_buffer_append(params->buf, tmpbuf, sizeof(tmpbuf));
+ caplen = sizeof(tmpbuf) + payload_length_valid;
+ len = sizeof(tmpbuf) + payload_length;
+
+ if (payload_length_valid > 0 && !blf_read_bytes(params, data_start + sizeof(frheader), ws_buffer_end_ptr(params->buf), payload_length_valid, err, err_info)) {
+ ws_debug("copying flexray payload failed");
+ return FALSE;
+ }
+ params->buf->first_free += payload_length_valid;
+
+ blf_init_rec(params, flags, object_timestamp, WTAP_ENCAP_FLEXRAY, frheader.channel, UINT16_MAX, caplen, len);
+ blf_add_direction_option(params, frheader.dir);
+
+ return TRUE;
+}
+
+static gboolean
+blf_read_flexraymessage(blf_params_t *params, int *err, gchar **err_info, gint64 block_start, gint64 data_start, gint64 object_length, guint32 flags, guint64 object_timestamp) {
+ blf_flexraymessage_t frheader;
+
+ guint8 payload_length;
+ guint8 payload_length_valid;
+ guint8 tmpbuf[7];
+ guint caplen, len;
+
+ if (object_length < (data_start - block_start) + (int) sizeof(frheader)) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("blf: FLEXRAY_MESSAGE: not enough bytes for flexrayheader in object");
+ ws_debug("not enough bytes for flexrayheader in object");
+ return FALSE;
+ }
+
+ if (!blf_read_bytes(params, data_start, &frheader, sizeof(frheader), err, err_info)) {
+ ws_debug("not enough bytes for flexrayheader header in file");
+ return FALSE;
+ }
+ fix_endianness_blf_flexraymessage(&frheader);
+
+ payload_length = frheader.length;
+ payload_length_valid = payload_length;
+
+ if ((frheader.length & 0x01) == 0x01) {
+ ws_debug("reading odd length in FlexRay!?");
+ }
+
+ if (payload_length_valid > object_length - (data_start - block_start) - sizeof(frheader)) {
+ ws_debug("shortening FlexRay payload because buffer is too short!");
+ payload_length_valid = (guint8)(object_length - (data_start - block_start) - sizeof(frheader));
+ }
+
+ if (frheader.channel != 0 && frheader.channel != 1) {
+ ws_debug("FlexRay supports only two channels.");
+ }
+
+ /* Measurement Header */
+ if (frheader.channel == 0) {
+ tmpbuf[0] = BLF_FLEXRAYDATA_FRAME;
+ } else {
+ tmpbuf[0] = BLF_FLEXRAYDATA_FRAME | BLF_FLEXRAYDATA_CHANNEL_B;
+ }
+
+ /* Error Flags */
+ tmpbuf[1] = 0;
+
+ /* Frame Header */
+ tmpbuf[2] = ((0x0700 & frheader.frameId) >> 8);
+ if ((frheader.frameState & BLF_FLEXRAYMESSAGE_STATE_PPI) == BLF_FLEXRAYMESSAGE_STATE_PPI) {
+ tmpbuf[2] |= BLF_DLT_FLEXRAY_PPI;
+ }
+
+ if ((frheader.frameState & BLF_FLEXRAYMESSAGE_STATE_SFI) == BLF_FLEXRAYMESSAGE_STATE_SFI) {
+ tmpbuf[2] |= BLF_DLT_FLEXRAY_SFI;
+ }
+
+ if ((frheader.frameState & BLF_FLEXRAYMESSAGE_STATE_NFI) != BLF_FLEXRAYMESSAGE_STATE_NFI) {
+ /* NFI needs to be inversed !? */
+ tmpbuf[2] |= BLF_DLT_FLEXRAY_NFI;
+ }
+
+ if ((frheader.frameState & BLF_FLEXRAYMESSAGE_STATE_STFI) == BLF_FLEXRAYMESSAGE_STATE_STFI) {
+ tmpbuf[2] |= BLF_DLT_FLEXRAY_STFI;
+ }
+
+ tmpbuf[3] = 0x00ff & frheader.frameId;
+ tmpbuf[4] = (0xfe & frheader.length) | ((frheader.headerCrc & 0x0400) >> 10);
+ tmpbuf[5] = (0x03fc & frheader.headerCrc) >> 2;
+ tmpbuf[6] = ((0x0003 & frheader.headerCrc) << 6) | (0x3f & frheader.cycle);
+
+ ws_buffer_assure_space(params->buf, sizeof(tmpbuf) + payload_length_valid);
+ ws_buffer_append(params->buf, tmpbuf, sizeof(tmpbuf));
+ caplen = sizeof(tmpbuf) + payload_length_valid;
+ len = sizeof(tmpbuf) + payload_length;
+
+ if (payload_length_valid > 0 && !blf_read_bytes(params, data_start + sizeof(frheader), ws_buffer_end_ptr(params->buf), payload_length_valid, err, err_info)) {
+ ws_debug("copying flexray payload failed");
+ return FALSE;
+ }
+ params->buf->first_free += payload_length_valid;
+
+ blf_init_rec(params, flags, object_timestamp, WTAP_ENCAP_FLEXRAY, frheader.channel, UINT16_MAX, caplen, len);
+ blf_add_direction_option(params, frheader.dir);
+
+ return TRUE;
+}
+
+static gboolean
+blf_read_flexrayrcvmessageex(blf_params_t *params, int *err, gchar **err_info, gint64 block_start, gint64 data_start, gint64 object_length, guint32 flags, guint64 object_timestamp, gboolean ext) {
+ blf_flexrayrcvmessage_t frheader;
+
+ guint16 payload_length;
+ guint16 payload_length_valid;
+ guint8 tmpbuf[7];
+ gint frheadersize = sizeof(frheader);
+ guint caplen, len;
+
+ if (ext) {
+ frheadersize += 40;
+ }
+
+ if ((gint64)object_length < (data_start - block_start) + frheadersize) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("blf: %s: not enough bytes for flexrayheader in object",
+ ext ? "FLEXRAY_RCVMESSAGE_EX" : "FLEXRAY_RCVMESSAGE");
+ ws_debug("not enough bytes for flexrayheader in object");
+ return FALSE;
+ }
+
+ if (!blf_read_bytes(params, data_start, &frheader, sizeof(frheader), err, err_info)) {
+ ws_debug("not enough bytes for flexrayheader header in file");
+ return FALSE;
+ }
+ fix_endianness_blf_flexrayrcvmessage(&frheader);
+
+ if (!ext) {
+ frheader.dir &= 0xff;
+ frheader.cycle &= 0xff;
+ }
+
+ payload_length = frheader.payloadLength;
+ payload_length_valid = frheader.payloadLengthValid;
+
+ if ((frheader.payloadLength & 0x01) == 0x01) {
+ ws_debug("reading odd length in FlexRay!?");
+ }
+
+ if (payload_length_valid > object_length - (data_start - block_start) - frheadersize) {
+ ws_debug("shortening FlexRay payload because buffer is too short!");
+ payload_length_valid = (guint8)(object_length - (data_start - block_start) - frheadersize);
+ }
+
+ /* Measurement Header */
+ /* TODO: It seems that this format support both channels at the same time!? */
+ if (frheader.channelMask == BLF_FLEXRAYRCVMSG_CHANNELMASK_A) {
+ tmpbuf[0] = BLF_FLEXRAYDATA_FRAME;
+ } else {
+ tmpbuf[0] = BLF_FLEXRAYDATA_FRAME | BLF_FLEXRAYDATA_CHANNEL_B;
+ }
+
+ /* Error Flags */
+ tmpbuf[1] = 0;
+
+ /* Frame Header */
+ tmpbuf[2] = ((0x0700 & frheader.frameId) >> 8);
+ if ((frheader.data & BLF_FLEXRAYRCVMSG_DATA_FLAG_PAYLOAD_PREAM) == BLF_FLEXRAYRCVMSG_DATA_FLAG_PAYLOAD_PREAM) {
+ tmpbuf[2] |= BLF_DLT_FLEXRAY_PPI;
+ }
+
+ if ((frheader.data & BLF_FLEXRAYRCVMSG_DATA_FLAG_SYNC) == BLF_FLEXRAYRCVMSG_DATA_FLAG_SYNC) {
+ tmpbuf[2] |= BLF_DLT_FLEXRAY_SFI;
+ }
+
+ if ((frheader.data & BLF_FLEXRAYRCVMSG_DATA_FLAG_NULL_FRAME) != BLF_FLEXRAYRCVMSG_DATA_FLAG_NULL_FRAME) {
+ /* NFI needs to be inversed !? */
+ tmpbuf[2] |= BLF_DLT_FLEXRAY_NFI;
+ }
+
+ if ((frheader.data & BLF_FLEXRAYRCVMSG_DATA_FLAG_STARTUP) == BLF_FLEXRAYRCVMSG_DATA_FLAG_STARTUP) {
+ tmpbuf[2] |= BLF_DLT_FLEXRAY_STFI;
+ }
+
+ tmpbuf[3] = 0x00ff & frheader.frameId;
+ tmpbuf[4] = (0xfe & frheader.payloadLength) | ((frheader.headerCrc1 & 0x0400) >> 10);
+ tmpbuf[5] = (0x03fc & frheader.headerCrc1) >> 2;
+ tmpbuf[6] = ((0x0003 & frheader.headerCrc1) << 6) | (0x3f & frheader.cycle);
+
+ ws_buffer_assure_space(params->buf, sizeof(tmpbuf) + payload_length_valid);
+ ws_buffer_append(params->buf, tmpbuf, sizeof(tmpbuf));
+ caplen = sizeof(tmpbuf) + payload_length_valid;
+ len = sizeof(tmpbuf) + payload_length;
+
+ if (payload_length_valid > 0 && !blf_read_bytes(params, data_start + frheadersize, ws_buffer_end_ptr(params->buf), payload_length_valid, err, err_info)) {
+ ws_debug("copying flexray payload failed");
+ return FALSE;
+ }
+ params->buf->first_free += payload_length_valid;
+
+ blf_init_rec(params, flags, object_timestamp, WTAP_ENCAP_FLEXRAY, frheader.channelMask, UINT16_MAX, caplen, len);
+ blf_add_direction_option(params, frheader.dir);
+
+ return TRUE;
+}
+
+static gboolean
+blf_read_linmessage(blf_params_t *params, int *err, gchar **err_info, gint64 block_start, gint64 data_start, gint64 object_length, guint32 flags, guint64 object_timestamp) {
+ blf_linmessage_t linheader;
+ blf_linmessage_trailer_t lintrailer;
+
+ guint8 payload_length;
+ guint8 payload_length_valid;
+ guint caplen, len;
+
+ if (object_length < (data_start - block_start) + (int)sizeof(linheader)) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("blf: LIN_MESSAGE: not enough bytes for linmessage header in object");
+ ws_debug("not enough bytes for linmessage header in object");
+ return FALSE;
+ }
+
+ if (!blf_read_bytes(params, data_start, &linheader, sizeof(linheader), err, err_info)) {
+ ws_debug("not enough bytes for linmessage header in file");
+ return FALSE;
+ }
+ fix_endianness_blf_linmessage(&linheader);
+
+ if (linheader.dlc > 15) {
+ linheader.dlc = 15;
+ }
+
+ payload_length = linheader.dlc;
+ payload_length_valid = payload_length;
+
+ if (payload_length_valid > object_length - (data_start - block_start)) {
+ ws_debug("shortening LIN payload because buffer is too short!");
+ payload_length_valid = (guint8)(object_length - (data_start - block_start));
+ }
+
+ guint8 tmpbuf[8];
+ tmpbuf[0] = 1; /* message format rev = 1 */
+ tmpbuf[1] = 0; /* reserved */
+ tmpbuf[2] = 0; /* reserved */
+ tmpbuf[3] = 0; /* reserved */
+ tmpbuf[4] = (linheader.dlc << 4) | 0; /* dlc (4bit) | type (2bit) | checksum type (2bit) */
+ tmpbuf[5] = linheader.id;
+ tmpbuf[6] = 0; /* checksum */
+ tmpbuf[7] = 0; /* errors */
+
+ ws_buffer_assure_space(params->buf, sizeof(tmpbuf) + payload_length_valid);
+ ws_buffer_append(params->buf, tmpbuf, sizeof(tmpbuf));
+ caplen = sizeof(tmpbuf) + payload_length_valid;
+ len = sizeof(tmpbuf) + payload_length;
+
+ if (payload_length_valid > 0 && !blf_read_bytes(params, data_start + 4, ws_buffer_end_ptr(params->buf), payload_length_valid, err, err_info)) {
+ ws_debug("copying can payload failed");
+ return FALSE;
+ }
+ params->buf->first_free += payload_length_valid;
+
+ if (object_length < (data_start - block_start) + (int)sizeof(linheader) + payload_length_valid + (int)sizeof(lintrailer)) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("blf: LIN_MESSAGE: not enough bytes for linmessage trailer");
+ ws_debug("not enough bytes for linmessage trailer");
+ return FALSE;
+ }
+ if (!blf_read_bytes(params, data_start + sizeof(linheader) + payload_length_valid, &lintrailer, sizeof(lintrailer), err, err_info)) {
+ ws_debug("not enough bytes for linmessage trailer in file");
+ return FALSE;
+ }
+ fix_endianness_blf_linmessage_trailer(&lintrailer);
+ /* we are not using it right now since the CRC is too big to convert */
+
+ blf_init_rec(params, flags, object_timestamp, WTAP_ENCAP_LIN, linheader.channel, UINT16_MAX, caplen, len);
+ blf_add_direction_option(params, lintrailer.dir);
+
+ return TRUE;
+}
+
+static int
+blf_read_apptextmessage(blf_params_t *params, int *err, gchar **err_info, gint64 block_start, gint64 data_start, gint64 object_length, guint32 flags, guint64 object_timestamp) {
+ blf_apptext_t apptextheader;
+
+ if (object_length < (data_start - block_start) + (int)sizeof(apptextheader)) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("blf: APP_TEXT: not enough bytes for apptext header in object");
+ ws_debug("not enough bytes for apptext header in object");
+ return BLF_APPTEXT_FAILED;
+ }
+
+ if (!blf_read_bytes(params, data_start, &apptextheader, sizeof(apptextheader), err, err_info)) {
+ ws_debug("not enough bytes for apptext header in file");
+ return BLF_APPTEXT_FAILED;
+ }
+ fix_endianness_blf_apptext_header(&apptextheader);
+
+ /* Add an extra byte for a terminating '\0' */
+ gchar* text = g_try_malloc((gsize)apptextheader.textLength + 1);
+
+ if (!blf_read_bytes(params, data_start + sizeof(apptextheader), text, apptextheader.textLength, err, err_info)) {
+ ws_debug("not enough bytes for apptext text in file");
+ g_free(text);
+ return BLF_APPTEXT_FAILED;
+ }
+ text[apptextheader.textLength] = '\0'; /* Here's the '\0' */
+
+ switch (apptextheader.source) {
+ case BLF_APPTEXT_CHANNEL:
+ {
+
+ /* returns a NULL terminated array of NULL terminates strings */
+ gchar** tokens = g_strsplit_set(text, ";", -1);
+
+ if (tokens == NULL || tokens[0] == NULL || tokens[1] == NULL) {
+ if (tokens != NULL) {
+ g_strfreev(tokens);
+ }
+ g_free(text);
+ return BLF_APPTEXT_CHANNEL;
+ }
+
+ guint16 channel = (apptextheader.reservedAppText1 >> 8) & 0xff;
+ int pkt_encap;
+
+ switch ((apptextheader.reservedAppText1 >> 16) & 0xff) {
+ case BLF_BUSTYPE_CAN:
+ pkt_encap = WTAP_ENCAP_SOCKETCAN;
+ break;
+
+ case BLF_BUSTYPE_FLEXRAY:
+ pkt_encap = WTAP_ENCAP_FLEXRAY;
+ break;
+
+ case BLF_BUSTYPE_LIN:
+ pkt_encap = WTAP_ENCAP_LIN;
+ break;
+
+ case BLF_BUSTYPE_ETHERNET:
+ pkt_encap = WTAP_ENCAP_ETHERNET;
+ break;
+
+ case BLF_BUSTYPE_WLAN:
+ pkt_encap = WTAP_ENCAP_IEEE_802_11;
+ break;
+
+ default:
+ pkt_encap = 0xffffffff;
+ }
+
+ /* we use lookup to create interface, if not existing yet */
+ blf_lookup_interface(params, pkt_encap, channel, UINT16_MAX, tokens[1]);
+
+ g_strfreev(tokens);
+ g_free(text);
+ return BLF_APPTEXT_CHANNEL;
+ break;
+ }
+ case BLF_APPTEXT_METADATA:
+ case BLF_APPTEXT_COMMENT:
+ if (apptextheader.textLength < 5) {
+ /* Arbitrary length chosen */
+ g_free(text);
+ return BLF_APPTEXT_CHANNEL; /* Cheat - no block to write */
+ }
+ wtap_buffer_append_epdu_string(params->buf, EXP_PDU_TAG_DISSECTOR_NAME, "data-text-lines");
+ wtap_buffer_append_epdu_string(params->buf, EXP_PDU_TAG_COL_PROT_TEXT, "BLF App text");
+ if (apptextheader.source == BLF_APPTEXT_METADATA) {
+ wtap_buffer_append_epdu_string(params->buf, EXP_PDU_TAG_COL_INFO_TEXT, "Metadata");
+ } else {
+ wtap_buffer_append_epdu_string(params->buf, EXP_PDU_TAG_COL_INFO_TEXT, "Comment");
+ }
+
+ wtap_buffer_append_epdu_end(params->buf);
+
+ ws_buffer_assure_space(params->buf, apptextheader.textLength); /* The dissector doesn't need NULL-terminated strings */
+ ws_buffer_append(params->buf, text, apptextheader.textLength);
+
+ /* We'll write this as a WS UPPER PDU packet with a text blob */
+ blf_init_rec(params, flags, object_timestamp, WTAP_ENCAP_WIRESHARK_UPPER_PDU, 0, UINT16_MAX, (guint32)ws_buffer_length(params->buf), (guint32)ws_buffer_length(params->buf));
+ g_free(text);
+ return apptextheader.source;
+ break;
+ default:
+ g_free(text);
+ return BLF_APPTEXT_CHANNEL; /* Cheat - no block to write */;
+ break;
+ }
+ return BLF_APPTEXT_CHANNEL; /* Cheat - no block to write */
+}
+
+static gboolean
+blf_read_ethernet_status(blf_params_t* params, int* err, gchar** err_info, gint64 block_start, gint64 data_start, gint64 object_length, guint32 flags, guint64 object_timestamp) {
+ blf_ethernet_status_t ethernet_status_header;
+ guint8 tmpbuf[16];
+
+ if (object_length < (data_start - block_start) + (int)sizeof(ethernet_status_header)) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("blf: ETHERNET_STATUS: not enough bytes for ethernet status header in object");
+ ws_debug("not enough bytes for ethernet status header in object");
+ return FALSE;
+ }
+
+ if (!blf_read_bytes(params, data_start, &ethernet_status_header, sizeof(ethernet_status_header), err, err_info)) {
+ ws_debug("not enough bytes for ethernet_status_header header in file");
+ return FALSE;
+ }
+
+ fix_endianness_blf_ethernet_status_header(&ethernet_status_header);
+
+ tmpbuf[0] = (ethernet_status_header.channel & 0xff00) >> 8;
+ tmpbuf[1] = (ethernet_status_header.channel & 0x00ff);
+ tmpbuf[2] = (ethernet_status_header.flags & 0xff00) >> 8;
+ tmpbuf[3] = (ethernet_status_header.flags & 0x00ff);
+ tmpbuf[4] = (ethernet_status_header.linkStatus);
+ tmpbuf[5] = (ethernet_status_header.ethernetPhy);
+ tmpbuf[6] = (ethernet_status_header.duplex);
+ tmpbuf[7] = (ethernet_status_header.mdi);
+ tmpbuf[8] = (ethernet_status_header.connector);
+ tmpbuf[9] = (ethernet_status_header.clockMode);
+ tmpbuf[10] = (ethernet_status_header.pairs);
+ tmpbuf[11] = (ethernet_status_header.hardwareChannel);
+ tmpbuf[12] = (ethernet_status_header.bitrate & 0xff000000) >> 24;
+ tmpbuf[13] = (ethernet_status_header.bitrate & 0x00ff0000) >> 16;
+ tmpbuf[14] = (ethernet_status_header.bitrate & 0x0000ff00) >> 8;
+ tmpbuf[15] = (ethernet_status_header.bitrate & 0x000000ff);
+
+ wtap_buffer_append_epdu_string(params->buf, EXP_PDU_TAG_DISSECTOR_NAME, "blf-ethernetstatus-obj");
+ wtap_buffer_append_epdu_end(params->buf);
+
+ ws_buffer_assure_space(params->buf, sizeof(ethernet_status_header));
+ ws_buffer_append(params->buf, tmpbuf, (gsize)16);
+
+ /* We'll write this as a WS UPPER PDU packet with a data blob */
+ /* This will create an interface with the "name" of the matching
+ * WTAP_ENCAP_ETHERNET interface with the same channel and hardware
+ * channel prefixed with "STATUS" and with a different interface ID,
+ * because IDBs in pcapng can only have one linktype.
+ * The other option would be to write everything as UPPER_PDU, including
+ * the Ethernet data (with one of the "eth_" dissectors.)
+ */
+ char* iface_name = ws_strdup_printf("STATUS-ETH-%u-%u", ethernet_status_header.channel, ethernet_status_header.hardwareChannel);
+ blf_lookup_interface(params, WTAP_ENCAP_WIRESHARK_UPPER_PDU, ethernet_status_header.channel, ethernet_status_header.hardwareChannel, iface_name);
+ g_free(iface_name);
+ blf_init_rec(params, flags, object_timestamp, WTAP_ENCAP_WIRESHARK_UPPER_PDU, ethernet_status_header.channel, ethernet_status_header.hardwareChannel, (guint32)ws_buffer_length(params->buf), (guint32)ws_buffer_length(params->buf));
+
+ if ((ethernet_status_header.flags & BLF_ETH_STATUS_HARDWARECHANNEL) == BLF_ETH_STATUS_HARDWARECHANNEL) {
+ /* If HW channel valid */
+ wtap_block_add_uint32_option(params->rec->block, OPT_PKT_QUEUE, ethernet_status_header.hardwareChannel);
+ }
+
+ return TRUE;
+}
+
+static gboolean
+blf_read_block(blf_params_t *params, gint64 start_pos, int *err, gchar **err_info) {
+ blf_blockheader_t header;
+ blf_logobjectheader_t logheader;
+ blf_logobjectheader2_t logheader2;
+ blf_logobjectheader3_t logheader3;
+ guint32 flags;
+ guint64 object_timestamp;
+
+ while (1) {
+ /* Find Object */
+
+ /* Resetting buffer */
+ params->buf->first_free = params->buf->start;
+
+ while (1) {
+ if (!blf_read_bytes_or_eof(params, start_pos, &header, sizeof header, err, err_info)) {
+ ws_debug("not enough bytes for block header or unsupported file");
+ if (*err == WTAP_ERR_SHORT_READ) {
+ /* we have found the end that is not a short read therefore. */
+ *err = 0;
+ g_free(*err_info);
+ }
+ return FALSE;
+ }
+
+ fix_endianness_blf_blockheader(&header);
+
+ if (memcmp(header.magic, blf_obj_magic, sizeof(blf_obj_magic))) {
+ ws_debug("object magic is not LOBJ (pos: 0x%" PRIx64 ")", start_pos);
+ } else {
+ break;
+ }
+
+ /* we are moving back and try again but 1 byte later */
+ /* TODO: better understand how this paddings works... */
+ start_pos++;
+ }
+ params->blf_data->start_of_last_obj = start_pos;
+
+ switch (header.header_type) {
+ case BLF_HEADER_TYPE_DEFAULT:
+ if (!blf_read_log_object_header(params, err, err_info, start_pos + sizeof(blf_blockheader_t), start_pos + header.header_length, &logheader)) {
+ return FALSE;
+ }
+ flags = logheader.flags;
+ object_timestamp = logheader.object_timestamp;
+ break;
+
+ case BLF_HEADER_TYPE_2:
+ if (!blf_read_log_object_header2(params, err, err_info, start_pos + sizeof(blf_blockheader_t), start_pos + header.header_length, &logheader2)) {
+ return FALSE;
+ }
+ flags = logheader2.flags;
+ object_timestamp = logheader2.object_timestamp;
+ break;
+
+ case BLF_HEADER_TYPE_3:
+ if (!blf_read_log_object_header3(params, err, err_info, start_pos + sizeof(blf_blockheader_t), start_pos + header.header_length, &logheader3)) {
+ return FALSE;
+ }
+ flags = logheader3.flags;
+ object_timestamp = logheader3.object_timestamp;
+ break;
+
+ default:
+ *err = WTAP_ERR_UNSUPPORTED;
+ *err_info = ws_strdup_printf("blf: unknown header type %u", header.header_type);
+ ws_debug("unknown header type");
+ return FALSE;
+ }
+
+ /* already making sure that we start after this object next time. */
+ params->blf_data->current_real_seek_pos = start_pos + MAX(MAX(16, header.object_length), header.header_length);
+
+ switch (header.object_type) {
+ case BLF_OBJTYPE_LOG_CONTAINER:
+ *err = WTAP_ERR_UNSUPPORTED;
+ *err_info = ws_strdup_printf("blf: log container in log container not supported");
+ ws_debug("log container in log container not supported");
+ return FALSE;
+ break;
+
+ case BLF_OBJTYPE_ETHERNET_FRAME:
+ return blf_read_ethernetframe(params, err, err_info, start_pos, start_pos + header.header_length, header.object_length, flags, object_timestamp);
+ break;
+
+ case BLF_OBJTYPE_ETHERNET_FRAME_EX:
+ return blf_read_ethernetframe_ext(params, err, err_info, start_pos, start_pos + header.header_length, header.object_length, flags, object_timestamp);
+ break;
+
+ case BLF_OBJTYPE_WLAN_FRAME:
+ return blf_read_wlanframe(params, err, err_info, start_pos, start_pos + header.header_length, header.object_length, flags, object_timestamp);
+ break;
+
+ case BLF_OBJTYPE_CAN_MESSAGE:
+ return blf_read_canmessage(params, err, err_info, start_pos, start_pos + header.header_length, header.object_length, flags, object_timestamp, FALSE);
+ break;
+
+ case BLF_OBJTYPE_CAN_ERROR:
+ return blf_read_canerror(params, err, err_info, start_pos, start_pos + header.header_length, header.object_length, flags, object_timestamp);
+ break;
+
+ case BLF_OBJTYPE_CAN_MESSAGE2:
+ return blf_read_canmessage(params, err, err_info, start_pos, start_pos + header.header_length, header.object_length, flags, object_timestamp, TRUE);
+ break;
+
+ case BLF_OBJTYPE_CAN_ERROR_EXT:
+ return blf_read_canerrorext(params, err, err_info, start_pos, start_pos + header.header_length, header.object_length, flags, object_timestamp);
+ break;
+
+ case BLF_OBJTYPE_CAN_FD_MESSAGE:
+ return blf_read_canfdmessage(params, err, err_info, start_pos, start_pos + header.header_length, header.object_length, flags, object_timestamp);
+ break;
+
+ case BLF_OBJTYPE_CAN_FD_MESSAGE_64:
+ return blf_read_canfdmessage64(params, err, err_info, start_pos, start_pos + header.header_length, header.object_length, flags, object_timestamp);
+ break;
+
+ case BLF_OBJTYPE_CAN_FD_ERROR_64:
+ return blf_read_canfderror64(params, err, err_info, start_pos, start_pos + header.header_length, header.object_length, flags, object_timestamp);
+ break;
+
+ case BLF_OBJTYPE_FLEXRAY_DATA:
+ return blf_read_flexraydata(params, err, err_info, start_pos, start_pos + header.header_length, header.object_length, flags, object_timestamp);
+ break;
+
+ case BLF_OBJTYPE_FLEXRAY_MESSAGE:
+ return blf_read_flexraymessage(params, err, err_info, start_pos, start_pos + header.header_length, header.object_length, flags, object_timestamp);
+ break;
+
+ case BLF_OBJTYPE_FLEXRAY_RCVMESSAGE:
+ return blf_read_flexrayrcvmessageex(params, err, err_info, start_pos, start_pos + header.header_length, header.object_length, flags, object_timestamp, FALSE);
+ break;
+
+ case BLF_OBJTYPE_FLEXRAY_RCVMESSAGE_EX:
+ return blf_read_flexrayrcvmessageex(params, err, err_info, start_pos, start_pos + header.header_length, header.object_length, flags, object_timestamp, TRUE);
+ break;
+
+ case BLF_OBJTYPE_LIN_MESSAGE:
+ return blf_read_linmessage(params, err, err_info, start_pos, start_pos + header.header_length, header.object_length, flags, object_timestamp);
+ break;
+
+ case BLF_OBJTYPE_APP_TEXT:
+ {
+ int result = blf_read_apptextmessage(params, err, err_info, start_pos, start_pos + header.header_length, header.object_length, flags, object_timestamp);
+ switch (result) {
+ case BLF_APPTEXT_FAILED:
+ return FALSE;
+ case BLF_APPTEXT_METADATA:
+ return TRUE;
+ case BLF_APPTEXT_COMMENT:
+ return TRUE;
+ case BLF_APPTEXT_CHANNEL:
+ default:
+ /* we do not return since there is no packet to show here */
+ start_pos += MAX(MAX(16, header.object_length), header.header_length);
+ }
+ }
+ break;
+
+ case BLF_OBJTYPE_ETHERNET_STATUS:
+ return blf_read_ethernet_status(params, err, err_info, start_pos, start_pos + header.header_length, header.object_length, flags, object_timestamp);
+ break;
+ default:
+ ws_debug("unknown object type 0x%04x", header.object_type);
+ start_pos += MAX(MAX(16, header.object_length), header.header_length);
+ }
+ }
+ return TRUE;
+}
+
+static gboolean blf_read(wtap *wth, wtap_rec *rec, Buffer *buf, int *err, gchar **err_info, gint64 *data_offset) {
+ blf_params_t blf_tmp;
+
+ blf_tmp.wth = wth;
+ blf_tmp.fh = wth->fh;
+ blf_tmp.rec = rec;
+ blf_tmp.buf = buf;
+ blf_tmp.blf_data = (blf_t *)wth->priv;
+
+ if (!blf_read_block(&blf_tmp, blf_tmp.blf_data->current_real_seek_pos, err, err_info)) {
+ return FALSE;
+ }
+ *data_offset = blf_tmp.blf_data->start_of_last_obj;
+
+ return TRUE;
+}
+
+static gboolean blf_seek_read(wtap *wth, gint64 seek_off, wtap_rec *rec, Buffer *buf, int *err, gchar **err_info) {
+ blf_params_t blf_tmp;
+
+ blf_tmp.wth = wth;
+ blf_tmp.fh = wth->random_fh;
+ blf_tmp.rec = rec;
+ blf_tmp.buf = buf;
+ blf_tmp.blf_data = (blf_t *)wth->priv;
+
+ if (!blf_read_block(&blf_tmp, seek_off, err, err_info)) {
+ ws_debug("couldn't read packet block (err=%d).", *err);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void blf_close(wtap *wth) {
+ blf_t *blf = (blf_t *)wth->priv;
+
+ if (blf != NULL && blf->log_containers != NULL) {
+ for (guint i = 0; i < blf->log_containers->len; i++) {
+ blf_log_container_t *log_container = &g_array_index(blf->log_containers, blf_log_container_t, i);
+ if (log_container->real_data != NULL) {
+ g_free(log_container->real_data);
+ }
+ }
+ g_array_free(blf->log_containers, TRUE);
+ blf->log_containers = NULL;
+ }
+
+ if (blf != NULL && blf->channel_to_iface_ht != NULL) {
+ g_hash_table_destroy(blf->channel_to_iface_ht);
+ blf->channel_to_iface_ht = NULL;
+ }
+
+ /* TODO: do we need to reverse the wtap_add_idb? how? */
+
+ return;
+}
+
+wtap_open_return_val
+blf_open(wtap *wth, int *err, gchar **err_info) {
+ blf_fileheader_t header;
+ blf_t *blf;
+ blf_params_t params;
+
+ ws_debug("opening file");
+
+ if (!wtap_read_bytes_or_eof(wth->fh, &header, sizeof header, err, err_info)) {
+
+ ws_debug("wtap_read_bytes_or_eof() failed, err = %d.", *err);
+ if (*err == 0 || *err == WTAP_ERR_SHORT_READ) {
+ /*
+ * Short read or EOF.
+ *
+ * We're reading this as part of an open, so
+ * the file is too short to be a blf file.
+ */
+ *err = 0;
+ g_free(*err_info);
+ *err_info = NULL;
+ return WTAP_OPEN_NOT_MINE;
+ }
+ return WTAP_OPEN_ERROR;
+ }
+
+ fix_endianness_blf_fileheader(&header);
+
+ if (memcmp(header.magic, blf_magic, sizeof(blf_magic))) {
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ /* This seems to be an BLF! */
+ /* skip unknown part of header */
+ file_seek(wth->fh, header.header_length, SEEK_SET, err);
+
+ struct tm timestamp;
+ timestamp.tm_year = (header.start_date.year > 1970) ? header.start_date.year - 1900 : 70;
+ timestamp.tm_mon = header.start_date.month -1;
+ timestamp.tm_mday = header.start_date.day;
+ timestamp.tm_hour = header.start_date.hour;
+ timestamp.tm_min = header.start_date.mins;
+ timestamp.tm_sec = header.start_date.sec;
+ timestamp.tm_isdst = -1;
+
+ /* Prepare our private context. */
+ blf = g_new(blf_t, 1);
+ blf->log_containers = NULL;
+ blf->current_log_container = 0;
+ blf->current_real_seek_pos = 0;
+ blf->start_offset_ns = 1000 * 1000 * 1000 * (guint64)mktime(&timestamp);
+ blf->start_offset_ns += 1000 * 1000 * header.start_date.ms;
+
+ blf->channel_to_iface_ht = g_hash_table_new_full(g_int64_hash, g_int64_equal, &blf_free_key, &blf_free_channel_to_iface_entry);
+ blf->next_interface_id = 0;
+
+ /* embed in params */
+ params.blf_data = blf;
+ params.buf = NULL;
+ params.fh = wth->fh;
+ params.rec = NULL;
+ params.wth = wth;
+ params.blf_data->current_real_seek_pos = 0;
+
+ /* lets check out the layout of all log containers */
+ blf_scan_file_for_logcontainers(&params);
+
+ wth->priv = (void *)blf;
+ wth->file_encap = WTAP_ENCAP_NONE;
+ wth->snapshot_length = 0;
+ wth->file_tsprec = WTAP_TSPREC_UNKNOWN;
+ wth->subtype_read = blf_read;
+ wth->subtype_seek_read = blf_seek_read;
+ wth->subtype_close = blf_close;
+ wth->file_type_subtype = blf_file_type_subtype;
+
+ return WTAP_OPEN_MINE;
+}
+
+/* Options for interface blocks. */
+static const struct supported_option_type interface_block_options_supported[] = {
+ /* No comments, just an interface name. */
+ { OPT_IDB_NAME, ONE_OPTION_SUPPORTED }
+};
+
+static const struct supported_block_type blf_blocks_supported[] = {
+ { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED },
+ { WTAP_BLOCK_IF_ID_AND_INFO, MULTIPLE_BLOCKS_SUPPORTED, OPTION_TYPES_SUPPORTED(interface_block_options_supported) },
+};
+
+static const struct file_type_subtype_info blf_info = {
+ "Vector Informatik Binary Logging Format (BLF) logfile", "blf", "blf", NULL,
+ FALSE, BLOCKS_SUPPORTED(blf_blocks_supported),
+ NULL, NULL, NULL
+};
+
+void register_blf(void)
+{
+ blf_file_type_subtype = wtap_register_file_type_subtype(&blf_info);
+
+ /*
+ * Register name for backwards compatibility with the
+ * wtap_filetypes table in Lua.
+ */
+ wtap_register_backwards_compatibility_lua_name("BLF", blf_file_type_subtype);
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * vi: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */
diff --git a/wiretap/blf.h b/wiretap/blf.h
new file mode 100644
index 00000000..d372f952
--- /dev/null
+++ b/wiretap/blf.h
@@ -0,0 +1,650 @@
+/** @file
+ *
+ * Binary Log File (BLF) file format from Vector Informatik decoder
+ * for the Wiretap library.
+ *
+ * Copyright (c) 2021-2022 by Dr. Lars Voelker <lars.voelker@technica-engineering.de>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+ /*
+ * The following was used as a reference for the file format:
+ * https://bitbucket.org/tobylorenz/vector_blf
+ * The repo above includes multiple examples files as well.
+ */
+
+#ifndef __W_BLF_H__
+#define __W_BLF_H__
+
+#include "wtap.h"
+#include <epan/value_string.h>
+
+wtap_open_return_val blf_open(wtap *wth, int *err, gchar **err_info);
+
+
+#define BLF_HEADER_TYPE_DEFAULT 1
+#define BLF_HEADER_TYPE_2 2
+#define BLF_HEADER_TYPE_3 3
+
+
+#define BLF_COMPRESSION_NONE 0
+#define BLF_COMPRESSION_ZLIB 2
+
+#define BLF_TIMESTAMP_RESOLUTION_10US 1
+#define BLF_TIMESTAMP_RESOLUTION_1NS 2
+
+typedef struct blf_date {
+ guint16 year;
+ guint16 month;
+ guint16 dayofweek;
+ guint16 day;
+ guint16 hour;
+ guint16 mins;
+ guint16 sec;
+ guint16 ms;
+} blf_date_t;
+
+/* BLF Header */
+typedef struct blf_fileheader {
+ guint8 magic[4];
+ guint32 header_length;
+
+ guint8 applications[4];
+ guint8 api[4];
+
+ guint64 len_compressed;
+ guint64 len_uncompressed;
+
+ guint32 obj_count;
+ guint32 obj_read;
+
+ blf_date_t start_date;
+ blf_date_t end_date;
+
+ guint32 length3;
+} blf_fileheader_t;
+
+typedef struct blf_blockheader {
+ guint8 magic[4];
+ guint16 header_length; /* length of header starting with magic */
+ guint16 header_type; /* header format ? */
+ guint32 object_length; /* complete length including header */
+ guint32 object_type;
+} blf_blockheader_t;
+
+typedef struct blf_logcontainerheader {
+ guint16 compression_method; /* 0 uncompressed, 2 zlib */
+ guint16 res1;
+ guint32 res2;
+ guint32 uncompressed_size;
+ guint32 res4;
+} blf_logcontainerheader_t;
+
+typedef struct blf_logobjectheader {
+ guint32 flags;
+ guint16 client_index;
+ guint16 object_version;
+ guint64 object_timestamp;
+} blf_logobjectheader_t;
+
+#define BLF_TS_STATUS_ORIG_TS_VALID 0x01
+#define BLF_TS_STATUS_SW_TS 0x02
+#define BLF_TS_STATUS_PROTO_SPECIFIC 0x10
+
+typedef struct blf_logobjectheader2 {
+ guint32 flags;
+ guint8 timestamp_status;
+ guint8 res1;
+ guint16 object_version;
+ guint64 object_timestamp;
+ guint64 original_timestamp;
+} blf_logobjectheader2_t;
+
+typedef struct blf_logobjectheader3 {
+ guint32 flags;
+ guint16 static_size;
+ guint16 object_version;
+ guint64 object_timestamp;
+} blf_logobjectheader3_t;
+
+
+#define BLF_DIR_RX 0
+#define BLF_DIR_TX 1
+#define BLF_DIR_TX_RQ 2
+
+typedef struct blf_ethernetframeheader {
+ guint8 src_addr[6];
+ guint16 channel;
+ guint8 dst_addr[6];
+ guint16 direction;
+ guint16 ethtype;
+ guint16 tpid;
+ guint16 tci;
+ guint16 payloadlength;
+ guint64 res;
+} blf_ethernetframeheader_t;
+
+typedef struct blf_ethernetframeheader_ex {
+ guint16 struct_length;
+ guint16 flags;
+ guint16 channel;
+ guint16 hw_channel;
+ guint64 frame_duration;
+ guint32 frame_checksum;
+ guint16 direction;
+ guint16 frame_length;
+ guint32 frame_handle;
+ guint32 error;
+} blf_ethernetframeheader_ex_t;
+
+typedef struct blf_wlanframeheader {
+ guint16 channel;
+ guint16 flags;
+ guint8 direction;
+ guint8 radio_channel;
+ guint16 signal_strength;
+ guint16 signal_quality;
+ guint16 frame_length;
+ guint32 res;
+} blf_wlanframeheader_t;
+
+/* see https://bitbucket.org/tobylorenz/vector_blf/src/master/src/Vector/BLF/CanMessage.h */
+
+/* shared for CAN message and CAN message2 and CANFD message */
+#define BLF_CANMESSAGE_FLAG_TX 0x01
+#define BLF_CANMESSAGE_FLAG_NERR 0x20
+#define BLF_CANMESSAGE_FLAG_WU 0x40
+#define BLF_CANMESSAGE_FLAG_RTR 0x80
+
+/* shared for CAN message and CAN message2*/
+typedef struct blf_canmessage {
+ guint16 channel;
+ guint8 flags;
+ guint8 dlc;
+ guint32 id;
+} blf_canmessage_t;
+
+
+/* see https://bitbucket.org/tobylorenz/vector_blf/src/master/src/Vector/BLF/CanMessage2.h */
+
+typedef struct blf_canmessage2_trailer {
+ guint32 frameLength_in_ns;
+ guint8 bitCount;
+ guint8 reserved1;
+ guint16 reserved2;
+} blf_canmessage2_trailer_t;
+
+
+/* see https://bitbucket.org/tobylorenz/vector_blf/src/master/src/Vector/BLF/CanFdMessage.h */
+
+/* EDL 0: CAN, 1: CAN-FD*/
+#define BLF_CANFDMESSAGE_CANFDFLAG_EDL 0x01
+#define BLF_CANFDMESSAGE_CANFDFLAG_BRS 0x02
+#define BLF_CANFDMESSAGE_CANFDFLAG_ESI 0x04
+
+typedef struct blf_canfdmessage {
+ guint16 channel;
+ guint8 flags;
+ guint8 dlc;
+ guint32 id;
+ guint32 frameLength_in_ns;
+ guint8 arbitration_bit_count;
+ guint8 canfdflags;
+ guint8 validDataBytes;
+ guint8 reservedCanFdMessage1;
+ guint32 reservedCanFdMessage2;
+ /* DATA */
+ /* guint32 reservedCanFdMessage3 */
+} blf_canfdmessage_t;
+
+
+/* see https://bitbucket.org/tobylorenz/vector_blf/src/master/src/Vector/BLF/CanFdMessage64.h */
+
+#define BLF_CANFDMESSAGE64_FLAG_NERR 0x000004
+#define BLF_CANFDMESSAGE64_FLAG_HIGH_VOLT_WAKE_UP 0x000008
+#define BLF_CANFDMESSAGE64_FLAG_REMOTE_FRAME 0x000010
+#define BLF_CANFDMESSAGE64_FLAG_TX_ACK 0x000040
+#define BLF_CANFDMESSAGE64_FLAG_TX_REQ 0x000080
+#define BLF_CANFDMESSAGE64_FLAG_SRR 0x000200
+#define BLF_CANFDMESSAGE64_FLAG_R0 0x000400
+#define BLF_CANFDMESSAGE64_FLAG_R1 0x000800
+/* EDL 0: CAN, 1: CAN-FD*/
+#define BLF_CANFDMESSAGE64_FLAG_EDL 0x001000
+#define BLF_CANFDMESSAGE64_FLAG_BRS 0x002000
+#define BLF_CANFDMESSAGE64_FLAG_ESI 0x004000
+#define BLF_CANFDMESSAGE64_FLAG_BURST 0x200000
+
+typedef struct blf_canfdmessage64 {
+ guint8 channel;
+ guint8 dlc;
+ guint8 validDataBytes;
+ guint8 txCount;
+ guint32 id;
+ guint32 frameLength_in_ns;
+ guint32 flags;
+ guint32 btrCfgArb;
+ guint32 btrCfgData;
+ guint32 timeOffsetBrsNs;
+ guint32 timeOffsetCrcDelNs;
+ guint16 bitCount;
+ guint8 dir;
+ guint8 extDataOffset;
+ guint32 crc;
+} blf_canfdmessage64_t;
+
+
+/* see https://bitbucket.org/tobylorenz/vector_blf/src/master/src/Vector/BLF/CanErrorFrame.h */
+
+typedef struct blf_canerror {
+ guint16 channel;
+ guint16 length;
+} blf_canerror_t;
+
+
+/* see https://bitbucket.org/tobylorenz/vector_blf/src/master/src/Vector/BLF/CanErrorFrameExt.h */
+
+#define BLF_CANERROREXT_FLAG_SJA 0x01
+#define BLF_CANERROREXT_FLAG_CANCORE 0x02
+#define BLF_CANERROREXT_EXTECC_TX 0x1000
+#define BLF_CANERROREXT_EXTECC_NOT_ACK 0x2000
+#define BLF_CANERROREXT_ECC_MEANING_BIT_ERROR 0x0
+#define BLF_CANERROREXT_ECC_MEANING_FORM_ERROR 0x1
+#define BLF_CANERROREXT_ECC_MEANING_STUFF_ERROR 0x2
+#define BLF_CANERROREXT_ECC_MEANING_OTHER_ERROR 0x3
+#define BLF_CANERROREXT_ECC_MEANING_CRC_ERROR 0x4
+#define BLF_CANERROREXT_ECC_MEANING_ACKDEL_ERROR 0x5
+#define BLF_CANERROREXT_ECC_MEANING_OTHER_ERROR2 0x6
+#define BLF_CANERROREXT_ECC_MEANING_NACK_ERROR 0x7
+#define BLF_CANERROREXT_ECC_MEANING_OVERLOAD 0x8
+#define BLF_CANERROREXT_ECC_FDF_BIT_ERROR 0x9
+
+typedef struct blf_canerrorext {
+ guint16 channel;
+ guint16 length;
+ guint32 flags;
+ guint8 ecc;
+ guint8 position;
+ guint8 dlc;
+ guint8 reserved1;
+ guint32 frameLength_in_ns;
+ guint32 id;
+ guint16 errorCodeExt;
+ guint16 reserved2;
+} blf_canerrorext_t;
+
+
+/* see https://bitbucket.org/tobylorenz/vector_blf/src/master/src/Vector/BLF/CanFdErrorFrame64.h */
+
+typedef struct blf_canfderror64 {
+ guint8 channel;
+ guint8 dlc;
+ guint8 validDataBytes;
+ guint8 ecc;
+ guint16 flags;
+ guint16 errorCodeExt;
+ guint16 extFlags;
+ guint8 extDataOffset;
+ guint8 reserved1;
+ guint32 id;
+ guint32 frameLength_in_ns;
+ guint32 btrCfgArb;
+ guint32 btrCfgData;
+ guint32 timeOffsetBrsNs;
+ guint32 timeOffsetCrcDelNs;
+ guint32 crc;
+ guint16 errorPosition;
+ guint16 reserved2;
+} blf_canfderror64_t;
+
+
+/* see https://bitbucket.org/tobylorenz/vector_blf/src/master/src/Vector/BLF/FlexRayData.h */
+
+#define BLF_FLEXRAYDATA_FRAME 0x01
+#define BLF_FLEXRAYDATA_CHANNEL_B 0x80
+
+typedef struct blf_flexraydata {
+ guint16 channel;
+ guint8 mux;
+ guint8 len;
+ guint16 messageId;
+ guint16 crc;
+ guint8 dir;
+ guint8 reservedFlexRayData1;
+ guint16 reservedFlexRayData2;
+} blf_flexraydata_t;
+
+
+/* see https://bitbucket.org/tobylorenz/vector_blf/src/master/src/Vector/BLF/FlexRayV6Message.h */
+
+#define BLF_FLEXRAYMESSAGE_DIR_RX 0x01
+#define BLF_FLEXRAYMESSAGE_DIR_TX 0x02
+#define BLF_FLEXRAYMESSAGE_DIR_TX_REQ 0x04
+
+#define BLF_FLEXRAYMESSAGE_STATE_PPI 0x01
+#define BLF_FLEXRAYMESSAGE_STATE_SFI 0x02
+#define BLF_FLEXRAYMESSAGE_STATE_RES_BIT2 0x04
+#define BLF_FLEXRAYMESSAGE_STATE_NFI 0x08
+#define BLF_FLEXRAYMESSAGE_STATE_STFI 0x10
+#define BLF_FLEXRAYMESSAGE_STATE_FORMAT 0xe0
+
+#define BLF_FLEXRAYMESSAGE_HEADER_BIT_NM 0x01
+#define BLF_FLEXRAYMESSAGE_HEADER_BIT_SYNC 0x02
+#define BLF_FLEXRAYMESSAGE_HEADER_BIT_RES 0x04
+
+#define BLF_DLT_FLEXRAY_STFI 0x08
+#define BLF_DLT_FLEXRAY_SFI 0x10
+#define BLF_DLT_FLEXRAY_NFI 0x20
+#define BLF_DLT_FLEXRAY_PPI 0x40
+
+typedef struct blf_flexraymessage {
+ guint16 channel;
+ guint8 dir; /* Flags: 0 RX, 1 TX, 2 TX Req, 3 internal, 4 internal*/
+ guint8 lowTime;
+ guint32 fpgaTick;
+ guint32 fpgaTickOverflow;
+ guint32 clientIndexFlexRayV6Message;
+ guint32 clusterTime;
+ guint16 frameId;
+ guint16 headerCrc;
+ guint16 frameState;
+ guint8 length;
+ guint8 cycle;
+ guint8 headerBitMask;
+ guint8 reservedFlexRayV6Message1;
+ guint16 reservedFlexRayV6Message2;
+} blf_flexraymessage_t;
+
+
+/* see https://bitbucket.org/tobylorenz/vector_blf/src/master/src/Vector/BLF/FlexRayVFrReceiveMsg.h */
+
+#define BLF_FLEXRAYRCVMSG_DIR_RX 0x01
+#define BLF_FLEXRAYRCVMSG_DIR_TX 0x02
+#define BLF_FLEXRAYRCVMSG_DIR_TX_REQ 0x04
+
+#define BLF_FLEXRAYRCVMSG_CHANNELMASK_RES 0x00
+#define BLF_FLEXRAYRCVMSG_CHANNELMASK_A 0x01
+#define BLF_FLEXRAYRCVMSG_CHANNELMASK_B 0x02
+#define BLF_FLEXRAYRCVMSG_CHANNELMASK_AB 0x03
+
+#define BLF_FLEXRAYRCVMSG_DATA_FLAG_NULL_FRAME 0x00000001
+#define BLF_FLEXRAYRCVMSG_DATA_FLAG_VALID_DATA 0x00000002
+#define BLF_FLEXRAYRCVMSG_DATA_FLAG_SYNC 0x00000004
+#define BLF_FLEXRAYRCVMSG_DATA_FLAG_STARTUP 0x00000008
+#define BLF_FLEXRAYRCVMSG_DATA_FLAG_PAYLOAD_PREAM 0x00000010
+#define BLF_FLEXRAYRCVMSG_DATA_FLAG_RES_20 0x00000020
+#define BLF_FLEXRAYRCVMSG_DATA_FLAG_ERROR 0x00000040
+#define BLF_FLEXRAYRCVMSG_DATA_FLAG_RES_80 0x00000080
+
+typedef struct blf_flexrayrcvmessage {
+ guint16 channel;
+ guint16 version;
+ guint16 channelMask; /* 0 res, 1 A, 2 B, 3 A+B */
+ guint16 dir; /* 0 RX, 1 TX, 2 TX Req, 3 internal, 4 internal*/ /* high byte reserved! */
+ guint32 clientIndex;
+ guint32 clusterNo;
+ guint16 frameId;
+ guint16 headerCrc1;
+ guint16 headerCrc2;
+ guint16 payloadLength;
+ guint16 payloadLengthValid;
+ guint16 cycle; /* high byte reserved! */
+ guint32 tag;
+ guint32 data;
+ guint32 frameFlags;
+ guint32 appParameter;
+ /* if ext, skip 40 bytes */
+ /* payload bytes */
+ /* guint16 res3 */
+ /* guint32 res4 */
+} blf_flexrayrcvmessage_t;
+
+
+/* see https://bitbucket.org/tobylorenz/vector_blf/src/master/src/Vector/BLF/FlexRayVFrReceiveMsgEx.h */
+
+/* defines see above BLF_FLEXRAYRCVMSG_* */
+
+typedef struct blf_flexrayrcvmessageex {
+ guint16 channel;
+ guint16 version;
+ guint16 channelMask; /* 0 res, 1 A, 2 B, 3 A+B */
+ guint16 dir; /* 0 RX, 1 TX, 2 TX Req, 3 internal, 4 internal*/
+ guint32 clientIndex;
+ guint32 clusterNo;
+ guint16 frameId;
+ guint16 headerCrc1;
+ guint16 headerCrc2;
+ guint16 payloadLength;
+ guint16 payloadLengthValid;
+ guint16 cycle;
+ guint32 tag;
+ guint32 data;
+ guint32 frameFlags;
+ guint32 appParameter;
+ guint32 frameCRC;
+ guint32 frameLengthInNs;
+ guint16 frameId1;
+ guint16 pduOffset;
+ guint16 blfLogMask;
+ guint16 res1;
+ guint32 res2;
+ guint32 res3;
+ guint32 res4;
+ guint32 res5;
+ guint32 res6;
+ guint32 res7;
+ /* payload bytes */
+} blf_flexrayrcvmessageex_t;
+
+
+/* see https://bitbucket.org/tobylorenz/vector_blf/src/master/src/Vector/BLF/LinMessage.h */
+
+typedef struct blf_linmessage {
+ guint16 channel;
+ guint8 id;
+ guint8 dlc;
+} blf_linmessage_t;
+
+typedef struct blf_linmessage_trailer {
+ guint8 fsmId;
+ guint8 fsmState;
+ guint8 headerTime;
+ guint8 fullTime;
+ guint16 crc;
+ guint8 dir; /* 0 RX, 1 TX Receipt, 2 TX Req */
+ guint8 res1;
+/* This field is optional and skipping does not hurt us.
+ guint32 res2;
+*/
+} blf_linmessage_trailer_t;
+
+
+/* see https://bitbucket.org/tobylorenz/vector_blf/src/master/src/Vector/BLF/AppText.h */
+
+typedef struct blf_apptext {
+ guint32 source;
+ guint32 reservedAppText1;
+ guint32 textLength;
+ guint32 reservedAppText2;
+} blf_apptext_t;
+
+#define BLF_APPTEXT_COMMENT 0x00000000
+#define BLF_APPTEXT_CHANNEL 0x00000001
+#define BLF_APPTEXT_METADATA 0x00000002
+#define BLF_APPTEXT_FAILED 0x000000FF
+
+
+#define BLF_BUSTYPE_CAN 1
+#define BLF_BUSTYPE_LIN 5
+#define BLF_BUSTYPE_MOST 6
+#define BLF_BUSTYPE_FLEXRAY 7
+#define BLF_BUSTYPE_J1708 9
+#define BLF_BUSTYPE_ETHERNET 11
+#define BLF_BUSTYPE_WLAN 13
+#define BLF_BUSTYPE_AFDX 14
+
+/* see https://bitbucket.org/tobylorenz/vector_blf/src/master/src/Vector/BLF/EthernetStatus.h */
+typedef struct blf_ethernet_status {
+ uint16_t channel;
+ uint16_t flags;
+ uint8_t linkStatus;
+ uint8_t ethernetPhy;
+ uint8_t duplex;
+ uint8_t mdi;
+ uint8_t connector;
+ uint8_t clockMode;
+ uint8_t pairs;
+ uint8_t hardwareChannel;
+ uint32_t bitrate;
+} blf_ethernet_status_t;
+
+
+/* see https://bitbucket.org/tobylorenz/vector_blf/src/master/src/Vector/BLF/ObjectHeaderBase.h */
+
+#define BLF_OBJTYPE_UNKNOWN 0
+#define BLF_OBJTYPE_CAN_MESSAGE 1
+#define BLF_OBJTYPE_CAN_ERROR 2
+#define BLF_OBJTYPE_CAN_OVERLOAD 3
+#define BLF_OBJTYPE_CAN_STATISTIC 4
+#define BLF_OBJTYPE_APP_TRIGGER 5
+#define BLF_OBJTYPE_ENV_INTEGER 6
+#define BLF_OBJTYPE_ENV_DOUBLE 7
+#define BLF_OBJTYPE_ENV_STRING 8
+#define BLF_OBJTYPE_ENV_DATA 9
+#define BLF_OBJTYPE_LOG_CONTAINER 10
+#define BLF_OBJTYPE_LIN_MESSAGE 11
+#define BLF_OBJTYPE_LIN_CRC_ERROR 12
+#define BLF_OBJTYPE_LIN_DLC_INFO 13
+#define BLF_OBJTYPE_LIN_RCV_ERROR 14
+#define BLF_OBJTYPE_LIN_SND_ERROR 15
+#define BLF_OBJTYPE_LIN_SLV_TIMEOUT 16
+#define BLF_OBJTYPE_LIN_SCHED_MODCH 17
+#define BLF_OBJTYPE_LIN_SYN_ERROR 18
+#define BLF_OBJTYPE_LIN_BAUDRATE 19
+#define BLF_OBJTYPE_LIN_SLEEP 20
+#define BLF_OBJTYPE_LIN_WAKEUP 21
+#define BLF_OBJTYPE_MOST_SPY 22
+#define BLF_OBJTYPE_MOST_CTRL 23
+#define BLF_OBJTYPE_MOST_LIGHTLOCK 24
+#define BLF_OBJTYPE_MOST_STATISTIC 25
+#define BLF_OBJTYPE_FLEXRAY_DATA 29
+#define BLF_OBJTYPE_FLEXRAY_SYNC 30
+#define BLF_OBJTYPE_CAN_DRIVER_ERROR 31
+#define BLF_OBJTYPE_MOST_PKT 32
+#define BLF_OBJTYPE_MOST_PKT2 33
+#define BLF_OBJTYPE_MOST_HWMODE 34
+#define BLF_OBJTYPE_MOST_REG 35
+#define BLF_OBJTYPE_MOST_GENREG 36
+#define BLF_OBJTYPE_MOST_NETSTATE 37
+#define BLF_OBJTYPE_MOST_DATALOST 38
+#define BLF_OBJTYPE_MOST_TRIGGER 39
+#define BLF_OBJTYPE_FLEXRAY_CYCLE 40
+#define BLF_OBJTYPE_FLEXRAY_MESSAGE 41
+#define BLF_OBJTYPE_LIN_CHECKSUM_INFO 42
+#define BLF_OBJTYPE_LIN_SPIKE_EVENT 43
+#define BLF_OBJTYPE_CAN_DRIVER_SYNC 44
+#define BLF_OBJTYPE_FLEXRAY_STATUS 45
+#define BLF_OBJTYPE_GPS_EVENT 46
+#define BLF_OBJTYPE_FLEXRAY_ERROR 47
+#define BLF_OBJTYPE_FLEXRAY_STATUS2 48
+#define BLF_OBJTYPE_FLEXRAY_STARTCYCLE 49
+#define BLF_OBJTYPE_FLEXRAY_RCVMESSAGE 50
+#define BLF_OBJTYPE_REALTIMECLOCK 51
+#define BLF_OBJTYPE_LIN_STATISTIC 54
+#define BLF_OBJTYPE_J1708_MESSAGE 55
+#define BLF_OBJTYPE_J1708_VIRTUAL_MSG 56
+#define BLF_OBJTYPE_LIN_MESSAGE2 57
+#define BLF_OBJTYPE_LIN_SND_ERROR2 58
+#define BLF_OBJTYPE_LIN_SYN_ERROR2 59
+#define BLF_OBJTYPE_LIN_CRC_ERROR2 60
+#define BLF_OBJTYPE_LIN_RCV_ERROR2 61
+#define BLF_OBJTYPE_LIN_WAKEUP2 62
+#define BLF_OBJTYPE_LIN_SPIKE_EVENT2 63
+#define BLF_OBJTYPE_LIN_LONG_DOM_SIG 64
+#define BLF_OBJTYPE_APP_TEXT 65
+#define BLF_OBJTYPE_FLEXRAY_RCVMESSAGE_EX 66
+#define BLF_OBJTYPE_MOST_STATISTICEX 67
+#define BLF_OBJTYPE_MOST_TXLIGHT 68
+#define BLF_OBJTYPE_MOST_ALLOCTAB 69
+#define BLF_OBJTYPE_MOST_STRESS 70
+#define BLF_OBJTYPE_ETHERNET_FRAME 71
+#define BLF_OBJTYPE_SYS_VARIABLE 72
+#define BLF_OBJTYPE_CAN_ERROR_EXT 73
+#define BLF_OBJTYPE_CAN_DRIVER_ERROR_EXT 74
+#define BLF_OBJTYPE_LIN_LONG_DOM_SIG2 75
+#define BLF_OBJTYPE_MOST_150_MESSAGE 76
+#define BLF_OBJTYPE_MOST_150_PKT 77
+#define BLF_OBJTYPE_MOST_ETHERNET_PKT 78
+#define BLF_OBJTYPE_MOST_150_MESSAGE_FRAGMENT 79
+#define BLF_OBJTYPE_MOST_150_PKT_FRAGMENT 80
+#define BLF_OBJTYPE_MOST_ETHERNET_PKT_FRAGMENT 81
+#define BLF_OBJTYPE_MOST_SYSTEM_EVENT 82
+#define BLF_OBJTYPE_MOST_150_ALLOCTAB 83
+#define BLF_OBJTYPE_MOST_50_MESSAGE 84
+#define BLF_OBJTYPE_MOST_50_PKT 85
+#define BLF_OBJTYPE_CAN_MESSAGE2 86
+#define BLF_OBJTYPE_LIN_UNEXPECTED_WAKEUP 87
+#define BLF_OBJTYPE_LIN_SHORT_OR_SLOW_RESPONSE 88
+#define BLF_OBJTYPE_LIN_DISTURBANCE_EVENT 89
+#define BLF_OBJTYPE_SERIAL_EVENT 90
+#define BLF_OBJTYPE_OVERRUN_ERROR 91
+#define BLF_OBJTYPE_EVENT_COMMENT 92
+#define BLF_OBJTYPE_WLAN_FRAME 93
+#define BLF_OBJTYPE_WLAN_STATISTIC 94
+#define BLF_OBJTYPE_MOST_ECL 95
+#define BLF_OBJTYPE_GLOBAL_MARKER 96
+#define BLF_OBJTYPE_AFDX_FRAME 97
+#define BLF_OBJTYPE_AFDX_STATISTIC 98
+#define BLF_OBJTYPE_KLINE_STATUSEVENT 99
+#define BLF_OBJTYPE_CAN_FD_MESSAGE 100
+#define BLF_OBJTYPE_CAN_FD_MESSAGE_64 101
+#define BLF_OBJTYPE_ETHERNET_RX_ERROR 102
+#define BLF_OBJTYPE_ETHERNET_STATUS 103
+#define BLF_OBJTYPE_CAN_FD_ERROR_64 104
+#define BLF_OBJTYPE_AFDX_STATUS 106
+#define BLF_OBJTYPE_AFDX_BUS_STATISTIC 107
+#define BLF_OBJTYPE_AFDX_ERROR_EVENT 109
+#define BLF_OBJTYPE_A429_ERROR 110
+#define BLF_OBJTYPE_A429_STATUS 111
+#define BLF_OBJTYPE_A429_BUS_STATISTIC 112
+#define BLF_OBJTYPE_A429_MESSAGE 113
+#define BLF_OBJTYPE_ETHERNET_STATISTIC 114
+#define BLF_OBJTYPE_TEST_STRUCTURE 118
+#define BLF_OBJTYPE_DIAG_REQUEST_INTERPRETATION 119
+#define BLF_OBJTYPE_ETHERNET_FRAME_EX 120
+#define BLF_OBJTYPE_ETHERNET_FRAME_FORWARDED 121
+#define BLF_OBJTYPE_ETHERNET_ERROR_EX 122
+#define BLF_OBJTYPE_ETHERNET_ERROR_FORWARDED 123
+#define BLF_OBJTYPE_FUNCTION_BUS 124
+#define BLF_OBJTYPE_DATA_LOST_BEGIN 125
+#define BLF_OBJTYPE_DATA_LOST_END 126
+#define BLF_OBJTYPE_WATER_MARK_EVENT 127
+#define BLF_OBJTYPE_TRIGGER_CONDITION 128
+#define BLF_OBJTYPE_CAN_SETTING_CHANGED 129
+#define BLF_OBJTYPE_DISTRIBUTED_OBJECT_MEMBER 130
+#define BLF_OBJTYPE_ATTRIBUTE_EVENT 131
+
+#define BLF_ETH_STATUS_LINKSTATUS 0x0001
+#define BLF_ETH_STATUS_BITRATE 0x0002
+#define BLF_ETH_STATUS_ETHERNETPHY 0x0004
+#define BLF_ETH_STATUS_DUPLEX 0x0008
+#define BLF_ETH_STATUS_MDITYPE 0x0010
+#define BLF_ETH_STATUS_CONNECTOR 0x0020
+#define BLF_ETH_STATUS_CLOCKMODE 0x0040
+#define BLF_ETH_STATUS_BRPAIR 0x0080
+#define BLF_ETH_STATUS_HARDWARECHANNEL 0x0100
+
+#endif
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * vi: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */
diff --git a/wiretap/btsnoop.c b/wiretap/btsnoop.c
new file mode 100644
index 00000000..3ef14656
--- /dev/null
+++ b/wiretap/btsnoop.c
@@ -0,0 +1,455 @@
+/* btsnoop.c
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "config.h"
+#include <string.h>
+#include "wtap-int.h"
+#include "file_wrappers.h"
+#include "btsnoop.h"
+
+/*
+ * Symbian's btsnoop format is derived from Sun's snoop format.
+ * See RFC 1761 for a description of the "snoop" file format.
+ * See
+ *
+ * https://gitlab.com/wireshark/wireshark/uploads/6d44fa94c164b58516e8577f44a6ccdc/btmodified_rfc1761.txt
+ *
+ * for a description of the btsnoop format.
+ */
+
+/* Magic number in "btsnoop" files. */
+static const char btsnoop_magic[] = {
+ 'b', 't', 's', 'n', 'o', 'o', 'p', '\0'
+};
+
+/* "btsnoop" file header (minus magic number). */
+struct btsnoop_hdr {
+ guint32 version; /* version number (should be 1) */
+ guint32 datalink; /* datalink type */
+};
+
+/* "btsnoop" record header. */
+struct btsnooprec_hdr {
+ guint32 orig_len; /* actual length of packet */
+ guint32 incl_len; /* number of octets captured in file */
+ guint32 flags; /* packet flags */
+ guint32 cum_drops; /* cumulative number of dropped packets */
+ gint64 ts_usec; /* timestamp microseconds */
+};
+
+/* H1 is unframed data with the packet type encoded in the flags field of capture header */
+/* It can be used for any datalink by placing logging above the datalink layer of HCI */
+#define KHciLoggerDatalinkTypeH1 1001
+/* H4 is the serial HCI with packet type encoded in the first byte of each packet */
+#define KHciLoggerDatalinkTypeH4 1002
+/* CSR's PPP derived bluecore serial protocol - in practice we log in H1 format after deframing */
+#define KHciLoggerDatalinkTypeBCSP 1003
+/* H5 is the official three wire serial protocol derived from BCSP*/
+#define KHciLoggerDatalinkTypeH5 1004
+/* Linux Monitor */
+#define KHciLoggerDatalinkLinuxMonitor 2001
+/* BlueZ 5 Simulator */
+#define KHciLoggerDatalinkBlueZ5Simulator 2002
+
+#define KHciLoggerHostToController 0
+#define KHciLoggerControllerToHost 0x00000001
+#define KHciLoggerACLDataFrame 0
+#define KHciLoggerCommandOrEvent 0x00000002
+
+static const gint64 KUnixTimeBase = G_GINT64_CONSTANT(0x00dcddb30f2f8000); /* offset from symbian - unix time */
+
+static gboolean btsnoop_read(wtap *wth, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info, gint64 *offset);
+static gboolean btsnoop_seek_read(wtap *wth, gint64 seek_off,
+ wtap_rec *rec, Buffer *buf, int *err, gchar **err_info);
+static gboolean btsnoop_read_record(wtap *wth, FILE_T fh,
+ wtap_rec *rec, Buffer *buf, int *err, gchar **err_info);
+
+static int btsnoop_file_type_subtype = -1;
+
+void register_btsnoop(void);
+
+wtap_open_return_val btsnoop_open(wtap *wth, int *err, gchar **err_info)
+{
+ char magic[sizeof btsnoop_magic];
+ struct btsnoop_hdr hdr;
+
+ int file_encap=WTAP_ENCAP_UNKNOWN;
+
+ /* Read in the string that should be at the start of a "btsnoop" file */
+ if (!wtap_read_bytes(wth->fh, magic, sizeof magic, err, err_info)) {
+ if (*err != WTAP_ERR_SHORT_READ)
+ return WTAP_OPEN_ERROR;
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ if (memcmp(magic, btsnoop_magic, sizeof btsnoop_magic) != 0) {
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ /* Read the rest of the header. */
+ if (!wtap_read_bytes(wth->fh, &hdr, sizeof hdr, err, err_info))
+ return WTAP_OPEN_ERROR;
+
+ /*
+ * Make sure it's a version we support.
+ */
+ hdr.version = g_ntohl(hdr.version);
+ if (hdr.version != 1) {
+ *err = WTAP_ERR_UNSUPPORTED;
+ *err_info = ws_strdup_printf("btsnoop: version %u unsupported", hdr.version);
+ return WTAP_OPEN_ERROR;
+ }
+
+ hdr.datalink = g_ntohl(hdr.datalink);
+ switch (hdr.datalink) {
+ case KHciLoggerDatalinkTypeH1:
+ file_encap=WTAP_ENCAP_BLUETOOTH_HCI;
+ break;
+ case KHciLoggerDatalinkTypeH4:
+ file_encap=WTAP_ENCAP_BLUETOOTH_H4_WITH_PHDR;
+ break;
+ case KHciLoggerDatalinkTypeBCSP:
+ *err = WTAP_ERR_UNSUPPORTED;
+ *err_info = g_strdup("btsnoop: BCSP capture logs unsupported");
+ return WTAP_OPEN_ERROR;
+ case KHciLoggerDatalinkTypeH5:
+ *err = WTAP_ERR_UNSUPPORTED;
+ *err_info = g_strdup("btsnoop: H5 capture logs unsupported");
+ return WTAP_OPEN_ERROR;
+ case KHciLoggerDatalinkLinuxMonitor:
+ file_encap=WTAP_ENCAP_BLUETOOTH_LINUX_MONITOR;
+ break;
+ case KHciLoggerDatalinkBlueZ5Simulator:
+ *err = WTAP_ERR_UNSUPPORTED;
+ *err_info = g_strdup("btsnoop: BlueZ 5 Simulator capture logs unsupported");
+ return WTAP_OPEN_ERROR;
+ default:
+ *err = WTAP_ERR_UNSUPPORTED;
+ *err_info = ws_strdup_printf("btsnoop: datalink type %u unknown or unsupported", hdr.datalink);
+ return WTAP_OPEN_ERROR;
+ }
+
+ wth->subtype_read = btsnoop_read;
+ wth->subtype_seek_read = btsnoop_seek_read;
+ wth->file_encap = file_encap;
+ wth->snapshot_length = 0; /* not available in header */
+ wth->file_tsprec = WTAP_TSPREC_USEC;
+ wth->file_type_subtype = btsnoop_file_type_subtype;
+
+ /*
+ * Add an IDB; we don't know how many interfaces were
+ * involved, so we just say one interface, about which
+ * we only know the link-layer type, snapshot length,
+ * and time stamp resolution.
+ */
+ wtap_add_generated_idb(wth);
+
+ return WTAP_OPEN_MINE;
+}
+
+static gboolean btsnoop_read(wtap *wth, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info, gint64 *offset)
+{
+ *offset = file_tell(wth->fh);
+
+ return btsnoop_read_record(wth, wth->fh, rec, buf, err, err_info);
+}
+
+static gboolean btsnoop_seek_read(wtap *wth, gint64 seek_off,
+ wtap_rec *rec, Buffer *buf, int *err, gchar **err_info)
+{
+ if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
+ return FALSE;
+
+ return btsnoop_read_record(wth, wth->random_fh, rec, buf, err, err_info);
+}
+
+static gboolean btsnoop_read_record(wtap *wth, FILE_T fh,
+ wtap_rec *rec, Buffer *buf, int *err, gchar **err_info)
+{
+ struct btsnooprec_hdr hdr;
+ guint32 packet_size;
+ guint32 flags;
+ guint32 orig_size;
+ gint64 ts;
+
+ /* Read record header. */
+
+ if (!wtap_read_bytes_or_eof(fh, &hdr, sizeof hdr, err, err_info))
+ return FALSE;
+
+ packet_size = g_ntohl(hdr.incl_len);
+ orig_size = g_ntohl(hdr.orig_len);
+ flags = g_ntohl(hdr.flags);
+ if (packet_size > WTAP_MAX_PACKET_SIZE_STANDARD) {
+ /*
+ * Probably a corrupt capture file; don't blow up trying
+ * to allocate space for an immensely-large packet.
+ */
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("btsnoop: File has %u-byte packet, bigger than maximum of %u",
+ packet_size, WTAP_MAX_PACKET_SIZE_STANDARD);
+ return FALSE;
+ }
+
+ ts = GINT64_FROM_BE(hdr.ts_usec);
+ ts -= KUnixTimeBase;
+
+ rec->rec_type = REC_TYPE_PACKET;
+ rec->block = wtap_block_create(WTAP_BLOCK_PACKET);
+ rec->presence_flags = WTAP_HAS_TS|WTAP_HAS_CAP_LEN;
+ rec->ts.secs = (guint)(ts / 1000000);
+ rec->ts.nsecs = (guint)((ts % 1000000) * 1000);
+ rec->rec_header.packet_header.caplen = packet_size;
+ rec->rec_header.packet_header.len = orig_size;
+ if(wth->file_encap == WTAP_ENCAP_BLUETOOTH_H4_WITH_PHDR)
+ {
+ rec->rec_header.packet_header.pseudo_header.p2p.sent = (flags & KHciLoggerControllerToHost) ? FALSE : TRUE;
+ } else if(wth->file_encap == WTAP_ENCAP_BLUETOOTH_HCI) {
+ rec->rec_header.packet_header.pseudo_header.bthci.sent = (flags & KHciLoggerControllerToHost) ? FALSE : TRUE;
+ if(flags & KHciLoggerCommandOrEvent)
+ {
+ if(rec->rec_header.packet_header.pseudo_header.bthci.sent)
+ {
+ rec->rec_header.packet_header.pseudo_header.bthci.channel = BTHCI_CHANNEL_COMMAND;
+ }
+ else
+ {
+ rec->rec_header.packet_header.pseudo_header.bthci.channel = BTHCI_CHANNEL_EVENT;
+ }
+ }
+ else
+ {
+ rec->rec_header.packet_header.pseudo_header.bthci.channel = BTHCI_CHANNEL_ACL;
+ }
+ } else if (wth->file_encap == WTAP_ENCAP_BLUETOOTH_LINUX_MONITOR) {
+ rec->rec_header.packet_header.pseudo_header.btmon.opcode = flags & 0xFFFF;
+ rec->rec_header.packet_header.pseudo_header.btmon.adapter_id = flags >> 16;
+ }
+
+
+ /* Read packet data. */
+ return wtap_read_packet_bytes(fh, buf, rec->rec_header.packet_header.caplen, err, err_info);
+}
+
+/* Returns 0 if we could write the specified encapsulation type,
+ an error indication otherwise. */
+static int btsnoop_dump_can_write_encap(int encap)
+{
+ /* Per-packet encapsulations aren't supported. */
+ if (encap == WTAP_ENCAP_PER_PACKET)
+ return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
+
+ /*
+ * XXX - for now we only support WTAP_ENCAP_BLUETOOTH_HCI,
+ * WTAP_ENCAP_BLUETOOTH_H4_WITH_PHDR, and
+ * WTAP_ENCAP_BLUETOOTH_LINUX_MONITOR.
+ */
+ if (encap != WTAP_ENCAP_BLUETOOTH_HCI &&
+ encap != WTAP_ENCAP_BLUETOOTH_H4_WITH_PHDR &&
+ encap != WTAP_ENCAP_BLUETOOTH_LINUX_MONITOR)
+ return WTAP_ERR_UNWRITABLE_ENCAP;
+
+ return 0;
+}
+
+static gboolean btsnoop_dump(wtap_dumper *wdh,
+ const wtap_rec *rec,
+ const guint8 *pd, int *err, gchar **err_info)
+{
+ const union wtap_pseudo_header *pseudo_header = &rec->rec_header.packet_header.pseudo_header;
+ struct btsnooprec_hdr rec_hdr;
+ guint32 flags;
+ gint64 nsecs;
+ gint64 ts_usec;
+
+ /* We can only write packet records. */
+ if (rec->rec_type != REC_TYPE_PACKET) {
+ *err = WTAP_ERR_UNWRITABLE_REC_TYPE;
+ return FALSE;
+ }
+
+ /*
+ * Make sure this packet doesn't have a link-layer type that
+ * differs from the one for the file.
+ */
+ if (wdh->file_encap != rec->rec_header.packet_header.pkt_encap) {
+ *err = WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
+ return FALSE;
+ }
+
+ /* Don't write out anything bigger than we can read. */
+ if (rec->rec_header.packet_header.caplen > WTAP_MAX_PACKET_SIZE_STANDARD) {
+ *err = WTAP_ERR_PACKET_TOO_LARGE;
+ return FALSE;
+ }
+
+ rec_hdr.incl_len = GUINT32_TO_BE(rec->rec_header.packet_header.caplen);
+ rec_hdr.orig_len = GUINT32_TO_BE(rec->rec_header.packet_header.len);
+
+ switch (wdh->file_encap) {
+
+ case WTAP_ENCAP_BLUETOOTH_HCI:
+ switch (pseudo_header->bthci.channel) {
+
+ case BTHCI_CHANNEL_COMMAND:
+ if (!pseudo_header->bthci.sent) {
+ *err = WTAP_ERR_UNWRITABLE_REC_DATA;
+ *err_info = ws_strdup_printf("btsnoop: Command channel, sent FALSE");
+ return FALSE;
+ }
+ flags = KHciLoggerCommandOrEvent|KHciLoggerHostToController;
+ break;
+
+ case BTHCI_CHANNEL_EVENT:
+ if (pseudo_header->bthci.sent) {
+ *err = WTAP_ERR_UNWRITABLE_REC_DATA;
+ *err_info = ws_strdup_printf("btsnoop: Event channel, sent TRUE");
+ return FALSE;
+ }
+ flags = KHciLoggerCommandOrEvent|KHciLoggerControllerToHost;
+ break;
+
+ case BTHCI_CHANNEL_ACL:
+ if (pseudo_header->bthci.sent)
+ flags = KHciLoggerACLDataFrame|KHciLoggerHostToController;
+ else
+ flags = KHciLoggerACLDataFrame|KHciLoggerControllerToHost;
+ break;
+
+ default:
+ *err = WTAP_ERR_UNWRITABLE_REC_DATA;
+ *err_info = ws_strdup_printf("btsnoop: Unknown channel %u",
+ pseudo_header->bthci.channel);
+ return FALSE;
+ }
+ break;
+
+ case WTAP_ENCAP_BLUETOOTH_H4_WITH_PHDR:
+ if (pseudo_header->p2p.sent)
+ flags = KHciLoggerHostToController;
+ else
+ flags = KHciLoggerControllerToHost;
+ if (rec->rec_header.packet_header.caplen >= 1 &&
+ (pd[0] == 0x01 || pd[0] == 0x04))
+ flags |= KHciLoggerCommandOrEvent;
+ break;
+
+ case WTAP_ENCAP_BLUETOOTH_LINUX_MONITOR:
+ flags = (pseudo_header->btmon.adapter_id << 16) | pseudo_header->btmon.opcode;
+ break;
+
+ default:
+ /* We should never get here - our open routine should only get
+ called for the types above. */
+ *err = WTAP_ERR_INTERNAL;
+ *err_info = ws_strdup_printf("btsnoop: invalid encapsulation %u",
+ wdh->file_encap);
+ return FALSE;
+ }
+ rec_hdr.flags = GUINT32_TO_BE(flags);
+ rec_hdr.cum_drops = GUINT32_TO_BE(0);
+
+ nsecs = rec->ts.nsecs;
+ ts_usec = ((gint64) rec->ts.secs * 1000000) + (nsecs / 1000);
+ ts_usec += KUnixTimeBase;
+ rec_hdr.ts_usec = GINT64_TO_BE(ts_usec);
+
+ if (!wtap_dump_file_write(wdh, &rec_hdr, sizeof rec_hdr, err))
+ return FALSE;
+ if (!wtap_dump_file_write(wdh, pd, rec->rec_header.packet_header.caplen, err))
+ return FALSE;
+ return TRUE;
+}
+
+/* Returns TRUE on success, FALSE on failure; sets "*err" to an error code on
+ failure */
+static gboolean btsnoop_dump_open(wtap_dumper *wdh, int *err, gchar **err_info _U_)
+{
+ struct btsnoop_hdr file_hdr;
+ guint32 datalink;
+
+ /* This is a btsnoop file */
+ wdh->subtype_write = btsnoop_dump;
+
+ switch (wdh->file_encap) {
+
+ case WTAP_ENCAP_BLUETOOTH_HCI:
+ datalink = KHciLoggerDatalinkTypeH1;
+ break;
+
+ case WTAP_ENCAP_BLUETOOTH_H4_WITH_PHDR:
+ datalink = KHciLoggerDatalinkTypeH4;
+ break;
+
+ case WTAP_ENCAP_BLUETOOTH_LINUX_MONITOR:
+ datalink = KHciLoggerDatalinkLinuxMonitor;
+ break;
+
+ default:
+ /* We should never get here - our open routine should only get
+ called for the types above. */
+ *err = WTAP_ERR_INTERNAL;
+ *err_info = ws_strdup_printf("btsnoop: invalid encapsulation %u",
+ wdh->file_encap);
+ return FALSE;
+ }
+
+ /* Write the file header. */
+ if (!wtap_dump_file_write(wdh, btsnoop_magic, sizeof btsnoop_magic, err))
+ return FALSE;
+
+ /* current "btsnoop" format is 1 */
+ file_hdr.version = GUINT32_TO_BE(1);
+ /* HCI type encoded in first byte */
+ file_hdr.datalink = GUINT32_TO_BE(datalink);
+
+ if (!wtap_dump_file_write(wdh, &file_hdr, sizeof file_hdr, err))
+ return FALSE;
+
+ return TRUE;
+}
+
+static const struct supported_block_type btsnoop_blocks_supported[] = {
+ /*
+ * We support packet blocks, with no comments or other options.
+ */
+ { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
+};
+
+static const struct file_type_subtype_info btsnoop_info = {
+ "Symbian OS btsnoop", "btsnoop", "log", NULL,
+ FALSE, BLOCKS_SUPPORTED(btsnoop_blocks_supported),
+ btsnoop_dump_can_write_encap, btsnoop_dump_open, NULL
+};
+
+void register_btsnoop(void)
+{
+ btsnoop_file_type_subtype = wtap_register_file_type_subtype(&btsnoop_info);
+
+ /*
+ * Register name for backwards compatibility with the
+ * wtap_filetypes table in Lua.
+ */
+ wtap_register_backwards_compatibility_lua_name("BTSNOOP",
+ btsnoop_file_type_subtype);
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * vi: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */
diff --git a/wiretap/btsnoop.h b/wiretap/btsnoop.h
new file mode 100644
index 00000000..3f070212
--- /dev/null
+++ b/wiretap/btsnoop.h
@@ -0,0 +1,16 @@
+/** @file
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __W_BTSNOOP_H__
+#define __W_BTSNOOP_H__
+#include <glib.h>
+#include "ws_symbol_export.h"
+
+wtap_open_return_val btsnoop_open(wtap *wth, int *err, gchar **err_info);
+
+#endif
diff --git a/wiretap/busmaster.c b/wiretap/busmaster.c
new file mode 100644
index 00000000..ec2b3bc8
--- /dev/null
+++ b/wiretap/busmaster.c
@@ -0,0 +1,460 @@
+/* busmaster.c
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * Support for Busmaster log file format
+ * Copyright (c) 2019 by Maksim Salau <maksim.salau@gmail.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "config.h"
+#include <wtap-int.h>
+#include <file_wrappers.h>
+#include <epan/dissectors/packet-socketcan.h>
+#include <wsutil/exported_pdu_tlvs.h>
+#include "busmaster.h"
+#include "busmaster_priv.h"
+#include <inttypes.h>
+#include <string.h>
+#include <errno.h>
+
+static void
+busmaster_close(wtap *wth);
+
+static gboolean
+busmaster_read(wtap *wth, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info,
+ gint64 *data_offset);
+
+static gboolean
+busmaster_seek_read(wtap *wth, gint64 seek_off,
+ wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info);
+
+static int busmaster_file_type_subtype = -1;
+
+void register_busmaster(void);
+
+/*
+ * See
+ *
+ * http://rbei-etas.github.io/busmaster/
+ *
+ * for the BUSMASTER software.
+ */
+
+static gboolean
+busmaster_gen_packet(wtap_rec *rec, Buffer *buf,
+ const busmaster_priv_t *priv_entry, const msg_t *msg,
+ int *err, gchar **err_info)
+{
+ time_t secs = 0;
+ guint32 nsecs = 0;
+ gboolean has_ts = FALSE;
+ gboolean is_fd = (msg->type == MSG_TYPE_STD_FD)
+ || (msg->type == MSG_TYPE_EXT_FD);
+ gboolean is_eff = (msg->type == MSG_TYPE_EXT)
+ || (msg->type == MSG_TYPE_EXT_RTR)
+ || (msg->type == MSG_TYPE_EXT_FD);
+ gboolean is_rtr = (msg->type == MSG_TYPE_STD_RTR)
+ || (msg->type == MSG_TYPE_EXT_RTR);
+ gboolean is_err = (msg->type == MSG_TYPE_ERR);
+
+ if (!priv_entry)
+ {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("Header is missing");
+ return FALSE;
+ }
+
+ /* Generate Exported PDU tags for the packet info */
+ ws_buffer_clean(buf);
+ if (is_fd)
+ {
+ wtap_buffer_append_epdu_string(buf, EXP_PDU_TAG_DISSECTOR_NAME, "canfd");
+ }
+ else
+ {
+ wtap_buffer_append_epdu_string(buf, EXP_PDU_TAG_DISSECTOR_NAME, "can-hostendian");
+ }
+ wtap_buffer_append_epdu_end(buf);
+
+ if (is_fd)
+ {
+ canfd_frame_t canfd_frame = {0};
+
+ canfd_frame.can_id = (msg->id & (is_eff ? CAN_EFF_MASK : CAN_SFF_MASK)) |
+ (is_eff ? CAN_EFF_FLAG : 0) |
+ (is_err ? CAN_ERR_FLAG : 0);
+ canfd_frame.flags = 0;
+ canfd_frame.len = msg->data.length;
+
+ memcpy(canfd_frame.data,
+ msg->data.data,
+ MIN(msg->data.length, sizeof(canfd_frame.data)));
+
+ ws_buffer_append(buf,
+ (guint8 *)&canfd_frame,
+ sizeof(canfd_frame));
+ }
+ else
+ {
+ can_frame_t can_frame = {0};
+
+ can_frame.can_id = (msg->id & (is_eff ? CAN_EFF_MASK : CAN_SFF_MASK)) |
+ (is_rtr ? CAN_RTR_FLAG : 0) |
+ (is_eff ? CAN_EFF_FLAG : 0) |
+ (is_err ? CAN_ERR_FLAG : 0);
+ can_frame.can_dlc = msg->data.length;
+
+ memcpy(can_frame.data,
+ msg->data.data,
+ MIN(msg->data.length, sizeof(can_frame.data)));
+
+ ws_buffer_append(buf,
+ (guint8 *)&can_frame,
+ sizeof(can_frame));
+ }
+
+ if (priv_entry->time_mode == TIME_MODE_SYSTEM)
+ {
+ struct tm tm;
+
+ tm.tm_year = priv_entry->start_date.year - 1900;
+ tm.tm_mon = priv_entry->start_date.month - 1;
+ tm.tm_mday = priv_entry->start_date.day;
+ tm.tm_hour = msg->timestamp.hours;
+ tm.tm_min = msg->timestamp.minutes;
+ tm.tm_sec = msg->timestamp.seconds;
+ tm.tm_isdst = -1;
+
+ secs = mktime(&tm);
+ nsecs = msg->timestamp.micros * 1000u;
+ has_ts = TRUE;
+ }
+ else if (priv_entry->time_mode == TIME_MODE_ABSOLUTE)
+ {
+ struct tm tm;
+ guint32 micros;
+
+ tm.tm_year = priv_entry->start_date.year - 1900;
+ tm.tm_mon = priv_entry->start_date.month - 1;
+ tm.tm_mday = priv_entry->start_date.day;
+ tm.tm_hour = priv_entry->start_time.hours;
+ tm.tm_min = priv_entry->start_time.minutes;
+ tm.tm_sec = priv_entry->start_time.seconds;
+ tm.tm_isdst = -1;
+
+ secs = mktime(&tm);
+
+ secs += msg->timestamp.hours * 3600;
+ secs += msg->timestamp.minutes * 60;
+ secs += msg->timestamp.seconds;
+
+ micros = priv_entry->start_time.micros + msg->timestamp.micros;
+ if (micros >= 1000000u)
+ {
+ micros -= 1000000u;
+ secs += 1;
+ }
+
+ nsecs = micros * 1000u;
+ has_ts = TRUE;
+ }
+
+ rec->rec_type = REC_TYPE_PACKET;
+ rec->block = wtap_block_create(WTAP_BLOCK_PACKET);
+ rec->presence_flags = has_ts ? WTAP_HAS_TS : 0;
+ rec->ts.secs = secs;
+ rec->ts.nsecs = nsecs;
+
+ rec->rec_header.packet_header.caplen = (guint32)ws_buffer_length(buf);
+ rec->rec_header.packet_header.len = (guint32)ws_buffer_length(buf);
+
+ return TRUE;
+}
+
+static log_entry_type_t
+busmaster_parse(FILE_T fh, busmaster_state_t *state, int *err, char **err_info)
+{
+ gboolean ok;
+ gint64 seek_off;
+
+ busmaster_debug_printf("%s: Running busmaster file decoder\n", G_STRFUNC);
+
+ state->fh = fh;
+
+ do
+ {
+ if (file_eof(fh))
+ return LOG_ENTRY_EOF;
+
+ seek_off = file_tell(fh);
+ busmaster_debug_printf("%s: Starting parser at offset %" PRIi64 "\n",
+ G_STRFUNC, seek_off);
+ state->file_bytes_read = 0;
+ ok = run_busmaster_parser(state, err, err_info);
+
+ /* Rewind the file to the offset we have finished parsing */
+ busmaster_debug_printf("%s: Rewinding to offset %" PRIi64 "\n",
+ G_STRFUNC, seek_off + state->file_bytes_read);
+ if (file_seek(fh, seek_off + state->file_bytes_read, SEEK_SET, err) == -1)
+ {
+ g_free(*err_info);
+ *err = errno;
+ *err_info = g_strdup(g_strerror(errno));
+ return LOG_ENTRY_ERROR;
+ }
+ }
+ while (ok && state->entry_type == LOG_ENTRY_NONE);
+
+ if (!ok)
+ return LOG_ENTRY_ERROR;
+
+ busmaster_debug_printf("%s: Success\n", G_STRFUNC);
+
+ return state->entry_type;
+}
+
+wtap_open_return_val
+busmaster_open(wtap *wth, int *err, char **err_info)
+{
+ busmaster_state_t state = {0};
+ log_entry_type_t entry;
+
+ busmaster_debug_printf("%s: Trying to open with busmaster log reader\n",
+ G_STRFUNC);
+
+ /* Rewind to the beginning */
+ if (file_seek(wth->fh, 0, SEEK_SET, err) == -1)
+ return WTAP_OPEN_ERROR;
+
+ entry = busmaster_parse(wth->fh, &state, err, err_info);
+
+ g_free(*err_info);
+ *err_info = NULL;
+ *err = 0;
+
+ if (entry != LOG_ENTRY_HEADER)
+ return WTAP_OPEN_NOT_MINE;
+
+ /* Rewind to the beginning, so busmaster_read may read from the very beginning */
+ if (file_seek(wth->fh, 0, SEEK_SET, err) == -1)
+ return WTAP_OPEN_ERROR;
+
+ busmaster_debug_printf("%s: That's a busmaster log\n", G_STRFUNC);
+
+ wth->priv = NULL;
+ wth->subtype_close = busmaster_close;
+ wth->subtype_read = busmaster_read;
+ wth->subtype_seek_read = busmaster_seek_read;
+ wth->file_type_subtype = busmaster_file_type_subtype;
+ wth->file_encap = WTAP_ENCAP_WIRESHARK_UPPER_PDU;
+ wth->file_tsprec = WTAP_TSPREC_USEC;
+
+ return WTAP_OPEN_MINE;
+}
+
+static void
+busmaster_close(wtap *wth)
+{
+ busmaster_debug_printf("%s\n", G_STRFUNC);
+
+ g_slist_free_full((GSList *)wth->priv, g_free);
+ wth->priv = NULL;
+}
+
+static busmaster_priv_t *
+busmaster_find_priv_entry(void *priv, gint64 offset)
+{
+ GSList *list;
+
+ for (list = (GSList *)priv; list; list = g_slist_next(list))
+ {
+ busmaster_priv_t *entry = (busmaster_priv_t *)list->data;
+
+ if (((entry->file_end_offset == -1)
+ && (g_slist_next(list) == NULL))
+ || ((offset >= entry->file_start_offset)
+ && (offset <= entry->file_end_offset)))
+ {
+ return entry;
+ }
+ }
+
+ return NULL;
+}
+
+static gboolean
+busmaster_read(wtap *wth, wtap_rec *rec, Buffer *buf, int *err, gchar **err_info,
+ gint64 *data_offset)
+{
+ log_entry_type_t entry;
+ busmaster_state_t state;
+ busmaster_priv_t *priv_entry;
+ gboolean is_msg = FALSE;
+ gboolean is_ok = TRUE;
+
+ while (!is_msg && is_ok)
+ {
+ busmaster_debug_printf("%s: offset = %" PRIi64 "\n",
+ G_STRFUNC, file_tell(wth->fh));
+
+ if (file_eof(wth->fh))
+ {
+ busmaster_debug_printf("%s: End of file detected, nothing to do here\n",
+ G_STRFUNC);
+ *err = 0;
+ *err_info = NULL;
+ return FALSE;
+ }
+
+ *data_offset = file_tell(wth->fh);
+ priv_entry = busmaster_find_priv_entry(wth->priv, *data_offset);
+
+ memset(&state, 0, sizeof(state));
+ if (priv_entry)
+ state.header = *priv_entry;
+ entry = busmaster_parse(wth->fh, &state, err, err_info);
+
+ busmaster_debug_printf("%s: analyzing output\n", G_STRFUNC);
+ switch (entry)
+ {
+ case LOG_ENTRY_EMPTY:
+ break;
+ case LOG_ENTRY_FOOTER_AND_HEADER:
+ case LOG_ENTRY_FOOTER:
+ priv_entry = (busmaster_priv_t *)g_slist_last((GSList *)wth->priv)->data;
+ if (!priv_entry)
+ {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("Header is missing");
+ return FALSE;
+ }
+ priv_entry->file_end_offset = *data_offset;
+ if (entry == LOG_ENTRY_FOOTER)
+ break;
+ /* fall-through */
+ case LOG_ENTRY_HEADER:
+ if (state.header.protocol != PROTOCOL_CAN &&
+ state.header.protocol != PROTOCOL_J1939)
+ {
+ *err = WTAP_ERR_UNSUPPORTED;
+ *err_info = g_strdup("Unsupported protocol type");
+ return FALSE;
+ }
+
+ if (wth->priv)
+ {
+ /* Check that the previous section has a footer */
+ priv_entry = (busmaster_priv_t *)g_slist_last((GSList *)wth->priv)->data;
+
+ if (priv_entry && priv_entry->file_end_offset == -1)
+ {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("Footer is missing");
+ return FALSE;
+ }
+ }
+
+ /* Start a new section */
+ priv_entry = g_new(busmaster_priv_t, 1);
+
+ priv_entry[0] = state.header;
+ priv_entry->file_start_offset = file_tell(wth->fh);
+ priv_entry->file_end_offset = -1;
+
+ wth->priv = g_slist_append((GSList *)wth->priv, priv_entry);
+ break;
+ case LOG_ENTRY_MSG:
+ is_msg = TRUE;
+ priv_entry = busmaster_find_priv_entry(wth->priv, *data_offset);
+ is_ok = busmaster_gen_packet(rec, buf, priv_entry, &state.msg, err, err_info);
+ break;
+ case LOG_ENTRY_EOF:
+ case LOG_ENTRY_ERROR:
+ case LOG_ENTRY_NONE:
+ default:
+ is_ok = FALSE;
+ break;
+ }
+ }
+
+ busmaster_debug_printf("%s: stopped at offset %" PRIi64 " with entry %d\n",
+ G_STRFUNC, file_tell(wth->fh), entry);
+
+ return is_ok;
+}
+
+static gboolean
+busmaster_seek_read(wtap *wth, gint64 seek_off, wtap_rec *rec,
+ Buffer *buf, int *err, gchar **err_info)
+{
+ busmaster_priv_t *priv_entry;
+ busmaster_state_t state = {0};
+ log_entry_type_t entry;
+
+ busmaster_debug_printf("%s: offset = %" PRIi64 "\n", G_STRFUNC, seek_off);
+
+ priv_entry = busmaster_find_priv_entry(wth->priv, seek_off);
+ if (!priv_entry)
+ {
+ busmaster_debug_printf("%s: analyzing output\n", G_STRFUNC);
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("Malformed header");
+ return FALSE;
+ }
+
+ if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
+ return FALSE;
+
+ state.header = *priv_entry;
+ entry = busmaster_parse(wth->random_fh, &state, err, err_info);
+
+ busmaster_debug_printf("%s: analyzing output\n", G_STRFUNC);
+
+ if (entry == LOG_ENTRY_ERROR || entry == LOG_ENTRY_NONE)
+ return FALSE;
+
+ if (entry != LOG_ENTRY_MSG)
+ {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("Failed to read a frame");
+ return FALSE;
+ }
+
+ return busmaster_gen_packet(rec, buf, priv_entry, &state.msg, err, err_info);
+}
+
+static const struct supported_block_type busmaster_blocks_supported[] = {
+ /*
+ * We support packet blocks, with no comments or other options.
+ */
+ { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
+};
+
+static const struct file_type_subtype_info busmaster_info = {
+ "BUSMASTER log file", "busmaster", "log", NULL,
+ FALSE, BLOCKS_SUPPORTED(busmaster_blocks_supported),
+ NULL, NULL, NULL
+};
+
+void register_busmaster(void)
+{
+ busmaster_file_type_subtype = wtap_register_file_type_subtype(&busmaster_info);
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * vi: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */
diff --git a/wiretap/busmaster.h b/wiretap/busmaster.h
new file mode 100644
index 00000000..404fee66
--- /dev/null
+++ b/wiretap/busmaster.h
@@ -0,0 +1,20 @@
+/** @file
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * Support for Busmaster log file format
+ * Copyright (c) 2019 by Maksim Salau <maksim.salau@gmail.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef BUSMASTER_H__
+#define BUSMASTER_H__
+
+#include <wiretap/wtap.h>
+
+wtap_open_return_val
+busmaster_open(wtap *wth, int *err, char **err_info);
+
+#endif /* BUSMASTER_H__ */
diff --git a/wiretap/busmaster_parser.lemon b/wiretap/busmaster_parser.lemon
new file mode 100644
index 00000000..ce62aada
--- /dev/null
+++ b/wiretap/busmaster_parser.lemon
@@ -0,0 +1,455 @@
+%include {
+
+/* busmaster_parser.lemon
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * Support for Busmaster log file format
+ * Copyright (c) 2019 by Maksim Salau <maksim.salau@gmail.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "config.h"
+#include <assert.h>
+#include <string.h>
+#include <wireshark.h>
+#include <wiretap/file_wrappers.h>
+#include "busmaster_priv.h"
+
+extern void *BusmasterParserAlloc(void *(*mallocProc)(size_t));
+extern void BusmasterParser(void *yyp, int yymajor, token_t yyminor, busmaster_state_t *state);
+extern void BusmasterParserFree(void *p, void (*freeProc)(void*));
+
+#if defined(BUSMASTER_DEBUG) || defined(BUSMASTER_PARSER_TRACE)
+extern void BusmasterParserTrace(FILE *TraceFILE, char *zTracePrompt);
+#undef NDEBUG
+#endif
+
+static void merge_msg_data(msg_data_t *dst, const msg_data_t *a, const msg_data_t *b)
+{
+ dst->length = a->length + b->length;
+ memcpy(&dst->data[0], &a->data[0], a->length);
+ memcpy(&dst->data[a->length], &b->data[0], b->length);
+}
+
+DIAG_OFF_LEMON()
+} /* end of %include */
+
+%code {
+DIAG_ON_LEMON()
+}
+
+%name BusmasterParser
+
+%token_prefix TOKEN_
+
+%token_type { token_t }
+
+%token_destructor
+{
+ (void)state;
+ (void)yypParser;
+ (void)yypminor;
+}
+
+%extra_argument { busmaster_state_t* state }
+
+%syntax_error
+{
+ (void)yypParser;
+ (void)yyminor;
+
+#ifdef BUSMASTER_DEBUG
+ const int n = sizeof(yyTokenName) / sizeof(yyTokenName[0]);
+ busmaster_debug_printf("%s: got token: %s\n", G_STRFUNC, yyTokenName[yymajor]);
+ for (int i = 0; i < n; ++i) {
+ int a = yy_find_shift_action((YYCODETYPE)i, yypParser->yytos->stateno);
+ if (a < YYNSTATE + YYNRULE) {
+ busmaster_debug_printf("%s: possible token: %s\n", G_STRFUNC, yyTokenName[i]);
+ }
+ }
+#endif
+
+ g_free(state->parse_error);
+ state->entry_type = LOG_ENTRY_ERROR;
+ state->parse_error = ws_strdup_printf("Syntax Error");
+ busmaster_debug_printf("%s: Syntax Error\n", G_STRFUNC);
+}
+
+%parse_failure
+{
+ g_free(state->parse_error);
+ state->entry_type = LOG_ENTRY_ERROR;
+ state->parse_error = g_strdup("Parse Error");
+ busmaster_debug_printf("%s: Parse Error\n", G_STRFUNC);
+}
+
+%stack_overflow
+{
+ g_free(state->parse_error);
+ state->entry_type = LOG_ENTRY_ERROR;
+ state->parse_error = g_strdup("Parser stack overflow");
+ busmaster_debug_printf("%s: Parser stack overflow\n", G_STRFUNC);
+}
+
+%type msg_time { msg_time_t }
+%type msg_type { msg_type_t }
+%type err_msg_type { msg_type_t }
+%type msg_length { guint }
+%type msg_id { guint32 }
+
+%type ref_date { msg_date_t }
+%type ref_time { msg_time_t }
+
+%type start_time { msg_date_time_t }
+
+%type byte { guint8 }
+%type data { msg_data_t }
+%type data0 { msg_data_t }
+%type data1 { msg_data_t }
+%type data2 { msg_data_t }
+%type data3 { msg_data_t }
+%type data4 { msg_data_t }
+%type data5 { msg_data_t }
+%type data6 { msg_data_t }
+%type data7 { msg_data_t }
+%type data8 { msg_data_t }
+%type data12 { msg_data_t }
+%type data16 { msg_data_t }
+%type data20 { msg_data_t }
+%type data24 { msg_data_t }
+%type data32 { msg_data_t }
+%type data48 { msg_data_t }
+%type data64 { msg_data_t }
+
+%nonassoc INVALID_CHAR .
+%nonassoc INVALID_NUMBER .
+
+%start_symbol entry
+
+entry ::= empty_line .
+entry ::= footer_and_header .
+entry ::= header .
+entry ::= footer .
+entry ::= msg .
+entry ::= err_msg .
+entry ::= j1939_msg .
+
+empty_line ::= .
+{
+ busmaster_debug_printf("%s: EMPTY\n", G_STRFUNC);
+ state->entry_type = LOG_ENTRY_EMPTY;
+}
+
+footer_and_header ::= footer ENDL header .
+{
+ busmaster_debug_printf("%s: FOOTER AND HEADER\n", G_STRFUNC);
+ state->entry_type = LOG_ENTRY_FOOTER_AND_HEADER;
+}
+
+header ::= version ENDL maybe_lines
+ PROTOCOL_TYPE(P) ENDL maybe_lines
+ START_SESSION ENDL maybe_lines
+ start_time(S) ENDL maybe_lines
+ DATA_MODE(D) ENDL maybe_lines
+ TIME_MODE(T) ENDL anything .
+{
+ busmaster_debug_printf("%s: HEADER\n", G_STRFUNC);
+
+ state->entry_type = LOG_ENTRY_HEADER;
+ state->header.start_date = S.date;
+ state->header.start_time = S.time;
+ state->header.protocol = (protocol_type_t)P.v0;
+ state->header.data_mode = (data_mode_t)D.v0;
+ state->header.time_mode = (time_mode_t)T.v0;
+}
+
+version ::= HEADER_VER maybe_chars .
+
+maybe_chars ::= .
+maybe_chars ::= maybe_chars HEADER_CHAR .
+
+maybe_lines ::= .
+maybe_lines ::= maybe_lines maybe_chars ENDL .
+
+anything ::= .
+anything ::= anything HEADER_CHAR .
+anything ::= anything ENDL .
+
+start_time(R) ::= START_TIME ref_date(D) ref_time(T) .
+{
+ R.date = D;
+ R.time = T;
+}
+
+footer ::= end_time ENDL STOP_SESSION .
+{
+ busmaster_debug_printf("%s: FOOTER\n", G_STRFUNC);
+ state->entry_type = LOG_ENTRY_FOOTER;
+}
+
+end_time ::= END_TIME ref_date ref_time .
+
+/* <Time><Tx/Rx><Channel><CAN ID><Type><DLC><DataBytes> */
+msg ::= msg_time(msg_time) MSG_DIR INT msg_id(msg_id) msg_type(msg_type) msg_length(msg_length) data(msg_data) .
+{
+ msg_t msg;
+
+ /* DLC is always in DEC mode, thus we need to fix the value
+ * if it was read initially as HEX. */
+ if (state->header.data_mode == DATA_MODE_HEX)
+ {
+ msg_length = (msg_length / 16) * 10 + (msg_length % 16);
+ }
+
+ /* Fix data in RTR frames. Data may not be present,
+ * but length field is set. */
+ if (msg_type == MSG_TYPE_STD_RTR ||
+ msg_type == MSG_TYPE_EXT_RTR)
+ {
+ memset(&msg_data, 0, sizeof(msg_data));
+ msg_data.length = msg_length;
+ }
+
+ msg.timestamp = msg_time;
+ msg.id = msg_id;
+ msg.type = msg_type;
+ msg.data = msg_data;
+
+ busmaster_debug_printf("%s: MSG\n", G_STRFUNC);
+
+ state->msg = msg;
+ state->entry_type = LOG_ENTRY_MSG;
+}
+
+/* <Time><Tx/Rx><Channel><CAN ID><Type><Text> */
+err_msg ::= msg_time(msg_time) MSG_DIR INT INT err_msg_type(msg_type) .
+{
+ msg_t msg;
+
+ msg.timestamp = msg_time;
+ msg.id = 0;
+ msg.type = msg_type;
+ msg.data.length = CAN_MAX_DLEN;
+
+ memset(msg.data.data, 0, sizeof(msg.data.data));
+
+ busmaster_debug_printf("%s: ERR MSG\n", G_STRFUNC);
+
+ state->msg = msg;
+ state->entry_type = LOG_ENTRY_MSG;
+}
+
+/* <Time><Channel><CAN ID><PGN><Type><Source Node><Destination Node><Priority><Tx/Rx><DLC><DataBytes> */
+j1939_msg ::= msg_time(msg_time) INT msg_id(msg_id) INT J1939_MSG_TYPE INT INT INT MSG_DIR msg_length data(msg_data) .
+{
+ msg_t msg;
+
+ msg.timestamp = msg_time;
+ msg.id = msg_id;
+ msg.type = MSG_TYPE_EXT;
+ msg.data = msg_data;
+
+ busmaster_debug_printf("%s: J1939 MSG\n", G_STRFUNC);
+
+ state->msg = msg;
+ state->entry_type = LOG_ENTRY_MSG;
+}
+
+ref_date(R) ::= INT(D) COLON INT(M) COLON INT(Y) .
+{
+ R.year = (guint)Y.v0;
+ R.month = (guint)M.v0;
+ R.day = (guint)D.v0;
+}
+
+ref_time(R) ::= INT(H) COLON INT(M) COLON INT(S) COLON INT(U) .
+{
+ R.hours = (guint)H.v0;
+ R.minutes = (guint)M.v0;
+ R.seconds = (guint)S.v0;
+ R.micros = (guint)U.v0 * 1000;
+}
+
+msg_time(R) ::= MSG_TIME(M) .
+{
+ R.hours = (guint)M.v0;
+ R.minutes = (guint)M.v1;
+ R.seconds = (guint)M.v2;
+ R.micros = (guint)M.v3 * 100;
+}
+
+msg_id(R) ::= INT(V) .
+{
+ R = (guint)V.v0;
+}
+
+msg_length(R) ::= INT(V) .
+{
+ R = (guint)V.v0;
+}
+
+msg_type(R) ::= MSG_TYPE(V) .
+{
+ R = (msg_type_t)V.v0;
+}
+
+err_msg_type(R) ::= ERR_MSG_TYPE(V) .
+{
+ R = (msg_type_t)V.v0;
+}
+
+data(R) ::= data0(A) . { R = A; }
+data(R) ::= data1(A) . { R = A; }
+data(R) ::= data2(A) . { R = A; }
+data(R) ::= data3(A) . { R = A; }
+data(R) ::= data4(A) . { R = A; }
+data(R) ::= data5(A) . { R = A; }
+data(R) ::= data6(A) . { R = A; }
+data(R) ::= data7(A) . { R = A; }
+data(R) ::= data8(A) . { R = A; }
+data(R) ::= data12(A) . { R = A; }
+data(R) ::= data16(A) . { R = A; }
+data(R) ::= data20(A) . { R = A; }
+data(R) ::= data24(A) . { R = A; }
+data(R) ::= data32(A) . { R = A; }
+data(R) ::= data48(A) . { R = A; }
+data(R) ::= data64(A) . { R = A; }
+
+byte(R) ::= INT(A) .
+{
+ R = (guint8)A.v0;
+}
+
+data0(R) ::= .
+{
+ R.length = 0;
+}
+
+data1(R) ::= byte(A) .
+{
+ R.length = 1;
+ R.data[0] = A;
+}
+
+data2(R) ::= byte(A) byte(B) .
+{
+ R.length = 2;
+ R.data[0] = A;
+ R.data[1] = B;
+}
+
+data3(R) ::= byte(A) byte(B) byte(C) .
+{
+ R.length = 3;
+ R.data[0] = A;
+ R.data[1] = B;
+ R.data[2] = C;
+}
+
+data4(R) ::= byte(A) byte(B) byte(C) byte(D) .
+{
+ R.length = 4;
+ R.data[0] = A;
+ R.data[1] = B;
+ R.data[2] = C;
+ R.data[3] = D;
+}
+
+data5(R) ::= data4(A) data1(B) . { merge_msg_data(&R, &A, &B); }
+data6(R) ::= data4(A) data2(B) . { merge_msg_data(&R, &A, &B); }
+data7(R) ::= data4(A) data3(B) . { merge_msg_data(&R, &A, &B); }
+data8(R) ::= data4(A) data4(B) . { merge_msg_data(&R, &A, &B); }
+data12(R) ::= data8(A) data4(B) . { merge_msg_data(&R, &A, &B); }
+data16(R) ::= data8(A) data8(B) . { merge_msg_data(&R, &A, &B); }
+data20(R) ::= data16(A) data4(B) . { merge_msg_data(&R, &A, &B); }
+data24(R) ::= data16(A) data8(B) . { merge_msg_data(&R, &A, &B); }
+data32(R) ::= data16(A) data16(B) . { merge_msg_data(&R, &A, &B); }
+data48(R) ::= data32(A) data16(B) . { merge_msg_data(&R, &A, &B); }
+data64(R) ::= data32(A) data32(B) . { merge_msg_data(&R, &A, &B); }
+
+%code {
+
+#include "busmaster_scanner_lex.h"
+#include "busmaster_parser.h"
+
+gboolean
+run_busmaster_parser(busmaster_state_t *state,
+ int *err, gchar **err_info)
+{
+ int lex_code;
+ yyscan_t scanner;
+ void *parser;
+
+ state->entry_type = LOG_ENTRY_NONE;
+ state->parse_error = NULL;
+ state->err = 0;
+ state->err_info = NULL;
+
+ if (busmaster_lex_init_extra(state, &scanner) != 0)
+ {
+ *err = errno;
+ *err_info = g_strdup(g_strerror(errno));
+ return FALSE;
+ }
+
+ parser = BusmasterParserAlloc(g_malloc);
+
+#ifdef BUSMASTER_PARSER_TRACE
+ BusmasterParserTrace(stdout, "BusmasterParser >> ");
+#endif
+
+ busmaster_debug_printf("%s: Starting parsing of the line\n", G_STRFUNC);
+
+ do
+ {
+ lex_code = busmaster_lex(scanner);
+
+#ifdef BUSMASTER_DEBUG
+ if (lex_code)
+ busmaster_debug_printf("%s: Feeding %s '%s'\n",
+ G_STRFUNC, yyTokenName[lex_code],
+ busmaster_get_text(scanner));
+ else
+ busmaster_debug_printf("%s: Feeding %s\n",
+ G_STRFUNC, yyTokenName[lex_code]);
+#endif
+
+ BusmasterParser(parser, lex_code, state->token, state);
+
+ if (state->err || state->err_info || state->parse_error)
+ break;
+ }
+ while (lex_code);
+
+ busmaster_debug_printf("%s: Done (%d)\n", G_STRFUNC, lex_code);
+
+ BusmasterParserFree(parser, g_free);
+ busmaster_lex_destroy(scanner);
+
+ if (state->err || state->err_info || state->parse_error)
+ {
+ if (state->err_info)
+ {
+ *err_info = state->err_info;
+ g_free(state->parse_error);
+ }
+ else
+ {
+ *err_info = state->parse_error;
+ }
+
+ if (state->err)
+ *err = state->err;
+ else
+ *err = WTAP_ERR_BAD_FILE;
+
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+}
diff --git a/wiretap/busmaster_priv.h b/wiretap/busmaster_priv.h
new file mode 100644
index 00000000..4c9cad67
--- /dev/null
+++ b/wiretap/busmaster_priv.h
@@ -0,0 +1,136 @@
+/** @file
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * Support for Busmaster log file format
+ * Copyright (c) 2019 by Maksim Salau <maksim.salau@gmail.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef BUSMASTER_PRIV_H__
+#define BUSMASTER_PRIV_H__
+
+#include <gmodule.h>
+#include <wiretap/wtap.h>
+#include <wiretap/socketcan.h>
+
+//#define BUSMASTER_DEBUG
+//#define BUSMASTER_PARSER_TRACE
+
+typedef enum {
+ LOG_ENTRY_ERROR = -1,
+ LOG_ENTRY_NONE = 0,
+ LOG_ENTRY_EMPTY,
+ LOG_ENTRY_HEADER,
+ LOG_ENTRY_FOOTER,
+ LOG_ENTRY_FOOTER_AND_HEADER,
+ LOG_ENTRY_MSG,
+ LOG_ENTRY_EOF,
+} log_entry_type_t;
+
+typedef enum {
+ PROTOCOL_UNKNOWN = 0,
+ PROTOCOL_CAN,
+ PROTOCOL_LIN,
+ PROTOCOL_J1939,
+} protocol_type_t;
+
+typedef enum {
+ DATA_MODE_UNKNOWN = 0,
+ DATA_MODE_HEX,
+ DATA_MODE_DEC,
+} data_mode_t;
+
+typedef enum {
+ TIME_MODE_UNKNOWN = 0,
+ TIME_MODE_ABSOLUTE,
+ TIME_MODE_SYSTEM,
+ TIME_MODE_RELATIVE,
+} time_mode_t;
+
+typedef enum {
+ MSG_TYPE_STD,
+ MSG_TYPE_EXT,
+ MSG_TYPE_STD_RTR,
+ MSG_TYPE_EXT_RTR,
+ MSG_TYPE_STD_FD,
+ MSG_TYPE_EXT_FD,
+ MSG_TYPE_ERR,
+} msg_type_t;
+
+typedef struct {
+ guint year;
+ guint month;
+ guint day;
+} msg_date_t;
+
+typedef struct {
+ guint hours;
+ guint minutes;
+ guint seconds;
+ guint micros;
+} msg_time_t;
+
+typedef struct {
+ msg_date_t date;
+ msg_time_t time;
+} msg_date_time_t;
+
+typedef struct {
+ guint length;
+ guint8 data[CANFD_MAX_DLEN];
+} msg_data_t;
+
+typedef struct {
+ msg_time_t timestamp;
+ msg_type_t type;
+ guint32 id;
+ msg_data_t data;
+} msg_t;
+
+typedef struct {
+ gint64 v0;
+ gint64 v1;
+ gint64 v2;
+ gint64 v3;
+} token_t;
+
+typedef struct {
+ gint64 file_start_offset;
+ gint64 file_end_offset;
+ protocol_type_t protocol;
+ data_mode_t data_mode;
+ time_mode_t time_mode;
+ msg_date_t start_date;
+ msg_time_t start_time;
+} busmaster_priv_t;
+
+typedef struct {
+ FILE_T fh;
+ gint64 file_bytes_read;
+
+ gchar *parse_error;
+ int err;
+ gchar *err_info;
+
+ token_t token;
+
+ log_entry_type_t entry_type;
+ busmaster_priv_t header;
+ msg_t msg;
+} busmaster_state_t;
+
+gboolean
+run_busmaster_parser(busmaster_state_t *state,
+ int *err, gchar **err_info);
+
+#ifdef BUSMASTER_DEBUG
+#include <stdio.h>
+#define busmaster_debug_printf(...) printf(__VA_ARGS__)
+#else
+#define busmaster_debug_printf(...) (void)0
+#endif
+
+#endif /* BUSMASTER_PRIV_H__ */
diff --git a/wiretap/busmaster_scanner.l b/wiretap/busmaster_scanner.l
new file mode 100644
index 00000000..eeaa88f5
--- /dev/null
+++ b/wiretap/busmaster_scanner.l
@@ -0,0 +1,199 @@
+/* busmaster_scanner.l
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * Support for Busmaster log file format
+ * Copyright (c) 2019 by Maksim Salau <maksim.salau@gmail.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+%top {
+/* Include this before everything else, for various large-file definitions */
+#include "config.h"
+#include <wireshark.h>
+}
+
+%option noyywrap
+%option noinput
+%option nounput
+%option batch
+%option never-interactive
+%option nodefault
+%option prefix="busmaster_"
+%option reentrant
+%option extra-type="busmaster_state_t *"
+
+%option noyy_scan_buffer
+%option noyy_scan_bytes
+%option noyy_scan_string
+
+/*
+ * We have to override the memory allocators so that we don't get
+ * "unused argument" warnings from the yyscanner argument (which
+ * we don't use, as we have a global memory allocator).
+ *
+ * We provide, as macros, our own versions of the routines generated by Flex,
+ * which just call malloc()/realloc()/free() (as the Flex versions do),
+ * discarding the extra argument.
+ */
+%option noyyalloc
+%option noyyrealloc
+%option noyyfree
+
+%{
+
+#include <ws_diag_control.h>
+#include <wiretap/file_wrappers.h>
+#include "busmaster_parser.h"
+#include "busmaster_priv.h"
+
+#ifndef HAVE_UNISTD_H
+#define YY_NO_UNISTD_H
+#endif
+
+static int busmaster_yyinput(void *buf, unsigned int length, busmaster_state_t *state)
+{
+ int ret = file_read(buf, length, state->fh);
+
+ if (ret < 0)
+ {
+ state->err = file_error(state->fh, &state->err_info);
+ return YY_NULL;
+ }
+
+ return ret;
+}
+
+#define YY_INPUT(buf, result, max_size) \
+ do { (result) = busmaster_yyinput((buf), (max_size), yyextra); } while (0)
+
+/* Count bytes read. This is required in order to rewind the file
+ * to the beginning of the next packet, since flex reads more bytes
+ * before executing the action that does yyterminate(). */
+#define YY_USER_ACTION do { yyextra->file_bytes_read += yyleng; } while (0);
+
+/*
+ * Sleazy hack to suppress compiler warnings in yy_fatal_error().
+ */
+#define YY_EXIT_FAILURE ((void)yyscanner, 2)
+
+/*
+ * Macros for the allocators, to discard the extra argument.
+ */
+#define busmaster_alloc(size, yyscanner) (void *)malloc(size)
+#define busmaster_realloc(ptr, size, yyscanner) (void *)realloc((char *)(ptr), (size))
+#define busmaster_free(ptr, yyscanner) free((char *)(ptr))
+
+DIAG_OFF_FLEX()
+
+%}
+
+SPC [ \t]+
+ENDL [\r\n][ \t\r\n]*
+INT [0-9]+
+NUM (0x)?[0-9A-Fa-f]+
+
+%x HEADER TIME
+%x HEADER_CHANNELS HEADER_DB_FILES
+
+%%
+
+<*>{SPC} ;
+<INITIAL>{ENDL} { yyterminate(); };
+<HEADER,TIME>{ENDL} { YY_FATAL_ERROR("Unterminated header statement"); }
+
+"***" { BEGIN(HEADER); }
+<HEADER,TIME>"***"{ENDL}"***" { BEGIN(HEADER); return TOKEN_ENDL; }
+<HEADER>"***"{ENDL} { BEGIN(INITIAL); yyterminate(); }
+<HEADER>"BUSMASTER" { return TOKEN_HEADER_VER; }
+<HEADER>"PROTOCOL CAN" { yyextra->token.v0 = PROTOCOL_CAN; return TOKEN_PROTOCOL_TYPE; }
+<HEADER>"PROTOCOL J1939" { yyextra->token.v0 = PROTOCOL_J1939; return TOKEN_PROTOCOL_TYPE; }
+<HEADER>"PROTOCOL LIN" { yyextra->token.v0 = PROTOCOL_LIN; return TOKEN_PROTOCOL_TYPE; }
+<HEADER>"START DATE AND TIME" { BEGIN(TIME); return TOKEN_START_TIME; }
+<HEADER>"END DATE AND TIME" { BEGIN(TIME); return TOKEN_END_TIME; }
+<HEADER>"DEC" { yyextra->token.v0 = DATA_MODE_DEC; return TOKEN_DATA_MODE; }
+<HEADER>"HEX" { yyextra->token.v0 = DATA_MODE_HEX; return TOKEN_DATA_MODE; }
+<HEADER>"ABSOLUTE MODE" { yyextra->token.v0 = TIME_MODE_ABSOLUTE; return TOKEN_TIME_MODE; }
+<HEADER>"SYSTEM MODE" { yyextra->token.v0 = TIME_MODE_SYSTEM; return TOKEN_TIME_MODE; }
+<HEADER>"RELATIVE MODE" { yyextra->token.v0 = TIME_MODE_RELATIVE; return TOKEN_TIME_MODE; }
+<HEADER>"[START LOGGING SESSION]" { return TOKEN_START_SESSION; }
+<HEADER>"[STOP LOGGING SESSION]" { return TOKEN_STOP_SESSION; }
+<HEADER>"START CHANNEL BAUD RATE***" { BEGIN(HEADER_CHANNELS); }
+<HEADER>"START DATABASE FILES (DBF/DBC)***" { BEGIN(HEADER_DB_FILES); }
+<HEADER>. { return TOKEN_HEADER_CHAR; }
+
+<HEADER_CHANNELS>"***END CHANNEL BAUD RATE***"{ENDL}"***" { BEGIN(HEADER); }
+<HEADER_CHANNELS>.+ ;
+<HEADER_CHANNELS>{ENDL} ;
+
+<HEADER_DB_FILES>"***END DATABASE FILES (DBF/DBC)***"{ENDL}"***" { BEGIN(HEADER); }
+<HEADER_DB_FILES>"***END OF DATABASE FILES (DBF/DBC)***"{ENDL}"***" { BEGIN(HEADER); }
+<HEADER_DB_FILES>.+ ;
+<HEADER_DB_FILES>{ENDL} ;
+
+<TIME>{INT} { yyextra->token.v0 = strtoul(yytext, NULL, 10); return TOKEN_INT; }
+<TIME>: { return TOKEN_COLON; }
+<TIME>. { return TOKEN_INVALID_CHAR; }
+
+<INITIAL>{INT}:{INT}:{INT}:{INT} {
+ char *endp;
+ char *strp;
+
+ yyextra->token.v0 = strtoul(yytext, &endp, 10);
+ if (*endp != ':' || endp == yytext)
+ return TOKEN_INVALID_NUMBER;
+
+ strp = endp + 1;
+ yyextra->token.v1 = strtoul(strp, &endp, 10);
+ if (*endp != ':' || endp == strp)
+ return TOKEN_INVALID_NUMBER;
+
+ strp = endp + 1;
+ yyextra->token.v2 = strtoul(strp, &endp, 10);
+ if (*endp != ':' || endp == strp)
+ return TOKEN_INVALID_NUMBER;
+
+ strp = endp + 1;
+ yyextra->token.v3 = strtoul(strp, &endp, 10);
+ if (*endp != '\0' || endp == strp)
+ return TOKEN_INVALID_NUMBER;
+
+ return TOKEN_MSG_TIME;
+ }
+
+<INITIAL>{NUM} {
+ char *endp;
+
+ if (yyextra->header.data_mode == DATA_MODE_HEX)
+ yyextra->token.v0 = strtoul(yytext, &endp, 16);
+ else if (yyextra->header.data_mode == DATA_MODE_DEC)
+ yyextra->token.v0 = strtoul(yytext, &endp, 10);
+ else
+ return TOKEN_INVALID_NUMBER;
+
+ if (*endp != '\0' || endp == yytext)
+ return TOKEN_INVALID_NUMBER;
+
+ return TOKEN_INT;
+ }
+
+<INITIAL>[RT]x { return TOKEN_MSG_DIR; }
+<INITIAL>s { yyextra->token.v0 = MSG_TYPE_STD; return TOKEN_MSG_TYPE; }
+<INITIAL>sr { yyextra->token.v0 = MSG_TYPE_STD_RTR; return TOKEN_MSG_TYPE; }
+<INITIAL>x { yyextra->token.v0 = MSG_TYPE_EXT; return TOKEN_MSG_TYPE; }
+<INITIAL>xr { yyextra->token.v0 = MSG_TYPE_EXT_RTR; return TOKEN_MSG_TYPE; }
+<INITIAL>s-fd { yyextra->token.v0 = MSG_TYPE_STD_FD; return TOKEN_MSG_TYPE; }
+<INITIAL>x-fd { yyextra->token.v0 = MSG_TYPE_EXT_FD; return TOKEN_MSG_TYPE; }
+<INITIAL>ERR.* { yyextra->token.v0 = MSG_TYPE_ERR; return TOKEN_ERR_MSG_TYPE; }
+
+<INITIAL>("NONE"|"CMD"|"RQST"|"DATA"|"BROADCAST"|"ACK"|"GRP_FUNC"|"ACL"|"RQST_ACL"|"CA"|"BAM"|"RTS"|"CTS"|"EOM"|"CON_ABORT"|"TPDT") {
+ return TOKEN_J1939_MSG_TYPE;
+}
+
+<INITIAL>. { return TOKEN_INVALID_CHAR; }
+
+%%
+
+DIAG_ON_FLEX()
diff --git a/wiretap/camins.c b/wiretap/camins.c
new file mode 100644
index 00000000..27d26111
--- /dev/null
+++ b/wiretap/camins.c
@@ -0,0 +1,501 @@
+/* camins.c
+ *
+ * File format support for Rabbit Labs CAM Inspector files
+ * Copyright (c) 2013 by Martin Kaiser <martin@kaiser.cx>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+
+/* CAM Inspector is a commercial log tool for DVB-CI
+ it stores recorded packets between a CI module and a DVB receiver,
+ using a proprietary file format
+
+ a CAM Inspector file consists of 16bit blocks
+ the first byte contains payload data,
+ the second byte contains a "transaction type"
+
+ we currently support the following transaction types
+
+ 0x20 == data transfer from CI module to host
+ 0x22 == host reads the lower byte of the size register
+ 0x23 == host reads the higher byte of the size register
+ 0x2A == host writes the lower byte of the size register
+ 0x2B == host writes the higher byte of the size register
+ 0x28 == data transfer from host to CI module
+
+ using these transaction types, we can identify and assemble data transfers
+ from the host to the CAM and vice versa
+
+ a host->module data transfer will use the following transactions
+ one 0x2A and one 0x2B transaction to write the 16bit size
+ <size> 0x28 transactions to transfer one byte at a time
+ this will be assembled into one packet
+
+ the module->host transfer is similar
+
+ a CAM Inspector file uses a 44-bit time counter to keep track of the
+ time. the counter is in units of 1us. a timestamp block in the file
+ updates a part of the global time counter. a timestamp contains a 2-bit
+ relative position within the time counter and an 11-bit value for
+ this position.
+
+ error handling
+ when we run into an error while assembling a data transfer, the
+ primary goal is to recover so that we can handle the next transfer
+ correctly (all files I used for testing contained errors where
+ apparently the logging hardware missed some bytes)
+*/
+
+#include "config.h"
+
+#include <glib.h>
+#include <string.h>
+#include "wtap-int.h"
+#include "file_wrappers.h"
+
+#include "camins.h"
+
+
+#define TRANS_CAM_HOST 0x20
+#define TRANS_READ_SIZE_LOW 0x22
+#define TRANS_READ_SIZE_HIGH 0x23
+#define TRANS_HOST_CAM 0x28
+#define TRANS_WRITE_SIZE_LOW 0x2A
+#define TRANS_WRITE_SIZE_HIGH 0x2B
+
+#define IS_TRANS_SIZE(x) \
+ ((x)==TRANS_WRITE_SIZE_LOW || (x)==TRANS_WRITE_SIZE_HIGH || \
+ (x)==TRANS_READ_SIZE_LOW || (x)==TRANS_READ_SIZE_HIGH)
+
+/* a block contains a timestamp if the upper three bits are 0 */
+#define IS_TIMESTAMP(x) (((x) & 0xE0) == 0x00)
+
+/* a timestamp consists of a 2-bit position, followed by an 11-bit value. */
+#define TS_VALUE_SHIFT 11
+#define TS_POS_MASK (0x3 << TS_VALUE_SHIFT)
+#define TS_VALUE_MASK G_GUINT64_CONSTANT((1 << TS_VALUE_SHIFT) - 1)
+
+typedef enum {
+ SIZE_HAVE_NONE,
+ SIZE_HAVE_LOW,
+ SIZE_HAVE_HIGH,
+ SIZE_HAVE_ALL
+} size_read_t;
+
+#define RESET_STAT_VALS \
+{ \
+ *dat_trans_type = 0x00; \
+ *dat_len = 0x00; \
+ size_stat = SIZE_HAVE_NONE; \
+}
+
+#define SIZE_ADD_LOW \
+{ size_stat = (size_stat==SIZE_HAVE_HIGH ? SIZE_HAVE_ALL : SIZE_HAVE_LOW); }
+
+#define SIZE_ADD_HIGH \
+{ size_stat = (size_stat==SIZE_HAVE_LOW ? SIZE_HAVE_ALL : SIZE_HAVE_HIGH); }
+
+/* PCAP DVB-CI pseudo-header, see https://www.kaiser.cx/pcap-dvbci.html */
+#define DVB_CI_PSEUDO_HDR_VER 0
+#define DVB_CI_PSEUDO_HDR_LEN 4
+#define DVB_CI_PSEUDO_HDR_CAM_TO_HOST 0xFF
+#define DVB_CI_PSEUDO_HDR_HOST_TO_CAM 0xFE
+
+/* Maximum number of bytes to read before making a heuristic decision
+ * of whether this is our file type or not. Arbitrary. */
+#define CAMINS_BYTES_TO_CHECK 0x3FFFFFFFU
+
+static int camins_file_type_subtype = -1;
+
+void register_camins(void);
+
+/* Detect a camins file by looking at the blocks that access the 16bit
+ size register. The matching blocks to access the upper and lower 8bit
+ must be no further than 5 blocks apart.
+ A file may have errors that affect the size blocks. Therefore, we
+ read CAMINS_BYTES_TO_CHECK bytes and require that we have many more
+ valid pairs than errors. */
+static wtap_open_return_val detect_camins_file(FILE_T fh)
+{
+ int err;
+ gchar *err_info;
+ guint8 block[2];
+ guint8 search_block = 0;
+ guint8 gap_count = 0;
+ guint32 valid_pairs = 0, invalid_pairs = 0;
+ guint64 read_bytes = 0;
+
+ while (wtap_read_bytes(fh, block, sizeof(block), &err, &err_info)) {
+ if (search_block != 0) {
+ /* We're searching for a matching block to complete the pair. */
+
+ if (block[1] == search_block) {
+ /* We found it */
+ valid_pairs++;
+ search_block = 0;
+ }
+ else {
+ /* We didn't find it. */
+ gap_count++;
+ if (gap_count > 5) {
+ /* Give up the search, we have no pair. */
+ invalid_pairs++;
+ search_block = 0;
+ }
+ }
+ }
+ else {
+ /* We're not searching for a matching block at the moment.
+ If we see a size read/write block of one type, the matching
+ block is the other type and we can start searching. */
+
+ if (block[1] == TRANS_READ_SIZE_LOW) {
+ search_block = TRANS_READ_SIZE_HIGH;
+ gap_count = 0;
+ }
+ else if (block[1] == TRANS_READ_SIZE_HIGH) {
+ search_block = TRANS_READ_SIZE_LOW;
+ gap_count = 0;
+ }
+ else if (block[1] == TRANS_WRITE_SIZE_LOW) {
+ search_block = TRANS_WRITE_SIZE_HIGH;
+ gap_count = 0;
+ }
+ else if (block[1] == TRANS_WRITE_SIZE_HIGH) {
+ search_block = TRANS_WRITE_SIZE_LOW;
+ gap_count = 0;
+ }
+ }
+ read_bytes += sizeof(block);
+ if (read_bytes > CAMINS_BYTES_TO_CHECK) {
+ err = 0;
+ break;
+ }
+ }
+
+ if ((err != 0) && (err != WTAP_ERR_SHORT_READ)) {
+ /* A real read error. */
+ return WTAP_OPEN_ERROR;
+ }
+
+ /* For valid_pairs == invalid_pairs == 0, this isn't a camins file.
+ Don't change > into >= */
+ if (valid_pairs > 10 * invalid_pairs)
+ return WTAP_OPEN_MINE;
+
+ return WTAP_OPEN_NOT_MINE;
+}
+
+
+/* update the current time counter with infos from a timestamp block */
+static void process_timestamp(guint16 timestamp, guint64 *time_us)
+{
+ guint8 pos, shift;
+ guint64 val;
+
+ if (!time_us)
+ return;
+
+ val = timestamp & TS_VALUE_MASK;
+ pos = (timestamp & TS_POS_MASK) >> TS_VALUE_SHIFT;
+ shift = TS_VALUE_SHIFT * pos;
+
+ *time_us &= ~(TS_VALUE_MASK << shift);
+ *time_us |= (val << shift);
+}
+
+
+/* find the transaction type for the data bytes of the next packet
+ and the number of data bytes in that packet
+ the fd is moved such that it can be used in a subsequent call
+ to retrieve the data
+ if requested by the caller, we increment the time counter as we
+ walk through the file */
+static gboolean
+find_next_pkt_info(FILE_T fh,
+ guint8 *dat_trans_type, /* transaction type used for the data bytes */
+ guint16 *dat_len, /* the number of data bytes in the packet */
+ guint64 *time_us,
+ int *err, gchar **err_info)
+{
+ guint8 block[2];
+ size_read_t size_stat;
+
+ if (!dat_trans_type || !dat_len)
+ return FALSE;
+
+ RESET_STAT_VALS;
+
+ do {
+ if (!wtap_read_bytes_or_eof(fh, block, sizeof(block), err, err_info)) {
+ RESET_STAT_VALS;
+ return FALSE;
+ }
+
+ /* our strategy is to continue reading until we have a high and a
+ low size byte for the same direction, duplicates or spurious data
+ bytes are ignored */
+
+ switch (block[1]) {
+ case TRANS_READ_SIZE_LOW:
+ if (*dat_trans_type != TRANS_CAM_HOST)
+ RESET_STAT_VALS;
+ *dat_trans_type = TRANS_CAM_HOST;
+ *dat_len |= block[0];
+ SIZE_ADD_LOW;
+ break;
+ case TRANS_READ_SIZE_HIGH:
+ if (*dat_trans_type != TRANS_CAM_HOST)
+ RESET_STAT_VALS;
+ *dat_trans_type = TRANS_CAM_HOST;
+ *dat_len |= (block[0] << 8);
+ SIZE_ADD_HIGH;
+ break;
+ case TRANS_WRITE_SIZE_LOW:
+ if (*dat_trans_type != TRANS_HOST_CAM)
+ RESET_STAT_VALS;
+ *dat_trans_type = TRANS_HOST_CAM;
+ *dat_len |= block[0];
+ SIZE_ADD_LOW;
+ break;
+ case TRANS_WRITE_SIZE_HIGH:
+ if (*dat_trans_type != TRANS_HOST_CAM)
+ RESET_STAT_VALS;
+ *dat_trans_type = TRANS_HOST_CAM;
+ *dat_len |= (block[0] << 8);
+ SIZE_ADD_HIGH;
+ break;
+ default:
+ if (IS_TIMESTAMP(block[1]))
+ process_timestamp(pletoh16(block), time_us);
+ break;
+ }
+ } while (size_stat != SIZE_HAVE_ALL);
+
+ return TRUE;
+}
+
+
+/* buffer allocated by the caller, must be long enough to hold
+ dat_len bytes, ... */
+static gint
+read_packet_data(FILE_T fh, guint8 dat_trans_type, guint8 *buf, guint16 dat_len,
+ guint64 *time_us, int *err, gchar **err_info)
+{
+ guint8 *p;
+ guint8 block[2];
+ guint16 bytes_count = 0;
+
+ if (!buf)
+ return -1;
+
+ /* we're not checking for end-of-file here, we read as many bytes as
+ we can get (up to dat_len) and return those
+ end-of-file will be detected when we search for the next packet */
+
+ p = buf;
+ while (bytes_count < dat_len) {
+ if (!wtap_read_bytes_or_eof(fh, block, sizeof(block), err, err_info))
+ break;
+
+ if (block[1] == dat_trans_type) {
+ *p++ = block[0];
+ bytes_count++;
+ }
+ else if (IS_TIMESTAMP(block[1])) {
+ process_timestamp(pletoh16(block), time_us);
+ }
+ else if (IS_TRANS_SIZE(block[1])) {
+ /* go back before the size transaction block
+ the next packet should be able to pick up this block */
+ if (-1 == file_seek(fh, -(gint64)sizeof(block), SEEK_CUR, err))
+ return -1;
+ break;
+ }
+ }
+
+ return bytes_count;
+}
+
+
+/* create a DVB-CI pseudo header
+ return its length or -1 for error */
+static gint
+create_pseudo_hdr(guint8 *buf, guint8 dat_trans_type, guint16 dat_len,
+ gchar **err_info)
+{
+ buf[0] = DVB_CI_PSEUDO_HDR_VER;
+
+ if (dat_trans_type==TRANS_CAM_HOST)
+ buf[1] = DVB_CI_PSEUDO_HDR_CAM_TO_HOST;
+ else if (dat_trans_type==TRANS_HOST_CAM)
+ buf[1] = DVB_CI_PSEUDO_HDR_HOST_TO_CAM;
+ else {
+ *err_info = ws_strdup_printf("camins: invalid dat_trans_type %u", dat_trans_type);
+ return -1;
+ }
+
+ buf[2] = (dat_len>>8) & 0xFF;
+ buf[3] = dat_len & 0xFF;
+
+ return DVB_CI_PSEUDO_HDR_LEN;
+}
+
+
+static gboolean
+camins_read_packet(FILE_T fh, wtap_rec *rec, Buffer *buf,
+ guint64 *time_us, int *err, gchar **err_info)
+{
+ guint8 dat_trans_type;
+ guint16 dat_len;
+ guint8 *p;
+ gint offset, bytes_read;
+
+ if (!find_next_pkt_info(
+ fh, &dat_trans_type, &dat_len, time_us, err, err_info))
+ return FALSE;
+ /*
+ * The maximum value of length is 65535, which, even after
+ * DVB_CI_PSEUDO_HDR_LEN is added to it, is less than
+ * WTAP_MAX_PACKET_SIZE_STANDARD will ever be, so we don't need to check
+ * it.
+ */
+
+ ws_buffer_assure_space(buf, DVB_CI_PSEUDO_HDR_LEN+dat_len);
+ p = ws_buffer_start_ptr(buf);
+ offset = create_pseudo_hdr(p, dat_trans_type, dat_len, err_info);
+ if (offset<0) {
+ /* shouldn't happen, all invalid packets must be detected by
+ find_next_pkt_info() */
+ *err = WTAP_ERR_INTERNAL;
+ /* create_pseudo_hdr() set err_info appropriately */
+ return FALSE;
+ }
+
+ bytes_read = read_packet_data(fh, dat_trans_type,
+ &p[offset], dat_len, time_us, err, err_info);
+ /* 0<=bytes_read<=dat_len is very likely a corrupted packet
+ we let the dissector handle this */
+ if (bytes_read < 0)
+ return FALSE;
+ offset += bytes_read;
+
+ rec->rec_type = REC_TYPE_PACKET;
+ rec->block = wtap_block_create(WTAP_BLOCK_PACKET);
+ rec->presence_flags = 0; /* we may or may not have a time stamp */
+ rec->rec_header.packet_header.pkt_encap = WTAP_ENCAP_DVBCI;
+ if (time_us) {
+ rec->presence_flags = WTAP_HAS_TS;
+ rec->ts.secs = (time_t)(*time_us / (1000 * 1000));
+ rec->ts.nsecs = (int)(*time_us % (1000 *1000) * 1000);
+ }
+ rec->rec_header.packet_header.caplen = offset;
+ rec->rec_header.packet_header.len = offset;
+
+ return TRUE;
+}
+
+
+static gboolean
+camins_read(wtap *wth, wtap_rec *rec, Buffer *buf, int *err,
+ gchar **err_info, gint64 *data_offset)
+{
+ *data_offset = file_tell(wth->fh);
+
+ return camins_read_packet(wth->fh, rec, buf, (guint64 *)(wth->priv),
+ err, err_info);
+}
+
+
+static gboolean
+camins_seek_read(wtap *wth, gint64 seek_off, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info)
+{
+ if (-1 == file_seek(wth->random_fh, seek_off, SEEK_SET, err))
+ return FALSE;
+
+ return camins_read_packet(wth->random_fh, rec, buf, NULL, err, err_info);
+}
+
+
+wtap_open_return_val camins_open(wtap *wth, int *err, gchar **err_info _U_)
+{
+ wtap_open_return_val status;
+
+ status = detect_camins_file(wth->fh);
+ if (status != WTAP_OPEN_MINE) {
+ /* A read error or a failed heuristic. */
+ return status;
+ }
+
+ /* rewind the fh so we re-read from the beginning */
+ if (-1 == file_seek(wth->fh, 0, SEEK_SET, err))
+ return WTAP_OPEN_ERROR;
+
+ wth->file_encap = WTAP_ENCAP_DVBCI;
+ wth->snapshot_length = 0;
+ wth->file_tsprec = WTAP_TSPREC_USEC;
+
+ /* wth->priv stores a pointer to the global time counter. we update
+ it as we go through the file sequentially. */
+ wth->priv = g_new0(guint64, 1);
+
+ wth->subtype_read = camins_read;
+ wth->subtype_seek_read = camins_seek_read;
+ wth->file_type_subtype = camins_file_type_subtype;
+
+ *err = 0;
+
+ /*
+ * Add an IDB; we don't know how many interfaces were
+ * involved, so we just say one interface, about which
+ * we only know the link-layer type, snapshot length,
+ * and time stamp resolution.
+ */
+ wtap_add_generated_idb(wth);
+
+ return WTAP_OPEN_MINE;
+}
+
+static const struct supported_block_type camins_blocks_supported[] = {
+ /*
+ * We support packet blocks, with no comments or other options.
+ */
+ { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
+};
+
+static const struct file_type_subtype_info camins_info = {
+ "CAM Inspector file", "camins", "camins", NULL,
+ FALSE, BLOCKS_SUPPORTED(camins_blocks_supported),
+ NULL, NULL, NULL
+};
+
+void register_camins(void)
+{
+ camins_file_type_subtype = wtap_register_file_type_subtype(&camins_info);
+
+ /*
+ * Register name for backwards compatibility with the
+ * wtap_filetypes table in Lua.
+ */
+ wtap_register_backwards_compatibility_lua_name("CAMINS",
+ camins_file_type_subtype);
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * vi: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */
diff --git a/wiretap/camins.h b/wiretap/camins.h
new file mode 100644
index 00000000..090dd30c
--- /dev/null
+++ b/wiretap/camins.h
@@ -0,0 +1,21 @@
+/** @file
+ *
+ * File format support for Rabbit Labs CAM Inspector files
+ * Copyright (c) 2013 by Martin Kaiser <martin@kaiser.cx>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef _CAMINS_H
+#define _CAMINS_H
+
+#include <glib.h>
+#include <wiretap/wtap.h>
+
+wtap_open_return_val camins_open(wtap *wth, int *err, gchar **err_info _U_);
+
+#endif /* _CAMINS_H */
diff --git a/wiretap/candump.c b/wiretap/candump.c
new file mode 100644
index 00000000..15a0ca16
--- /dev/null
+++ b/wiretap/candump.c
@@ -0,0 +1,271 @@
+/* candump.c
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * Support for candump log file format
+ * Copyright (c) 2019 by Maksim Salau <maksim.salau@gmail.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include <config.h>
+#include <wtap-int.h>
+#include <file_wrappers.h>
+#include <wsutil/exported_pdu_tlvs.h>
+#include <string.h>
+#include <inttypes.h>
+#include <errno.h>
+#include "candump.h"
+#include "candump_priv.h"
+
+static gboolean candump_read(wtap *wth, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info,
+ gint64 *data_offset);
+static gboolean candump_seek_read(wtap *wth, gint64 seek_off,
+ wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info);
+
+static int candump_file_type_subtype = -1;
+
+void register_candump(void);
+
+/*
+ * This is written by the candump utility on Linux.
+ */
+
+static gboolean
+candump_write_packet(wtap_rec *rec, Buffer *buf, const msg_t *msg, int *err,
+ gchar **err_info)
+{
+ /* Generate Exported PDU tags for the packet info */
+ ws_buffer_clean(buf);
+ if (msg->is_fd)
+ {
+ wtap_buffer_append_epdu_string(buf, EXP_PDU_TAG_DISSECTOR_NAME, "canfd");
+ }
+ else
+ {
+ wtap_buffer_append_epdu_string(buf, EXP_PDU_TAG_DISSECTOR_NAME, "can-hostendian");
+ }
+ wtap_buffer_append_epdu_end(buf);
+
+ if (msg->is_fd)
+ {
+ canfd_frame_t canfd_frame = {0};
+
+ /*
+ * There's a maximum of CANFD_MAX_DLEN bytes in a CAN-FD frame.
+ */
+ if (msg->data.length > CANFD_MAX_DLEN) {
+ *err = WTAP_ERR_BAD_FILE;
+ if (err_info != NULL) {
+ *err_info = ws_strdup_printf("candump: File has %u-byte CAN FD packet, bigger than maximum of %u",
+ msg->data.length, CANFD_MAX_DLEN);
+ }
+ return FALSE;
+ }
+
+ canfd_frame.can_id = msg->id;
+ canfd_frame.flags = msg->flags;
+ canfd_frame.len = msg->data.length;
+ memcpy(canfd_frame.data, msg->data.data, msg->data.length);
+
+ ws_buffer_append(buf, (guint8 *)&canfd_frame, sizeof(canfd_frame));
+ }
+ else
+ {
+ can_frame_t can_frame = {0};
+
+ /*
+ * There's a maximum of CAN_MAX_DLEN bytes in a CAN frame.
+ */
+ if (msg->data.length > CAN_MAX_DLEN) {
+ *err = WTAP_ERR_BAD_FILE;
+ if (err_info != NULL) {
+ *err_info = ws_strdup_printf("candump: File has %u-byte CAN packet, bigger than maximum of %u",
+ msg->data.length, CAN_MAX_DLEN);
+ }
+ return FALSE;
+ }
+
+ can_frame.can_id = msg->id;
+ can_frame.can_dlc = msg->data.length;
+ memcpy(can_frame.data, msg->data.data, msg->data.length);
+
+ ws_buffer_append(buf, (guint8 *)&can_frame, sizeof(can_frame));
+ }
+
+ rec->rec_type = REC_TYPE_PACKET;
+ rec->block = wtap_block_create(WTAP_BLOCK_PACKET);
+ rec->presence_flags = WTAP_HAS_TS;
+ rec->ts = msg->ts;
+ rec->tsprec = WTAP_TSPREC_USEC;
+
+ rec->rec_header.packet_header.caplen = (guint32)ws_buffer_length(buf);
+ rec->rec_header.packet_header.len = (guint32)ws_buffer_length(buf);
+
+ return TRUE;
+}
+
+static gboolean
+candump_parse(FILE_T fh, msg_t *msg, gint64 *offset, int *err, char **err_info)
+{
+ candump_state_t state = {0};
+ gboolean ok;
+ gint64 seek_off;
+
+#ifdef CANDUMP_DEBUG
+ candump_debug_printf("%s: Trying candump file decoder\n", G_STRFUNC);
+#endif
+
+ state.fh = fh;
+
+ do
+ {
+ if (file_eof(fh))
+ return FALSE;
+
+ seek_off = file_tell(fh);
+#ifdef CANDUMP_DEBUG
+ candump_debug_printf("%s: Starting parser at offset %" PRIi64 "\n", G_STRFUNC, seek_off);
+#endif
+ state.file_bytes_read = 0;
+ ok = run_candump_parser(&state, err, err_info);
+
+ /* Rewind the file to the offset we have finished parsing */
+ if (file_seek(fh, seek_off + state.file_bytes_read, SEEK_SET, err) == -1)
+ {
+ g_free(*err_info);
+ *err = errno;
+ *err_info = g_strdup(g_strerror(errno));
+ return FALSE;
+ }
+ }
+ while (ok && !state.is_msg_valid);
+
+ if (!ok)
+ return FALSE;
+
+#ifdef CANDUMP_DEBUG
+ candump_debug_printf("%s: Success\n", G_STRFUNC);
+#endif
+
+ if (offset)
+ *offset = seek_off;
+
+ if (msg)
+ *msg = state.msg;
+
+ return TRUE;
+}
+
+wtap_open_return_val
+candump_open(wtap *wth, int *err, char **err_info)
+{
+ if (!candump_parse(wth->fh, NULL, NULL, err, err_info))
+ {
+ g_free(*err_info);
+
+ *err = 0;
+ *err_info = NULL;
+
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+#ifdef CANDUMP_DEBUG
+ candump_debug_printf("%s: This is our file\n", G_STRFUNC);
+#endif
+
+ if (file_seek(wth->fh, 0, SEEK_SET, err) == -1)
+ {
+ *err = errno;
+ *err_info = g_strdup(g_strerror(errno));
+
+ return WTAP_OPEN_ERROR;
+ }
+
+ wth->priv = NULL;
+ wth->file_type_subtype = candump_file_type_subtype;
+ wth->file_encap = WTAP_ENCAP_WIRESHARK_UPPER_PDU;
+ wth->file_tsprec = WTAP_TSPREC_USEC;
+ wth->subtype_read = candump_read;
+ wth->subtype_seek_read = candump_seek_read;
+
+ return WTAP_OPEN_MINE;
+}
+
+static gboolean
+candump_read(wtap *wth, wtap_rec *rec, Buffer *buf, int *err, gchar **err_info,
+ gint64 *data_offset)
+{
+ msg_t msg;
+
+#ifdef CANDUMP_DEBUG
+ candump_debug_printf("%s: Try reading at offset %" PRIi64 "\n", G_STRFUNC, file_tell(wth->fh));
+#endif
+
+ if (!candump_parse(wth->fh, &msg, data_offset, err, err_info))
+ return FALSE;
+
+#ifdef CANDUMP_DEBUG
+ candump_debug_printf("%s: Stopped at offset %" PRIi64 "\n", G_STRFUNC, file_tell(wth->fh));
+#endif
+
+ return candump_write_packet(rec, buf, &msg, err, err_info);
+}
+
+static gboolean
+candump_seek_read(wtap *wth , gint64 seek_off, wtap_rec *rec,
+ Buffer *buf, int *err, gchar **err_info)
+{
+ msg_t msg;
+
+#ifdef CANDUMP_DEBUG
+ candump_debug_printf("%s: Read at offset %" PRIi64 "\n", G_STRFUNC, seek_off);
+#endif
+
+ if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
+ {
+ *err = errno;
+ *err_info = g_strdup(g_strerror(errno));
+
+ return FALSE;
+ }
+
+ if (!candump_parse(wth->random_fh, &msg, NULL, err, err_info))
+ return FALSE;
+
+ return candump_write_packet(rec, buf, &msg, err, err_info);
+}
+
+static const struct supported_block_type candump_blocks_supported[] = {
+ /*
+ * We support packet blocks, with no comments or other options.
+ */
+ { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
+};
+
+static const struct file_type_subtype_info candump_info = {
+ "Linux candump file", "candump", NULL, NULL,
+ FALSE, BLOCKS_SUPPORTED(candump_blocks_supported),
+ NULL, NULL, NULL
+};
+
+void register_candump(void)
+{
+ candump_file_type_subtype = wtap_register_file_type_subtype(&candump_info);
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * vi: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */
diff --git a/wiretap/candump.h b/wiretap/candump.h
new file mode 100644
index 00000000..4315b518
--- /dev/null
+++ b/wiretap/candump.h
@@ -0,0 +1,20 @@
+/** @file
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * Support for candump log file format
+ * Copyright (c) 2019 by Maksim Salau <maksim.salau@gmail.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef CANDUMP_H__
+#define CANDUMP_H__
+
+#include <wiretap/wtap.h>
+
+wtap_open_return_val
+candump_open(wtap *wth, int *err, char **err_info);
+
+#endif /* CANDUMP_H__ */
diff --git a/wiretap/candump_parser.lemon b/wiretap/candump_parser.lemon
new file mode 100644
index 00000000..149c3a7d
--- /dev/null
+++ b/wiretap/candump_parser.lemon
@@ -0,0 +1,355 @@
+%include {
+
+/* candump_parser.lemon
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * Support for candump log file format
+ * Copyright (c) 2019 by Maksim Salau <maksim.salau@gmail.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "config.h"
+
+#include <ws_diag_control.h>
+#include <assert.h>
+#include <string.h>
+#include <wiretap/file_wrappers.h>
+#include "candump_priv.h"
+
+extern void *CandumpParserAlloc(void *(*mallocProc)(size_t));
+extern void CandumpParser(void *yyp, int yymajor, token_t yyminor, candump_state_t *state);
+extern void CandumpParserFree(void *p, void (*freeProc)(void*));
+
+#ifdef CANDUMP_DEBUG
+extern void CandumpParserTrace(FILE *TraceFILE, char *zTracePrompt);
+#endif
+
+static void merge_msg_data(msg_data_t *dst, const msg_data_t *a, const msg_data_t *b)
+{
+ dst->length = a->length + b->length;
+ memcpy(&dst->data[0], &a->data[0], a->length);
+ memcpy(&dst->data[a->length], &b->data[0], b->length);
+}
+
+DIAG_OFF_LEMON()
+} /* end of %include */
+
+%code {
+DIAG_ON_LEMON()
+}
+
+%name CandumpParser
+
+%token_prefix TOKEN_
+
+%token_type { token_t }
+
+%token_destructor
+{
+ (void)state;
+ (void)yypParser;
+ (void)yypminor;
+}
+
+%extra_argument { candump_state_t* state }
+
+%syntax_error
+{
+ (void)yypParser;
+ (void)yyminor;
+
+#ifdef CANDUMP_DEBUG
+ const int n = sizeof(yyTokenName) / sizeof(yyTokenName[0]);
+ candump_debug_printf("%s: got token: %s\n", G_STRFUNC, yyTokenName[yymajor]);
+ for (int i = 0; i < n; ++i) {
+ int a = yy_find_shift_action((YYCODETYPE)i, yypParser->yytos->stateno);
+ if (a < YYNSTATE + YYNRULE) {
+ candump_debug_printf("%s: possible token: %s\n", G_STRFUNC, yyTokenName[i]);
+ }
+ }
+#endif
+
+ g_free(state->parse_error);
+ state->parse_error = ws_strdup_printf("Syntax Error");
+#ifdef CANDUMP_DEBUG
+ candump_debug_printf("%s: Syntax Error\n", G_STRFUNC);
+#endif
+}
+
+%parse_failure
+{
+ g_free(state->parse_error);
+ state->parse_error = g_strdup("Parse Error");
+#ifdef CANDUMP_DEBUG
+ candump_debug_printf("%s: Parse Error\n", G_STRFUNC);
+#endif
+}
+
+%type msg { msg_t }
+
+%type timestamp { nstime_t }
+%type id { guint32 }
+%type flags { guint8 }
+
+%type byte { guint8 }
+%type data_max_8 { msg_data_t }
+%type data_max_64 { msg_data_t }
+%type data0 { msg_data_t }
+%type data1 { msg_data_t }
+%type data2 { msg_data_t }
+%type data3 { msg_data_t }
+%type data4 { msg_data_t }
+%type data5 { msg_data_t }
+%type data6 { msg_data_t }
+%type data7 { msg_data_t }
+%type data8 { msg_data_t }
+%type data12 { msg_data_t }
+%type data16 { msg_data_t }
+%type data20 { msg_data_t }
+%type data24 { msg_data_t }
+%type data32 { msg_data_t }
+%type data48 { msg_data_t }
+%type data64 { msg_data_t }
+
+%start_symbol line
+
+line ::= maybe_spaces msg(M) .
+{
+#ifdef CANDUMP_DEBUG
+ candump_debug_printf("%s: read message\n", G_STRFUNC);
+#endif
+
+ state->msg = M;
+ state->is_msg_valid = TRUE;
+}
+
+line ::= maybe_spaces .
+{
+#ifdef CANDUMP_DEBUG
+ candump_debug_printf("%s: read empty line\n", G_STRFUNC);
+#endif
+}
+
+maybe_spaces ::= maybe_spaces SPACE .
+maybe_spaces ::= .
+
+msg(M) ::= timestamp(T) SPACE ifname SPACE id(I) RTR(R) .
+{
+ M.ts = T;
+ M.is_fd = FALSE;
+ M.id = I | CAN_RTR_FLAG;
+ M.data.length = (guint8)R.v0;
+
+ memset(M.data.data, 0, sizeof(M.data.data));
+}
+
+msg(M) ::= timestamp(T) SPACE ifname SPACE id(I) data_max_8(D) .
+{
+ M.ts = T;
+ M.is_fd = FALSE;
+ M.id = I;
+ M.data = D;
+}
+
+msg(M) ::= timestamp(T) SPACE ifname SPACE id(I) flags(F) data_max_64(D) .
+{
+ M.ts = T;
+ M.is_fd = TRUE;
+ M.id = I;
+ M.flags = F;
+ M.data = D;
+}
+
+timestamp(R) ::= TIMESTAMP(A) .
+{
+ R.secs = (time_t)A.v0;
+ R.nsecs = (int)A.v1 * 1000;
+}
+
+ifname ::= ifname any .
+ifname ::= any .
+
+any ::= UNKNOWN .
+any ::= RTR .
+any ::= STD_ID .
+any ::= EXT_ID .
+any ::= FLAGS .
+any ::= TIMESTAMP .
+any ::= BYTE .
+
+id(R) ::= STD_ID(A) .
+{
+ R = (guint32)A.v0;
+}
+
+id(R) ::= EXT_ID(A) .
+{
+ R = (guint32)A.v0;
+
+ if (!(R & CAN_ERR_FLAG))
+ R |= CAN_EFF_FLAG;
+}
+
+flags(R) ::= FLAGS(A) .
+{
+ R = (guint8)A.v0;
+}
+
+data_max_8 ::= data0 .
+data_max_8 ::= data1 .
+data_max_8 ::= data2 .
+data_max_8 ::= data3 .
+data_max_8 ::= data4 .
+data_max_8 ::= data5 .
+data_max_8 ::= data6 .
+data_max_8 ::= data7 .
+data_max_8 ::= data8 .
+
+data_max_64 ::= data_max_8 .
+data_max_64 ::= data12 .
+data_max_64 ::= data16 .
+data_max_64 ::= data20 .
+data_max_64 ::= data24 .
+data_max_64 ::= data32 .
+data_max_64 ::= data48 .
+data_max_64 ::= data64 .
+
+byte(R) ::= BYTE(A) .
+{
+ R = (guint8)A.v0;
+}
+
+data0(R) ::= .
+{
+ R.length = 0;
+}
+
+data1(R) ::= byte(A) .
+{
+ R.length = 1;
+ R.data[0] = A;
+}
+
+data2(R) ::= byte(A) byte(B) .
+{
+ R.length = 2;
+ R.data[0] = A;
+ R.data[1] = B;
+}
+
+data3(R) ::= byte(A) byte(B) byte(C) .
+{
+ R.length = 3;
+ R.data[0] = A;
+ R.data[1] = B;
+ R.data[2] = C;
+}
+
+data4(R) ::= byte(A) byte(B) byte(C) byte(D) .
+{
+ R.length = 4;
+ R.data[0] = A;
+ R.data[1] = B;
+ R.data[2] = C;
+ R.data[3] = D;
+}
+
+data5(R) ::= data4(A) data1(B) . { merge_msg_data(&R, &A, &B); }
+data6(R) ::= data4(A) data2(B) . { merge_msg_data(&R, &A, &B); }
+data7(R) ::= data4(A) data3(B) . { merge_msg_data(&R, &A, &B); }
+data8(R) ::= data4(A) data4(B) . { merge_msg_data(&R, &A, &B); }
+data12(R) ::= data8(A) data4(B) . { merge_msg_data(&R, &A, &B); }
+data16(R) ::= data8(A) data8(B) . { merge_msg_data(&R, &A, &B); }
+data20(R) ::= data16(A) data4(B) . { merge_msg_data(&R, &A, &B); }
+data24(R) ::= data16(A) data8(B) . { merge_msg_data(&R, &A, &B); }
+data32(R) ::= data16(A) data16(B) . { merge_msg_data(&R, &A, &B); }
+data48(R) ::= data32(A) data16(B) . { merge_msg_data(&R, &A, &B); }
+data64(R) ::= data32(A) data32(B) . { merge_msg_data(&R, &A, &B); }
+
+%code {
+
+#include "candump_scanner_lex.h"
+#include "candump_parser.h"
+
+gboolean
+run_candump_parser(candump_state_t *state, int *err, gchar **err_info)
+{
+ int lex_code;
+ yyscan_t scanner;
+ void *parser;
+
+ state->err = 0;
+ state->err_info = NULL;
+ state->parse_error = NULL;
+
+ if (candump_lex_init_extra(state, &scanner) != 0)
+ {
+ *err = errno;
+ *err_info = g_strdup(g_strerror(errno));
+
+ return FALSE;
+ }
+
+ parser = CandumpParserAlloc(g_malloc);
+
+#ifdef CANDUMP_DEBUG
+ CandumpParserTrace(stdout, "parser >> ");
+
+ candump_debug_printf("%s: Starting parsing\n", G_STRFUNC);
+#endif
+
+ do
+ {
+ lex_code = candump_lex(scanner);
+
+#ifdef CANDUMP_DEBUG
+ if (lex_code)
+ candump_debug_printf("%s: Feeding %s '%s'\n",
+ G_STRFUNC, yyTokenName[lex_code],
+ candump_get_text(scanner));
+ else
+ candump_debug_printf("%s: Feeding %s\n",
+ G_STRFUNC, yyTokenName[lex_code]);
+#endif
+
+ CandumpParser(parser, lex_code, state->token, state);
+
+ if (state->err || state->err_info || state->parse_error)
+ break;
+ }
+ while (lex_code);
+
+#ifdef CANDUMP_DEBUG
+ candump_debug_printf("%s: Done (%d)\n", G_STRFUNC, lex_code);
+#endif
+
+ CandumpParserFree(parser, g_free);
+ candump_lex_destroy(scanner);
+
+ if (state->err || state->err_info || state->parse_error)
+ {
+ if (state->err_info)
+ {
+ *err_info = state->err_info;
+ g_free(state->parse_error);
+ }
+ else
+ {
+ *err_info = state->parse_error;
+ }
+
+ if (state->err)
+ *err = state->err;
+ else
+ *err = WTAP_ERR_BAD_FILE;
+
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+}
diff --git a/wiretap/candump_priv.h b/wiretap/candump_priv.h
new file mode 100644
index 00000000..417a44d4
--- /dev/null
+++ b/wiretap/candump_priv.h
@@ -0,0 +1,69 @@
+/** @file
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * Support for candump log file format
+ * Copyright (c) 2019 by Maksim Salau <maksim.salau@gmail.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef CANDUMP_PRIV_H__
+#define CANDUMP_PRIV_H__
+
+#include <gmodule.h>
+#include <wiretap/wtap.h>
+#include <wiretap/socketcan.h>
+#include <epan/dissectors/packet-socketcan.h>
+
+//#define CANDUMP_DEBUG
+
+typedef struct {
+ guint8 length;
+ guint8 data[CANFD_MAX_DLEN];
+} msg_data_t;
+
+typedef struct {
+ nstime_t ts;
+ guint32 id;
+ gboolean is_fd;
+ guint8 flags;
+ msg_data_t data;
+} msg_t;
+
+typedef struct {
+ gint64 v0;
+ gint64 v1;
+} token_t;
+
+typedef struct {
+ wtap *tmp_file;
+ char *tmp_filename;
+} candump_priv_t;
+
+typedef struct {
+ gboolean is_msg_valid;
+ msg_t msg;
+
+ FILE_T fh;
+ guint64 file_bytes_read;
+
+ int err;
+ gchar *err_info;
+ gchar *parse_error;
+
+ token_t token;
+} candump_state_t;
+
+gboolean
+run_candump_parser(candump_state_t *state, int *err, gchar **err_info);
+
+#ifdef CANDUMP_DEBUG
+#include <stdio.h>
+#define candump_debug_printf(...) printf(__VA_ARGS__)
+#else
+#define candump_debug_printf(...) (void)0
+#endif
+
+#endif /* CANDUMP_PRIV_H__ */
diff --git a/wiretap/candump_scanner.l b/wiretap/candump_scanner.l
new file mode 100644
index 00000000..c43c5349
--- /dev/null
+++ b/wiretap/candump_scanner.l
@@ -0,0 +1,144 @@
+/* candump_scanner.l
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * Support for candump log file format
+ * Copyright (c) 2019 by Maksim Salau <maksim.salau@gmail.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+%top {
+/* Include this before everything else, for various large-file definitions */
+#include "config.h"
+#include <wireshark.h>
+}
+
+%option reentrant
+%option noyywrap
+%option noinput
+%option nounput
+%option batch
+%option never-interactive
+%option prefix="candump_"
+%option extra-type="candump_state_t *"
+%option yylineno
+%option nodefault
+
+%option noyy_scan_buffer
+%option noyy_scan_bytes
+%option noyy_scan_string
+
+/*
+ * We have to override the memory allocators so that we don't get
+ * "unused argument" warnings from the yyscanner argument (which
+ * we don't use, as we have a global memory allocator).
+ *
+ * We provide, as macros, our own versions of the routines generated by Flex,
+ * which just call malloc()/realloc()/free() (as the Flex versions do),
+ * discarding the extra argument.
+ */
+%option noyyalloc
+%option noyyrealloc
+%option noyyfree
+
+%{
+
+#include <ws_diag_control.h>
+#include <wiretap/file_wrappers.h>
+#include "candump_parser.h"
+#include "candump_priv.h"
+
+#ifndef HAVE_UNISTD_H
+#define YY_NO_UNISTD_H
+#endif
+
+static int candump_yyinput(void *buf, candump_state_t *state)
+{
+ int c = file_getc(state->fh);
+
+ if (c == EOF)
+ {
+ state->err = file_error(state->fh, &state->err_info);
+ return YY_NULL;
+ }
+
+ *(char *)buf = c;
+
+ return 1;
+}
+
+#define YY_INPUT(buf, result, max_size) \
+ do { (result) = candump_yyinput((buf), yyextra); } while (0)
+
+/* Count bytes read. This is required in order to rewind the file
+ * to the beginning of the next packet, since flex reads more bytes
+ * before executing the action that does yyterminate(). */
+#define YY_USER_ACTION do { yyextra->file_bytes_read += yyleng; } while (0);
+
+/*
+ * Sleazy hack to suppress compiler warnings in yy_fatal_error().
+ */
+#define YY_EXIT_FAILURE ((void)yyscanner, 2)
+
+/*
+ * Macros for the allocators, to discard the extra argument.
+ */
+#define candump_alloc(size, yyscanner) (void *)malloc(size)
+#define candump_realloc(ptr, size, yyscanner) (void *)realloc((char *)(ptr), (size))
+#define candump_free(ptr, yyscanner) free((char *)(ptr))
+
+DIAG_OFF_FLEX()
+
+%}
+
+INT [0-9]
+HEX [0-9A-Fa-f]
+
+%%
+
+[ \t] { return TOKEN_SPACE; };
+[\r\n][ \t\r\n]* { yyterminate(); }
+
+\({INT}+\.{INT}+\) {
+ yyextra->token.v0 = strtoul(yytext + 1, NULL, 10);
+ yyextra->token.v1 = strtoul(strchr(yytext, '.') + 1, NULL, 10);
+ return TOKEN_TIMESTAMP;
+ }
+
+R{INT} {
+ yyextra->token.v0 = strtoul(yytext + 1, NULL, 10);
+ return TOKEN_RTR;
+ }
+
+R {
+ yyextra->token.v0 = 0;
+ return TOKEN_RTR;
+ }
+
+{HEX}{8}# {
+ yyextra->token.v0 = strtoul(yytext, NULL, 16);
+ return TOKEN_EXT_ID;
+ }
+
+{HEX}{3}# {
+ yyextra->token.v0 = strtoul(yytext, NULL, 16);
+ return TOKEN_STD_ID;
+ }
+
+{HEX}{HEX} {
+ yyextra->token.v0 = strtoul(yytext, NULL, 16);
+ return TOKEN_BYTE;
+ }
+
+#{HEX} {
+ yyextra->token.v0 = strtoul(yytext + 1, NULL, 16);
+ return TOKEN_FLAGS;
+ }
+
+. { return TOKEN_UNKNOWN; }
+
+%%
+
+DIAG_ON_FLEX()
diff --git a/wiretap/capsa.c b/wiretap/capsa.c
new file mode 100644
index 00000000..6083e38f
--- /dev/null
+++ b/wiretap/capsa.c
@@ -0,0 +1,490 @@
+/* capsa.c
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "config.h"
+#include <string.h>
+#include "wtap-int.h"
+#include "file_wrappers.h"
+#include "capsa.h"
+#include <wsutil/ws_assert.h>
+
+/*
+ * A file begins with a header containing:
+ *
+ * a 4-byte magic number, with 'c', 'p', 's', 'e';
+ *
+ * either a 2-byte little-endian "format indicator" (version number?),
+ * or a 1-byte major version number followed by a 1-byte minor version
+ * number, or a 1-byte "format indicator" followed by something else
+ * that's always been 0;
+ *
+ * a 2-byte 0xe8 0x03 (1000 - a data rate? megabits/second?)
+ *
+ * 4 bytes of 0x01 0x00 0x01 0x00;
+ *
+ * either a 4-byte little-endian file size followed by 0x00 0x00 0x00 0x00
+ * or an 8-byte little-endian file size;
+ *
+ * a 4-byte little-endian packet count (in dns_error_of_udp, it exceeds?)
+ *
+ * a 4-byte little-endian number?
+ *
+ * hex 2c 01 c8 00 00 00 da 36 00 00 00 00 00 00;
+ *
+ * the same 4-byte little-endian number as above (yes, misaligned);
+ *
+ * 0x01 or 0x03;
+ *
+ * a bunch of 0s, up to an offset of 0x36d6;
+ *
+ * more stuff.
+ *
+ * Following that is a sequence of { record offset block, up to 200 records }
+ * pairs.
+ *
+ * A record offset block has 1 byte with the value 0xfe, a sequence of
+ * up to 200 4-byte little-endian record offsets, and 4 or more bytes
+ * of unknown data, making the block 805 bytes long.
+ *
+ * The record offsets are offsets, from the beginning of the record offset
+ * block (i.e., from the 0xfe byte), of the records following the block.
+ */
+
+/* Magic number in Capsa files. */
+static const char capsa_magic[] = {
+ 'c', 'p', 's', 'e'
+};
+
+/*
+ * Before each group of 200 or fewer records there's a block of frame
+ * offsets, giving the offsets, from the beginning of that block minus
+ * one(1), of the next N records.
+ */
+#define N_RECORDS_PER_GROUP 200
+
+/* Capsa (format indicator 1) record header. */
+struct capsarec_hdr {
+ guint32 unknown1; /* low-order 32 bits of a number? */
+ guint32 unknown2; /* 0x00 0x00 0x00 0x00 */
+ guint32 timestamplo; /* low-order 32 bits of the time stamp, in microseconds since January 1, 1970, 00:00:00 UTC */
+ guint32 timestamphi; /* high-order 32 bits of the time stamp, in microseconds since January 1, 1970, 00:00:00 UTC */
+ guint16 rec_len; /* length of record */
+ guint16 incl_len; /* number of octets captured in file */
+ guint16 orig_len; /* actual length of packet */
+ guint16 unknown5; /* 0x00 0x00 */
+ guint8 count1; /* count1*4 bytes after unknown8 */
+ guint8 count2; /* count2*4 bytes after that */
+ guint16 unknown7; /* 0x01 0x10 */
+ guint32 unknown8; /* 0x00 0x00 0x00 0x00 or random numbers */
+};
+
+/* Packet Builder (format indicator 2) record header. */
+struct pbrec_hdr {
+ guint16 rec_len; /* length of record */
+ guint16 incl_len; /* number of octets captured in file */
+ guint16 orig_len; /* actual length of packet */
+ guint16 unknown1;
+ guint16 unknown2;
+ guint16 unknown3;
+ guint32 unknown4;
+ guint32 timestamplo; /* low-order 32 bits of the time stamp, in microseconds since January 1, 1970, 00:00:00 UTC */
+ guint32 timestamphi; /* high-order 32 bits of the time stamp, in microseconds since January 1, 1970, 00:00:00 UTC */
+ guint32 unknown5;
+ guint32 unknown6;
+};
+
+typedef struct {
+ guint16 format_indicator;
+ guint32 number_of_frames;
+ guint32 frame_count;
+ gint64 base_offset;
+ guint32 record_offsets[N_RECORDS_PER_GROUP];
+} capsa_t;
+
+static gboolean capsa_read(wtap *wth, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info, gint64 *data_offset);
+static gboolean capsa_seek_read(wtap *wth, gint64 seek_off,
+ wtap_rec *rec, Buffer *buf, int *err, gchar **err_info);
+static int capsa_read_packet(wtap *wth, FILE_T fh, wtap_rec *rec,
+ Buffer *buf, int *err, gchar **err_info);
+
+static int capsa_file_type_subtype = -1;
+static int packet_builder_file_type_subtype = -1;
+
+void register_capsa(void);
+
+wtap_open_return_val capsa_open(wtap *wth, int *err, gchar **err_info)
+{
+ char magic[sizeof capsa_magic];
+ guint16 format_indicator;
+ int file_type_subtype;
+ guint32 number_of_frames;
+ capsa_t *capsa;
+
+ /* Read in the string that should be at the start of a Capsa file */
+ if (!wtap_read_bytes(wth->fh, magic, sizeof magic, err, err_info)) {
+ if (*err != WTAP_ERR_SHORT_READ)
+ return WTAP_OPEN_ERROR;
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ if (memcmp(magic, capsa_magic, sizeof capsa_magic) != 0) {
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ /* Read the mysterious "format indicator" */
+ if (!wtap_read_bytes(wth->fh, &format_indicator, sizeof format_indicator,
+ err, err_info))
+ return WTAP_OPEN_ERROR;
+ format_indicator = GUINT16_FROM_LE(format_indicator);
+
+ /*
+ * Make sure it's a format we support.
+ */
+ switch (format_indicator) {
+
+ case 1: /* Capsa */
+ file_type_subtype = capsa_file_type_subtype;
+ break;
+
+ case 2: /* Packet Builder */
+ file_type_subtype = packet_builder_file_type_subtype;
+ break;
+
+ default:
+ *err = WTAP_ERR_UNSUPPORTED;
+ *err_info = ws_strdup_printf("capsa: format indicator %u unsupported",
+ format_indicator);
+ return WTAP_OPEN_ERROR;
+ }
+
+ /*
+ * Link speed, in megabytes/second?
+ */
+ if (!wtap_read_bytes(wth->fh, NULL, 2, err, err_info))
+ return WTAP_OPEN_ERROR;
+
+ /*
+ * Flags of some sort? Four 1-byte numbers, two of which are 1
+ * and two of which are zero? Two 2-byte numbers or flag fields,
+ * both of which are 1?
+ */
+ if (!wtap_read_bytes(wth->fh, NULL, 4, err, err_info))
+ return WTAP_OPEN_ERROR;
+
+ /*
+ * File size, in bytes.
+ */
+ if (!wtap_read_bytes(wth->fh, NULL, 4, err, err_info))
+ return WTAP_OPEN_ERROR;
+
+ /*
+ * Zeroes? Or upper 4 bytes of file size?
+ */
+ if (!wtap_read_bytes(wth->fh, NULL, 4, err, err_info))
+ return WTAP_OPEN_ERROR;
+
+ /*
+ * Count of packets.
+ */
+ if (!wtap_read_bytes(wth->fh, &number_of_frames, sizeof number_of_frames,
+ err, err_info))
+ return WTAP_OPEN_ERROR;
+ number_of_frames = GUINT32_FROM_LE(number_of_frames);
+
+ /*
+ * Skip past what we think is file header.
+ */
+ if (!file_seek(wth->fh, 0x44ef, SEEK_SET, err))
+ return WTAP_OPEN_ERROR;
+
+ wth->file_type_subtype = file_type_subtype;
+ capsa = g_new(capsa_t, 1);
+ capsa->format_indicator = format_indicator;
+ capsa->number_of_frames = number_of_frames;
+ capsa->frame_count = 0;
+ wth->priv = (void *)capsa;
+ wth->subtype_read = capsa_read;
+ wth->subtype_seek_read = capsa_seek_read;
+ /*
+ * XXX - we've never seen a Wi-Fi Capsa capture, so we don't
+ * yet know how to handle them.
+ */
+ wth->file_encap = WTAP_ENCAP_ETHERNET;
+ wth->snapshot_length = 0; /* not available in header */
+ wth->file_tsprec = WTAP_TSPREC_USEC;
+
+ /*
+ * Add an IDB; we don't know how many interfaces were
+ * involved, so we just say one interface, about which
+ * we only know the link-layer type, snapshot length,
+ * and time stamp resolution.
+ */
+ wtap_add_generated_idb(wth);
+
+ return WTAP_OPEN_MINE;
+}
+
+/* Read the next packet */
+static gboolean capsa_read(wtap *wth, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info, gint64 *data_offset)
+{
+ capsa_t *capsa = (capsa_t *)wth->priv;
+ guint32 frame_within_block;
+ int padbytes;
+
+ if (capsa->frame_count == capsa->number_of_frames) {
+ /*
+ * No more frames left. Return an EOF.
+ */
+ *err = 0;
+ return FALSE;
+ }
+ frame_within_block = capsa->frame_count % N_RECORDS_PER_GROUP;
+ if (frame_within_block == 0) {
+ /*
+ * Here's a record offset block.
+ * Get the offset of the block, and then skip the
+ * first byte.
+ */
+ capsa->base_offset = file_tell(wth->fh);
+ if (!wtap_read_bytes(wth->fh, NULL, 1, err, err_info))
+ return FALSE;
+
+ /*
+ * Now read the record offsets.
+ */
+ if (!wtap_read_bytes(wth->fh, &capsa->record_offsets,
+ sizeof capsa->record_offsets, err, err_info))
+ return FALSE;
+
+ /*
+ * And finish processing all 805 bytes by skipping
+ * the last 4 bytes.
+ */
+ if (!wtap_read_bytes(wth->fh, NULL, 4, err, err_info))
+ return FALSE;
+ }
+
+ *data_offset = capsa->base_offset +
+ GUINT32_FROM_LE(capsa->record_offsets[frame_within_block]);
+ if (!file_seek(wth->fh, *data_offset, SEEK_SET, err))
+ return FALSE;
+
+ padbytes = capsa_read_packet(wth, wth->fh, rec, buf, err, err_info);
+ if (padbytes == -1)
+ return FALSE;
+
+ /*
+ * Skip over the padding, if any.
+ */
+ if (padbytes != 0) {
+ if (!wtap_read_bytes(wth->fh, NULL, padbytes, err, err_info))
+ return FALSE;
+ }
+
+ capsa->frame_count++;
+
+ return TRUE;
+}
+
+static gboolean
+capsa_seek_read(wtap *wth, gint64 seek_off,
+ wtap_rec *rec, Buffer *buf, int *err, gchar **err_info)
+{
+ if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
+ return FALSE;
+
+ if (capsa_read_packet(wth, wth->random_fh, rec, buf, err, err_info) == -1) {
+ if (*err == 0)
+ *err = WTAP_ERR_SHORT_READ;
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static int
+capsa_read_packet(wtap *wth, FILE_T fh, wtap_rec *rec,
+ Buffer *buf, int *err, gchar **err_info)
+{
+ capsa_t *capsa = (capsa_t *)wth->priv;
+ struct capsarec_hdr capsarec_hdr;
+ struct pbrec_hdr pbrec_hdr;
+ guint32 rec_size;
+ guint32 packet_size;
+ guint32 orig_size;
+ guint32 header_size;
+ guint64 timestamp;
+
+ /* Read record header. */
+ switch (capsa->format_indicator) {
+
+ case 1:
+ if (!wtap_read_bytes_or_eof(fh, &capsarec_hdr,
+ sizeof capsarec_hdr, err, err_info))
+ return -1;
+ rec_size = GUINT16_FROM_LE(capsarec_hdr.rec_len);
+ orig_size = GUINT16_FROM_LE(capsarec_hdr.orig_len);
+ packet_size = GUINT16_FROM_LE(capsarec_hdr.incl_len);
+ header_size = sizeof capsarec_hdr;
+ timestamp = (((guint64)GUINT32_FROM_LE(capsarec_hdr.timestamphi))<<32) + GUINT32_FROM_LE(capsarec_hdr.timestamplo);
+
+ /*
+ * OK, the rest of this is variable-length.
+ * We skip: (count1+count2)*4 bytes.
+ * XXX - what is that? Measured statistics?
+ * Calculated statistics?
+ */
+ if (!wtap_read_bytes(fh, NULL,
+ (capsarec_hdr.count1 + capsarec_hdr.count2)*4,
+ err, err_info))
+ return -1;
+ header_size += (capsarec_hdr.count1 + capsarec_hdr.count2)*4;
+ break;
+
+ case 2:
+ if (!wtap_read_bytes_or_eof(fh, &pbrec_hdr,
+ sizeof pbrec_hdr, err, err_info))
+ return -1;
+ rec_size = GUINT16_FROM_LE(pbrec_hdr.rec_len);
+ orig_size = GUINT16_FROM_LE(pbrec_hdr.orig_len);
+ packet_size = GUINT16_FROM_LE(pbrec_hdr.incl_len);
+ header_size = sizeof pbrec_hdr;
+ timestamp = (((guint64)GUINT32_FROM_LE(pbrec_hdr.timestamphi))<<32) + GUINT32_FROM_LE(pbrec_hdr.timestamplo);
+ /*
+ * XXX - from the results of some conversions between
+ * Capsa format and pcap by Colasoft Packet Builder,
+ * I do not trust its conversion of time stamps (at
+ * least one of Colasoft's sample files, when
+ * converted to pcap format, has, as its time stamps,
+ * time stamps on the day after the conversion was
+ * done, which seems like more than just coincidence).
+ */
+ break;
+
+ default:
+ ws_assert_not_reached();
+ *err = WTAP_ERR_INTERNAL;
+ *err_info = ws_strdup_printf("capsa: format indicator is %u", capsa->format_indicator);
+ return -1;
+ }
+ if (orig_size > WTAP_MAX_PACKET_SIZE_STANDARD) {
+ /*
+ * Probably a corrupt capture file; don't blow up trying
+ * to allocate space for an immensely-large packet.
+ */
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("capsa: File has %u-byte original length, bigger than maximum of %u",
+ orig_size, WTAP_MAX_PACKET_SIZE_STANDARD);
+ return -1;
+ }
+ if (packet_size > WTAP_MAX_PACKET_SIZE_STANDARD) {
+ /*
+ * Probably a corrupt capture file; don't blow up trying
+ * to allocate space for an immensely-large packet.
+ */
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("capsa: File has %u-byte packet, bigger than maximum of %u",
+ packet_size, WTAP_MAX_PACKET_SIZE_STANDARD);
+ return -1;
+ }
+ if (header_size + packet_size > rec_size) {
+ /*
+ * Probably a corrupt capture file.
+ */
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("capsa: File has %u-byte packet with %u-byte record header, bigger than record size %u",
+ packet_size, header_size, rec_size);
+ return -1;
+ }
+
+ /*
+ * The "on the wire" record size always includes the CRC.
+ * If it's greater than the "captured" size by 4, then
+ * we subtract 4 from it, to reflect the way the "on the wire"
+ * record size works for other file formats.
+ */
+ if (orig_size == packet_size + 4)
+ orig_size = packet_size;
+
+ /*
+ * We assume there's no FCS in this frame.
+ * XXX - is there ever one?
+ */
+ rec->rec_header.packet_header.pseudo_header.eth.fcs_len = 0;
+
+ rec->rec_type = REC_TYPE_PACKET;
+ rec->block = wtap_block_create(WTAP_BLOCK_PACKET);
+ rec->rec_header.packet_header.caplen = packet_size;
+ rec->rec_header.packet_header.len = orig_size;
+ rec->presence_flags = WTAP_HAS_CAP_LEN|WTAP_HAS_TS;
+ rec->ts.secs = (time_t)(timestamp / 1000000);
+ rec->ts.nsecs = ((int)(timestamp % 1000000))*1000;
+
+ /*
+ * Read the packet data.
+ */
+ if (!wtap_read_packet_bytes(fh, buf, packet_size, err, err_info))
+ return -1; /* failed */
+
+ return rec_size - (header_size + packet_size);
+}
+
+static const struct supported_block_type capsa_blocks_supported[] = {
+ /*
+ * We support packet blocks, with no comments or other options.
+ */
+ { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
+};
+
+static const struct file_type_subtype_info capsa_info = {
+ "Colasoft Capsa format", "capsa", "cscpkt", NULL,
+ FALSE, BLOCKS_SUPPORTED(capsa_blocks_supported),
+ NULL, NULL, NULL
+};
+
+static const struct supported_block_type packet_builder_blocks_supported[] = {
+ /*
+ * We support packet blocks, with no comments or other options.
+ */
+ { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
+};
+
+static const struct file_type_subtype_info packet_builder_info = {
+ "Colasoft Packet Builder format", "colasoft-pb", "cscpkt", NULL,
+ FALSE, BLOCKS_SUPPORTED(packet_builder_blocks_supported),
+ NULL, NULL, NULL
+};
+
+void register_capsa(void)
+{
+ capsa_file_type_subtype = wtap_register_file_type_subtype(&capsa_info);
+ packet_builder_file_type_subtype = wtap_register_file_type_subtype(&packet_builder_info);
+
+ /*
+ * Register names for backwards compatibility with the
+ * wtap_filetypes table in Lua.
+ */
+ wtap_register_backwards_compatibility_lua_name("COLASOFT_CAPSA",
+ capsa_file_type_subtype);
+ wtap_register_backwards_compatibility_lua_name("COLASOFT_PACKET_BUILDER",
+ packet_builder_file_type_subtype);
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/wiretap/capsa.h b/wiretap/capsa.h
new file mode 100644
index 00000000..2ec42577
--- /dev/null
+++ b/wiretap/capsa.h
@@ -0,0 +1,18 @@
+/** @file
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __W_CAPSA_H__
+#define __W_CAPSA_H__
+
+#include <glib.h>
+#include "wtap.h"
+#include "ws_symbol_export.h"
+
+wtap_open_return_val capsa_open(wtap *wth, int *err, gchar **err_info);
+
+#endif
diff --git a/wiretap/catapult_dct2000.c b/wiretap/catapult_dct2000.c
new file mode 100644
index 00000000..c7f1dde3
--- /dev/null
+++ b/wiretap/catapult_dct2000.c
@@ -0,0 +1,1701 @@
+/* catapult_dct2000.c
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "config.h"
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <wsutil/strtoi.h>
+
+#include "wtap-int.h"
+#include "file_wrappers.h"
+
+#include "catapult_dct2000.h"
+
+#define MAX_FIRST_LINE_LENGTH 150
+#define MAX_TIMESTAMP_LINE_LENGTH 50
+#define MAX_LINE_LENGTH 131072
+#define MAX_SECONDS_CHARS 16
+#define MAX_TIMESTAMP_LEN (MAX_SECONDS_CHARS+5)
+#define MAX_SUBSECOND_DECIMALS 4
+#define MAX_CONTEXT_NAME 64
+#define MAX_PROTOCOL_NAME 64
+#define MAX_PORT_DIGITS 2
+#define MAX_VARIANT_DIGITS 16
+#define MAX_OUTHDR_NAME 256
+#define AAL_HEADER_CHARS 12
+
+/* 's' or 'r' of a packet as read from .out file */
+typedef enum packet_direction_t
+{
+ sent,
+ received
+} packet_direction_t;
+
+
+/***********************************************************************/
+/* For each line, store (in case we need to dump): */
+/* - String before time field */
+/* - Whether or not " l " appears after timestamp */
+typedef struct
+{
+ gchar *before_time;
+ gboolean has_l;
+} line_prefix_info_t;
+
+
+/*******************************************************************/
+/* Information stored external to a file (wtap) needed for reading and dumping */
+typedef struct dct2000_file_externals
+{
+ /* Remember the time at the start of capture */
+ time_t start_secs;
+ guint32 start_usecs;
+
+ /*
+ * The following information is needed only for dumping.
+ *
+ * XXX - Wiretap is not *supposed* to require that a packet being
+ * dumped come from a file of the same type that you currently have
+ * open; this should be fixed.
+ */
+
+ /* Buffer to hold first line, including magic and format number */
+ gchar firstline[MAX_FIRST_LINE_LENGTH];
+ gint firstline_length;
+
+ /* Buffer to hold second line with formatted file creation data/time */
+ gchar secondline[MAX_TIMESTAMP_LINE_LENGTH];
+ gint secondline_length;
+
+ /* Hash table to store text prefix data part of displayed packets.
+ Records (file offset -> line_prefix_info_t)
+ */
+ GHashTable *packet_prefix_table;
+} dct2000_file_externals_t;
+
+/* 'Magic number' at start of Catapult DCT2000 .out files. */
+static const gchar catapult_dct2000_magic[] = "Session Transcript";
+
+/************************************************************/
+/* Functions called from wiretap core */
+static gboolean catapult_dct2000_read(wtap *wth, wtap_rec *rec,
+ Buffer *buf, int *err, gchar **err_info,
+ gint64 *data_offset);
+static gboolean catapult_dct2000_seek_read(wtap *wth, gint64 seek_off,
+ wtap_rec *rec,
+ Buffer *buf, int *err,
+ gchar **err_info);
+static void catapult_dct2000_close(wtap *wth);
+
+static gboolean catapult_dct2000_dump(wtap_dumper *wdh, const wtap_rec *rec,
+ const guint8 *pd, int *err, gchar **err_info);
+
+
+/************************************************************/
+/* Private helper functions */
+static gboolean read_new_line(FILE_T fh, gint *length,
+ gchar *buf, size_t bufsize, int *err,
+ gchar **err_info);
+static gboolean parse_line(char *linebuff, gint line_length,
+ gint *seconds, gint *useconds,
+ long *before_time_offset, long *after_time_offset,
+ long *data_offset,
+ gint *data_chars,
+ packet_direction_t *direction,
+ int *encap, int *is_comment, int *is_sprint,
+ gchar *aal_header_chars,
+ gchar *context_name, guint8 *context_portp,
+ gchar *protocol_name, gchar *variant_name,
+ gchar *outhdr_name);
+static gboolean process_parsed_line(wtap *wth,
+ dct2000_file_externals_t *file_externals,
+ wtap_rec *rec,
+ Buffer *buf, gint64 file_offset,
+ char *linebuff, long dollar_offset,
+ int seconds, int useconds,
+ gchar *timestamp_string,
+ packet_direction_t direction, int encap,
+ gchar *context_name, guint8 context_port,
+ gchar *protocol_name, gchar *variant_name,
+ gchar *outhdr_name, gchar *aal_header_chars,
+ gboolean is_comment, int data_chars,
+ int *err, gchar **err_info);
+static guint8 hex_from_char(gchar c);
+static void prepare_hex_byte_from_chars_table(void);
+static guint8 hex_byte_from_chars(gchar *c);
+static gchar char_from_hex(guint8 hex);
+
+static void set_aal_info(union wtap_pseudo_header *pseudo_header,
+ packet_direction_t direction,
+ gchar *aal_header_chars);
+static void set_isdn_info(union wtap_pseudo_header *pseudo_header,
+ packet_direction_t direction);
+static void set_ppp_info(union wtap_pseudo_header *pseudo_header,
+ packet_direction_t direction);
+
+static gint packet_offset_equal(gconstpointer v, gconstpointer v2);
+static guint packet_offset_hash_func(gconstpointer v);
+
+static gboolean get_file_time_stamp(gchar *linebuff, time_t *secs, guint32 *usecs);
+static gboolean free_line_prefix_info(gpointer key, gpointer value, gpointer user_data);
+
+static int dct2000_file_type_subtype = -1;
+
+void register_dct2000(void);
+
+
+/********************************************/
+/* Open file (for reading) */
+/********************************************/
+wtap_open_return_val
+catapult_dct2000_open(wtap *wth, int *err, gchar **err_info)
+{
+ time_t timestamp;
+ guint32 usecs;
+ gint firstline_length = 0;
+ dct2000_file_externals_t *file_externals;
+ static gchar linebuff[MAX_LINE_LENGTH];
+ static gboolean hex_byte_table_values_set = FALSE;
+
+ /* Clear errno before reading from the file */
+ errno = 0;
+
+
+ /********************************************************************/
+ /* First line needs to contain at least as many characters as magic */
+
+ if (!read_new_line(wth->fh, &firstline_length, linebuff,
+ sizeof linebuff, err, err_info)) {
+ if (*err != 0 && *err != WTAP_ERR_SHORT_READ) {
+ return WTAP_OPEN_ERROR;
+ }
+ else {
+ return WTAP_OPEN_NOT_MINE;
+ }
+ }
+ if (((size_t)firstline_length < strlen(catapult_dct2000_magic)) ||
+ firstline_length >= MAX_FIRST_LINE_LENGTH) {
+
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ /* This file is not for us if it doesn't match our signature */
+ if (memcmp(catapult_dct2000_magic, linebuff, strlen(catapult_dct2000_magic)) != 0) {
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ /* Make sure table is ready for use */
+ if (!hex_byte_table_values_set) {
+ prepare_hex_byte_from_chars_table();
+ hex_byte_table_values_set = TRUE;
+ }
+
+ /*********************************************************************/
+ /* Need entry in file_externals table */
+
+ /* Allocate a new file_externals structure for this file */
+ file_externals = g_new0(dct2000_file_externals_t, 1);
+
+ /* Copy this first line into buffer so could write out later */
+ (void) g_strlcpy(file_externals->firstline, linebuff, firstline_length+1);
+ file_externals->firstline_length = firstline_length;
+
+
+ /***********************************************************/
+ /* Second line contains file timestamp */
+ /* Store this offset in file_externals */
+
+ if (!read_new_line(wth->fh, &(file_externals->secondline_length),
+ linebuff, sizeof linebuff, err, err_info)) {
+ g_free(file_externals);
+ if (*err != 0 && *err != WTAP_ERR_SHORT_READ) {
+ return WTAP_OPEN_ERROR;
+ }
+ else {
+ return WTAP_OPEN_NOT_MINE;
+ }
+ }
+ if ((file_externals->secondline_length >= MAX_TIMESTAMP_LINE_LENGTH) ||
+ (!get_file_time_stamp(linebuff, &timestamp, &usecs))) {
+
+ /* Give up if file time line wasn't valid */
+ g_free(file_externals);
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ /* Fill in timestamp */
+ file_externals->start_secs = timestamp;
+ file_externals->start_usecs = usecs;
+
+ /* Copy this second line into buffer so could write out later */
+ (void) g_strlcpy(file_externals->secondline, linebuff, file_externals->secondline_length+1);
+
+
+ /************************************************************/
+ /* File is for us. Fill in details so packets can be read */
+
+ /* Set our file type */
+ wth->file_type_subtype = dct2000_file_type_subtype;
+
+ /* Use our own encapsulation to send all packets to our stub dissector */
+ wth->file_encap = WTAP_ENCAP_CATAPULT_DCT2000;
+
+ /* Callbacks for reading operations */
+ wth->subtype_read = catapult_dct2000_read;
+ wth->subtype_seek_read = catapult_dct2000_seek_read;
+ wth->subtype_close = catapult_dct2000_close;
+
+ /* Choose microseconds (have 4 decimal places...) */
+ wth->file_tsprec = WTAP_TSPREC_USEC;
+
+
+ /***************************************************************/
+ /* Initialise packet_prefix_table (index is offset into file) */
+ file_externals->packet_prefix_table =
+ g_hash_table_new(packet_offset_hash_func, packet_offset_equal);
+
+ /* Set this wtap to point to the file_externals */
+ wth->priv = (void*)file_externals;
+
+ *err = errno;
+
+ /*
+ * Add an IDB; we don't know how many interfaces were
+ * involved, so we just say one interface, about which
+ * we only know the link-layer type, snapshot length,
+ * and time stamp resolution.
+ */
+ wtap_add_generated_idb(wth);
+
+ return WTAP_OPEN_MINE;
+}
+
+/* Ugly, but much faster than using snprintf! */
+static void write_timestamp_string(char *timestamp_string, int secs, int tenthousandths)
+{
+ int idx = 0;
+
+ /* Secs */
+ if (secs < 10) {
+ timestamp_string[idx++] = ((secs % 10)) + '0';
+ }
+ else if (secs < 100) {
+ timestamp_string[idx++] = ( secs / 10) + '0';
+ timestamp_string[idx++] = ((secs % 10)) + '0';
+ }
+ else if (secs < 1000) {
+ timestamp_string[idx++] = ((secs) / 100) + '0';
+ timestamp_string[idx++] = ((secs % 100)) / 10 + '0';
+ timestamp_string[idx++] = ((secs % 10)) + '0';
+ }
+ else if (secs < 10000) {
+ timestamp_string[idx++] = ((secs) / 1000) + '0';
+ timestamp_string[idx++] = ((secs % 1000)) / 100 + '0';
+ timestamp_string[idx++] = ((secs % 100)) / 10 + '0';
+ timestamp_string[idx++] = ((secs % 10)) + '0';
+ }
+ else if (secs < 100000) {
+ timestamp_string[idx++] = ((secs) / 10000) + '0';
+ timestamp_string[idx++] = ((secs % 10000)) / 1000 + '0';
+ timestamp_string[idx++] = ((secs % 1000)) / 100 + '0';
+ timestamp_string[idx++] = ((secs % 100)) / 10 + '0';
+ timestamp_string[idx++] = ((secs % 10)) + '0';
+ }
+ else if (secs < 1000000) {
+ timestamp_string[idx++] = ((secs) / 100000) + '0';
+ timestamp_string[idx++] = ((secs % 100000)) / 10000 + '0';
+ timestamp_string[idx++] = ((secs % 10000)) / 1000 + '0';
+ timestamp_string[idx++] = ((secs % 1000)) / 100 + '0';
+ timestamp_string[idx++] = ((secs % 100)) / 10 + '0';
+ timestamp_string[idx++] = ((secs % 10)) + '0';
+ }
+ else {
+ snprintf(timestamp_string, MAX_TIMESTAMP_LEN, "%d.%04d", secs, tenthousandths);
+ return;
+ }
+
+ timestamp_string[idx++] = '.';
+ timestamp_string[idx++] = ( tenthousandths / 1000) + '0';
+ timestamp_string[idx++] = ((tenthousandths % 1000) / 100) + '0';
+ timestamp_string[idx++] = ((tenthousandths % 100) / 10) + '0';
+ timestamp_string[idx++] = ((tenthousandths % 10)) + '0';
+ timestamp_string[idx] = '\0';
+}
+
+/**************************************************/
+/* Read packet function. */
+/* Look for and read the next usable packet */
+/* - return TRUE and details if found */
+/**************************************************/
+static gboolean
+catapult_dct2000_read(wtap *wth, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info, gint64 *data_offset)
+{
+ long dollar_offset, before_time_offset, after_time_offset;
+ packet_direction_t direction;
+ int encap;
+
+ /* Get wtap external structure for this wtap */
+ dct2000_file_externals_t *file_externals =
+ (dct2000_file_externals_t*)wth->priv;
+
+ /* Search for a line containing a usable packet */
+ while (1) {
+ int line_length, seconds, useconds, data_chars;
+ int is_comment = FALSE;
+ int is_sprint = FALSE;
+ gint64 this_offset;
+ static gchar linebuff[MAX_LINE_LENGTH+1];
+ gchar aal_header_chars[AAL_HEADER_CHARS];
+ gchar context_name[MAX_CONTEXT_NAME];
+ guint8 context_port = 0;
+ gchar protocol_name[MAX_PROTOCOL_NAME+1];
+ gchar variant_name[MAX_VARIANT_DIGITS+1];
+ gchar outhdr_name[MAX_OUTHDR_NAME+1];
+
+ /* Get starting offset of the line we're about to read */
+ this_offset = file_tell(wth->fh);
+
+ /* Read a new line from file into linebuff */
+ if (!read_new_line(wth->fh, &line_length, linebuff,
+ sizeof linebuff, err, err_info)) {
+ if (*err != 0) {
+ return FALSE; /* error */
+ }
+ /* No more lines can be read, so quit. */
+ break;
+ }
+
+ /* Try to parse the line as a frame record */
+ if (parse_line(linebuff, line_length, &seconds, &useconds,
+ &before_time_offset, &after_time_offset,
+ &dollar_offset,
+ &data_chars, &direction, &encap, &is_comment, &is_sprint,
+ aal_header_chars,
+ context_name, &context_port,
+ protocol_name, variant_name, outhdr_name)) {
+ line_prefix_info_t *line_prefix_info;
+ gint64 *pkey = NULL;
+ char timestamp_string[MAX_TIMESTAMP_LEN+1];
+ write_timestamp_string(timestamp_string, seconds, useconds/100);
+
+ /* Set data_offset to the beginning of the line we're returning.
+ This will be the seek_off parameter when this frame is re-read.
+ */
+ *data_offset = this_offset;
+
+ if (!process_parsed_line(wth, file_externals,
+ rec, buf, this_offset,
+ linebuff, dollar_offset,
+ seconds, useconds,
+ timestamp_string,
+ direction, encap,
+ context_name, context_port,
+ protocol_name, variant_name,
+ outhdr_name, aal_header_chars,
+ is_comment, data_chars,
+ err, err_info))
+ return FALSE;
+
+ /* Store the packet prefix in the hash table */
+ line_prefix_info = g_new(line_prefix_info_t,1);
+
+ /* Create and use buffer for contents before time */
+ line_prefix_info->before_time = (gchar *)g_malloc(before_time_offset+1);
+ memcpy(line_prefix_info->before_time, linebuff, before_time_offset);
+ line_prefix_info->before_time[before_time_offset] = '\0';
+
+ /* There is usually a ' l ' between the timestamp and the data. Set flag to record this. */
+ line_prefix_info->has_l = ((size_t)(dollar_offset - after_time_offset -1) == strlen(" l ")) &&
+ (strncmp(linebuff+after_time_offset, " l ", 3) == 0);
+
+ /* Add packet entry into table */
+ pkey = (gint64 *)g_malloc(sizeof(*pkey));
+ *pkey = this_offset;
+ g_hash_table_insert(file_externals->packet_prefix_table, pkey, line_prefix_info);
+
+ /* OK, we have packet details to return */
+ return TRUE;
+ }
+ }
+
+ /* No packet details to return... */
+ return FALSE;
+}
+
+
+/**************************************************/
+/* Read & seek function. */
+/**************************************************/
+static gboolean
+catapult_dct2000_seek_read(wtap *wth, gint64 seek_off,
+ wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info)
+{
+ int length;
+ long dollar_offset, before_time_offset, after_time_offset;
+ static gchar linebuff[MAX_LINE_LENGTH+1];
+ gchar aal_header_chars[AAL_HEADER_CHARS];
+ gchar context_name[MAX_CONTEXT_NAME];
+ guint8 context_port = 0;
+ gchar protocol_name[MAX_PROTOCOL_NAME+1];
+ gchar variant_name[MAX_VARIANT_DIGITS+1];
+ gchar outhdr_name[MAX_OUTHDR_NAME+1];
+ int is_comment = FALSE;
+ int is_sprint = FALSE;
+ packet_direction_t direction;
+ int encap;
+ int seconds, useconds, data_chars;
+
+ /* Get wtap external structure for this wtap */
+ dct2000_file_externals_t *file_externals =
+ (dct2000_file_externals_t*)wth->priv;
+
+ /* Reset errno */
+ *err = errno = 0;
+
+ /* Seek to beginning of packet */
+ if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1) {
+ return FALSE;
+ }
+
+ /* Re-read whole line (this really should succeed) */
+ if (!read_new_line(wth->random_fh, &length, linebuff,
+ sizeof linebuff, err, err_info)) {
+ return FALSE;
+ }
+
+ /* Try to parse this line again (should succeed as re-reading...) */
+ if (parse_line(linebuff, length, &seconds, &useconds,
+ &before_time_offset, &after_time_offset,
+ &dollar_offset,
+ &data_chars, &direction, &encap, &is_comment, &is_sprint,
+ aal_header_chars,
+ context_name, &context_port,
+ protocol_name, variant_name, outhdr_name)) {
+
+ char timestamp_string[MAX_TIMESTAMP_LEN+1];
+ write_timestamp_string(timestamp_string, seconds, useconds/100);
+
+ if (!process_parsed_line(wth, file_externals,
+ rec, buf, seek_off,
+ linebuff, dollar_offset,
+ seconds, useconds,
+ timestamp_string,
+ direction, encap,
+ context_name, context_port,
+ protocol_name, variant_name,
+ outhdr_name, aal_header_chars,
+ is_comment, data_chars,
+ err, err_info)) {
+ return FALSE;
+ }
+
+ *err = errno = 0;
+ return TRUE;
+ }
+
+ /* If get here, must have failed */
+ *err = errno;
+ *err_info = ws_strdup_printf("catapult dct2000: seek_read failed to read/parse "
+ "line at position %" PRId64,
+ seek_off);
+ return FALSE;
+}
+
+
+/***************************************************************************/
+/* Free dct2000-specific capture info from file that was open for reading */
+/***************************************************************************/
+static void
+catapult_dct2000_close(wtap *wth)
+{
+ /* Get externals for this file */
+ dct2000_file_externals_t *file_externals =
+ (dct2000_file_externals_t*)wth->priv;
+
+ /* Free up its line prefix values */
+ g_hash_table_foreach_remove(file_externals->packet_prefix_table,
+ free_line_prefix_info, NULL);
+ /* Free up its line prefix table */
+ g_hash_table_destroy(file_externals->packet_prefix_table);
+}
+
+
+
+
+/***************************/
+/* Dump functions */
+/***************************/
+
+typedef struct {
+ gboolean first_packet_written;
+ nstime_t start_time;
+} dct2000_dump_t;
+
+/*****************************************************/
+/* The file that we are writing to has been opened. */
+/* Set other dump callbacks. */
+/*****************************************************/
+static gboolean
+catapult_dct2000_dump_open(wtap_dumper *wdh, int *err _U_, gchar **err_info _U_)
+{
+ /* Fill in other dump callbacks */
+ wdh->subtype_write = catapult_dct2000_dump;
+
+ return TRUE;
+}
+
+/*********************************************************/
+/* Respond to queries about which encap types we support */
+/* writing to. */
+/*********************************************************/
+static int
+catapult_dct2000_dump_can_write_encap(int encap)
+{
+ switch (encap) {
+ case WTAP_ENCAP_CATAPULT_DCT2000:
+ /* We support this */
+ return 0;
+
+ default:
+ /* But can't write to any other formats... */
+ return WTAP_ERR_UNWRITABLE_ENCAP;
+ }
+}
+
+
+/*****************************************/
+/* Write a single packet out to the file */
+/*****************************************/
+
+static gboolean
+catapult_dct2000_dump(wtap_dumper *wdh, const wtap_rec *rec,
+ const guint8 *pd, int *err, gchar **err_info _U_)
+{
+ const union wtap_pseudo_header *pseudo_header = &rec->rec_header.packet_header.pseudo_header;
+ guint32 n;
+ line_prefix_info_t *prefix = NULL;
+ gchar time_string[MAX_TIMESTAMP_LEN];
+ gboolean is_comment;
+ gboolean is_sprint = FALSE;
+ dct2000_dump_t *dct2000;
+ int consecutive_slashes=0;
+ char *p_c;
+
+ /******************************************************/
+ /* Get the file_externals structure for this file */
+ /* Find wtap external structure for this wtap */
+ dct2000_file_externals_t *file_externals =
+ (dct2000_file_externals_t*)pseudo_header->dct2000.wth->priv;
+
+ /* We can only write packet records. */
+ if (rec->rec_type != REC_TYPE_PACKET) {
+ *err = WTAP_ERR_UNWRITABLE_REC_TYPE;
+ return FALSE;
+ }
+
+ /*
+ * Make sure this packet doesn't have a link-layer type that
+ * differs from the one for the file (which should always
+ * be WTAP_ENCAP_CATAPULT_DCT2000).
+ */
+ if (wdh->file_encap != rec->rec_header.packet_header.pkt_encap) {
+ *err = WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
+ return FALSE;
+ }
+
+ dct2000 = (dct2000_dump_t *)wdh->priv;
+ if (dct2000 == NULL) {
+
+ /* Write out saved first line */
+ if (!wtap_dump_file_write(wdh, file_externals->firstline,
+ file_externals->firstline_length, err)) {
+ return FALSE;
+ }
+ if (!wtap_dump_file_write(wdh, "\n", 1, err)) {
+ return FALSE;
+ }
+
+ /* Also write out saved second line with timestamp corresponding to the
+ opening time of the log.
+ */
+ if (!wtap_dump_file_write(wdh, file_externals->secondline,
+ file_externals->secondline_length, err)) {
+ return FALSE;
+ }
+ if (!wtap_dump_file_write(wdh, "\n", 1, err)) {
+ return FALSE;
+ }
+
+ /* Allocate the dct2000-specific dump structure */
+ dct2000 = g_new(dct2000_dump_t, 1);
+ wdh->priv = (void *)dct2000;
+
+ /* Copy time of beginning of file */
+ dct2000->start_time.secs = file_externals->start_secs;
+ dct2000->start_time.nsecs =
+ (file_externals->start_usecs * 1000);
+
+ /* Set flag so don't write header out again */
+ dct2000->first_packet_written = TRUE;
+ }
+
+
+ /******************************************************************/
+ /* Write out this packet's prefix, including calculated timestamp */
+
+ /* Look up line data prefix using stored offset */
+ prefix = (line_prefix_info_t*)g_hash_table_lookup(file_externals->packet_prefix_table,
+ (const void*)&(pseudo_header->dct2000.seek_off));
+
+ /* Write out text before timestamp */
+ if (!wtap_dump_file_write(wdh, prefix->before_time,
+ strlen(prefix->before_time), err)) {
+ return FALSE;
+ }
+
+ /* Can infer from prefix if this is a comment (whose payload is displayed differently) */
+ /* This is much faster than strstr() for "/////" */
+ p_c = prefix->before_time;
+ while (p_c && (*p_c != '/')) {
+ p_c++;
+ }
+ while (p_c && (*p_c == '/')) {
+ consecutive_slashes++;
+ p_c++;
+ }
+ is_comment = (consecutive_slashes == 5);
+
+ /* Calculate time of this packet to write, relative to start of dump */
+ if (rec->ts.nsecs >= dct2000->start_time.nsecs) {
+ write_timestamp_string(time_string,
+ (int)(rec->ts.secs - dct2000->start_time.secs),
+ (rec->ts.nsecs - dct2000->start_time.nsecs) / 100000);
+ }
+ else {
+ write_timestamp_string(time_string,
+ (int)(rec->ts.secs - dct2000->start_time.secs-1),
+ ((1000000000 + (rec->ts.nsecs / 100000)) - (dct2000->start_time.nsecs / 100000)) % 10000);
+ }
+
+ /* Write out the calculated timestamp */
+ if (!wtap_dump_file_write(wdh, time_string, strlen(time_string), err)) {
+ return FALSE;
+ }
+
+ /* Write out text between timestamp and start of hex data */
+ if (prefix->has_l) {
+ if (!wtap_dump_file_write(wdh, " l ", 3, err)) {
+ return FALSE;
+ }
+ }
+
+ /****************************************************************/
+ /* Need to skip stub header at start of pd before we reach data */
+
+ /* Context name */
+ for (n=0; pd[n] != '\0'; n++);
+ n++;
+
+ /* Context port number */
+ n++;
+
+ /* Timestamp */
+ for (; pd[n] != '\0'; n++);
+ n++;
+
+ /* Protocol name */
+ if (is_comment) {
+ is_sprint = (strcmp((const char *)pd+n, "sprint") == 0);
+ }
+ for (; pd[n] != '\0'; n++);
+ n++;
+
+ /* Variant number (as string) */
+ for (; pd[n] != '\0'; n++);
+ n++;
+
+ /* Outhdr (as string) */
+ for (; pd[n] != '\0'; n++);
+ n++;
+
+ /* Direction & encap */
+ n += 2;
+
+
+ /**************************************/
+ /* Remainder is encapsulated protocol */
+ if (!wtap_dump_file_write(wdh, is_sprint ? " " : "$", 1, err)) {
+ return FALSE;
+ }
+
+ if (!is_comment) {
+ /* Each binary byte is written out as 2 hex string chars */
+ for (; n < rec->rec_header.packet_header.len; n++) {
+ gchar c[2];
+ c[0] = char_from_hex((guint8)(pd[n] >> 4));
+ c[1] = char_from_hex((guint8)(pd[n] & 0x0f));
+
+ /* Write both hex chars of byte together */
+ if (!wtap_dump_file_write(wdh, c, 2, err)) {
+ return FALSE;
+ }
+ }
+ }
+ else {
+ /* Comment */
+ if (!wtap_dump_file_write(wdh, pd+n, rec->rec_header.packet_header.len-n, err)) {
+ return FALSE;
+ }
+ }
+
+ /* End the line */
+ if (!wtap_dump_file_write(wdh, "\n", 1, err)) {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+/****************************/
+/* Private helper functions */
+/****************************/
+
+/**********************************************************************/
+/* Read a new line from the file, starting at offset. */
+/* - writes data to its argument linebuff */
+/* - on return 'offset' will point to the next position to read from */
+/* - return TRUE if this read is successful */
+/**********************************************************************/
+static gboolean
+read_new_line(FILE_T fh, gint *length,
+ gchar *linebuff, size_t linebuffsize, int *err, gchar **err_info)
+{
+ /* Read in a line */
+ gint64 pos_before = file_tell(fh);
+
+ if (file_gets(linebuff, (int)linebuffsize - 1, fh) == NULL) {
+ /* No characters found, or error */
+ *err = file_error(fh, err_info);
+ return FALSE;
+ }
+
+ /* Set length (avoiding strlen()) and offset.. */
+ *length = (gint)(file_tell(fh) - pos_before);
+
+ /* ...but don't want to include newline in line length */
+ if (*length > 0 && linebuff[*length-1] == '\n') {
+ linebuff[*length-1] = '\0';
+ *length = *length - 1;
+ }
+ /* Nor do we want '\r' (as will be written when log is created on windows) */
+ if (*length > 0 && linebuff[*length-1] == '\r') {
+ linebuff[*length-1] = '\0';
+ *length = *length - 1;
+ }
+
+ return TRUE;
+}
+
+
+/**********************************************************************/
+/* Parse a line from buffer, by identifying: */
+/* - context, port and direction of packet */
+/* - timestamp */
+/* - data position and length */
+/* Return TRUE if this packet looks valid and can be displayed */
+/**********************************************************************/
+static gboolean
+parse_line(gchar *linebuff, gint line_length,
+ gint *seconds, gint *useconds,
+ long *before_time_offset, long *after_time_offset,
+ long *data_offset, gint *data_chars,
+ packet_direction_t *direction,
+ int *encap, int *is_comment, int *is_sprint,
+ gchar *aal_header_chars,
+ gchar *context_name, guint8 *context_portp,
+ gchar *protocol_name, gchar *variant_name,
+ gchar *outhdr_name)
+{
+ int n = 0;
+ int port_digits;
+ char port_number_string[MAX_PORT_DIGITS+1];
+ int variant_digits;
+ int variant = 1;
+ int protocol_chars;
+ int outhdr_chars;
+
+ char seconds_buff[MAX_SECONDS_CHARS+1];
+ int seconds_chars;
+ char subsecond_decimals_buff[MAX_SUBSECOND_DECIMALS+1];
+ int subsecond_decimals_chars;
+ int skip_first_byte = FALSE;
+ gboolean atm_header_present = FALSE;
+
+ *is_comment = FALSE;
+ *is_sprint = FALSE;
+
+ /* Read context name until find '.' */
+ for (n=0; (n < MAX_CONTEXT_NAME) && (n+1 < line_length) && (linebuff[n] != '.'); n++) {
+ if (linebuff[n] == '/') {
+ context_name[n] = '\0';
+
+ /* If not a comment (/////), not a valid line */
+ if (strncmp(linebuff+n, "/////", 5) != 0) {
+ return FALSE;
+ }
+
+ /* There is no variant, outhdr, etc. Set protocol to be a comment */
+ (void) g_strlcpy(protocol_name, "comment", MAX_PROTOCOL_NAME);
+ *is_comment = TRUE;
+ break;
+ }
+ if (!g_ascii_isalnum(linebuff[n]) && (linebuff[n] != '_') && (linebuff[n] != '-')) {
+ return FALSE;
+ }
+ context_name[n] = linebuff[n];
+ }
+ if (n == MAX_CONTEXT_NAME || (n+1 >= line_length)) {
+ return FALSE;
+ }
+
+ /* Reset strings (that won't be set by comments) */
+ variant_name[0] = '\0';
+ outhdr_name[0] = '\0';
+ port_number_string[0] = '\0';
+
+ if (!(*is_comment)) {
+ /* '.' must follow context name */
+ if (linebuff[n] != '.') {
+ return FALSE;
+ }
+ context_name[n] = '\0';
+ /* Skip it */
+ n++;
+
+ /* Now read port number */
+ for (port_digits = 0;
+ (linebuff[n] != '/') && (port_digits <= MAX_PORT_DIGITS) && (n+1 < line_length);
+ n++, port_digits++) {
+
+ if (!g_ascii_isdigit(linebuff[n])) {
+ return FALSE;
+ }
+ port_number_string[port_digits] = linebuff[n];
+ }
+ if (port_digits > MAX_PORT_DIGITS || (n+1 >= line_length)) {
+ return FALSE;
+ }
+
+ /* Slash char must follow port number */
+ if (linebuff[n] != '/')
+ {
+ return FALSE;
+ }
+ port_number_string[port_digits] = '\0';
+ if (port_digits == 1) {
+ *context_portp = port_number_string[0] - '0';
+ }
+ else {
+ /* Everything in here is a digit, so we don't need to check
+ whether what follows the number is anything other than
+ a '\0'. */
+ if (!ws_strtou8(port_number_string, NULL, context_portp)) {
+ return FALSE;
+ }
+ }
+ /* Skip it */
+ n++;
+
+ /* Now for the protocol name */
+ for (protocol_chars = 0;
+ (linebuff[n] != '/') && (protocol_chars < MAX_PROTOCOL_NAME) && (n < line_length);
+ n++, protocol_chars++) {
+
+ if (!g_ascii_isalnum(linebuff[n]) && (linebuff[n] != '_') && (linebuff[n] != '.')) {
+ return FALSE;
+ }
+ protocol_name[protocol_chars] = linebuff[n];
+ }
+ if (protocol_chars == MAX_PROTOCOL_NAME || n >= line_length) {
+ /* If doesn't fit, fail rather than truncate */
+ return FALSE;
+ }
+ protocol_name[protocol_chars] = '\0';
+
+ /* Slash char must follow protocol name */
+ if (linebuff[n] != '/') {
+ return FALSE;
+ }
+ /* Skip it */
+ n++;
+
+
+ /* Following the / is the variant number. No digits indicate 1 */
+ for (variant_digits = 0;
+ (g_ascii_isdigit(linebuff[n])) && (variant_digits <= MAX_VARIANT_DIGITS) && (n+1 < line_length);
+ n++, variant_digits++) {
+
+ if (!g_ascii_isdigit(linebuff[n])) {
+ return FALSE;
+ }
+ variant_name[variant_digits] = linebuff[n];
+ }
+ if (variant_digits > MAX_VARIANT_DIGITS || (n+1 >= line_length)) {
+ return FALSE;
+ }
+
+ if (variant_digits > 0) {
+ variant_name[variant_digits] = '\0';
+ if (variant_digits == 1) {
+ variant = variant_name[0] - '0';
+ }
+ else {
+ if (!ws_strtoi32(variant_name, NULL, &variant)) {
+ return FALSE;
+ }
+ }
+ }
+ else {
+ variant_name[0] = '1';
+ variant_name[1] = '\0';
+ }
+
+
+ /* Outheader values may follow */
+ if (linebuff[n] == ',') {
+ /* Skip , */
+ n++;
+
+ for (outhdr_chars = 0;
+ (g_ascii_isdigit(linebuff[n]) || linebuff[n] == ',') &&
+ (outhdr_chars <= MAX_OUTHDR_NAME) && (n+1 < line_length);
+ n++, outhdr_chars++) {
+
+ if (!g_ascii_isdigit(linebuff[n]) && (linebuff[n] != ',')) {
+ return FALSE;
+ }
+ outhdr_name[outhdr_chars] = linebuff[n];
+ }
+ if (outhdr_chars > MAX_OUTHDR_NAME || (n+1 >= line_length)) {
+ return FALSE;
+ }
+ /* Terminate (possibly empty) string */
+ outhdr_name[outhdr_chars] = '\0';
+ }
+ }
+
+
+ /******************************************************************/
+ /* Now check whether we know how to use a packet of this protocol */
+
+ if ((strcmp(protocol_name, "ip") == 0) ||
+ (strcmp(protocol_name, "sctp") == 0) ||
+ (strcmp(protocol_name, "gre") == 0) ||
+ (strcmp(protocol_name, "mipv6") == 0) ||
+ (strcmp(protocol_name, "igmp") == 0)) {
+
+ *encap = WTAP_ENCAP_RAW_IP;
+ }
+
+ /* FP may be carried over ATM, which has separate atm header to parse */
+ else
+ if ((strcmp(protocol_name, "fp") == 0) ||
+ (strncmp(protocol_name, "fp_r", 4) == 0)) {
+
+ if ((variant > 256) && (variant % 256 == 3)) {
+ /* FP over udp is contained in IPPrim... */
+ *encap = 0;
+ }
+ else {
+ /* FP over AAL0 or AAL2 */
+ *encap = WTAP_ENCAP_ATM_PDUS_UNTRUNCATED;
+ atm_header_present = TRUE;
+ }
+ }
+ else if (strcmp(protocol_name, "fpiur_r5") == 0) {
+ /* FP (IuR) over AAL2 */
+ *encap = WTAP_ENCAP_ATM_PDUS_UNTRUNCATED;
+ atm_header_present = TRUE;
+ }
+
+ else
+ if (strcmp(protocol_name, "ppp") == 0) {
+ *encap = WTAP_ENCAP_PPP;
+ }
+ else
+ if (strcmp(protocol_name, "isdn_l3") == 0) {
+ /* TODO: find out what this byte means... */
+ skip_first_byte = TRUE;
+ *encap = WTAP_ENCAP_ISDN;
+ }
+ else
+ if (strcmp(protocol_name, "isdn_l2") == 0) {
+ *encap = WTAP_ENCAP_ISDN;
+ }
+ else
+ if (strcmp(protocol_name, "ethernet") == 0) {
+ *encap = WTAP_ENCAP_ETHERNET;
+ }
+ else
+ if ((strcmp(protocol_name, "saalnni_sscop") == 0) ||
+ (strcmp(protocol_name, "saaluni_sscop") == 0)) {
+
+ *encap = DCT2000_ENCAP_SSCOP;
+ }
+ else
+ if (strcmp(protocol_name, "frelay_l2") == 0) {
+ *encap = WTAP_ENCAP_FRELAY;
+ }
+ else
+ if (strcmp(protocol_name, "ss7_mtp2") == 0) {
+ *encap = DCT2000_ENCAP_MTP2;
+ }
+ else
+ if ((strcmp(protocol_name, "nbap") == 0) ||
+ (strcmp(protocol_name, "nbap_r4") == 0) ||
+ (strncmp(protocol_name, "nbap_sscfuni", strlen("nbap_sscfuni")) == 0)) {
+
+ /* The entire message in these cases is nbap, so use an encap value. */
+ *encap = DCT2000_ENCAP_NBAP;
+ }
+ else {
+ /* Not a supported board port protocol/encap, but can show as raw data or
+ in some cases find protocol embedded inside primitive */
+ *encap = DCT2000_ENCAP_UNHANDLED;
+ }
+
+
+ /* Find separate ATM header if necessary */
+ if (atm_header_present) {
+ int header_chars_seen = 0;
+
+ /* Scan ahead to the next $ */
+ for (; (linebuff[n] != '$') && (n+1 < line_length); n++);
+ /* Skip it */
+ n++;
+ if (n+1 >= line_length) {
+ return FALSE;
+ }
+
+ /* Read consecutive hex chars into atm header buffer */
+ for (;
+ ((n < line_length) &&
+ (linebuff[n] >= '0') && (linebuff[n] <= '?') &&
+ (header_chars_seen < AAL_HEADER_CHARS));
+ n++, header_chars_seen++) {
+
+ aal_header_chars[header_chars_seen] = linebuff[n];
+ /* Next 6 characters after '9' are mapped to a->f */
+ if (!g_ascii_isdigit(linebuff[n])) {
+ aal_header_chars[header_chars_seen] = 'a' + (linebuff[n] - '9') -1;
+ }
+ }
+
+ if (header_chars_seen != AAL_HEADER_CHARS || n >= line_length) {
+ return FALSE;
+ }
+ }
+
+ /* Skip next '/' */
+ n++;
+
+ /* If there is a number, skip all info to next '/'.
+ TODO: for IP encapsulation, should store PDCP ueid, drb in pseudo info
+ and display dct2000 dissector... */
+ if (g_ascii_isdigit(linebuff[n])) {
+ while ((n+1 < line_length) && linebuff[n] != '/') {
+ n++;
+ }
+ }
+
+ /* Skip '/' */
+ while ((n+1 < line_length) && linebuff[n] == '/') {
+ n++;
+ }
+
+ /* Skip a space that may happen here */
+ if ((n+1 < line_length) && linebuff[n] == ' ') {
+ n++;
+ }
+
+ /* Next character gives direction of message (must be 's' or 'r') */
+ if (!(*is_comment)) {
+ if (linebuff[n] == 's') {
+ *direction = sent;
+ }
+ else
+ if (linebuff[n] == 'r') {
+ *direction = received;
+ }
+ else {
+ return FALSE;
+ }
+ /* Skip it */
+ n++;
+ }
+ else {
+ *direction = sent;
+ }
+
+
+ /*********************************************************************/
+ /* Find and read the timestamp */
+
+ /* Now scan to the next digit, which should be the start of the timestamp */
+ /* This will involve skipping " tm " */
+
+ for (; ((linebuff[n] != 't') || (linebuff[n+1] != 'm')) && (n+1 < line_length); n++);
+ if (n >= line_length) {
+ return FALSE;
+ }
+
+ for (; (n < line_length) && !g_ascii_isdigit(linebuff[n]); n++);
+ if (n >= line_length) {
+ return FALSE;
+ }
+
+ *before_time_offset = n;
+
+ /* Seconds */
+ for (seconds_chars = 0;
+ (linebuff[n] != '.') &&
+ (seconds_chars <= MAX_SECONDS_CHARS) &&
+ (n < line_length);
+ n++, seconds_chars++) {
+
+ if (!g_ascii_isdigit(linebuff[n])) {
+ /* Found a non-digit before decimal point. Fail */
+ return FALSE;
+ }
+ seconds_buff[seconds_chars] = linebuff[n];
+ }
+ if (seconds_chars > MAX_SECONDS_CHARS || n >= line_length) {
+ /* Didn't fit in buffer. Fail rather than use truncated */
+ return FALSE;
+ }
+
+ /* Convert found value into number */
+ seconds_buff[seconds_chars] = '\0';
+ /* Already know they are digits, so avoid expense of ws_strtoi32() */
+ int multiplier = 1;
+ *seconds = 0;
+ for (int d=seconds_chars-1; d >= 0; d--) {
+ *seconds += ((seconds_buff[d]-'0')*multiplier);
+ multiplier *= 10;
+ }
+
+ /* The decimal point must follow the last of the seconds digits */
+ if (linebuff[n] != '.') {
+ return FALSE;
+ }
+ /* Skip it */
+ n++;
+
+ /* Subsecond decimal digits (expect 4-digit accuracy) */
+ for (subsecond_decimals_chars = 0;
+ (linebuff[n] != ' ') &&
+ (subsecond_decimals_chars <= MAX_SUBSECOND_DECIMALS) &&
+ (n < line_length);
+ n++, subsecond_decimals_chars++) {
+
+ if (!g_ascii_isdigit(linebuff[n])) {
+ return FALSE;
+ }
+ subsecond_decimals_buff[subsecond_decimals_chars] = linebuff[n];
+ }
+ if (subsecond_decimals_chars != MAX_SUBSECOND_DECIMALS || n >= line_length) {
+ /* There should be exactly 4 subsecond digits - give up if not */
+ return FALSE;
+ }
+ /* Convert found value into microseconds */
+ subsecond_decimals_buff[subsecond_decimals_chars] = '\0';
+ /* Already know they are digits, so avoid expense of ws_strtoi32() */
+ *useconds = ((subsecond_decimals_buff[0]-'0') * 100000) +
+ ((subsecond_decimals_buff[1]-'0') * 10000) +
+ ((subsecond_decimals_buff[2]-'0') * 1000) +
+ ((subsecond_decimals_buff[3]-'0') * 100);
+
+ /* Space character must follow end of timestamp */
+ if (linebuff[n] != ' ') {
+ return FALSE;
+ }
+
+ *after_time_offset = n++;
+
+ /* If we have a string message, it could either be a comment (with '$') or
+ a sprint line (no '$') */
+ if (*is_comment) {
+ if (strncmp(linebuff+n, "l $", 3) != 0) {
+ *is_sprint = TRUE;
+ (void) g_strlcpy(protocol_name, "sprint", MAX_PROTOCOL_NAME);
+ }
+ }
+
+ if (!(*is_sprint)) {
+ /* Now skip ahead to find start of data (marked by '$') */
+ for (; (linebuff[n] != '$') && (linebuff[n] != '\'') && (n+1 < line_length); n++);
+ if ((linebuff[n] == '\'') || (n+1 >= line_length)) {
+ return FALSE;
+ }
+ /* Skip it */
+ n++;
+ }
+
+ /* Set offset to data start within line */
+ *data_offset = n;
+
+ /* Set number of chars that comprise the hex string protocol data */
+ *data_chars = line_length - n;
+
+ /* May need to skip first byte (2 hex string chars) */
+ if (skip_first_byte) {
+ *data_offset += 2;
+ *data_chars -= 2;
+ }
+
+ return TRUE;
+}
+
+/***********************************/
+/* Process results of parse_line() */
+/***********************************/
+static gboolean
+process_parsed_line(wtap *wth, dct2000_file_externals_t *file_externals,
+ wtap_rec *rec,
+ Buffer *buf, gint64 file_offset,
+ char *linebuff, long dollar_offset,
+ int seconds, int useconds, gchar *timestamp_string,
+ packet_direction_t direction, int encap,
+ gchar *context_name, guint8 context_port,
+ gchar *protocol_name, gchar *variant_name,
+ gchar *outhdr_name, gchar *aal_header_chars,
+ gboolean is_comment, int data_chars,
+ int *err, gchar **err_info)
+{
+ int n;
+ int stub_offset = 0;
+ gsize length;
+ guint8 *frame_buffer;
+
+ rec->rec_type = REC_TYPE_PACKET;
+ rec->block = wtap_block_create(WTAP_BLOCK_PACKET);
+ rec->presence_flags = WTAP_HAS_TS;
+
+ /* Make sure all packets go to Catapult DCT2000 dissector */
+ rec->rec_header.packet_header.pkt_encap = WTAP_ENCAP_CATAPULT_DCT2000;
+
+ /* Fill in timestamp (capture base + packet offset) */
+ rec->ts.secs = file_externals->start_secs + seconds;
+ if ((file_externals->start_usecs + useconds) >= 1000000) {
+ rec->ts.secs++;
+ }
+ rec->ts.nsecs =
+ ((file_externals->start_usecs + useconds) % 1000000) *1000;
+
+ /*
+ * Calculate the length of the stub info and the packet data.
+ * The packet data length is half bytestring length.
+ */
+ rec->rec_header.packet_header.caplen = (guint)strlen(context_name)+1 + /* Context name */
+ 1 + /* port */
+ (guint)strlen(timestamp_string)+1 + /* timestamp */
+ (guint)strlen(variant_name)+1 + /* variant */
+ (guint)strlen(outhdr_name)+1 + /* outhdr */
+ (guint)strlen(protocol_name)+1 + /* Protocol name */
+ 1 + /* direction */
+ 1 + /* encap */
+ (is_comment ? data_chars : (data_chars/2));
+ if (rec->rec_header.packet_header.caplen > WTAP_MAX_PACKET_SIZE_STANDARD) {
+ /*
+ * Probably a corrupt capture file; return an error,
+ * so that our caller doesn't blow up trying to allocate
+ * space for an immensely-large packet.
+ */
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("catapult dct2000: File has %u-byte packet, bigger than maximum of %u",
+ rec->rec_header.packet_header.caplen, WTAP_MAX_PACKET_SIZE_STANDARD);
+ return FALSE;
+ }
+ rec->rec_header.packet_header.len = rec->rec_header.packet_header.caplen;
+
+ /*****************************/
+ /* Get the data buffer ready */
+ ws_buffer_assure_space(buf, rec->rec_header.packet_header.caplen);
+ frame_buffer = ws_buffer_start_ptr(buf);
+
+ /******************************************/
+ /* Write the stub info to the data buffer */
+
+ /* Context name */
+ length = g_strlcpy((char*)frame_buffer, context_name, MAX_CONTEXT_NAME+1);
+ stub_offset += (int)(length + 1);
+
+ /* Context port number */
+ frame_buffer[stub_offset] = context_port;
+ stub_offset++;
+
+ /* Timestamp within file (terminated string) */
+ length = g_strlcpy((char*)&frame_buffer[stub_offset], timestamp_string, MAX_TIMESTAMP_LEN+1);
+ stub_offset += (int)(length + 1);
+
+ /* Protocol name (terminated string) */
+ length = g_strlcpy((char*)&frame_buffer[stub_offset], protocol_name, MAX_PROTOCOL_NAME+1);
+ stub_offset += (int)(length + 1);
+
+ /* Protocol variant number (as terminated string) */
+ length = g_strlcpy((gchar*)&frame_buffer[stub_offset], variant_name, MAX_VARIANT_DIGITS+1);
+ stub_offset += (int)(length + 1);
+
+ /* Outhdr (terminated string) */
+ length = g_strlcpy((char*)&frame_buffer[stub_offset], outhdr_name, MAX_OUTHDR_NAME+1);
+ stub_offset += (int)(length + 1);
+
+ /* Direction */
+ frame_buffer[stub_offset++] = direction;
+
+ /* Encap */
+ frame_buffer[stub_offset++] = (guint8)encap;
+
+ if (!is_comment) {
+ /***********************************************************/
+ /* Copy packet data into buffer, converting from ascii hex */
+ for (n=0; n < data_chars; n+=2) {
+ frame_buffer[stub_offset + n/2] =
+ hex_byte_from_chars(linebuff+dollar_offset+n);
+ }
+ }
+ else {
+ /***********************************************************/
+ /* Copy packet data into buffer, just copying ascii chars */
+ for (n=0; n < data_chars; n++) {
+ frame_buffer[stub_offset + n] = linebuff[dollar_offset+n];
+ }
+ }
+
+ /*****************************************/
+ /* Set packet pseudo-header if necessary */
+ rec->rec_header.packet_header.pseudo_header.dct2000.seek_off = file_offset;
+ rec->rec_header.packet_header.pseudo_header.dct2000.wth = wth;
+
+ switch (encap) {
+ case WTAP_ENCAP_ATM_PDUS_UNTRUNCATED:
+ set_aal_info(&rec->rec_header.packet_header.pseudo_header, direction, aal_header_chars);
+ break;
+ case WTAP_ENCAP_ISDN:
+ set_isdn_info(&rec->rec_header.packet_header.pseudo_header, direction);
+ break;
+ case WTAP_ENCAP_PPP:
+ set_ppp_info(&rec->rec_header.packet_header.pseudo_header, direction);
+ break;
+
+ default:
+ /* Other supported types don't need to set anything here... */
+ break;
+ }
+
+ return TRUE;
+}
+
+/*********************************************/
+/* Fill in atm pseudo-header with known info */
+/*********************************************/
+static void
+set_aal_info(union wtap_pseudo_header *pseudo_header,
+ packet_direction_t direction,
+ gchar *aal_header_chars)
+{
+ /* 'aal_head_chars' has this format (for AAL2 at least):
+ Global Flow Control (4 bits) | VPI (8 bits) | VCI (16 bits) |
+ Payload Type (4 bits) | Padding (3 bits?) | Link? (1 bit) |
+ Channel Identifier (8 bits) | ...
+ */
+
+ /* Indicate that this is a reassembled PDU */
+ pseudo_header->dct2000.inner_pseudo_header.atm.flags = 0x00;
+
+ /* Channel 0 is DTE->DCE, 1 is DCE->DTE. Always set 0 for now.
+ TODO: Can we infer the correct value here?
+ Meanwhile, just use the direction to make them distinguishable...
+ */
+ pseudo_header->dct2000.inner_pseudo_header.atm.channel = (direction == received);
+
+ /* Assume always AAL2 for FP */
+ pseudo_header->dct2000.inner_pseudo_header.atm.aal = AAL_2;
+
+ pseudo_header->dct2000.inner_pseudo_header.atm.type = TRAF_UMTS_FP;
+ pseudo_header->dct2000.inner_pseudo_header.atm.subtype = TRAF_ST_UNKNOWN;
+
+ /* vpi is 8 bits (2nd & 3rd nibble) */
+ pseudo_header->dct2000.inner_pseudo_header.atm.vpi =
+ hex_byte_from_chars(aal_header_chars+1);
+
+ /* vci is next 16 bits */
+ pseudo_header->dct2000.inner_pseudo_header.atm.vci =
+ ((hex_from_char(aal_header_chars[3]) << 12) |
+ (hex_from_char(aal_header_chars[4]) << 8) |
+ (hex_from_char(aal_header_chars[5]) << 4) |
+ hex_from_char(aal_header_chars[6]));
+
+ /* 0 means we don't know how many cells the frame comprises. */
+ pseudo_header->dct2000.inner_pseudo_header.atm.cells = 0;
+
+ /* cid is usually last byte. Unless last char is not hex digit, in which
+ case cid is derived from last char in ascii */
+ if (g_ascii_isalnum(aal_header_chars[11])) {
+ pseudo_header->dct2000.inner_pseudo_header.atm.aal2_cid =
+ hex_byte_from_chars(aal_header_chars+10);
+ }
+ else {
+ pseudo_header->dct2000.inner_pseudo_header.atm.aal2_cid =
+ (int)aal_header_chars[11] - '0';
+ }
+}
+
+
+/**********************************************/
+/* Fill in isdn pseudo-header with known info */
+/**********************************************/
+static void
+set_isdn_info(union wtap_pseudo_header *pseudo_header,
+ packet_direction_t direction)
+{
+ /* This field is used to set the 'Source' and 'Destination' columns to
+ 'User' or 'Network'. If we assume that we're simulating the network,
+ treat Received messages as being destined for the network.
+ */
+ pseudo_header->dct2000.inner_pseudo_header.isdn.uton = (direction == received);
+
+ /* This corresponds to the circuit ID. 0 is treated as LAPD,
+ everything else would be treated as a B-channel
+ */
+ pseudo_header->dct2000.inner_pseudo_header.isdn.channel = 0;
+}
+
+
+/*********************************************/
+/* Fill in ppp pseudo-header with known info */
+/*********************************************/
+static void
+set_ppp_info(union wtap_pseudo_header *pseudo_header,
+ packet_direction_t direction)
+{
+ /* Set direction. */
+ pseudo_header->dct2000.inner_pseudo_header.p2p.sent = (direction == sent);
+}
+
+
+/********************************************************/
+/* Return hex nibble equivalent of hex string character */
+/********************************************************/
+static guint8
+hex_from_char(gchar c)
+{
+ if ((c >= '0') && (c <= '9')) {
+ return c - '0';
+ }
+
+ if ((c >= 'a') && (c <= 'f')) {
+ return 0x0a + (c - 'a');
+ }
+
+ /* Not a valid hex string character */
+ return 0xff;
+}
+
+
+
+/* Table allowing fast lookup from a pair of ascii hex characters to a guint8 */
+static guint8 s_tableValues[256][256];
+
+/* Prepare table values so ready so don't need to check inside hex_byte_from_chars() */
+static void prepare_hex_byte_from_chars_table(void)
+{
+ guchar hex_char_array[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ 'a', 'b', 'c', 'd', 'e', 'f' };
+
+ gint i, j;
+ for (i=0; i < 16; i++) {
+ for (j=0; j < 16; j++) {
+ s_tableValues[hex_char_array[i]][hex_char_array[j]] = i*16 + j;
+ }
+ }
+}
+
+/* Extract and return a byte value from 2 ascii hex chars, starting from the given pointer */
+static guint8 hex_byte_from_chars(gchar *c)
+{
+ /* Return value from quick table lookup */
+ return s_tableValues[(unsigned char)c[0]][(unsigned char)c[1]];
+}
+
+
+
+/********************************************************/
+/* Return character corresponding to hex nibble value */
+/********************************************************/
+static gchar
+char_from_hex(guint8 hex)
+{
+ static const char hex_lookup[16] =
+ { '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
+
+ if (hex > 15) {
+ return '?';
+ }
+
+ return hex_lookup[hex];
+}
+
+/***********************************************/
+/* Equality test for packet prefix hash tables */
+/***********************************************/
+static gint
+packet_offset_equal(gconstpointer v, gconstpointer v2)
+{
+ /* Dereferenced pointers must have same gint64 offset value */
+ return (*(const gint64*)v == *(const gint64*)v2);
+}
+
+
+/********************************************/
+/* Hash function for packet-prefix hash table */
+/********************************************/
+static guint
+packet_offset_hash_func(gconstpointer v)
+{
+ /* Use low-order bits of gint64 offset value */
+ return (guint)(*(const gint64*)v);
+}
+
+
+/************************************************************************/
+/* Parse year, month, day, hour, minute, seconds out of formatted line. */
+/* Set secs and usecs as output */
+/* Return FALSE if no valid time can be read */
+/************************************************************************/
+static gboolean
+get_file_time_stamp(gchar *linebuff, time_t *secs, guint32 *usecs)
+{
+ struct tm tm;
+ #define MAX_MONTH_LETTERS 9
+ char month[MAX_MONTH_LETTERS+1];
+
+ int day, year, hour, minute, second;
+ int scan_found;
+
+ /* If line longer than expected, file is probably not correctly formatted */
+ if (strlen(linebuff) > MAX_TIMESTAMP_LINE_LENGTH) {
+ return FALSE;
+ }
+
+ /********************************************************/
+ /* Scan for all fields */
+ scan_found = sscanf(linebuff, "%9s %2d, %4d %2d:%2d:%2d.%4u",
+ month, &day, &year, &hour, &minute, &second, usecs);
+ if (scan_found != 7) {
+ /* Give up if not all found */
+ return FALSE;
+ }
+
+ if (strcmp(month, "January" ) == 0) tm.tm_mon = 0;
+ else if (strcmp(month, "February" ) == 0) tm.tm_mon = 1;
+ else if (strcmp(month, "March" ) == 0) tm.tm_mon = 2;
+ else if (strcmp(month, "April" ) == 0) tm.tm_mon = 3;
+ else if (strcmp(month, "May" ) == 0) tm.tm_mon = 4;
+ else if (strcmp(month, "June" ) == 0) tm.tm_mon = 5;
+ else if (strcmp(month, "July" ) == 0) tm.tm_mon = 6;
+ else if (strcmp(month, "August" ) == 0) tm.tm_mon = 7;
+ else if (strcmp(month, "September") == 0) tm.tm_mon = 8;
+ else if (strcmp(month, "October" ) == 0) tm.tm_mon = 9;
+ else if (strcmp(month, "November" ) == 0) tm.tm_mon = 10;
+ else if (strcmp(month, "December" ) == 0) tm.tm_mon = 11;
+ else {
+ /* Give up if not found a properly-formatted date */
+ return FALSE;
+ }
+
+ /******************************************************/
+ /* Fill in remaining fields and return it in a time_t */
+ tm.tm_year = year - 1900;
+ tm.tm_mday = day;
+ tm.tm_hour = hour;
+ tm.tm_min = minute;
+ tm.tm_sec = second;
+ tm.tm_isdst = -1; /* daylight saving time info not known */
+
+ /* Get seconds from this time */
+ *secs = mktime(&tm);
+
+ /* Multiply 4 digits given to get micro-seconds */
+ *usecs = *usecs * 100;
+
+ return TRUE;
+}
+
+/* Free the data allocated inside a line_prefix_info_t */
+static gboolean
+free_line_prefix_info(gpointer key, gpointer value,
+ gpointer user_data _U_)
+{
+ line_prefix_info_t *info = (line_prefix_info_t*)value;
+
+ /* Free the 64-bit key value */
+ g_free(key);
+
+ /* Free string */
+ g_free(info->before_time);
+
+ /* And the structure itself */
+ g_free(info);
+
+ /* Item will always be removed from table */
+ return TRUE;
+}
+
+static const struct supported_block_type dct2000_blocks_supported[] = {
+ /*
+ * We support packet blocks, with no comments or other options.
+ */
+ { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
+};
+
+static const struct file_type_subtype_info dct2000_info = {
+ "Catapult DCT2000 trace (.out format)", "dct2000", "out", NULL,
+ FALSE, BLOCKS_SUPPORTED(dct2000_blocks_supported),
+ catapult_dct2000_dump_can_write_encap, catapult_dct2000_dump_open, NULL
+};
+
+void register_dct2000(void)
+{
+ dct2000_file_type_subtype = wtap_register_file_type_subtype(&dct2000_info);
+
+ /*
+ * Register name for backwards compatibility with the
+ * wtap_filetypes table in Lua.
+ */
+ wtap_register_backwards_compatibility_lua_name("CATAPULT_DCT2000",
+ dct2000_file_type_subtype);
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * vi: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */
diff --git a/wiretap/catapult_dct2000.h b/wiretap/catapult_dct2000.h
new file mode 100644
index 00000000..61b8ebb7
--- /dev/null
+++ b/wiretap/catapult_dct2000.h
@@ -0,0 +1,23 @@
+/** @file
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __W_CAT_DCT2K_H__
+#define __W_CAT_DCT2K_H__
+
+#include <glib.h>
+#include "ws_symbol_export.h"
+
+wtap_open_return_val catapult_dct2000_open(wtap *wth, int *err, gchar **err_info);
+
+#define DCT2000_ENCAP_UNHANDLED 0
+#define DCT2000_ENCAP_SSCOP 101
+#define DCT2000_ENCAP_MTP2 102
+#define DCT2000_ENCAP_NBAP 103
+
+#endif
+
diff --git a/wiretap/commview.c b/wiretap/commview.c
new file mode 100644
index 00000000..71f9ced2
--- /dev/null
+++ b/wiretap/commview.c
@@ -0,0 +1,1390 @@
+/* commview.c
+ * Routines for opening CommView NCF and NCFX file format packet captures
+ * Copyright 2007, Stephen Fisher (see AUTHORS file)
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * Based on csids.c and nettl.c
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+/* A brief description of these file formats is available at:
+ * https://www.tamos.com/htmlhelp/commview/logformat.htm
+ * https://www.tamos.com/htmlhelp/commwifi/logformat.htm
+ *
+ * Use
+ *
+ * https://web.archive.org/web/20171022225753/http://www.tamos.com/htmlhelp/commview/logformat.htm
+ *
+ * if that doesn't display anything.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "wtap-int.h"
+#include "file_wrappers.h"
+#include "commview.h"
+
+#include <wsutil/802_11-utils.h>
+
+/*
+ * Capture medium types used in NCF and NCFX;
+ * Token Ring isn't used in NCFX.
+ */
+#define MEDIUM_ETHERNET 0
+#define MEDIUM_WIFI 1
+#define MEDIUM_TOKEN_RING 2
+
+typedef struct commview_ncf_header {
+ guint16 data_len;
+ guint16 source_data_len;
+ guint8 version;
+ guint16 year;
+ guint8 month;
+ guint8 day;
+ guint8 hours;
+ guint8 minutes;
+ guint8 seconds;
+ guint32 usecs;
+ guint8 flags; /* Bit-field positions defined below */
+ guint8 signal_level_percent;
+ guint8 rate;
+ guint8 band;
+ guint8 channel;
+ guint8 direction; /* Or for WiFi, high order byte of
+ * packet rate. */
+ gint8 signal_level_dbm; /* WiFi-only */
+ gint8 noise_level_dbm; /* WiFi-only */
+} commview_ncf_header_t;
+
+#define COMMVIEW_NCF_HEADER_SIZE 24
+
+/* Bit-field positions for various fields in the flags variable of the header */
+#define FLAGS_MEDIUM 0x0F
+#define FLAGS_DECRYPTED 0x10
+#define FLAGS_BROKEN 0x20
+#define FLAGS_COMPRESSED 0x40
+#define FLAGS_RESERVED 0x80
+
+/* Values for the band variable of the header */
+#define BAND_11A 0x01
+#define BAND_11B 0x02
+#define BAND_11G 0x04
+#define BAND_11A_TURBO 0x08
+#define BAND_SUPERG 0x10
+#define BAND_PUBLIC_SAFETY 0x20 /* 4.99 GHz public safety */
+#define BAND_11N_5GHZ 0x40
+#define BAND_11N_2_4GHZ 0x80
+
+static gboolean commview_ncf_read(wtap *wth, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info, gint64 *data_offset);
+static gboolean commview_ncf_seek_read(wtap *wth, gint64 seek_off,
+ wtap_rec *rec,
+ Buffer *buf, int *err, gchar **err_info);
+static gboolean commview_ncf_read_header(commview_ncf_header_t *cv_hdr, FILE_T fh,
+ int *err, gchar **err_info);
+static gboolean commview_ncf_dump(wtap_dumper *wdh, const wtap_rec *rec,
+ const guint8 *pd, int *err, gchar **err_info);
+
+static int commview_ncf_file_type_subtype = -1;
+static int commview_ncfx_file_type_subtype = -1;
+
+void register_commview(void);
+
+wtap_open_return_val
+commview_ncf_open(wtap *wth, int *err, gchar **err_info)
+{
+ commview_ncf_header_t cv_hdr;
+
+ if(!commview_ncf_read_header(&cv_hdr, wth->fh, err, err_info)) {
+ if (*err != 0 && *err != WTAP_ERR_SHORT_READ)
+ return WTAP_OPEN_ERROR;
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ /* If any of these fields do not match what we expect, bail out. */
+ if(cv_hdr.version != 0 ||
+ cv_hdr.year < 1970 || cv_hdr.year >= 2038 ||
+ cv_hdr.month < 1 || cv_hdr.month > 12 ||
+ cv_hdr.day < 1 || cv_hdr.day > 31 ||
+ cv_hdr.hours > 23 ||
+ cv_hdr.minutes > 59 ||
+ cv_hdr.seconds > 60 ||
+ cv_hdr.signal_level_percent > 100 ||
+ (cv_hdr.flags & FLAGS_RESERVED) != 0 ||
+ ((cv_hdr.flags & FLAGS_MEDIUM) != MEDIUM_ETHERNET &&
+ (cv_hdr.flags & FLAGS_MEDIUM) != MEDIUM_WIFI &&
+ (cv_hdr.flags & FLAGS_MEDIUM) != MEDIUM_TOKEN_RING))
+ return WTAP_OPEN_NOT_MINE; /* Not our kind of file */
+
+ /* No file header. Reset the fh to 0 so we can read the first packet */
+ if (file_seek(wth->fh, 0, SEEK_SET, err) == -1)
+ return WTAP_OPEN_ERROR;
+
+ /* Set up the pointers to the handlers for this file type */
+ wth->subtype_read = commview_ncf_read;
+ wth->subtype_seek_read = commview_ncf_seek_read;
+
+ wth->file_type_subtype = commview_ncf_file_type_subtype;
+ wth->file_encap = WTAP_ENCAP_PER_PACKET;
+ wth->file_tsprec = WTAP_TSPREC_USEC;
+
+ return WTAP_OPEN_MINE; /* Our kind of file */
+}
+
+static int
+commview_ncf_read_packet(FILE_T fh, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info)
+{
+ commview_ncf_header_t cv_hdr;
+ struct tm tm;
+ guint frequency;
+
+ if(!commview_ncf_read_header(&cv_hdr, fh, err, err_info))
+ return FALSE;
+ /*
+ * The maximum value of cv_hdr.data_len is 65535, which is less
+ * than WTAP_MAX_PACKET_SIZE_STANDARD will ever be, so we don't need to
+ * check it.
+ */
+
+ switch(cv_hdr.flags & FLAGS_MEDIUM) {
+
+ case MEDIUM_ETHERNET :
+ rec->rec_header.packet_header.pkt_encap = WTAP_ENCAP_ETHERNET;
+ rec->rec_header.packet_header.pseudo_header.eth.fcs_len = -1; /* Unknown */
+ break;
+
+ case MEDIUM_WIFI :
+ rec->rec_header.packet_header.pkt_encap = WTAP_ENCAP_IEEE_802_11_WITH_RADIO;
+ memset(&rec->rec_header.packet_header.pseudo_header.ieee_802_11, 0, sizeof(rec->rec_header.packet_header.pseudo_header.ieee_802_11));
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.fcs_len = -1; /* Unknown */
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.decrypted = FALSE;
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.datapad = FALSE;
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy = PHDR_802_11_PHY_UNKNOWN;
+ switch (cv_hdr.band) {
+
+ case BAND_11A:
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy = PHDR_802_11_PHY_11A;
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy_info.info_11a.has_channel_type = FALSE;
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy_info.info_11a.has_turbo_type = TRUE;
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy_info.info_11a.turbo_type =
+ PHDR_802_11A_TURBO_TYPE_NORMAL;
+ frequency = ieee80211_chan_to_mhz(cv_hdr.channel, FALSE);
+ break;
+
+ case BAND_11B:
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy = PHDR_802_11_PHY_11B;
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy_info.info_11b.has_short_preamble = FALSE;
+ frequency = ieee80211_chan_to_mhz(cv_hdr.channel, TRUE);
+ break;
+
+ case BAND_11G:
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy = PHDR_802_11_PHY_11G;
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy_info.info_11g.has_mode = TRUE;
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy_info.info_11g.mode =
+ PHDR_802_11G_MODE_NORMAL;
+ frequency = ieee80211_chan_to_mhz(cv_hdr.channel, TRUE);
+ break;
+
+ case BAND_11A_TURBO:
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy = PHDR_802_11_PHY_11A;
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy_info.info_11a.has_turbo_type = TRUE;
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy_info.info_11a.turbo_type =
+ PHDR_802_11A_TURBO_TYPE_TURBO;
+ frequency = ieee80211_chan_to_mhz(cv_hdr.channel, FALSE);
+ break;
+
+ case BAND_SUPERG:
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy = PHDR_802_11_PHY_11G;
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy_info.info_11g.has_mode = TRUE;
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy_info.info_11g.mode =
+ PHDR_802_11G_MODE_SUPER_G;
+ frequency = ieee80211_chan_to_mhz(cv_hdr.channel, TRUE);
+ break;
+
+ case BAND_11N_5GHZ:
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy = PHDR_802_11_PHY_11N;
+ frequency = ieee80211_chan_to_mhz(cv_hdr.channel, FALSE);
+ break;
+
+ case BAND_11N_2_4GHZ:
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy = PHDR_802_11_PHY_11N;
+ frequency = ieee80211_chan_to_mhz(cv_hdr.channel, TRUE);
+ break;
+
+ case BAND_PUBLIC_SAFETY:
+ /*
+ * XXX - what do we do here? What are the channel
+ * numbers? How do we distinguish the several
+ * different flavors of 4.9 GHz frequencies?
+ */
+ frequency = 0;
+ break;
+
+ default:
+ frequency = 0;
+ break;
+ }
+ if (frequency != 0) {
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.has_frequency = TRUE;
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.frequency = frequency;
+ }
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.has_channel = TRUE;
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.channel = cv_hdr.channel;
+
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.has_data_rate = TRUE;
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.data_rate =
+ cv_hdr.rate | (cv_hdr.direction << 8);
+
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.has_signal_percent = TRUE;
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.signal_percent = cv_hdr.signal_level_percent;
+
+ /*
+ * XXX - these are positive in captures I've seen; does
+ * that mean that they are the negative of the actual
+ * dBm value? (80 dBm is a bit more power than most
+ * countries' regulatory agencies are likely to allow
+ * any individual to have in their home. :-))
+ *
+ * XXX - sometimes these are 0; assume that means that no
+ * value is provided.
+ */
+ if (cv_hdr.signal_level_dbm != 0) {
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.signal_dbm = -cv_hdr.signal_level_dbm;
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.has_signal_dbm = TRUE;
+ }
+ if (cv_hdr.noise_level_dbm != 0) {
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.noise_dbm = -cv_hdr.noise_level_dbm;
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.has_noise_dbm = TRUE;
+ }
+ if (rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy == PHDR_802_11_PHY_UNKNOWN) {
+ /*
+ * We don't know they PHY, but we do have the
+ * data rate; try to guess it based on the
+ * data rate and center frequency.
+ */
+ if (RATE_IS_DSSS(rec->rec_header.packet_header.pseudo_header.ieee_802_11.data_rate)) {
+ /* 11b */
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy = PHDR_802_11_PHY_11B;
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy_info.info_11b.has_short_preamble = FALSE;
+ } else if (RATE_IS_OFDM(rec->rec_header.packet_header.pseudo_header.ieee_802_11.data_rate)) {
+ /* 11a or 11g, depending on the band. */
+ if (rec->rec_header.packet_header.pseudo_header.ieee_802_11.has_frequency) {
+ if (FREQ_IS_BG(rec->rec_header.packet_header.pseudo_header.ieee_802_11.frequency)) {
+ /* 11g */
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy = PHDR_802_11_PHY_11G;
+ } else {
+ /* 11a */
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy = PHDR_802_11_PHY_11A;
+ }
+ }
+ }
+ } else if (rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy == PHDR_802_11_PHY_11G) {
+ if (RATE_IS_DSSS(rec->rec_header.packet_header.pseudo_header.ieee_802_11.data_rate)) {
+ /* DSSS, so 11b. */
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy = PHDR_802_11_PHY_11B;
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy_info.info_11b.has_short_preamble = FALSE;
+ }
+ }
+ break;
+
+ case MEDIUM_TOKEN_RING :
+ rec->rec_header.packet_header.pkt_encap = WTAP_ENCAP_TOKEN_RING;
+ break;
+
+ default :
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("commview: unsupported encap for NCF: %u",
+ cv_hdr.flags & FLAGS_MEDIUM);
+ return FALSE;
+ }
+
+ tm.tm_year = cv_hdr.year - 1900;
+ tm.tm_mon = cv_hdr.month - 1;
+ tm.tm_mday = cv_hdr.day;
+ tm.tm_hour = cv_hdr.hours;
+ tm.tm_min = cv_hdr.minutes;
+ tm.tm_sec = cv_hdr.seconds;
+ tm.tm_isdst = -1;
+
+ rec->rec_type = REC_TYPE_PACKET;
+ rec->block = wtap_block_create(WTAP_BLOCK_PACKET);
+ rec->presence_flags = WTAP_HAS_TS;
+
+ rec->rec_header.packet_header.len = cv_hdr.data_len;
+ rec->rec_header.packet_header.caplen = cv_hdr.data_len;
+
+ rec->ts.secs = mktime(&tm);
+ rec->ts.nsecs = cv_hdr.usecs * 1000;
+
+ return wtap_read_packet_bytes(fh, buf, rec->rec_header.packet_header.caplen, err, err_info);
+}
+
+static gboolean
+commview_ncf_read(wtap *wth, wtap_rec *rec, Buffer *buf, int *err,
+ gchar **err_info, gint64 *data_offset)
+{
+ *data_offset = file_tell(wth->fh);
+
+ return commview_ncf_read_packet(wth->fh, rec, buf, err, err_info);
+}
+
+static gboolean
+commview_ncf_seek_read(wtap *wth, gint64 seek_off, wtap_rec *rec,
+ Buffer *buf, int *err, gchar **err_info)
+{
+ if(file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
+ return FALSE;
+
+ return commview_ncf_read_packet(wth->random_fh, rec, buf, err, err_info);
+}
+
+static gboolean
+commview_ncf_read_header(commview_ncf_header_t *cv_hdr, FILE_T fh, int *err,
+ gchar **err_info)
+{
+ if (!wtap_read_bytes_or_eof(fh, &cv_hdr->data_len, 2, err, err_info))
+ return FALSE;
+ if (!wtap_read_bytes(fh, &cv_hdr->source_data_len, 2, err, err_info))
+ return FALSE;
+ if (!wtap_read_bytes(fh, &cv_hdr->version, 1, err, err_info))
+ return FALSE;
+ if (!wtap_read_bytes(fh, &cv_hdr->year, 2, err, err_info))
+ return FALSE;
+ if (!wtap_read_bytes(fh, &cv_hdr->month, 1, err, err_info))
+ return FALSE;
+ if (!wtap_read_bytes(fh, &cv_hdr->day, 1, err, err_info))
+ return FALSE;
+ if (!wtap_read_bytes(fh, &cv_hdr->hours, 1, err, err_info))
+ return FALSE;
+ if (!wtap_read_bytes(fh, &cv_hdr->minutes, 1, err, err_info))
+ return FALSE;
+ if (!wtap_read_bytes(fh, &cv_hdr->seconds, 1, err, err_info))
+ return FALSE;
+ if (!wtap_read_bytes(fh, &cv_hdr->usecs, 4, err, err_info))
+ return FALSE;
+ if (!wtap_read_bytes(fh, &cv_hdr->flags, 1, err, err_info))
+ return FALSE;
+ if (!wtap_read_bytes(fh, &cv_hdr->signal_level_percent, 1, err, err_info))
+ return FALSE;
+ if (!wtap_read_bytes(fh, &cv_hdr->rate, 1, err, err_info))
+ return FALSE;
+ if (!wtap_read_bytes(fh, &cv_hdr->band, 1, err, err_info))
+ return FALSE;
+ if (!wtap_read_bytes(fh, &cv_hdr->channel, 1, err, err_info))
+ return FALSE;
+ if (!wtap_read_bytes(fh, &cv_hdr->direction, 1, err, err_info))
+ return FALSE;
+ if (!wtap_read_bytes(fh, &cv_hdr->signal_level_dbm, 1, err, err_info))
+ return FALSE;
+ if (!wtap_read_bytes(fh, &cv_hdr->noise_level_dbm, 1, err, err_info))
+ return FALSE;
+
+ /* Convert multi-byte values from little endian to host endian format */
+ cv_hdr->data_len = GUINT16_FROM_LE(cv_hdr->data_len);
+ cv_hdr->source_data_len = GUINT16_FROM_LE(cv_hdr->source_data_len);
+ cv_hdr->year = GUINT16_FROM_LE(cv_hdr->year);
+ cv_hdr->usecs = GUINT32_FROM_LE(cv_hdr->usecs);
+
+ return TRUE;
+}
+
+/* Returns 0 if we can write out the specified encapsulation type
+ * into a CommView format file. */
+static int
+commview_ncf_dump_can_write_encap(int encap)
+{
+ switch (encap) {
+
+ case WTAP_ENCAP_ETHERNET :
+ case WTAP_ENCAP_IEEE_802_11 :
+ case WTAP_ENCAP_IEEE_802_11_WITH_RADIO :
+ case WTAP_ENCAP_TOKEN_RING :
+ case WTAP_ENCAP_PER_PACKET :
+ return 0;
+
+ default:
+ return WTAP_ERR_UNWRITABLE_ENCAP;
+ }
+}
+
+/* Returns TRUE on success, FALSE on failure;
+ sets "*err" to an error code on failure */
+static gboolean
+commview_ncf_dump_open(wtap_dumper *wdh, int *err _U_, gchar **err_info _U_)
+{
+ wdh->subtype_write = commview_ncf_dump;
+
+ /* There is no file header to write out */
+ return TRUE;
+}
+
+/* Write a record for a packet to a dump file.
+ * Returns TRUE on success, FALSE on failure. */
+static gboolean
+commview_ncf_dump(wtap_dumper *wdh, const wtap_rec *rec, const guint8 *pd,
+ int *err, gchar **err_info _U_)
+{
+ commview_ncf_header_t cv_hdr = {0};
+ struct tm *tm;
+
+ /* We can only write packet records. */
+ if (rec->rec_type != REC_TYPE_PACKET) {
+ *err = WTAP_ERR_UNWRITABLE_REC_TYPE;
+ return FALSE;
+ }
+
+ /* Don't write out anything bigger than we can read.
+ * (The length field in packet headers is 16 bits, which
+ * imposes a hard limit.) */
+ if (rec->rec_header.packet_header.caplen > 65535) {
+ *err = WTAP_ERR_PACKET_TOO_LARGE;
+ return FALSE;
+ }
+
+ cv_hdr.data_len = GUINT16_TO_LE((guint16)rec->rec_header.packet_header.caplen);
+ cv_hdr.source_data_len = GUINT16_TO_LE((guint16)rec->rec_header.packet_header.caplen);
+ cv_hdr.version = 0;
+
+ tm = localtime(&rec->ts.secs);
+ if (tm != NULL) {
+ cv_hdr.year = GUINT16_TO_LE(tm->tm_year + 1900);
+ cv_hdr.month = tm->tm_mon + 1;
+ cv_hdr.day = tm->tm_mday;
+ cv_hdr.hours = tm->tm_hour;
+ cv_hdr.minutes = tm->tm_min;
+ cv_hdr.seconds = tm->tm_sec;
+ cv_hdr.usecs = GUINT32_TO_LE(rec->ts.nsecs / 1000);
+ } else {
+ /*
+ * Second before the Epoch.
+ */
+ cv_hdr.year = GUINT16_TO_LE(1969);
+ cv_hdr.month = 12;
+ cv_hdr.day = 31;
+ cv_hdr.hours = 23;
+ cv_hdr.minutes = 59;
+ cv_hdr.seconds = 59;
+ cv_hdr.usecs = 0;
+ }
+
+ switch(rec->rec_header.packet_header.pkt_encap) {
+
+ case WTAP_ENCAP_ETHERNET :
+ cv_hdr.flags |= MEDIUM_ETHERNET;
+ break;
+
+ case WTAP_ENCAP_IEEE_802_11 :
+ cv_hdr.flags |= MEDIUM_WIFI;
+ break;
+
+ case WTAP_ENCAP_IEEE_802_11_WITH_RADIO :
+ cv_hdr.flags |= MEDIUM_WIFI;
+
+ switch (rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy) {
+
+ case PHDR_802_11_PHY_11A:
+ /*
+ * If we don't know whether it's turbo, say it's
+ * not.
+ */
+ if (!rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy_info.info_11a.has_turbo_type ||
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy_info.info_11a.turbo_type == PHDR_802_11A_TURBO_TYPE_NORMAL)
+ cv_hdr.band = BAND_11A;
+ else
+ cv_hdr.band = BAND_11A_TURBO;
+ break;
+
+ case PHDR_802_11_PHY_11B:
+ cv_hdr.band = BAND_11B;
+ break;
+
+ case PHDR_802_11_PHY_11G:
+ /*
+ * If we don't know whether it's Super G, say it's
+ * not.
+ */
+ if (!rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy_info.info_11g.has_mode)
+ cv_hdr.band = BAND_11G;
+ else {
+ switch (rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy_info.info_11g.mode) {
+
+ case PHDR_802_11G_MODE_NORMAL:
+ cv_hdr.band = BAND_11G;
+ break;
+
+ case PHDR_802_11G_MODE_SUPER_G:
+ cv_hdr.band = BAND_SUPERG;
+ break;
+
+ default:
+ cv_hdr.band = BAND_11G;
+ break;
+ }
+ }
+ break;
+
+ case PHDR_802_11_PHY_11N:
+ /*
+ * Pick the band based on the frequency.
+ */
+ if (rec->rec_header.packet_header.pseudo_header.ieee_802_11.has_frequency) {
+ if (rec->rec_header.packet_header.pseudo_header.ieee_802_11.frequency > 2484) {
+ /* 5 GHz band */
+ cv_hdr.band = BAND_11N_5GHZ;
+ } else {
+ /* 2.4 GHz band */
+ cv_hdr.band = BAND_11N_2_4GHZ;
+ }
+ } else {
+ /* Band is unknown. */
+ cv_hdr.band = 0;
+ }
+ break;
+
+ default:
+ /*
+ * It's not documented how they handle 11ac,
+ * and they don't support the older PHYs.
+ */
+ cv_hdr.band = 0;
+ break;
+ }
+ cv_hdr.channel =
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.has_channel ?
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.channel :
+ 0;
+ cv_hdr.rate =
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.has_data_rate ?
+ (guint8)(rec->rec_header.packet_header.pseudo_header.ieee_802_11.data_rate & 0xFF) :
+ 0;
+ cv_hdr.direction =
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.has_data_rate ?
+ (guint8)((rec->rec_header.packet_header.pseudo_header.ieee_802_11.data_rate >> 8) & 0xFF) :
+ 0;
+ cv_hdr.signal_level_percent =
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.has_signal_percent ?
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.signal_percent :
+ 0;
+ cv_hdr.signal_level_dbm =
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.has_signal_dbm ?
+ -rec->rec_header.packet_header.pseudo_header.ieee_802_11.signal_dbm :
+ 0;
+ cv_hdr.noise_level_dbm =
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.has_noise_dbm ?
+ -rec->rec_header.packet_header.pseudo_header.ieee_802_11.noise_dbm :
+ 0;
+ break;
+
+ case WTAP_ENCAP_TOKEN_RING :
+ cv_hdr.flags |= MEDIUM_TOKEN_RING;
+ break;
+
+ default :
+ *err = WTAP_ERR_UNWRITABLE_ENCAP;
+ return FALSE;
+ }
+
+ if (!wtap_dump_file_write(wdh, &cv_hdr.data_len, 2, err))
+ return FALSE;
+ if (!wtap_dump_file_write(wdh, &cv_hdr.source_data_len, 2, err))
+ return FALSE;
+ if (!wtap_dump_file_write(wdh, &cv_hdr.version, 1, err))
+ return FALSE;
+ if (!wtap_dump_file_write(wdh, &cv_hdr.year, 2, err))
+ return FALSE;
+ if (!wtap_dump_file_write(wdh, &cv_hdr.month, 1, err))
+ return FALSE;
+ if (!wtap_dump_file_write(wdh, &cv_hdr.day, 1, err))
+ return FALSE;
+ if (!wtap_dump_file_write(wdh, &cv_hdr.hours, 1, err))
+ return FALSE;
+ if (!wtap_dump_file_write(wdh, &cv_hdr.minutes, 1, err))
+ return FALSE;
+ if (!wtap_dump_file_write(wdh, &cv_hdr.seconds, 1, err))
+ return FALSE;
+ if (!wtap_dump_file_write(wdh, &cv_hdr.usecs, 4, err))
+ return FALSE;
+ if (!wtap_dump_file_write(wdh, &cv_hdr.flags, 1, err))
+ return FALSE;
+ if (!wtap_dump_file_write(wdh, &cv_hdr.signal_level_percent, 1, err))
+ return FALSE;
+ if (!wtap_dump_file_write(wdh, &cv_hdr.rate, 1, err))
+ return FALSE;
+ if (!wtap_dump_file_write(wdh, &cv_hdr.band, 1, err))
+ return FALSE;
+ if (!wtap_dump_file_write(wdh, &cv_hdr.channel, 1, err))
+ return FALSE;
+ if (!wtap_dump_file_write(wdh, &cv_hdr.direction, 1, err))
+ return FALSE;
+ if (!wtap_dump_file_write(wdh, &cv_hdr.signal_level_dbm, 1, err))
+ return FALSE;
+ if (!wtap_dump_file_write(wdh, &cv_hdr.noise_level_dbm, 1, err))
+ return FALSE;
+ if (!wtap_dump_file_write(wdh, pd, rec->rec_header.packet_header.caplen, err))
+ return FALSE;
+ return TRUE;
+}
+
+typedef struct commview_ncfx_header {
+ guint32 data_len;
+ guint16 year;
+ guint8 month;
+ guint8 day;
+ guint8 hours;
+ guint8 minutes;
+ guint8 seconds;
+ guint32 usecs;
+ guint8 medium_type;
+ guint8 decryption_flag;
+ guint8 direction;
+ guint8 reserved1;
+ guint8 reserved2;
+} commview_ncfx_header_t;
+
+#define COMMVIEW_NCFX_HEADER_SIZE 20
+
+typedef struct commview_ncfx_rf_header {
+ guint16 header_len; /* includes extension headers */
+ guint16 status_modulation;
+ guint16 frequency_band;
+ guint16 channel;
+ guint8 noise_level_dbm; /* abs(noise in dBm) */
+ guint8 signal_level_dbm; /* abs(signal in dBm) */
+ guint8 signal_level_percent;
+ guint8 reserved;
+ guint32 phy_rate; /* in 100Kbps units */
+ guint32 extensions_present;
+} commview_ncfx_rf_header_t;
+
+#define COMMVIEW_NCFX_RF_HEADER_SIZE 20
+
+typedef struct commview_ncfx_mcs_header {
+ guint8 mcs_index;
+ guint8 n_streams;
+ guint8 channel_width;
+ guint8 guard_interval;
+} commview_ncfx_mcs_header_t;
+
+#define COMMVIEW_NCFX_MCS_HEADER_SIZE 4
+
+/*
+ * Bit-field positions for various fields in the status_modulation variable
+ * of the header.
+ */
+#define STATUS_MODULATION_BAD_FCS 0x01
+#define STATUS_MODULATION_HT_PHY 0x02
+#define STATUS_MODULATION_VHT_PHY 0x04
+#define STATUS_MODULATION_HE_PHY 0x08
+#define STATUS_MODULATION_HE_OFDMA 0x10
+
+/* Values for the frequency_band variable of the header */
+#define BAND_5GHZ 0x40
+#define BAND_2_4GHZ 0x80
+
+/* Presence bits */
+#define PRESENCE_MCS_HEADER 0x00000001 /* type 0, bit 0 */
+
+static gboolean commview_ncfx_read(wtap *wth, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info, gint64 *data_offset);
+static gboolean commview_ncfx_seek_read(wtap *wth, gint64 seek_off,
+ wtap_rec *rec, Buffer *buf, int *err, gchar **err_info);
+static gboolean commview_ncfx_read_header(commview_ncfx_header_t *cv_hdr,
+ FILE_T fh, int *err, gchar **err_info);
+static gboolean commview_ncfx_read_rf_header(commview_ncfx_rf_header_t *cv_rf_hdr,
+ FILE_T fh, int *err, gchar **err_info);
+static gboolean commview_ncfx_read_mcs_header(commview_ncfx_mcs_header_t *cv_mcs_hdr,
+ FILE_T fh, int *err, gchar **err_info);
+static gboolean commview_ncfx_dump(wtap_dumper *wdh, const wtap_rec *rec,
+ const guint8 *pd, int *err, gchar **err_info);
+
+wtap_open_return_val
+commview_ncfx_open(wtap *wth, int *err, gchar **err_info)
+{
+ commview_ncfx_header_t cv_hdr;
+
+ if(!commview_ncfx_read_header(&cv_hdr, wth->fh, err, err_info)) {
+ if (*err != 0 && *err != WTAP_ERR_SHORT_READ)
+ return WTAP_OPEN_ERROR;
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ /* If any of these fields do not match what we expect, bail out. */
+ if(cv_hdr.year < 2000 || /* XXX - when was this format introduced? */
+ cv_hdr.month < 1 || cv_hdr.month > 12 ||
+ cv_hdr.day < 1 || cv_hdr.day > 31 ||
+ cv_hdr.hours > 23 ||
+ cv_hdr.minutes > 59 ||
+ cv_hdr.seconds > 60)
+ return WTAP_OPEN_NOT_MINE; /* Not our kind of file */
+ switch (cv_hdr.medium_type) {
+
+ case MEDIUM_ETHERNET:
+ if (cv_hdr.direction != 0x00 &&
+ cv_hdr.direction != 0x01 &&
+ cv_hdr.direction != 0x02)
+ return WTAP_OPEN_NOT_MINE; /* Not our kind of file */
+ break;
+
+ case MEDIUM_WIFI:
+ if (cv_hdr.decryption_flag != 0x00 &&
+ cv_hdr.decryption_flag != 0x01)
+ return WTAP_OPEN_NOT_MINE; /* Not our kind of file */
+ if (cv_hdr.direction != 0x00)
+ return WTAP_OPEN_NOT_MINE; /* Not our kind of file */
+ break;
+
+ default:
+ return WTAP_OPEN_NOT_MINE; /* Not our kind of file */
+ }
+
+ /* No file header. Reset the fh to 0 so we can read the first packet */
+ if (file_seek(wth->fh, 0, SEEK_SET, err) == -1)
+ return WTAP_OPEN_ERROR;
+
+ /* Set up the pointers to the handlers for this file type */
+ wth->subtype_read = commview_ncfx_read;
+ wth->subtype_seek_read = commview_ncfx_seek_read;
+
+ wth->file_type_subtype = commview_ncfx_file_type_subtype;
+ wth->file_encap = WTAP_ENCAP_PER_PACKET;
+ wth->file_tsprec = WTAP_TSPREC_USEC;
+
+ return WTAP_OPEN_MINE; /* Our kind of file */
+}
+
+static int
+commview_ncfx_read_packet(FILE_T fh, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info)
+{
+ commview_ncfx_header_t cv_hdr;
+ guint32 length_remaining;
+ struct tm tm;
+ commview_ncfx_rf_header_t cv_rf_hdr;
+ guint frequency;
+ commview_ncfx_mcs_header_t cv_mcs_hdr;
+
+ if (!commview_ncfx_read_header(&cv_hdr, fh, err, err_info))
+ return FALSE;
+
+ /* Amount of data remaining in the record, after the header */
+ length_remaining = cv_hdr.data_len - COMMVIEW_NCFX_HEADER_SIZE;
+
+ switch(cv_hdr.medium_type) {
+
+ case MEDIUM_ETHERNET :
+ rec->rec_header.packet_header.pkt_encap = WTAP_ENCAP_ETHERNET;
+ rec->rec_header.packet_header.pseudo_header.eth.fcs_len = -1; /* Unknown */
+ break;
+
+ case MEDIUM_WIFI :
+ rec->rec_header.packet_header.pkt_encap = WTAP_ENCAP_IEEE_802_11_WITH_RADIO;
+ memset(&rec->rec_header.packet_header.pseudo_header.ieee_802_11, 0, sizeof(rec->rec_header.packet_header.pseudo_header.ieee_802_11));
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.fcs_len = 0; /* No FCS */
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.decrypted = (cv_hdr.decryption_flag == 0x01);
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.datapad = FALSE;
+
+ /*
+ * Make sure we have enough data left for the RF header.
+ */
+ if (length_remaining < COMMVIEW_NCFX_RF_HEADER_SIZE) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("commview: RF header goes past the NCFX data length %u",
+ cv_hdr.data_len);
+ return FALSE;
+ }
+ length_remaining -= COMMVIEW_NCFX_RF_HEADER_SIZE;
+
+ /*
+ * Read the RF header.
+ */
+ if (!commview_ncfx_read_rf_header(&cv_rf_hdr, fh, err, err_info))
+ return FALSE;
+ if (cv_rf_hdr.status_modulation & STATUS_MODULATION_HE_PHY)
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy = PHDR_802_11_PHY_11AX;
+ else if (cv_rf_hdr.status_modulation & STATUS_MODULATION_VHT_PHY)
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy = PHDR_802_11_PHY_11AC;
+ else if (cv_rf_hdr.status_modulation & STATUS_MODULATION_HT_PHY)
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy = PHDR_802_11_PHY_11N;
+ else {
+ /*
+ * Unknown PHY, for now.
+ */
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy = PHDR_802_11_PHY_UNKNOWN;
+ }
+ switch (cv_rf_hdr.frequency_band) {
+
+ case BAND_5GHZ:
+ frequency = ieee80211_chan_to_mhz(cv_rf_hdr.channel, FALSE);
+ if (rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy == PHDR_802_11_PHY_UNKNOWN) {
+ /*
+ * None of the modulation bits were set, so
+ * this is presumably the 11a OFDM PHY.
+ */
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy = PHDR_802_11_PHY_11A;
+ }
+ break;
+
+ case BAND_2_4GHZ:
+ frequency = ieee80211_chan_to_mhz(cv_rf_hdr.channel, TRUE);
+ if (rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy == PHDR_802_11_PHY_UNKNOWN) {
+ /*
+ * None of the modulation bits were set, so
+ * guess the PHY based on the data rate.
+ *
+ * cv_rf_hdr.phy_rate is in units of 100
+ * Kbits/s.
+ */
+ if (cv_rf_hdr.phy_rate == 10 /* 1 Mb/s */ ||
+ cv_rf_hdr.phy_rate == 20 /* 2 Mb/s */ ||
+ cv_rf_hdr.phy_rate == 55 /* 5.5 Mb/s */ ||
+ cv_rf_hdr.phy_rate == 110 /* 11 Mb/s */ ||
+ cv_rf_hdr.phy_rate == 220 /* 22 Mb/s */ ||
+ cv_rf_hdr.phy_rate == 330 /* 33 Mb/s */)
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy = PHDR_802_11_PHY_11B;
+ else
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy = PHDR_802_11_PHY_11G;
+ }
+ break;
+
+ default:
+ frequency = 0;
+ break;
+ }
+ if (frequency != 0) {
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.has_frequency = TRUE;
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.frequency = frequency;
+ }
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.has_channel = TRUE;
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.channel = cv_rf_hdr.channel;
+
+ /*
+ * cv_rf_hdr.phy_rate is in units of 100 Kbits/s.
+ *
+ * pseudo_header.ieee_802_11.data_rate is in units of 500
+ * Kbits/s.
+ */
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.has_data_rate = TRUE;
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.data_rate =
+ cv_rf_hdr.phy_rate/5;
+
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.has_signal_percent = TRUE;
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.signal_percent = cv_rf_hdr.signal_level_percent;
+
+ /*
+ * These is the absolute value of the signal and noise,
+ * in dBm. The value is the negative of that.
+ *
+ * XXX - sometimes these are 0; assume that means that no
+ * value is provided.
+ */
+ if (cv_rf_hdr.signal_level_dbm != 0) {
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.signal_dbm = -cv_rf_hdr.signal_level_dbm;
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.has_signal_dbm = TRUE;
+ }
+ if (cv_rf_hdr.noise_level_dbm != 0) {
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.noise_dbm = -cv_rf_hdr.noise_level_dbm;
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.has_noise_dbm = TRUE;
+ }
+
+ if (cv_rf_hdr.extensions_present & PRESENCE_MCS_HEADER) {
+ /*
+ * Make sure we have enough data left for the
+ * MCS header.
+ */
+ if (length_remaining < COMMVIEW_NCFX_MCS_HEADER_SIZE) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("commview: MCS header goes past the NCFX data length %u",
+ cv_hdr.data_len);
+ return FALSE;
+ }
+ length_remaining -= COMMVIEW_NCFX_MCS_HEADER_SIZE;
+
+ /*
+ * Read the MCS header.
+ */
+ if (!commview_ncfx_read_mcs_header(&cv_mcs_hdr, fh,
+ err, err_info))
+ return FALSE;
+ switch (rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy) {
+
+ case PHDR_802_11_PHY_11N:
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy_info.info_11n.has_mcs_index = TRUE;
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy_info.info_11n.mcs_index = cv_mcs_hdr.mcs_index;
+ /* number of STBC streams? */
+ switch (cv_mcs_hdr.channel_width) {
+
+ case 0x00:
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy_info.info_11n.has_bandwidth = TRUE;
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy_info.info_11n.bandwidth = PHDR_802_11_BANDWIDTH_20_MHZ;
+ break;
+
+ case 0x01:
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy_info.info_11n.has_bandwidth = TRUE;
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy_info.info_11n.bandwidth = PHDR_802_11_BANDWIDTH_40_MHZ;
+ break;
+
+ default:
+ break;
+ }
+ /* Guard interval? */
+ break;
+
+ case PHDR_802_11_PHY_11AC:
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy_info.info_11ac.mcs[0] = cv_mcs_hdr.mcs_index;
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy_info.info_11ac.mcs[1] = 0;
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy_info.info_11ac.mcs[2] = 0;
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy_info.info_11ac.mcs[3] = 0;
+ /* Remaining MCS indices? */
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy_info.info_11ac.nss[0] = cv_mcs_hdr.n_streams;
+ switch (cv_mcs_hdr.channel_width) {
+
+ case 0x00:
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy_info.info_11ac.has_bandwidth = TRUE;
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy_info.info_11ac.bandwidth = PHDR_802_11_BANDWIDTH_20_MHZ;
+ break;
+
+ case 0x01:
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy_info.info_11ac.has_bandwidth = TRUE;
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy_info.info_11ac.bandwidth = PHDR_802_11_BANDWIDTH_40_MHZ;
+ break;
+
+ case 0x02:
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy_info.info_11ac.has_bandwidth = TRUE;
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy_info.info_11ac.bandwidth = PHDR_802_11_BANDWIDTH_80_MHZ;
+ break;
+
+ default:
+ break;
+ }
+ /* Guard interval? */
+ break;
+
+ case PHDR_802_11_PHY_11AX:
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy_info.info_11ax.has_mcs_index = TRUE;
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy_info.info_11ax.mcs = cv_mcs_hdr.mcs_index;
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy_info.info_11ax.nsts = cv_mcs_hdr.n_streams;
+ /* Bandwidth stuff? */
+ /* Guard interval? */
+ break;
+
+ default:
+ break;
+ }
+ }
+ break;
+
+ default :
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("commview: unsupported encap for NCFX: %u",
+ cv_hdr.medium_type);
+ return FALSE;
+ }
+
+ tm.tm_year = cv_hdr.year - 1900;
+ tm.tm_mon = cv_hdr.month - 1;
+ tm.tm_mday = cv_hdr.day;
+ tm.tm_hour = cv_hdr.hours;
+ tm.tm_min = cv_hdr.minutes;
+ tm.tm_sec = cv_hdr.seconds;
+ tm.tm_isdst = -1;
+
+ rec->rec_type = REC_TYPE_PACKET;
+ rec->block = wtap_block_create(WTAP_BLOCK_PACKET);
+ rec->presence_flags = WTAP_HAS_TS;
+
+ if (length_remaining > WTAP_MAX_PACKET_SIZE_STANDARD) {
+ /*
+ * Probably a corrupt capture file; don't blow up trying
+ * to allocate space for an immensely-large packet.
+ */
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("commview: File has %u-byte packet, bigger than maximum of %u",
+ length_remaining, WTAP_MAX_PACKET_SIZE_STANDARD);
+ return FALSE;
+ }
+
+ rec->rec_header.packet_header.len = length_remaining;
+ rec->rec_header.packet_header.caplen = length_remaining;
+
+ rec->ts.secs = mktime(&tm);
+ rec->ts.nsecs = cv_hdr.usecs * 1000;
+
+ return wtap_read_packet_bytes(fh, buf, rec->rec_header.packet_header.caplen, err, err_info);
+}
+
+static gboolean
+commview_ncfx_read(wtap *wth, wtap_rec *rec, Buffer *buf, int *err,
+ gchar **err_info, gint64 *data_offset)
+{
+ *data_offset = file_tell(wth->fh);
+
+ return commview_ncfx_read_packet(wth->fh, rec, buf, err, err_info);
+}
+
+static gboolean
+commview_ncfx_seek_read(wtap *wth, gint64 seek_off, wtap_rec *rec,
+ Buffer *buf, int *err, gchar **err_info)
+{
+ if(file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
+ return FALSE;
+
+ return commview_ncfx_read_packet(wth->random_fh, rec, buf, err, err_info);
+}
+
+static gboolean
+commview_ncfx_read_header(commview_ncfx_header_t *cv_hdr, FILE_T fh, int *err,
+ gchar **err_info)
+{
+ if (!wtap_read_bytes_or_eof(fh, &cv_hdr->data_len, 4, err, err_info))
+ return FALSE;
+
+ /* Convert data length from little endian to host endian format */
+ cv_hdr->data_len = GUINT32_FROM_LE(cv_hdr->data_len);
+
+ /* It must be at least the length of the general header. */
+ if (cv_hdr->data_len < COMMVIEW_NCFX_HEADER_SIZE) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("commview: NCFX data length %u < %u",
+ cv_hdr->data_len,
+ COMMVIEW_NCFX_HEADER_SIZE);
+ return FALSE;
+ }
+
+ if (!wtap_read_bytes(fh, &cv_hdr->year, 2, err, err_info))
+ return FALSE;
+ if (!wtap_read_bytes(fh, &cv_hdr->month, 1, err, err_info))
+ return FALSE;
+ if (!wtap_read_bytes(fh, &cv_hdr->day, 1, err, err_info))
+ return FALSE;
+ if (!wtap_read_bytes(fh, &cv_hdr->hours, 1, err, err_info))
+ return FALSE;
+ if (!wtap_read_bytes(fh, &cv_hdr->minutes, 1, err, err_info))
+ return FALSE;
+ if (!wtap_read_bytes(fh, &cv_hdr->seconds, 1, err, err_info))
+ return FALSE;
+ if (!wtap_read_bytes(fh, &cv_hdr->usecs, 4, err, err_info))
+ return FALSE;
+ if (!wtap_read_bytes(fh, &cv_hdr->medium_type, 1, err, err_info))
+ return FALSE;
+ if (!wtap_read_bytes(fh, &cv_hdr->decryption_flag, 1, err, err_info))
+ return FALSE;
+ if (!wtap_read_bytes(fh, &cv_hdr->direction, 1, err, err_info))
+ return FALSE;
+ if (!wtap_read_bytes(fh, &cv_hdr->reserved1, 1, err, err_info))
+ return FALSE;
+ if (!wtap_read_bytes(fh, &cv_hdr->reserved2, 1, err, err_info))
+ return FALSE;
+
+ /* Convert multi-byte values from little endian to host endian format */
+ cv_hdr->year = GUINT16_FROM_LE(cv_hdr->year);
+ cv_hdr->usecs = GUINT32_FROM_LE(cv_hdr->usecs);
+
+ return TRUE;
+}
+
+static gboolean
+commview_ncfx_read_rf_header(commview_ncfx_rf_header_t *cv_rf_hdr, FILE_T fh,
+ int *err, gchar **err_info)
+{
+ if (!wtap_read_bytes(fh, &cv_rf_hdr->header_len, 2, err, err_info))
+ return FALSE;
+
+ /* Convert header length from little endian to host endian format */
+ cv_rf_hdr->header_len = GUINT16_FROM_LE(cv_rf_hdr->header_len);
+
+ if (!wtap_read_bytes(fh, &cv_rf_hdr->status_modulation, 2, err, err_info))
+ return FALSE;
+ if (!wtap_read_bytes(fh, &cv_rf_hdr->frequency_band, 2, err, err_info))
+ return FALSE;
+ if (!wtap_read_bytes(fh, &cv_rf_hdr->channel, 2, err, err_info))
+ return FALSE;
+ if (!wtap_read_bytes(fh, &cv_rf_hdr->noise_level_dbm, 1, err, err_info))
+ return FALSE;
+ if (!wtap_read_bytes(fh, &cv_rf_hdr->signal_level_dbm, 1, err, err_info))
+ return FALSE;
+ if (!wtap_read_bytes(fh, &cv_rf_hdr->signal_level_percent, 1, err, err_info))
+ return FALSE;
+ if (!wtap_read_bytes(fh, &cv_rf_hdr->reserved, 1, err, err_info))
+ return FALSE;
+ if (!wtap_read_bytes(fh, &cv_rf_hdr->phy_rate, 4, err, err_info))
+ return FALSE;
+ if (!wtap_read_bytes(fh, &cv_rf_hdr->extensions_present, 4, err, err_info))
+ return FALSE;
+
+ /* Convert remaining multi-byte values from little endian to host endian format */
+ cv_rf_hdr->status_modulation = GUINT16_FROM_LE(cv_rf_hdr->status_modulation);
+ cv_rf_hdr->frequency_band = GUINT16_FROM_LE(cv_rf_hdr->frequency_band);
+ cv_rf_hdr->channel = GUINT16_FROM_LE(cv_rf_hdr->channel);
+ cv_rf_hdr->phy_rate = GUINT32_FROM_LE(cv_rf_hdr->phy_rate);
+ cv_rf_hdr->extensions_present = GUINT32_FROM_LE(cv_rf_hdr->extensions_present);
+
+ return TRUE;
+}
+
+static gboolean
+commview_ncfx_read_mcs_header(commview_ncfx_mcs_header_t *cv_mcs_hdr, FILE_T fh,
+ int *err, gchar **err_info)
+{
+ if (!wtap_read_bytes(fh, &cv_mcs_hdr->mcs_index, 1, err, err_info))
+ return FALSE;
+ if (!wtap_read_bytes(fh, &cv_mcs_hdr->n_streams, 1, err, err_info))
+ return FALSE;
+ if (!wtap_read_bytes(fh, &cv_mcs_hdr->channel_width, 1, err, err_info))
+ return FALSE;
+ if (!wtap_read_bytes(fh, &cv_mcs_hdr->guard_interval, 1, err, err_info))
+ return FALSE;
+
+ return TRUE;
+}
+
+/* Returns 0 if we can write out the specified encapsulation type
+ * into a CommView format file. */
+static int
+commview_ncfx_dump_can_write_encap(int encap)
+{
+ switch (encap) {
+
+ case WTAP_ENCAP_ETHERNET :
+ case WTAP_ENCAP_IEEE_802_11 :
+ case WTAP_ENCAP_IEEE_802_11_WITH_RADIO :
+ case WTAP_ENCAP_PER_PACKET :
+ return 0;
+
+ default:
+ return WTAP_ERR_UNWRITABLE_ENCAP;
+ }
+}
+
+/* Returns TRUE on success, FALSE on failure;
+ sets "*err" to an error code on failure */
+static gboolean
+commview_ncfx_dump_open(wtap_dumper *wdh, int *err _U_, gchar **err_info _U_)
+{
+ wdh->subtype_write = commview_ncfx_dump;
+
+ /* There is no file header to write out */
+ return TRUE;
+}
+
+/* Write a record for a packet to a dump file.
+ * Returns TRUE on success, FALSE on failure. */
+static gboolean
+commview_ncfx_dump(wtap_dumper *wdh, const wtap_rec *rec, const guint8 *pd,
+ int *err, gchar **err_info _U_)
+{
+ commview_ncfx_header_t cv_hdr = {0};
+ struct tm *tm;
+
+ /* We can only write packet records. */
+ if (rec->rec_type != REC_TYPE_PACKET) {
+ *err = WTAP_ERR_UNWRITABLE_REC_TYPE;
+ return FALSE;
+ }
+
+ /* Don't write out anything bigger than we can read.
+ * (The length field in packet headers is 16 bits, which
+ * imposes a hard limit.) */
+ if (rec->rec_header.packet_header.caplen > 65535) {
+ *err = WTAP_ERR_PACKET_TOO_LARGE;
+ return FALSE;
+ }
+
+ cv_hdr.data_len = GUINT32_TO_LE((guint32)rec->rec_header.packet_header.caplen);
+
+ tm = localtime(&rec->ts.secs);
+ if (tm != NULL) {
+ cv_hdr.year = GUINT16_TO_LE(tm->tm_year + 1900);
+ cv_hdr.month = tm->tm_mon + 1;
+ cv_hdr.day = tm->tm_mday;
+ cv_hdr.hours = tm->tm_hour;
+ cv_hdr.minutes = tm->tm_min;
+ cv_hdr.seconds = tm->tm_sec;
+ cv_hdr.usecs = GUINT32_TO_LE(rec->ts.nsecs / 1000);
+ } else {
+ /*
+ * Second before the Epoch.
+ */
+ cv_hdr.year = GUINT16_TO_LE(1969);
+ cv_hdr.month = 12;
+ cv_hdr.day = 31;
+ cv_hdr.hours = 23;
+ cv_hdr.minutes = 59;
+ cv_hdr.seconds = 59;
+ cv_hdr.usecs = 0;
+ }
+ cv_hdr.reserved1 = 0;
+ cv_hdr.reserved2 = 0;
+
+ switch(rec->rec_header.packet_header.pkt_encap) {
+
+ case WTAP_ENCAP_ETHERNET :
+ cv_hdr.medium_type = MEDIUM_ETHERNET;
+ cv_hdr.decryption_flag = 0x00;
+ cv_hdr.direction = 0x00; /* what does this mean? */
+ break;
+
+ case WTAP_ENCAP_IEEE_802_11 :
+ /* XXX - the claim is that the RF header is mandatory */
+ cv_hdr.medium_type = MEDIUM_WIFI;
+ break;
+
+ case WTAP_ENCAP_IEEE_802_11_WITH_RADIO :
+ cv_hdr.medium_type = MEDIUM_WIFI;
+
+#if 0
+ switch (rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy) {
+
+ case PHDR_802_11_PHY_11N:
+ cv_hdr.status_modulation = STATUS_MODULATION_HT_PHY;
+ break;
+
+ case PHDR_802_11_PHY_11AC:
+ cv_hdr.status_modulation = STATUS_MODULATION_VHT_PHY;
+ break;
+
+ case PHDR_802_11_PHY_11AX:
+ cv_hdr.status_modulation = STATUS_MODULATION_HE_PHY;
+ break;
+
+ default:
+ cv_hdr.status_modulation = 0;
+ break;
+ }
+
+ /*
+ * Pick the band based on the frequency.
+ */
+ if (rec->rec_header.packet_header.pseudo_header.ieee_802_11.has_frequency) {
+ if (rec->rec_header.packet_header.pseudo_header.ieee_802_11.frequency > 2484) {
+ /* 5 GHz band */
+ cv_hdr.frequency_band = BAND_5GHZ;
+ } else {
+ /* 2.4 GHz band */
+ cv_hdr.frequency_band = BAND_2_4GHZ;
+ }
+ } else {
+ /* Band is unknown. */
+ cv_hdr.band = 0;
+ }
+
+ cv_hdr.channel =
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.has_channel ?
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.channel :
+ 0;
+ cv_hdr.noise_level_dbm =
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.has_noise_dbm ?
+ -rec->rec_header.packet_header.pseudo_header.ieee_802_11.noise_dbm :
+ 0;
+ cv_hdr.signal_level_dbm =
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.has_signal_dbm ?
+ -rec->rec_header.packet_header.pseudo_header.ieee_802_11.signal_dbm :
+ 0;
+ cv_hdr.signal_level_percent =
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.has_signal_percent ?
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.signal_percent :
+ 0;
+ cv_hdr.reserved = 0;
+ cv_hdr.phy_rate =
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.has_data_rate ?
+ (guint32)(rec->rec_header.packet_header.pseudo_header.ieee_802_11.data_rate & 0xFF) :
+ 0;
+#endif
+ break;
+
+ default :
+ *err = WTAP_ERR_UNWRITABLE_ENCAP;
+ return FALSE;
+ }
+
+ if (!wtap_dump_file_write(wdh, &cv_hdr.data_len, 4, err))
+ return FALSE;
+ if (!wtap_dump_file_write(wdh, &cv_hdr.year, 2, err))
+ return FALSE;
+ if (!wtap_dump_file_write(wdh, &cv_hdr.month, 1, err))
+ return FALSE;
+ if (!wtap_dump_file_write(wdh, &cv_hdr.day, 1, err))
+ return FALSE;
+ if (!wtap_dump_file_write(wdh, &cv_hdr.hours, 1, err))
+ return FALSE;
+ if (!wtap_dump_file_write(wdh, &cv_hdr.minutes, 1, err))
+ return FALSE;
+ if (!wtap_dump_file_write(wdh, &cv_hdr.seconds, 1, err))
+ return FALSE;
+ if (!wtap_dump_file_write(wdh, &cv_hdr.usecs, 4, err))
+ return FALSE;
+ if (!wtap_dump_file_write(wdh, &cv_hdr.medium_type, 1, err))
+ return FALSE;
+ if (!wtap_dump_file_write(wdh, &cv_hdr.decryption_flag, 1, err))
+ return FALSE;
+ if (!wtap_dump_file_write(wdh, &cv_hdr.direction, 1, err))
+ return FALSE;
+ if (!wtap_dump_file_write(wdh, &cv_hdr.reserved1, 1, err))
+ return FALSE;
+ if (!wtap_dump_file_write(wdh, &cv_hdr.reserved2, 1, err))
+ return FALSE;
+
+ /* XXX - RF and MCS headers */
+
+ if (!wtap_dump_file_write(wdh, pd, rec->rec_header.packet_header.caplen, err))
+ return FALSE;
+
+ return TRUE;
+}
+
+static const struct supported_block_type commview_blocks_supported[] = {
+ /*
+ * We support packet blocks, with no comments or other options.
+ */
+ { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
+};
+
+static const struct file_type_subtype_info commview_ncf_info = {
+ "TamoSoft CommView NCF", "commview-ncf", "ncf", NULL,
+ FALSE, BLOCKS_SUPPORTED(commview_blocks_supported),
+ commview_ncf_dump_can_write_encap, commview_ncf_dump_open, NULL
+};
+
+static const struct file_type_subtype_info commview_ncfx_info = {
+ "TamoSoft CommView NCFX", "commview-ncfx", "ncfx", NULL,
+ FALSE, BLOCKS_SUPPORTED(commview_blocks_supported),
+ commview_ncfx_dump_can_write_encap, commview_ncfx_dump_open, NULL
+};
+
+void register_commview(void)
+{
+ commview_ncf_file_type_subtype = wtap_register_file_type_subtype(&commview_ncf_info);
+ commview_ncfx_file_type_subtype = wtap_register_file_type_subtype(&commview_ncfx_info);
+
+ /*
+ * Register name for backwards compatibility with the
+ * wtap_filetypes table in Lua.
+ *
+ * We don't need to register the new type, as the Wireshark
+ * version with which we're providing backwards compatibility
+ * didn't support the NCFX format. New code should fetch
+ * the file type/subtype with wtap_name_to_file_type_subtype().
+ */
+ wtap_register_backwards_compatibility_lua_name("COMMVIEW",
+ commview_ncf_file_type_subtype);
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/wiretap/commview.h b/wiretap/commview.h
new file mode 100644
index 00000000..59efd7c6
--- /dev/null
+++ b/wiretap/commview.h
@@ -0,0 +1,20 @@
+/** @file
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __COMMVIEW_H__
+#define __COMMVIEW_H__
+#include <glib.h>
+#include "ws_symbol_export.h"
+
+wtap_open_return_val commview_ncf_open(wtap *wth, int *err, gchar **err_info);
+
+wtap_open_return_val commview_ncfx_open(wtap *wth, int *err, gchar **err_info);
+
+#endif /* __COMMVIEW_H__ */
+
diff --git a/wiretap/cosine.c b/wiretap/cosine.c
new file mode 100644
index 00000000..f9adc43c
--- /dev/null
+++ b/wiretap/cosine.c
@@ -0,0 +1,528 @@
+/* cosine.c
+ *
+ * CoSine IPNOS L2 debug output parsing
+ * Copyright (c) 2002 by Motonori Shindo <motonori@shin.do>
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "config.h"
+#include "wtap-int.h"
+#include "cosine.h"
+#include "file_wrappers.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+/*
+
+ IPNOS: CONFIG VPN(100) VR(1.1.1.1)# diags
+ ipnos diags: Control (1/0) :: layer-2 ?
+ Registered commands for area "layer-2"
+ apply-pkt-log-profile Configure packet logging on an interface
+ create-pkt-log-profile Set packet-log-profile to be used for packet logging (see layer-2 pkt-log)
+ detail Get Layer 2 low-level details
+
+ ipnos diags: Control (1/0) :: layer-2 create ?
+ create-pkt-log-profile <pkt-log-profile-id ctl-tx-trace-length ctl-rx-trace-length data-tx-trace-length data-rx-trace-length pe-logging-or-control-blade>
+
+ ipnos diags: Control (1/0) :: layer-2 create 1 32 32 0 0 0
+ ipnos diags: Control (1/0) :: layer-2 create 2 32 32 100 100 0
+ ipnos diags: Control (1/0) :: layer-2 apply ?
+ apply-pkt-log-profile <slot port channel subif pkt-log-profile-id>
+
+ ipnos diags: Control (1/0) :: layer-2 apply 3 0x0701 100 0 1
+ Successfully applied packet-log-profile on LI
+
+ -- Note that only the control packets are logged because the data packet size parameters are 0 in profile 1
+ IPNOS: CONFIG VPN(200) VR(3.3.3.3)# ping 20.20.20.43
+ vpn 200 : [max tries 4, timeout 5 seconds, data length 64 bytes, ttl 255]
+ ping #1 ok, RTT 0.000 seconds
+ ping #2 ok, RTT 0.000 seconds
+ ping #3 ok, RTT 0.000 seconds
+ ping #4 ok, RTT 0.000 seconds
+ [finished]
+
+ IPNOS: CONFIG VPN(200) VR(3.3.3.3)# 2000-2-1,18:19:46.8: l2-tx (PPP:3/7/1:100), Length:16, Pro:0, Off:0, Pri:0, RM:0, Err:0 [0x4000, 0x0]
+
+
+ 2000-2-1,18:19:46.8: l2-rx (PPP:3/7/1:100), Length:16, Pro:0, Off:0, Pri:0, RM:0, Err:0 [0x4001, 0x30000]
+
+ 2000-2-1,18:19:46.8: l2-tx (PPP:3/7/1:100), Length:16, Pro:0, Off:0, Pri:0, RM:0, Err:0 [0x4000, 0x0]
+
+ 2000-2-1,18:19:46.8: l2-rx (PPP:3/7/1:100), Length:16, Pro:0, Off:0, Pri:0, RM:0, Err:0 [0x4001, 0x8030000]
+
+ ipnos diags: Control (1/0) :: layer-2 apply 3 0x0701 100 0 0
+ Successfully applied packet-log-profile on LI
+ ipnos diags: Control (1/0) :: layer-2 apply 3 0x0701 100 0 2
+ Successfully applied packet-log-profile on LI
+
+ -- Note that both control and data packets are logged because the data packet size parameter is 100 in profile 2
+ Please ignore the event-log messages getting mixed up with the ping command
+ ping 20.20.20.43 cou2000-2-1,18:20:17.0: l2-tx (PPP:3/7/1:100), Length:16, Pro:0, Off:0, Pri:0, RM:0, Err:0 [0x4000, 0x0]
+
+ 00 D0 D8 D2 FF 03 C0 21 09 29 00 08 6B 60 84 AA
+
+ 2000-2-1,18:20:17.0: l2-rx (PPP:3/7/1:100), Length:16, Pro:0, Off:0, Pri:0, RM:0, Err:0 [0x4001, 0x30000]
+ 00 D0 D8 D2 FF 03 C0 21 09 29 00 08 6D FE FA AA
+
+ 2000-2-1,18:20:17.0: l2-tx (PPP:3/7/1:100), Length:16, Pro:0, Off:0, Pri:0, RM:0, Err:0 [0x4000, 0x0]
+ 00 D0 D8 D2 FF 03 C0 21 0A 29 00 08 6B 60 84 AA
+
+ 2000-2-1,18:20:17.0: l2-rx (PPP:3/7/1:100), Length:16, Pro:0, Off:0, Pri:0, RM:0, Err:0 [0x4001, 0x8030000]
+ 00 D0 D8 D2 FF 03 C0 21 0A 29 00 08 6D FE FA AA
+
+ nt 1 length 500
+ vpn 200 : [max tries 1, timeout 5 seconds, data length 500 bytes, ttl 255]
+ 2000-2-1,18:20:24.1: l2-tx (PPP:3/7/1:100), Length:536, Pro:1, Off:8, Pri:7, RM:0, Err:0 [0x4070, 0x801]
+ 00 D0 D8 D2 FF 03 00 21 45 00 02 10 00 27 00 00
+ FF 01 69 51 14 14 14 22 14 14 14 2B 08 00 AD B8
+ 00 03 00 01 10 11 12 13 14 15 16 17 18 19 1A 1B
+ 1C 1D 1E 1F 20 21 22 23 24 25 26 27 28 29 2A 2B
+ 2C 2D 2E 2F 30 31 32 33 34 35 36 37 38 39 3A 3B
+ 3C 3D 3E 3F 40 41 42 43 44 45 46 47 48 49 4A 4B
+ 4C 4D 4E 4F
+
+ ping #1 ok, RTT 0.010 seconds
+ 2000-2-1,18:20:24.1: l2-rx (PPP:3/7/1:100), Length:536, Pro:1, Off:8, Pri:7, RM:0, Err:0 [0x4071, 0x30801]
+ 00 D0 D8 D2 FF 03 00 21 45 00 02 10 00 23 00 00
+ FF 01 69 55 14 14 14 2B 14 14 14 22 00 00 B5 B8
+ 00 03 00 01 10 11 12 13 14 15 16 17 18 19 1A 1B
+ 1C 1D 1E 1F 20 21 22 23 24 25 26 27 28 29 2A 2B
+ 2C 2D 2E 2F 30 31 32 33 34 35 36 37 38 39 3A 3B
+ 3C 3D 3E 3F 40 41 42 43 44 45 46 47 48 49 4A 4B
+ 4C 4D 4E 4F
+
+ [finished]
+
+ IPNOS: CONFIG VPN(200) VR(3.3.3.3)# 2000-2-1,18:20:27.0: l2-tx (PPP:3/7/1:100), Length:16, Pro:0, Off:0, Pri:0, RM:0, Err:0 [0x4000, 0x0]
+
+ 00 D0 D8 D2 FF 03 C0 21 09 2A 00 08 6B 60 84 AA
+
+ 2000-2-1,18:20:27.0: l2-rx (PPP:3/7/1:100), Length:16, Pro:0, Off:0, Pri:0, RM:0, Err:0 [0x4001, 0x30000]
+ 00 D0 D8 D2 FF 03 C0 21 09 2A 00 08 6D FE FA AA
+
+ 2000-2-1,18:20:27.0: l2-tx (PPP:3/7/1:100), Length:16, Pro:0, Off:0, Pri:0, RM:0, Err:0 [0x4000, 0x0]
+ 00 D0 D8 D2 FF 03 C0 21 0A 2A 00 08 6B 60 84 AA
+
+ 2000-2-1,18:20:27.0: l2-rx (PPP:3/7/1:100), Length:16, Pro:0, Off:0, Pri:0, RM:0, Err:0 [0x4001, 0x30000]
+ 00 D0 D8 D2 FF 03 C0 21 0A 2A 00 08 6D FE FA AA
+
+
+ ipnos diags: Control (1/0) :: layer-2 apply 3 0x0701 100 0 0
+ Successfully applied packet-log-profile on LI
+ ipnos diags: Control (1/0) ::
+
+ */
+
+/* XXX TODO:
+
+ o Handle a case where an empty line doesn't exists as a delimiter of
+ each packet. If the output is sent to a control blade and
+ displayed as an event log, there's always an empty line between
+ each packet output, but it may not be true when it is an PE
+ output.
+
+ o Some telnet client on Windows may put in a line break at 80
+ columns when it save the session to a text file ("CRT" is such an
+ example). I don't think it's a good idea for the telnet client to
+ do so, but CRT is widely used in Windows community, I should
+ take care of that in the future.
+
+*/
+
+/* Magic text to check for CoSine L2 debug output */
+#define COSINE_HDR_MAGIC_STR1 "l2-tx"
+#define COSINE_HDR_MAGIC_STR2 "l2-rx"
+
+/* Magic text for start of packet */
+#define COSINE_REC_MAGIC_STR1 COSINE_HDR_MAGIC_STR1
+#define COSINE_REC_MAGIC_STR2 COSINE_HDR_MAGIC_STR2
+
+#define COSINE_HEADER_LINES_TO_CHECK 200
+#define COSINE_LINE_LENGTH 240
+
+static gboolean empty_line(const gchar *line);
+static gint64 cosine_seek_next_packet(wtap *wth, int *err, gchar **err_info,
+ char *hdr);
+static gboolean cosine_check_file_type(wtap *wth, int *err, gchar **err_info);
+static gboolean cosine_read(wtap *wth, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info, gint64 *data_offset);
+static gboolean cosine_seek_read(wtap *wth, gint64 seek_off,
+ wtap_rec *rec, Buffer *buf, int *err, gchar **err_info);
+static int parse_cosine_packet(FILE_T fh, wtap_rec *rec, Buffer* buf,
+ char *line, int *err, gchar **err_info);
+static int parse_single_hex_dump_line(char* rec, guint8 *buf,
+ guint byte_offset);
+
+static int cosine_file_type_subtype = -1;
+
+void register_cosine(void);
+
+/* Returns TRUE if the line appears to be an empty line. Otherwise it
+ returns FALSE. */
+static gboolean empty_line(const gchar *line)
+{
+ while (*line) {
+ if (g_ascii_isspace(*line)) {
+ line++;
+ continue;
+ } else {
+ break;
+ }
+ }
+ if (*line == '\0')
+ return TRUE;
+ else
+ return FALSE;
+}
+
+/* Seeks to the beginning of the next packet, and returns the
+ byte offset. Copy the header line to hdr. Returns -1 on failure,
+ and sets "*err" to the error and sets "*err_info" to null or an
+ additional error string. */
+static gint64 cosine_seek_next_packet(wtap *wth, int *err, gchar **err_info,
+ char *hdr)
+{
+ gint64 cur_off;
+ char buf[COSINE_LINE_LENGTH];
+
+ while (1) {
+ cur_off = file_tell(wth->fh);
+ if (cur_off == -1) {
+ /* Error */
+ *err = file_error(wth->fh, err_info);
+ return -1;
+ }
+ if (file_gets(buf, sizeof(buf), wth->fh) == NULL) {
+ *err = file_error(wth->fh, err_info);
+ return -1;
+ }
+ if (strstr(buf, COSINE_REC_MAGIC_STR1) ||
+ strstr(buf, COSINE_REC_MAGIC_STR2)) {
+ (void) g_strlcpy(hdr, buf, COSINE_LINE_LENGTH);
+ return cur_off;
+ }
+ }
+ return -1;
+}
+
+/* Look through the first part of a file to see if this is
+ * a CoSine L2 debug output.
+ *
+ * Returns TRUE if it is, FALSE if it isn't or if we get an I/O error;
+ * if we get an I/O error, "*err" will be set to a non-zero value and
+ * "*err_info" will be set to null or an additional error string.
+ */
+static gboolean cosine_check_file_type(wtap *wth, int *err, gchar **err_info)
+{
+ char buf[COSINE_LINE_LENGTH];
+ gsize reclen;
+ guint line;
+
+ buf[COSINE_LINE_LENGTH-1] = '\0';
+
+ for (line = 0; line < COSINE_HEADER_LINES_TO_CHECK; line++) {
+ if (file_gets(buf, COSINE_LINE_LENGTH, wth->fh) == NULL) {
+ /* EOF or error. */
+ *err = file_error(wth->fh, err_info);
+ return FALSE;
+ }
+
+ reclen = strlen(buf);
+ if (reclen < MIN(strlen(COSINE_HDR_MAGIC_STR1), strlen(COSINE_HDR_MAGIC_STR2))) {
+ continue;
+ }
+
+ if (strstr(buf, COSINE_HDR_MAGIC_STR1) ||
+ strstr(buf, COSINE_HDR_MAGIC_STR2)) {
+ return TRUE;
+ }
+ }
+ *err = 0;
+ return FALSE;
+}
+
+
+wtap_open_return_val cosine_open(wtap *wth, int *err, gchar **err_info)
+{
+ /* Look for CoSine header */
+ if (!cosine_check_file_type(wth, err, err_info)) {
+ if (*err != 0 && *err != WTAP_ERR_SHORT_READ)
+ return WTAP_OPEN_ERROR;
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ if (file_seek(wth->fh, 0L, SEEK_SET, err) == -1) /* rewind */
+ return WTAP_OPEN_ERROR;
+
+ wth->file_encap = WTAP_ENCAP_COSINE;
+ wth->file_type_subtype = cosine_file_type_subtype;
+ wth->snapshot_length = 0; /* not known */
+ wth->subtype_read = cosine_read;
+ wth->subtype_seek_read = cosine_seek_read;
+ wth->file_tsprec = WTAP_TSPREC_10_MSEC;
+
+ /*
+ * Add an IDB; we don't know how many interfaces were
+ * involved, so we just say one interface, about which
+ * we only know the link-layer type, snapshot length,
+ * and time stamp resolution.
+ */
+ wtap_add_generated_idb(wth);
+
+ return WTAP_OPEN_MINE;
+}
+
+/* Find the next packet and parse it; called from wtap_read(). */
+static gboolean cosine_read(wtap *wth, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info, gint64 *data_offset)
+{
+ gint64 offset;
+ char line[COSINE_LINE_LENGTH];
+
+ /* Find the next packet */
+ offset = cosine_seek_next_packet(wth, err, err_info, line);
+ if (offset < 0)
+ return FALSE;
+ *data_offset = offset;
+
+ /* Parse the header and convert the ASCII hex dump to binary data */
+ return parse_cosine_packet(wth->fh, rec, buf, line, err, err_info);
+}
+
+/* Used to read packets in random-access fashion */
+static gboolean
+cosine_seek_read(wtap *wth, gint64 seek_off, wtap_rec *rec,
+ Buffer *buf, int *err, gchar **err_info)
+{
+ char line[COSINE_LINE_LENGTH];
+
+ if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
+ return FALSE;
+
+ if (file_gets(line, COSINE_LINE_LENGTH, wth->random_fh) == NULL) {
+ *err = file_error(wth->random_fh, err_info);
+ if (*err == 0) {
+ *err = WTAP_ERR_SHORT_READ;
+ }
+ return FALSE;
+ }
+
+ /* Parse the header and convert the ASCII hex dump to binary data */
+ return parse_cosine_packet(wth->random_fh, rec, buf, line, err,
+ err_info);
+}
+
+/* Parses a packet record header. There are two possible formats:
+ 1) output to a control blade with date and time
+ 2002-5-10,20:1:31.4: l2-tx (FR:3/7/1:1), Length:18, Pro:0, Off:0, Pri:0, RM:0, Err:0 [0x4000, 0x0]
+ 2) output to PE without date and time
+ l2-tx (FR:3/7/1:1), Length:18, Pro:0, Off:0, Pri:0, RM:0, Err:0 [0x4000, 0x0] */
+static gboolean
+parse_cosine_packet(FILE_T fh, wtap_rec *rec, Buffer *buf,
+ char *line, int *err, gchar **err_info)
+{
+ union wtap_pseudo_header *pseudo_header = &rec->rec_header.packet_header.pseudo_header;
+ int num_items_scanned;
+ int yy, mm, dd, hr, min, sec, csec, pkt_len;
+ int pro, off, pri, rm, error;
+ guint code1, code2;
+ char if_name[COSINE_MAX_IF_NAME_LEN] = "", direction[6] = "";
+ struct tm tm;
+ guint8 *pd;
+ int i, hex_lines, n, caplen = 0;
+
+ if (sscanf(line, "%4d-%2d-%2d,%2d:%2d:%2d.%9d:",
+ &yy, &mm, &dd, &hr, &min, &sec, &csec) == 7) {
+ /* appears to be output to a control blade */
+ num_items_scanned = sscanf(line,
+ "%4d-%2d-%2d,%2d:%2d:%2d.%9d: %5s (%127[A-Za-z0-9/:]), Length:%9d, Pro:%9d, Off:%9d, Pri:%9d, RM:%9d, Err:%9d [%8x, %8x]",
+ &yy, &mm, &dd, &hr, &min, &sec, &csec,
+ direction, if_name, &pkt_len,
+ &pro, &off, &pri, &rm, &error,
+ &code1, &code2);
+
+ if (num_items_scanned != 17) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("cosine: purported control blade line doesn't have code values");
+ return FALSE;
+ }
+ } else {
+ /* appears to be output to PE */
+ num_items_scanned = sscanf(line,
+ "%5s (%127[A-Za-z0-9/:]), Length:%9d, Pro:%9d, Off:%9d, Pri:%9d, RM:%9d, Err:%9d [%8x, %8x]",
+ direction, if_name, &pkt_len,
+ &pro, &off, &pri, &rm, &error,
+ &code1, &code2);
+
+ if (num_items_scanned != 10) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("cosine: header line is neither control blade nor PE output");
+ return FALSE;
+ }
+ yy = mm = dd = hr = min = sec = csec = 0;
+ }
+ if (pkt_len < 0) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("cosine: packet header has a negative packet length");
+ return FALSE;
+ }
+ if ((guint)pkt_len > WTAP_MAX_PACKET_SIZE_STANDARD) {
+ /*
+ * Probably a corrupt capture file; don't blow up trying
+ * to allocate space for an immensely-large packet.
+ */
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("cosine: File has %u-byte packet, bigger than maximum of %u",
+ (guint)pkt_len, WTAP_MAX_PACKET_SIZE_STANDARD);
+ return FALSE;
+ }
+
+ rec->rec_type = REC_TYPE_PACKET;
+ rec->block = wtap_block_create(WTAP_BLOCK_PACKET);
+ rec->presence_flags = WTAP_HAS_TS|WTAP_HAS_CAP_LEN;
+ tm.tm_year = yy - 1900;
+ tm.tm_mon = mm - 1;
+ tm.tm_mday = dd;
+ tm.tm_hour = hr;
+ tm.tm_min = min;
+ tm.tm_sec = sec;
+ tm.tm_isdst = -1;
+ rec->ts.secs = mktime(&tm);
+ rec->ts.nsecs = csec * 10000000;
+ rec->rec_header.packet_header.len = pkt_len;
+
+ /* XXX need to handle other encapsulations like Cisco HDLC,
+ Frame Relay and ATM */
+ if (strncmp(if_name, "TEST:", 5) == 0) {
+ pseudo_header->cosine.encap = COSINE_ENCAP_TEST;
+ } else if (strncmp(if_name, "PPoATM:", 7) == 0) {
+ pseudo_header->cosine.encap = COSINE_ENCAP_PPoATM;
+ } else if (strncmp(if_name, "PPoFR:", 6) == 0) {
+ pseudo_header->cosine.encap = COSINE_ENCAP_PPoFR;
+ } else if (strncmp(if_name, "ATM:", 4) == 0) {
+ pseudo_header->cosine.encap = COSINE_ENCAP_ATM;
+ } else if (strncmp(if_name, "FR:", 3) == 0) {
+ pseudo_header->cosine.encap = COSINE_ENCAP_FR;
+ } else if (strncmp(if_name, "HDLC:", 5) == 0) {
+ pseudo_header->cosine.encap = COSINE_ENCAP_HDLC;
+ } else if (strncmp(if_name, "PPP:", 4) == 0) {
+ pseudo_header->cosine.encap = COSINE_ENCAP_PPP;
+ } else if (strncmp(if_name, "ETH:", 4) == 0) {
+ pseudo_header->cosine.encap = COSINE_ENCAP_ETH;
+ } else {
+ pseudo_header->cosine.encap = COSINE_ENCAP_UNKNOWN;
+ }
+ if (strncmp(direction, "l2-tx", 5) == 0) {
+ pseudo_header->cosine.direction = COSINE_DIR_TX;
+ } else if (strncmp(direction, "l2-rx", 5) == 0) {
+ pseudo_header->cosine.direction = COSINE_DIR_RX;
+ }
+ (void) g_strlcpy(pseudo_header->cosine.if_name, if_name,
+ COSINE_MAX_IF_NAME_LEN);
+ pseudo_header->cosine.pro = pro;
+ pseudo_header->cosine.off = off;
+ pseudo_header->cosine.pri = pri;
+ pseudo_header->cosine.rm = rm;
+ pseudo_header->cosine.err = error;
+
+ /* Make sure we have enough room for the packet */
+ ws_buffer_assure_space(buf, pkt_len);
+ pd = ws_buffer_start_ptr(buf);
+
+ /* Calculate the number of hex dump lines, each
+ * containing 16 bytes of data */
+ hex_lines = pkt_len / 16 + ((pkt_len % 16) ? 1 : 0);
+
+ for (i = 0; i < hex_lines; i++) {
+ if (file_gets(line, COSINE_LINE_LENGTH, fh) == NULL) {
+ *err = file_error(fh, err_info);
+ if (*err == 0) {
+ *err = WTAP_ERR_SHORT_READ;
+ }
+ return FALSE;
+ }
+ if (empty_line(line)) {
+ break;
+ }
+ if ((n = parse_single_hex_dump_line(line, pd, i*16)) == -1) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("cosine: hex dump line doesn't have 16 numbers");
+ return FALSE;
+ }
+ caplen += n;
+ }
+ rec->rec_header.packet_header.caplen = caplen;
+ return TRUE;
+}
+
+/* Take a string representing one line from a hex dump and converts
+ * the text to binary data. We place the bytes in the buffer at the
+ * specified offset.
+ *
+ * Returns number of bytes successfully read, -1 if bad. */
+static int
+parse_single_hex_dump_line(char* rec, guint8 *buf, guint byte_offset)
+{
+ int num_items_scanned, i;
+ unsigned int bytes[16];
+
+ num_items_scanned = sscanf(rec, "%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x",
+ &bytes[0], &bytes[1], &bytes[2], &bytes[3],
+ &bytes[4], &bytes[5], &bytes[6], &bytes[7],
+ &bytes[8], &bytes[9], &bytes[10], &bytes[11],
+ &bytes[12], &bytes[13], &bytes[14], &bytes[15]);
+ if (num_items_scanned == 0)
+ return -1;
+
+ if (num_items_scanned > 16)
+ num_items_scanned = 16;
+
+ for (i=0; i<num_items_scanned; i++) {
+ buf[byte_offset + i] = (guint8)bytes[i];
+ }
+
+ return num_items_scanned;
+}
+
+static const struct supported_block_type cosine_blocks_supported[] = {
+ /*
+ * We support packet blocks, with no comments or other options.
+ */
+ { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
+};
+
+static const struct file_type_subtype_info cosine_info = {
+ "CoSine IPSX L2 capture", "cosine", "txt", NULL,
+ FALSE, BLOCKS_SUPPORTED(cosine_blocks_supported),
+ NULL, NULL, NULL
+};
+
+void register_cosine(void)
+{
+ cosine_file_type_subtype = wtap_register_file_type_subtype(&cosine_info);
+
+ /*
+ * Register name for backwards compatibility with the
+ * wtap_filetypes table in Lua.
+ */
+ wtap_register_backwards_compatibility_lua_name("COSINE",
+ cosine_file_type_subtype);
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/wiretap/cosine.h b/wiretap/cosine.h
new file mode 100644
index 00000000..6afa467a
--- /dev/null
+++ b/wiretap/cosine.h
@@ -0,0 +1,21 @@
+/** @file
+ *
+ * CoSine IPNOS L2 debug output parsing
+ * Copyright (c) 2002 by Motonori Shindo <motonori@shin.do>
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ */
+
+#ifndef __W_COSINE_H__
+#define __W_COSINE_H__
+#include <glib.h>
+#include "wtap.h"
+#include "ws_symbol_export.h"
+
+wtap_open_return_val cosine_open(wtap *wth, int *err, gchar **err_info);
+
+#endif
diff --git a/wiretap/csids.c b/wiretap/csids.c
new file mode 100644
index 00000000..584ef6e3
--- /dev/null
+++ b/wiretap/csids.c
@@ -0,0 +1,249 @@
+/* csids.c
+ *
+ * Copyright (c) 2000 by Mike Hall <mlh@io.com>
+ * Copyright (c) 2000 by Cisco Systems
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "config.h"
+#include "wtap-int.h"
+#include "csids.h"
+#include "file_wrappers.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+/*
+ * This module reads the output from the Cisco Secure Intrusion Detection
+ * System iplogging facility. The term iplogging is misleading since this
+ * logger will only output TCP. There is no link layer information.
+ * Packet format is 4 byte timestamp (seconds since epoch), and a 4 byte size
+ * of data following for that packet.
+ *
+ * For a time there was an error in iplogging and the ip length, flags, and id
+ * were byteswapped. We will check for this and handle it before handing to
+ * wireshark.
+ */
+
+typedef struct {
+ gboolean byteswapped;
+} csids_t;
+
+static gboolean csids_read(wtap *wth, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info, gint64 *data_offset);
+static gboolean csids_seek_read(wtap *wth, gint64 seek_off,
+ wtap_rec *rec, Buffer *buf, int *err, gchar **err_info);
+static gboolean csids_read_packet(FILE_T fh, csids_t *csids,
+ wtap_rec *rec, Buffer *buf, int *err, gchar **err_info);
+
+struct csids_header {
+ guint32 seconds; /* seconds since epoch */
+ guint16 zeropad; /* 2 byte zero'ed pads */
+ guint16 caplen; /* the capture length */
+};
+
+static int csids_file_type_subtype = -1;
+
+void register_csids(void);
+
+wtap_open_return_val csids_open(wtap *wth, int *err, gchar **err_info)
+{
+ /* There is no file header. There is only a header for each packet
+ * so we read a packet header and compare the caplen with iplen. They
+ * should always be equal except with the weird byteswap version.
+ *
+ * THIS IS BROKEN-- anytime the caplen is 0x0101 or 0x0202 up to 0x0505
+ * this will byteswap it. I need to fix this. XXX --mlh
+ */
+
+ int tmp,iplen;
+
+ gboolean byteswap = FALSE;
+ struct csids_header hdr;
+ csids_t *csids;
+
+ /* check the file to make sure it is a csids file. */
+ if( !wtap_read_bytes( wth->fh, &hdr, sizeof( struct csids_header), err, err_info ) ) {
+ if( *err != WTAP_ERR_SHORT_READ ) {
+ return WTAP_OPEN_ERROR;
+ }
+ return WTAP_OPEN_NOT_MINE;
+ }
+ if( hdr.zeropad != 0 || hdr.caplen == 0 ) {
+ return WTAP_OPEN_NOT_MINE;
+ }
+ hdr.seconds = pntoh32( &hdr.seconds );
+ hdr.caplen = pntoh16( &hdr.caplen );
+ if( !wtap_read_bytes( wth->fh, &tmp, 2, err, err_info ) ) {
+ if( *err != WTAP_ERR_SHORT_READ ) {
+ return WTAP_OPEN_ERROR;
+ }
+ return WTAP_OPEN_NOT_MINE;
+ }
+ if( !wtap_read_bytes(wth->fh, &iplen, 2, err, err_info ) ) {
+ if( *err != WTAP_ERR_SHORT_READ ) {
+ return WTAP_OPEN_ERROR;
+ }
+ return WTAP_OPEN_NOT_MINE;
+ }
+ iplen = pntoh16(&iplen);
+
+ if ( iplen == 0 )
+ return WTAP_OPEN_NOT_MINE;
+
+ /* if iplen and hdr.caplen are equal, default to no byteswap. */
+ if( iplen > hdr.caplen ) {
+ /* maybe this is just a byteswapped version. the iplen ipflags */
+ /* and ipid are swapped. We cannot use the normal swaps because */
+ /* we don't know the host */
+ iplen = GUINT16_SWAP_LE_BE(iplen);
+ if( iplen <= hdr.caplen ) {
+ /* we know this format */
+ byteswap = TRUE;
+ } else {
+ /* don't know this one */
+ return WTAP_OPEN_NOT_MINE;
+ }
+ } else {
+ byteswap = FALSE;
+ }
+
+ /* no file header. So reset the fh to 0 so we can read the first packet */
+ if (file_seek(wth->fh, 0, SEEK_SET, err) == -1)
+ return WTAP_OPEN_ERROR;
+
+ csids = g_new(csids_t, 1);
+ wth->priv = (void *)csids;
+ csids->byteswapped = byteswap;
+ wth->file_encap = WTAP_ENCAP_RAW_IP;
+ wth->file_type_subtype = csids_file_type_subtype;
+ wth->snapshot_length = 0; /* not known */
+ wth->subtype_read = csids_read;
+ wth->subtype_seek_read = csids_seek_read;
+ wth->file_tsprec = WTAP_TSPREC_SEC;
+
+ /*
+ * Add an IDB; we don't know how many interfaces were
+ * involved, so we just say one interface, about which
+ * we only know the link-layer type, snapshot length,
+ * and time stamp resolution.
+ */
+ wtap_add_generated_idb(wth);
+
+ return WTAP_OPEN_MINE;
+}
+
+/* Find the next packet and parse it; called from wtap_read(). */
+static gboolean csids_read(wtap *wth, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info, gint64 *data_offset)
+{
+ csids_t *csids = (csids_t *)wth->priv;
+
+ *data_offset = file_tell(wth->fh);
+
+ return csids_read_packet( wth->fh, csids, rec, buf, err, err_info );
+}
+
+/* Used to read packets in random-access fashion */
+static gboolean
+csids_seek_read(wtap *wth,
+ gint64 seek_off,
+ wtap_rec *rec,
+ Buffer *buf,
+ int *err,
+ gchar **err_info)
+{
+ csids_t *csids = (csids_t *)wth->priv;
+
+ if( file_seek( wth->random_fh, seek_off, SEEK_SET, err ) == -1 )
+ return FALSE;
+
+ if( !csids_read_packet( wth->random_fh, csids, rec, buf, err, err_info ) ) {
+ if( *err == 0 )
+ *err = WTAP_ERR_SHORT_READ;
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static gboolean
+csids_read_packet(FILE_T fh, csids_t *csids, wtap_rec *rec,
+ Buffer *buf, int *err, gchar **err_info)
+{
+ struct csids_header hdr;
+ guint8 *pd;
+
+ if( !wtap_read_bytes_or_eof( fh, &hdr, sizeof( struct csids_header), err, err_info ) )
+ return FALSE;
+ hdr.seconds = pntoh32(&hdr.seconds);
+ hdr.caplen = pntoh16(&hdr.caplen);
+ /*
+ * The maximum value of hdr.caplen is 65535, which is less than
+ * WTAP_MAX_PACKET_SIZE_STANDARD will ever be, so we don't need to check
+ * it.
+ */
+
+ rec->rec_type = REC_TYPE_PACKET;
+ rec->block = wtap_block_create(WTAP_BLOCK_PACKET);
+ rec->presence_flags = WTAP_HAS_TS;
+ rec->rec_header.packet_header.len = hdr.caplen;
+ rec->rec_header.packet_header.caplen = hdr.caplen;
+ rec->ts.secs = hdr.seconds;
+ rec->ts.nsecs = 0;
+
+ if( !wtap_read_packet_bytes( fh, buf, rec->rec_header.packet_header.caplen, err, err_info ) )
+ return FALSE;
+
+ pd = ws_buffer_start_ptr( buf );
+ if( csids->byteswapped ) {
+ if( rec->rec_header.packet_header.caplen >= 2 ) {
+ PBSWAP16(pd); /* the ip len */
+ if( rec->rec_header.packet_header.caplen >= 4 ) {
+ PBSWAP16(pd+2); /* ip id */
+ if( rec->rec_header.packet_header.caplen >= 6 )
+ PBSWAP16(pd+4); /* ip flags and fragoff */
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+static const struct supported_block_type csids_blocks_supported[] = {
+ /*
+ * We support packet blocks, with no comments or other options.
+ */
+ { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
+};
+
+static const struct file_type_subtype_info csids_info = {
+ "CSIDS IPLog", "csids", NULL, NULL,
+ FALSE, BLOCKS_SUPPORTED(csids_blocks_supported),
+ NULL, NULL, NULL
+};
+
+void register_csids(void)
+{
+ csids_file_type_subtype = wtap_register_file_type_subtype(&csids_info);
+
+ /*
+ * Register name for backwards compatibility with the
+ * wtap_filetypes table in Lua.
+ */
+ wtap_register_backwards_compatibility_lua_name("CSIDS",
+ csids_file_type_subtype);
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local Variables:
+ * c-basic-offset: 2
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * vi: set shiftwidth=2 tabstop=8 expandtab:
+ * :indentSize=2:tabSize=8:noTabs=true:
+ */
diff --git a/wiretap/csids.h b/wiretap/csids.h
new file mode 100644
index 00000000..64ea4aa6
--- /dev/null
+++ b/wiretap/csids.h
@@ -0,0 +1,18 @@
+/** @file
+ *
+ * Copyright (c) 2000 by Mike Hall <mlh@io.com>
+ * Copyright (c) Cisco Systems
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ */
+
+#ifndef __CSIDS_H__
+#define __CSIDS_H__
+#include <glib.h>
+#include "wtap.h"
+#include "ws_symbol_export.h"
+
+wtap_open_return_val csids_open(wtap *wth, int *err, gchar **err_info);
+
+#endif
diff --git a/wiretap/daintree-sna.c b/wiretap/daintree-sna.c
new file mode 100644
index 00000000..67ae4444
--- /dev/null
+++ b/wiretap/daintree-sna.c
@@ -0,0 +1,293 @@
+/* daintree_sna.c
+ * Routines for opening .dcf capture files created by Daintree's
+ * Sensor Network Analyzer for 802.15.4 radios
+ * Copyright 2009, Exegin Technologies Limited <fff@exegin.com>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * Started with packetlogger.c as a template, but little packetlogger code
+ * remains. Borrowed many snippets from dbs-etherwatch.c, the
+ * daintree_sna_process_hex_data function having the largest chunk.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+/* This module reads capture files saved by Daintree's Sensor Network Analyzer.
+ * Daintree captures are plain text files with a two line header,
+ * followed by packet records, one per line, with whitespace separated fields
+ * consisting of: packet number, time, bytes of capture data, capture data,
+ * unknown, unknown, signal strength?, unknown, etc, and terminated with CRLF.
+ */
+
+/* Example capture file:
+
+#Format=4
+# SNA v2.2.0.4 SUS:20090709 ACT:819705
+1 1233783799.326400 10 030809ffffffff07ffff 42 1 -69 25 2 0 1 32767
+2 1233783799.477440 5 02000bffff 110 1 -44 25 6 0 1 32767
+3 1233783799.809920 5 020013ffff 107 1 -45 25 43 0 1 3276
+
+*/
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "wtap-int.h"
+#include "file_wrappers.h"
+#include "daintree-sna.h"
+
+typedef struct daintree_sna_header {
+ guint32 len;
+ guint64 ts;
+} daintree_sna_header_t;
+
+#define DAINTREE_SNA_HEADER_SIZE 2
+#define FCS_LENGTH 2
+
+static const char daintree_magic_text[] = "#Format=";
+
+#define DAINTREE_MAGIC_TEXT_SIZE (sizeof daintree_magic_text - 1)
+#define DAINTREE_MAX_LINE_SIZE 512
+#define READDATA_BUF_SIZE (DAINTREE_MAX_LINE_SIZE/2)
+#define READDATA_MAX_FIELD_SIZE "255" /* DAINTREE_MAX_LINE_SIZE/2 -1 */
+
+#define COMMENT_LINE daintree_magic_text[0]
+
+static gboolean daintree_sna_read(wtap *wth, wtap_rec *rec,
+ Buffer *buf, int *err, gchar **err_info, gint64 *data_offset);
+
+static gboolean daintree_sna_seek_read(wtap *wth, gint64 seek_off,
+ wtap_rec *rec, Buffer *buf, int *err, gchar **err_info);
+
+static gboolean daintree_sna_read_packet(FILE_T fh, wtap_rec *rec,
+ Buffer *buf, int *err, gchar **err_info);
+
+static int daintree_sna_file_type_subtype = -1;
+
+void register_daintree_sna(void);
+
+/* Open a file and determine if it's a Daintree file */
+wtap_open_return_val daintree_sna_open(wtap *wth, int *err, gchar **err_info)
+{
+ char readLine[DAINTREE_MAX_LINE_SIZE];
+
+ /* get first line of file header */
+ if (file_gets(readLine, DAINTREE_MAX_LINE_SIZE, wth->fh)==NULL) {
+ *err = file_error(wth->fh, err_info);
+ if (*err != 0 && *err != WTAP_ERR_SHORT_READ)
+ return WTAP_OPEN_ERROR;
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ /* check magic text */
+ if (strncmp(readLine, daintree_magic_text, DAINTREE_MAGIC_TEXT_SIZE) != 0)
+ return WTAP_OPEN_NOT_MINE; /* not daintree format */
+
+ /* read second header line */
+ if (file_gets(readLine, DAINTREE_MAX_LINE_SIZE, wth->fh)==NULL) {
+ *err = file_error(wth->fh, err_info);
+ if (*err != 0 && *err != WTAP_ERR_SHORT_READ)
+ return WTAP_OPEN_ERROR;
+ return WTAP_OPEN_NOT_MINE;
+ }
+ if (readLine[0] != COMMENT_LINE)
+ return WTAP_OPEN_NOT_MINE; /* daintree files have a two line header */
+
+ /* set up the pointers to the handlers for this file type */
+ wth->subtype_read = daintree_sna_read;
+ wth->subtype_seek_read = daintree_sna_seek_read;
+
+ /* set up for file type */
+ wth->file_type_subtype = daintree_sna_file_type_subtype;
+ wth->file_encap = WTAP_ENCAP_IEEE802_15_4_NOFCS;
+ wth->file_tsprec = WTAP_TSPREC_USEC;
+ wth->snapshot_length = 0; /* not available in header */
+
+ /*
+ * Add an IDB; we don't know how many interfaces were
+ * involved, so we just say one interface, about which
+ * we only know the link-layer type, snapshot length,
+ * and time stamp resolution.
+ */
+ wtap_add_generated_idb(wth);
+
+ return WTAP_OPEN_MINE; /* it's a Daintree file */
+}
+
+/* Read the capture file sequentially
+ * Wireshark scans the file with sequential reads during preview and initial display. */
+static gboolean
+daintree_sna_read(wtap *wth, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info, gint64 *data_offset)
+{
+ *data_offset = file_tell(wth->fh);
+
+ /* parse that line and the following packet data */
+ return daintree_sna_read_packet(wth->fh, rec, buf, err, err_info);
+}
+
+/* Read the capture file randomly
+ * Wireshark opens the capture file for random access when displaying user-selected packets */
+static gboolean
+daintree_sna_seek_read(wtap *wth, gint64 seek_off, wtap_rec *rec,
+ Buffer *buf, int *err, gchar **err_info)
+{
+ if(file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
+ return FALSE;
+
+ /* parse that line and the following packet data */
+ return daintree_sna_read_packet(wth->random_fh, rec, buf, err,
+ err_info);
+}
+
+/* Read a header line, scan it, and fill in a struct wtap_rec.
+ * Then convert packet data from ASCII hex string to binary in place,
+ * sanity-check its length against what we assume is the packet length field,
+ * and copy it into a Buffer. */
+static gboolean
+daintree_sna_read_packet(FILE_T fh, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info)
+{
+ guint64 seconds;
+ int useconds;
+ char readLine[DAINTREE_MAX_LINE_SIZE];
+ char readData[READDATA_BUF_SIZE];
+ guchar *str = (guchar *)readData;
+ guint bytes;
+ guint8 *p;
+
+ /* we've only seen file header lines starting with '#', but
+ * if others appear in the file, they are tossed */
+ do {
+ if (file_gets(readLine, DAINTREE_MAX_LINE_SIZE, fh) == NULL) {
+ *err = file_error(fh, err_info);
+ return FALSE; /* all done */
+ }
+ } while (readLine[0] == COMMENT_LINE);
+
+ rec->rec_type = REC_TYPE_PACKET;
+ rec->block = wtap_block_create(WTAP_BLOCK_PACKET);
+ rec->presence_flags = WTAP_HAS_TS|WTAP_HAS_CAP_LEN;
+
+ if (sscanf(readLine, "%*s %18" SCNu64 ".%9d %9u %" READDATA_MAX_FIELD_SIZE "s",
+ &seconds, &useconds, &rec->rec_header.packet_header.len, readData) != 4) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("daintree_sna: invalid read record");
+ return FALSE;
+ }
+
+ /* Daintree doesn't store the FCS, but pads end of packet with 0xffff, which we toss */
+ if (rec->rec_header.packet_header.len <= FCS_LENGTH) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("daintree_sna: packet length <= %u bytes, no frame data present",
+ FCS_LENGTH);
+ return FALSE;
+ }
+ rec->rec_header.packet_header.len -= FCS_LENGTH;
+
+ rec->ts.secs = (time_t) seconds;
+ rec->ts.nsecs = useconds * 1000; /* convert mS to nS */
+
+ /*
+ * READDATA_BUF_SIZE is < WTAP_MAX_PACKET_SIZE_STANDARD, and is the maximum
+ * number of bytes of packet data we can generate, so we don't
+ * need to check the packet length.
+ */
+ p = str; /* overlay source buffer */
+ bytes = 0;
+ /* convert hex string to guint8 */
+ while(*str) {
+ /* most significant nibble */
+ if (!g_ascii_isxdigit(*str)) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("daintree_sna: non-hex digit in hex data");
+ return FALSE;
+ }
+ if(g_ascii_isdigit(*str)) {
+ *p = (*str - '0') << 4;
+ } else {
+ *p = ((g_ascii_tolower(*str) - 'a') + 10) << 4;
+ }
+ str++;
+
+ /* least significant nibble */
+ if (!g_ascii_isxdigit(*str)) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("daintree_sna: non-hex digit in hex data");
+ return FALSE;
+ }
+ if(g_ascii_isdigit(*str)) {
+ *p += *str - '0';
+ } else {
+ *p += (g_ascii_tolower(*str) - 'a') + 10;
+ }
+ str++;
+
+ /* next byte in buffer */
+ p++;
+ bytes++;
+ }
+
+ /* Daintree doesn't store the FCS, but pads end of packet with 0xffff, which we toss */
+ if (bytes <= FCS_LENGTH) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("daintree_sna: Only %u bytes of packet data",
+ bytes);
+ return FALSE;
+ }
+ bytes -= FCS_LENGTH;
+ if (bytes > rec->rec_header.packet_header.len) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("daintree_sna: capture length (%u) > packet length (%u)",
+ bytes, rec->rec_header.packet_header.len);
+ return FALSE;
+ }
+
+ rec->rec_header.packet_header.caplen = bytes;
+
+ ws_buffer_assure_space(buf, bytes);
+ memcpy(ws_buffer_start_ptr(buf), readData, bytes);
+ return TRUE;
+}
+
+static const struct supported_block_type daintree_sna_blocks_supported[] = {
+ /*
+ * We support packet blocks, with no comments or other options.
+ */
+ { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
+};
+
+static const struct file_type_subtype_info daintree_sna_info = {
+ "Daintree SNA", "dsna", "dcf", NULL,
+ FALSE, BLOCKS_SUPPORTED(daintree_sna_blocks_supported),
+ NULL, NULL, NULL
+};
+
+void register_daintree_sna(void)
+{
+ daintree_sna_file_type_subtype = wtap_register_file_type_subtype(&daintree_sna_info);
+
+ /*
+ * Register name for backwards compatibility with the
+ * wtap_filetypes table in Lua.
+ */
+ wtap_register_backwards_compatibility_lua_name("DAINTREE_SNA",
+ daintree_sna_file_type_subtype);
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/wiretap/daintree-sna.h b/wiretap/daintree-sna.h
new file mode 100644
index 00000000..95567c1f
--- /dev/null
+++ b/wiretap/daintree-sna.h
@@ -0,0 +1,19 @@
+/** @file
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __DAINTREE_SNA_H__
+#define __DAINTREE_SNA_H__
+#include <glib.h>
+#include "wtap.h"
+#include "ws_symbol_export.h"
+
+wtap_open_return_val daintree_sna_open(wtap *wth, int *err, gchar **err_info _U_);
+
+#endif /* __DAINTREE_SNA_H__ */
+
diff --git a/wiretap/dbs-etherwatch.c b/wiretap/dbs-etherwatch.c
new file mode 100644
index 00000000..4147b039
--- /dev/null
+++ b/wiretap/dbs-etherwatch.c
@@ -0,0 +1,670 @@
+/* dbs-etherwatch.c
+ *
+ * Wiretap Library
+ * Copyright (c) 2001 by Marc Milgram <ethereal@mmilgram.NOSPAMmail.net>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "config.h"
+#include "wtap-int.h"
+#include "dbs-etherwatch.h"
+#include "file_wrappers.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+/* This module reads the text output of the 'DBS-ETHERTRACE' command in VMS
+ * It was initially based on vms.c.
+ */
+
+/*
+ Example 'ETHERWATCH' output data (with "printable" characters in the
+ "printable characters" section of the output replaced by "." if they have
+ the 8th bit set, so as not to upset compilers that are expecting text
+ in comments to be in a particular character encoding that can't handle
+ those values):
+ETHERWATCH X5-008
+42 names and addresses were loaded
+Reading recorded data from PERSISTENCE
+------------------------------------------------------------------------------
+From 00-D0-C0-D2-4D-60 [MF1] to AA-00-04-00-FC-94 [PSERVB]
+Protocol 08-00 00 00-00-00-00-00, 60 byte buffer at 10-OCT-2001 10:20:45.16
+ [E..<8...........]- 0-[45 00 00 3C 38 93 00 00 1D 06 D2 12 80 93 11 1A]
+ [.........(......]- 16-[80 93 80 D6 02 D2 02 03 00 28 A4 90 00 00 00 00]
+ [................]- 32-[A0 02 FF FF 95 BD 00 00 02 04 05 B4 03 03 04 01]
+ [............ ]- 48-[01 01 08 0A 90 90 E5 14 00 00 00 00]
+------------------------------------------------------------------------------
+From 00-D0-C0-D2-4D-60 [MF1] to AA-00-04-00-FC-94 [PSERVB]
+Protocol 08-00 00 00-00-00-00-00, 50 byte buffer at 10-OCT-2001 10:20:45.17
+ [E..(8......%....]- 0-[45 00 00 28 38 94 00 00 1D 06 D2 25 80 93 11 1A]
+ [.........(..Z.4w]- 16-[80 93 80 D6 02 D2 02 03 00 28 A4 91 5A 1C 34 77]
+ [P.#(.s..........]- 32-[50 10 23 28 C1 73 00 00 02 04 05 B4 03 03 00 00]
+ [.. ]- 48-[02 04]
+
+
+Alternative HEX only output, slightly more efficient and all wireshark needs:
+------------------------------------------------------------------------------
+From 00-D0-C0-D2-4D-60 [MF1] to AA-00-04-00-FC-94 [PSERVB]
+Protocol 08-00 00 00-00-00-00-00, 50 byte buffer at 10-OCT-2001 10:20:45.17
+ 0-[45 00 00 28 38 9B 00 00 1D 06 D2 1E 80 93 11 1A 80 93 80 D6]
+ 20-[02 D2 02 03 00 28 A4 BF 5A 1C 34 79 50 10 23 28 C1 43 00 00]
+ 40-[03 30 30 30 30 30 00 00 03 30]
+ */
+
+/* Magic text to check for DBS-ETHERWATCH-ness of file */
+static const char dbs_etherwatch_hdr_magic[] =
+{ 'E', 'T', 'H', 'E', 'R', 'W', 'A', 'T', 'C', 'H', ' '};
+#define DBS_ETHERWATCH_HDR_MAGIC_SIZE \
+ (sizeof dbs_etherwatch_hdr_magic / sizeof dbs_etherwatch_hdr_magic[0])
+
+/* Magic text for start of packet */
+static const char dbs_etherwatch_rec_magic[] =
+{'F', 'r', 'o', 'm', ' '};
+#define DBS_ETHERWATCH_REC_MAGIC_SIZE \
+ (sizeof dbs_etherwatch_rec_magic / sizeof dbs_etherwatch_rec_magic[0])
+
+/*
+ * Default packet size - maximum normal Ethernet packet size, without an
+ * FCS.
+ */
+#define DBS_ETHERWATCH_MAX_ETHERNET_PACKET_LEN 1514
+
+static gboolean dbs_etherwatch_read(wtap *wth, wtap_rec *rec,
+ Buffer *buf, int *err, gchar **err_info, gint64 *data_offset);
+static gboolean dbs_etherwatch_seek_read(wtap *wth, gint64 seek_off,
+ wtap_rec *rec, Buffer *buf, int *err, gchar **err_info);
+static gboolean parse_dbs_etherwatch_packet(FILE_T fh, wtap_rec *rec,
+ Buffer* buf, int *err, gchar **err_info);
+static guint parse_single_hex_dump_line(char* rec, guint8 *buf,
+ int byte_offset);
+static guint parse_hex_dump(char* dump, guint8 *buf, char separator, char end);
+
+static int dbs_etherwatch_file_type_subtype = -1;
+
+void register_dbs_etherwatch(void);
+
+/* Seeks to the beginning of the next packet, and returns the
+ byte offset. Returns -1 on failure, and sets "*err" to the error
+ and "*err_info" to null or an additional error string. */
+static gint64 dbs_etherwatch_seek_next_packet(wtap *wth, int *err,
+ gchar **err_info)
+{
+ int byte;
+ unsigned int level = 0;
+ gint64 cur_off;
+
+ while ((byte = file_getc(wth->fh)) != EOF) {
+ if (byte == dbs_etherwatch_rec_magic[level]) {
+ level++;
+ if (level >= DBS_ETHERWATCH_REC_MAGIC_SIZE) {
+ /* note: we're leaving file pointer right after the magic characters */
+ cur_off = file_tell(wth->fh);
+ if (cur_off == -1) {
+ /* Error. */
+ *err = file_error(wth->fh, err_info);
+ return -1;
+ }
+ return cur_off + 1;
+ }
+ } else {
+ level = 0;
+ }
+ }
+ /* EOF or error. */
+ *err = file_error(wth->fh, err_info);
+ return -1;
+}
+
+#define DBS_ETHERWATCH_HEADER_LINES_TO_CHECK 200
+#define DBS_ETHERWATCH_LINE_LENGTH 240
+
+/* Look through the first part of a file to see if this is
+ * a DBS Ethertrace text trace file.
+ *
+ * Returns TRUE if it is, FALSE if it isn't or if we get an I/O error;
+ * if we get an I/O error, "*err" will be set to a non-zero value and
+ * "*err_info" will be set to null or an error string.
+ */
+static gboolean dbs_etherwatch_check_file_type(wtap *wth, int *err,
+ gchar **err_info)
+{
+ char buf[DBS_ETHERWATCH_LINE_LENGTH];
+ int line, byte;
+ gsize reclen;
+ unsigned int i, level;
+
+ buf[DBS_ETHERWATCH_LINE_LENGTH-1] = 0;
+
+ for (line = 0; line < DBS_ETHERWATCH_HEADER_LINES_TO_CHECK; line++) {
+ if (file_gets(buf, DBS_ETHERWATCH_LINE_LENGTH, wth->fh) == NULL) {
+ /* EOF or error. */
+ *err = file_error(wth->fh, err_info);
+ return FALSE;
+ }
+
+ reclen = strlen(buf);
+ if (reclen < DBS_ETHERWATCH_HDR_MAGIC_SIZE)
+ continue;
+
+ level = 0;
+ for (i = 0; i < reclen; i++) {
+ byte = buf[i];
+ if (byte == dbs_etherwatch_hdr_magic[level]) {
+ level++;
+ if (level >=
+ DBS_ETHERWATCH_HDR_MAGIC_SIZE) {
+ return TRUE;
+ }
+ }
+ else
+ level = 0;
+ }
+ }
+ *err = 0;
+ return FALSE;
+}
+
+
+wtap_open_return_val dbs_etherwatch_open(wtap *wth, int *err, gchar **err_info)
+{
+ /* Look for DBS ETHERWATCH header */
+ if (!dbs_etherwatch_check_file_type(wth, err, err_info)) {
+ if (*err != 0 && *err != WTAP_ERR_SHORT_READ)
+ return WTAP_OPEN_ERROR;
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ wth->file_encap = WTAP_ENCAP_ETHERNET;
+ wth->file_type_subtype = dbs_etherwatch_file_type_subtype;
+ wth->snapshot_length = 0; /* not known */
+ wth->subtype_read = dbs_etherwatch_read;
+ wth->subtype_seek_read = dbs_etherwatch_seek_read;
+ wth->file_tsprec = WTAP_TSPREC_10_MSEC;
+
+ /*
+ * Add an IDB; we don't know how many interfaces were
+ * involved, so we just say one interface, about which
+ * we only know the link-layer type, snapshot length,
+ * and time stamp resolution.
+ */
+ wtap_add_generated_idb(wth);
+
+ return WTAP_OPEN_MINE;
+}
+
+/* Find the next packet and parse it; called from wtap_read(). */
+static gboolean dbs_etherwatch_read(wtap *wth, wtap_rec *rec,
+ Buffer *buf, int *err, gchar **err_info, gint64 *data_offset)
+{
+ gint64 offset;
+
+ /* Find the next packet */
+ offset = dbs_etherwatch_seek_next_packet(wth, err, err_info);
+ if (offset < 1)
+ return FALSE;
+ *data_offset = offset;
+
+ /* Parse the packet */
+ return parse_dbs_etherwatch_packet(wth->fh, rec, buf, err, err_info);
+}
+
+/* Used to read packets in random-access fashion */
+static gboolean
+dbs_etherwatch_seek_read(wtap *wth, gint64 seek_off,
+ wtap_rec *rec, Buffer *buf, int *err, gchar **err_info)
+{
+ if (file_seek(wth->random_fh, seek_off - 1, SEEK_SET, err) == -1)
+ return FALSE;
+
+ return parse_dbs_etherwatch_packet(wth->random_fh, rec, buf, err,
+ err_info);
+}
+
+/* Parse a packet */
+/*
+Packet header:
+ 1 2 3 4
+0123456789012345678901234567890123456789012345
+From 00-D0-C0-D2-4D-60 [MF1] to AA-00-04-00-FC-94 [PSERVB]
+Protocol 08-00 00 00-00-00-00-00, 50 byte buffer at 10-OCT-2001 10:20:45.17
+*/
+#define MAC_ADDR_LENGTH 6 /* Length MAC address */
+#define DEST_MAC_PREFIX "] to " /* Prefix to the dest. MAC address */
+#define PROTOCOL_LENGTH 2 /* Length protocol */
+#define PROTOCOL_POS 9 /* Position protocol */
+#define SAP_LENGTH 2 /* Length DSAP+SSAP */
+#define SAP_POS 9 /* Position DSAP+SSAP */
+#define CTL_UNNUMB_LENGTH 1 /* Length unnumbered control field */
+#define CTL_NUMB_LENGTH 2 /* Length numbered control field */
+#define CTL_POS 15 /* Position control field */
+#define PID_LENGTH 5 /* Length PID */
+#define PID_POS 18 /* Position PID */
+#define LENGTH_POS 33 /* Position length */
+#define HEX_HDR_SPR '-' /* Separator char header hex values */
+#define HEX_HDR_END ' ' /* End char hdr. hex val. except PID */
+#define HEX_PID_END ',' /* End char PID hex value */
+#define IEEE802_LEN_LEN 2 /* Length of the IEEE 802 len. field */
+/*
+To check whether it is Ethernet II or IEEE 802 we check the values of the
+control field and PID, when they are all 0's we assume it is Ethernet II
+else IEEE 802. In IEEE 802 the DSAP and SSAP are behind protocol, the
+length in the IEEE data we have to construct.
+*/
+#define ETH_II_CHECK_POS 15
+#define ETH_II_CHECK_STR "00 00-00-00-00-00,"
+/*
+To check whether it IEEE 802.3 with SNAP we check that both the DSAP & SSAP
+values are 0xAA and the control field 0x03.
+*/
+#define SNAP_CHECK_POS 9
+#define SNAP_CHECK_STR "AA-AA 03"
+/*
+To check whether the control field is 1 or two octets we check if it is
+unnumbered. Unnumbered has length 1, numbered 2.
+*/
+#define CTL_UNNUMB_MASK 0x03
+#define CTL_UNNUMB_VALUE 0x03
+static gboolean
+parse_dbs_etherwatch_packet(FILE_T fh, wtap_rec *rec, Buffer* buf,
+ int *err, gchar **err_info)
+{
+ guint8 *pd;
+ char line[DBS_ETHERWATCH_LINE_LENGTH];
+ int num_items_scanned;
+ int eth_hdr_len, pkt_len, csec;
+ int length_pos, length_from, length;
+ struct tm tm;
+ char mon[4] = "xxx";
+ gchar *p;
+ static const gchar months[] = "JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC";
+ int count, line_count;
+
+ /* Make sure we have enough room for a regular Ethernet packet */
+ ws_buffer_assure_space(buf, DBS_ETHERWATCH_MAX_ETHERNET_PACKET_LEN);
+ pd = ws_buffer_start_ptr(buf);
+
+ eth_hdr_len = 0;
+ memset(&tm, 0, sizeof(tm));
+ /* Our file pointer should be on the first line containing the
+ * summary information for a packet. Read in that line and
+ * extract the useful information
+ */
+ if (file_gets(line, DBS_ETHERWATCH_LINE_LENGTH, fh) == NULL) {
+ *err = file_error(fh, err_info);
+ if (*err == 0) {
+ *err = WTAP_ERR_SHORT_READ;
+ }
+ return FALSE;
+ }
+
+ /* Get the destination address */
+ p = strstr(line, DEST_MAC_PREFIX);
+ if(!p) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("dbs_etherwatch: destination address not found");
+ return FALSE;
+ }
+ p += strlen(DEST_MAC_PREFIX);
+ if(parse_hex_dump(p, &pd[eth_hdr_len], HEX_HDR_SPR, HEX_HDR_END)
+ != MAC_ADDR_LENGTH) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("dbs_etherwatch: destination address not valid");
+ return FALSE;
+ }
+ eth_hdr_len += MAC_ADDR_LENGTH;
+
+ /* Get the source address */
+ /*
+ * Since the first part of the line is already skipped in order to find
+ * the start of the record we cannot index, just look for the first
+ * 'HEX' character
+ */
+ p = line;
+ while(!g_ascii_isxdigit(*p)) {
+ p++;
+ }
+ if(parse_hex_dump(p, &pd[eth_hdr_len], HEX_HDR_SPR,
+ HEX_HDR_END) != MAC_ADDR_LENGTH) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("dbs_etherwatch: source address not valid");
+ return FALSE;
+ }
+ eth_hdr_len += MAC_ADDR_LENGTH;
+
+ /* Read the next line of the record header */
+ if (file_gets(line, DBS_ETHERWATCH_LINE_LENGTH, fh) == NULL) {
+ *err = file_error(fh, err_info);
+ if (*err == 0) {
+ *err = WTAP_ERR_SHORT_READ;
+ }
+ return FALSE;
+ }
+
+ /* Check the lines is as least as long as the length position */
+ if(strlen(line) < LENGTH_POS) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("dbs_etherwatch: line too short");
+ return FALSE;
+ }
+
+ num_items_scanned = sscanf(line + LENGTH_POS,
+ "%9d byte buffer at %2d-%3s-%4d %2d:%2d:%2d.%9d",
+ &pkt_len,
+ &tm.tm_mday, mon,
+ &tm.tm_year, &tm.tm_hour, &tm.tm_min,
+ &tm.tm_sec, &csec);
+
+ if (num_items_scanned != 8) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("dbs_etherwatch: header line not valid");
+ return FALSE;
+ }
+
+ if (pkt_len < 0) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("dbs_etherwatch: packet header has a negative packet length");
+ return FALSE;
+ }
+
+ /* Determine whether it is Ethernet II or IEEE 802 */
+ if(strncmp(&line[ETH_II_CHECK_POS], ETH_II_CHECK_STR,
+ strlen(ETH_II_CHECK_STR)) == 0) {
+ /* Ethernet II */
+ /* Get the Protocol */
+ if(parse_hex_dump(&line[PROTOCOL_POS], &pd[eth_hdr_len], HEX_HDR_SPR,
+ HEX_HDR_END) != PROTOCOL_LENGTH) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("dbs_etherwatch: Ethernet II protocol value not valid");
+ return FALSE;
+ }
+ eth_hdr_len += PROTOCOL_LENGTH;
+ } else {
+ /* IEEE 802 */
+ /* Remember where to put the length in the header */
+ length_pos = eth_hdr_len;
+ /* Leave room in the header for the length */
+ eth_hdr_len += IEEE802_LEN_LEN;
+ /* Remember how much of the header should not be added to the length */
+ length_from = eth_hdr_len;
+ /* Get the DSAP + SSAP */
+ if(parse_hex_dump(&line[SAP_POS], &pd[eth_hdr_len], HEX_HDR_SPR,
+ HEX_HDR_END) != SAP_LENGTH) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("dbs_etherwatch: 802.2 DSAP+SSAP value not valid");
+ return FALSE;
+ }
+ eth_hdr_len += SAP_LENGTH;
+ /* Get the (first part of the) control field */
+ if(parse_hex_dump(&line[CTL_POS], &pd[eth_hdr_len], HEX_HDR_SPR,
+ HEX_HDR_END) != CTL_UNNUMB_LENGTH) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("dbs_etherwatch: 802.2 control field first part not valid");
+ return FALSE;
+ }
+ /* Determine whether the control is numbered, and thus longer */
+ if((pd[eth_hdr_len] & CTL_UNNUMB_MASK) != CTL_UNNUMB_VALUE) {
+ /* Get the rest of the control field, the first octet in the PID */
+ if(parse_hex_dump(&line[PID_POS],
+ &pd[eth_hdr_len + CTL_UNNUMB_LENGTH], HEX_HDR_END,
+ HEX_HDR_SPR) != CTL_NUMB_LENGTH - CTL_UNNUMB_LENGTH) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("dbs_etherwatch: 802.2 control field second part value not valid");
+ return FALSE;
+ }
+ eth_hdr_len += CTL_NUMB_LENGTH;
+ } else {
+ eth_hdr_len += CTL_UNNUMB_LENGTH;
+ }
+ /* Determine whether it is SNAP */
+ if(strncmp(&line[SNAP_CHECK_POS], SNAP_CHECK_STR,
+ strlen(SNAP_CHECK_STR)) == 0) {
+ /* Get the PID */
+ if(parse_hex_dump(&line[PID_POS], &pd[eth_hdr_len], HEX_HDR_SPR,
+ HEX_PID_END) != PID_LENGTH) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("dbs_etherwatch: 802.2 PID value not valid");
+ return FALSE;
+ }
+ eth_hdr_len += PID_LENGTH;
+ }
+ /* Write the length in the header */
+ length = eth_hdr_len - length_from + pkt_len;
+ pd[length_pos] = (length) >> 8;
+ pd[length_pos+1] = (length) & 0xFF;
+ }
+
+ rec->rec_type = REC_TYPE_PACKET;
+ rec->block = wtap_block_create(WTAP_BLOCK_PACKET);
+ rec->presence_flags = WTAP_HAS_TS|WTAP_HAS_CAP_LEN;
+
+ p = strstr(months, mon);
+ if (p)
+ tm.tm_mon = (int)(p - months) / 3;
+ tm.tm_year -= 1900;
+
+ tm.tm_isdst = -1;
+ rec->ts.secs = mktime(&tm);
+ rec->ts.nsecs = csec * 10000000;
+ rec->rec_header.packet_header.caplen = eth_hdr_len + pkt_len;
+ rec->rec_header.packet_header.len = eth_hdr_len + pkt_len;
+
+ if (rec->rec_header.packet_header.caplen > WTAP_MAX_PACKET_SIZE_STANDARD) {
+ /*
+ * Probably a corrupt capture file; return an error,
+ * so that our caller doesn't blow up trying to allocate
+ * space for an immensely-large packet.
+ */
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("dbs_etherwatch: File has %u-byte packet, bigger than maximum of %u",
+ rec->rec_header.packet_header.caplen, WTAP_MAX_PACKET_SIZE_STANDARD);
+ return FALSE;
+ }
+
+ /* Make sure we have enough room, even for an oversized Ethernet packet */
+ ws_buffer_assure_space(buf, rec->rec_header.packet_header.caplen);
+ pd = ws_buffer_start_ptr(buf);
+
+ /*
+ * We don't have an FCS in this frame.
+ */
+ rec->rec_header.packet_header.pseudo_header.eth.fcs_len = 0;
+
+ /* Parse the hex dump */
+ count = 0;
+ while (count < pkt_len) {
+ if (file_gets(line, DBS_ETHERWATCH_LINE_LENGTH, fh) == NULL) {
+ *err = file_error(fh, err_info);
+ if (*err == 0) {
+ *err = WTAP_ERR_SHORT_READ;
+ }
+ return FALSE;
+ }
+ if (!(line_count = parse_single_hex_dump_line(line,
+ &pd[eth_hdr_len + count], count))) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("dbs_etherwatch: packet data value not valid");
+ return FALSE;
+ }
+ count += line_count;
+ if (count > pkt_len) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("dbs_etherwatch: packet data value has too many bytes");
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+/* Parse a hex dump line */
+/*
+/DISPLAY=BOTH output:
+
+ 1 2 3 4
+0123456789012345678901234567890123456789012345
+ [E..(8...........]- 0-[45 00 00 28 38 9B 00 00 1D 06 D2 1E 80 93 11 1A]
+ [.........(..Z.4y]- 16-[80 93 80 D6 02 D2 02 03 00 28 A4 BF 5A 1C 34 79]
+ [P.#(.C...00000..]- 32-[50 10 23 28 C1 43 00 00 03 30 30 30 30 30 00 00]
+ [.0 ]- 48-[03 30]
+
+/DISPLAY=HEXADECIMAL output:
+
+ 1 2 3 4
+0123456789012345678901234567890123456789012345
+ 0-[45 00 00 28 38 9B 00 00 1D 06 D2 1E 80 93 11 1A 80 93 80 D6]
+ 20-[02 D2 02 03 00 28 A4 BF 5A 1C 34 79 50 10 23 28 C1 43 00 00]
+ 40-[03 30 30 30 30 30 00 00 03 30]
+
+*/
+
+#define TYPE_CHECK_POS 2 /* Position to check the type of hex dump */
+#define TYPE_CHECK_BOTH '[' /* Value at pos. that indicates BOTH type */
+#define COUNT_POS_BOTH 21 /* Count position BOTH type */
+#define COUNT_POS_HEX 1 /* Count position HEX type */
+#define COUNT_SIZE 5 /* Length counter */
+#define HEX_DUMP_START '[' /* Start char */
+#define HEX_DUMP_SPR ' ' /* Separator char */
+#define HEX_DUMP_END ']' /* End char */
+
+/* Take a string representing one line from a hex dump and converts the
+ * text to binary data. We check the printed offset with the offset
+ * we are passed to validate the record. We place the bytes in the buffer
+ * at the specified offset.
+ *
+ * Returns length parsed if a good hex dump, 0 if bad.
+ */
+static guint
+parse_single_hex_dump_line(char* rec, guint8 *buf, int byte_offset) {
+
+ int pos, i;
+ int value;
+
+
+ /* Check that the record is as least as long as the check offset */
+ for(i = 0; i < TYPE_CHECK_POS; i++)
+ {
+ if(rec[i] == '\0') {
+ return 0;
+ }
+ }
+ /* determine the format and thus the counter offset and hex dump length */
+ if(rec[TYPE_CHECK_POS] == TYPE_CHECK_BOTH)
+ {
+ pos = COUNT_POS_BOTH;
+ }
+ else
+ {
+ pos = COUNT_POS_HEX;
+ }
+
+ /* Check that the record is as least as long as the start position */
+ while(i < pos)
+ {
+ if(rec[i] == '\0') {
+ return 0;
+ }
+ i++;
+ }
+
+ /* Get the byte_offset directly from the record */
+ value = 0;
+ for(i = 0; i < COUNT_SIZE; i++) {
+ if(!g_ascii_isspace(rec[pos])) {
+ if(g_ascii_isdigit(rec[pos])) {
+ value *= 10;
+ value += rec[pos] - '0';
+ } else {
+ return 0;
+ }
+ }
+ pos++;
+ }
+
+ if (value != byte_offset) {
+ return 0;
+ }
+
+ /* find the start of the hex dump */
+ while(rec[pos] != HEX_DUMP_START) {
+ if(rec[pos] == '\0') {
+ return 0;
+ }
+ pos++;
+ }
+ pos++;
+ return parse_hex_dump(&rec[pos], buf, HEX_DUMP_SPR, HEX_DUMP_END);
+}
+
+/* Parse a hex dump */
+static guint
+parse_hex_dump(char* dump, guint8 *buf, char separator, char end) {
+ int pos, count;
+
+ /* Parse the hex dump */
+ pos = 0;
+ count = 0;
+ while(dump[pos] != end) {
+ /* Check the hex value */
+ if(!(g_ascii_isxdigit(dump[pos]) &&
+ g_ascii_isxdigit(dump[pos + 1]))) {
+ return 0;
+ }
+ /* Get the hex value */
+ if(g_ascii_isdigit(dump[pos])) {
+ buf[count] = (dump[pos] - '0') << 4;
+ } else {
+ buf[count] = (g_ascii_toupper(dump[pos]) - 'A' + 10) << 4;
+ }
+ pos++;
+ if(g_ascii_isdigit(dump[pos])) {
+ buf[count] += dump[pos] - '0';
+ } else {
+ buf[count] += g_ascii_toupper(dump[pos]) - 'A' + 10;
+ }
+ pos++;
+ count++;
+ /* Skip the separator characters */
+ while(dump[pos] == separator) {
+ pos++;
+ }
+ }
+ return count;
+}
+
+static const struct supported_block_type dbs_etherwatch_blocks_supported[] = {
+ /*
+ * We support packet blocks, with no comments or other options.
+ */
+ { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
+};
+
+static const struct file_type_subtype_info dbs_etherwatch_info = {
+ "DBS Etherwatch (VMS)", "etherwatch", "txt", NULL,
+ FALSE, BLOCKS_SUPPORTED(dbs_etherwatch_blocks_supported),
+ NULL, NULL, NULL
+};
+
+void register_dbs_etherwatch(void)
+{
+ dbs_etherwatch_file_type_subtype = wtap_register_file_type_subtype(&dbs_etherwatch_info);
+
+ /*
+ * Register name for backwards compatibility with the
+ * wtap_filetypes table in Lua.
+ */
+ wtap_register_backwards_compatibility_lua_name("DBS_ETHERWATCH",
+ dbs_etherwatch_file_type_subtype);
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * vi: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */
diff --git a/wiretap/dbs-etherwatch.h b/wiretap/dbs-etherwatch.h
new file mode 100644
index 00000000..0ea83c6e
--- /dev/null
+++ b/wiretap/dbs-etherwatch.h
@@ -0,0 +1,18 @@
+/** @file
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ */
+
+#ifndef __W_DBS_ETHERWATCH_H__
+#define __W_DBS_ETHERWATCH_H__
+#include <glib.h>
+#include "wtap.h"
+#include "ws_symbol_export.h"
+
+wtap_open_return_val dbs_etherwatch_open(wtap *wth, int *err, gchar **err_info);
+
+#endif
diff --git a/wiretap/dct3trace.c b/wiretap/dct3trace.c
new file mode 100644
index 00000000..1476247d
--- /dev/null
+++ b/wiretap/dct3trace.c
@@ -0,0 +1,442 @@
+/* dct3trace.c
+ * Routines for reading signalling traces generated by Gammu (www.gammu.org)
+ * from Nokia DCT3 phones in Netmonitor mode.
+ *
+ * gammu --nokiadebug nhm5_587.txt v18-19
+ *
+ * Duncan Salerno <duncan.salerno@googlemail.com>
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "config.h"
+#include "wtap-int.h"
+#include "dct3trace.h"
+#include "file_wrappers.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <wsutil/strtoi.h>
+
+/*
+ Example downlink data:
+
+<?xml version="1.0"?>
+<dump>
+<l1 direction="down" logicalchannel="96" physicalchannel="19" sequence="268116" error="0" timeshift="2992" bsic="22" data="31063F100DD0297A53E1020103C802398E0B2B2B2B2B2B" >
+<l2 data="063F100DD0297A53E1020103" rest="C802398E0B2B2B2B2B2B" >
+</l2>
+</l1>
+</dump>
+
+ Example uplink data (no raw L1):
+
+<?xml version="1.0"?>
+<dump>
+<l1 direction="up" logicalchannel="112" >
+<l2 type="U" subtype="Unknown" p="0" data="061500400000000000000000000000000000" >
+</l2>
+</l1>
+</dump>
+
+ */
+
+
+/* Magic text to check */
+static const char dct3trace_magic_line1[] = "<?xml version=\"1.0\"?>";
+static const char dct3trace_magic_line2[] = "<dump>";
+static const char dct3trace_magic_record_start[] = "<l1 ";
+static const char dct3trace_magic_record_end[] = "</l1>";
+static const char dct3trace_magic_l2_start[] = "<l2 ";
+#if 0 /* Not used ?? */
+static const char dct3trace_magic_l2_end[] = "</l2>";
+#endif
+static const char dct3trace_magic_end[] = "</dump>";
+
+#define MAX_PACKET_LEN 23
+
+static gboolean dct3trace_read(wtap *wth, wtap_rec *rec,
+ Buffer *buf, int *err, gchar **err_info, gint64 *data_offset);
+static gboolean dct3trace_seek_read(wtap *wth, gint64 seek_off,
+ wtap_rec *rec, Buffer *buf, int *err, gchar **err_info);
+
+static int dct3trace_file_type_subtype = -1;
+
+void register_dct3trace(void);
+
+/*
+ * Following 3 functions taken from gsmdecode-0.7bis, with permission:
+ *
+ * https://web.archive.org/web/20091218112927/http://wiki.thc.org/gsm
+ */
+
+static int
+hc2b(unsigned char hex)
+{
+ hex = g_ascii_tolower(hex);
+ if ((hex >= '0') && (hex <= '9'))
+ return hex - '0';
+ if ((hex >= 'a') && (hex <= 'f'))
+ return hex - 'a' + 10;
+ return -1;
+}
+
+static int
+hex2bin(guint8 *out, guint8 *out_end, char *in)
+{
+ guint8 *out_start = out;
+ int is_low = 0;
+ int c;
+
+ while (*in != '\0')
+ {
+ c = hc2b(*(unsigned char *)in);
+ if (c < 0)
+ {
+ in++;
+ continue;
+ }
+ if (out == out_end)
+ {
+ /* Too much data */
+ return -1;
+ }
+ if (is_low == 0)
+ {
+ *out = c << 4;
+ is_low = 1;
+ } else {
+ *out |= (c & 0x0f);
+ is_low = 0;
+ out++;
+ }
+ in++;
+ }
+
+ return (int)(out - out_start);
+}
+
+static gboolean
+xml_get_int(int *val, const char *str, const char *pattern, int *err, gchar **err_info)
+{
+ const char *ptr, *endptr;
+ char *start, *end;
+ char buf[32];
+
+ ptr = strstr(str, pattern);
+ if (ptr == NULL) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("dct3trace: %s not found", pattern);
+ return FALSE;
+ }
+ /*
+ * XXX - should we just skip past the pattern and check for ="?
+ */
+ start = strchr(ptr, '"');
+ if (start == NULL) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("dct3trace: opening quote for %s not found", pattern);
+ return FALSE;
+ }
+ start++;
+ /*
+ * XXX - should we just use ws_strtoi32() and check whether
+ * the character following the number is a "?
+ */
+ end = strchr(start, '"');
+ if (end == NULL) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("dct3trace: closing quote for %s not found", pattern);
+ return FALSE;
+ }
+ if (end - start > 31) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("dct3trace: %s value is too long", pattern);
+ return FALSE;
+ }
+
+ memcpy(buf, start, end - start);
+ buf[end - start] = '\0';
+ /*
+ * XXX - should we allow negative numbers in all cases? Or are
+ * there cases where the number is unsigned?
+ */
+ if (!ws_strtoi32(buf, &endptr, val)) {
+ *err = WTAP_ERR_BAD_FILE;
+ if (errno == ERANGE) {
+ if (*val < 0)
+ *err_info = ws_strdup_printf("dct3trace: %s value is too small, minimum is %d", pattern, *val);
+ else
+ *err_info = ws_strdup_printf("dct3trace: %s value is too large, maximum is %d", pattern, *val);
+ } else
+ *err_info = ws_strdup_printf("dct3trace: %s value \"%s\" not a number", pattern, buf);
+ return FALSE;
+ }
+ if (*endptr != '\0') {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("dct3trace: %s value \"%s\" not a number", pattern, buf);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+
+wtap_open_return_val dct3trace_open(wtap *wth, int *err, gchar **err_info)
+{
+ char line1[64], line2[64];
+
+ /* Look for Gammu DCT3 trace header */
+ if (file_gets(line1, sizeof(line1), wth->fh) == NULL ||
+ file_gets(line2, sizeof(line2), wth->fh) == NULL)
+ {
+ *err = file_error(wth->fh, err_info);
+ if (*err != 0 && *err != WTAP_ERR_SHORT_READ)
+ return WTAP_OPEN_ERROR;
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ /* Don't compare line endings */
+ if( strncmp(dct3trace_magic_line1, line1, strlen(dct3trace_magic_line1)) != 0 ||
+ strncmp(dct3trace_magic_line2, line2, strlen(dct3trace_magic_line2)) != 0)
+ {
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ wth->file_encap = WTAP_ENCAP_GSM_UM;
+ wth->file_type_subtype = dct3trace_file_type_subtype;
+ wth->snapshot_length = 0; /* not known */
+ wth->subtype_read = dct3trace_read;
+ wth->subtype_seek_read = dct3trace_seek_read;
+ wth->file_tsprec = WTAP_TSPREC_SEC;
+
+ /*
+ * Add an IDB; we don't know how many interfaces were
+ * involved, so we just say one interface, about which
+ * we only know the link-layer type, snapshot length,
+ * and time stamp resolution.
+ */
+ wtap_add_generated_idb(wth);
+
+ return WTAP_OPEN_MINE;
+}
+
+
+static gboolean dct3trace_get_packet(FILE_T fh, wtap_rec *rec,
+ Buffer *buf, int *err, gchar **err_info)
+{
+ char line[1024];
+ guint8 databuf[MAX_PACKET_LEN], *bufp;
+ gboolean have_data = FALSE;
+ int len = 0;
+
+ bufp = &databuf[0];
+ while (file_gets(line, sizeof(line), fh) != NULL)
+ {
+ if( memcmp(dct3trace_magic_end, line, strlen(dct3trace_magic_end)) == 0 )
+ {
+ /* Return on end of file </dump> */
+ *err = 0;
+ return FALSE;
+ }
+ else if( memcmp(dct3trace_magic_record_end, line, strlen(dct3trace_magic_record_end)) == 0 )
+ {
+ /* Return on end of record </l1> */
+ if( have_data )
+ {
+ /* We've got a full packet! */
+ rec->rec_type = REC_TYPE_PACKET;
+ rec->block = wtap_block_create(WTAP_BLOCK_PACKET);
+ rec->presence_flags = 0; /* no time stamp, no separate "on the wire" length */
+ rec->ts.secs = 0;
+ rec->ts.nsecs = 0;
+ rec->rec_header.packet_header.caplen = len;
+ rec->rec_header.packet_header.len = len;
+
+ *err = 0;
+
+ /* Make sure we have enough room for the packet */
+ ws_buffer_assure_space(buf, rec->rec_header.packet_header.caplen);
+ memcpy( ws_buffer_start_ptr(buf), databuf, rec->rec_header.packet_header.caplen );
+
+ return TRUE;
+ }
+ else
+ {
+ /* If not got any data return error */
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("dct3trace: record without data");
+ return FALSE;
+ }
+ }
+ else if( memcmp(dct3trace_magic_record_start, line, strlen(dct3trace_magic_record_start)) == 0 )
+ {
+ /* Parse L1 header <l1 ...>*/
+ int channel, tmp;
+ char *ptr;
+
+ rec->rec_header.packet_header.pseudo_header.gsm_um.uplink = !strstr(line, "direction=\"down\"");
+ if (!xml_get_int(&channel, line, "logicalchannel", err, err_info))
+ return FALSE;
+
+ /* Parse downlink only fields */
+ if( !rec->rec_header.packet_header.pseudo_header.gsm_um.uplink )
+ {
+ if (!xml_get_int(&tmp, line, "physicalchannel", err, err_info))
+ return FALSE;
+ rec->rec_header.packet_header.pseudo_header.gsm_um.arfcn = tmp;
+ if (!xml_get_int(&tmp, line, "sequence", err, err_info))
+ return FALSE;
+ rec->rec_header.packet_header.pseudo_header.gsm_um.tdma_frame = tmp;
+ if (!xml_get_int(&tmp, line, "bsic", err, err_info))
+ return FALSE;
+ rec->rec_header.packet_header.pseudo_header.gsm_um.bsic = tmp;
+ if (!xml_get_int(&tmp, line, "error", err, err_info))
+ return FALSE;
+ rec->rec_header.packet_header.pseudo_header.gsm_um.error = tmp;
+ if (!xml_get_int(&tmp, line, "timeshift", err, err_info))
+ return FALSE;
+ rec->rec_header.packet_header.pseudo_header.gsm_um.timeshift = tmp;
+ }
+
+ switch( channel )
+ {
+ case 128: rec->rec_header.packet_header.pseudo_header.gsm_um.channel = GSM_UM_CHANNEL_SDCCH; break;
+ case 112: rec->rec_header.packet_header.pseudo_header.gsm_um.channel = GSM_UM_CHANNEL_SACCH; break;
+ case 176: rec->rec_header.packet_header.pseudo_header.gsm_um.channel = GSM_UM_CHANNEL_FACCH; break;
+ case 96: rec->rec_header.packet_header.pseudo_header.gsm_um.channel = GSM_UM_CHANNEL_CCCH; break;
+ case 80: rec->rec_header.packet_header.pseudo_header.gsm_um.channel = GSM_UM_CHANNEL_BCCH; break;
+ default: rec->rec_header.packet_header.pseudo_header.gsm_um.channel = GSM_UM_CHANNEL_UNKNOWN; break;
+ }
+
+ /* Read data (if have it) into databuf */
+ ptr = strstr(line, "data=\"");
+ if( ptr )
+ {
+ have_data = TRUE; /* If has data... */
+ len = hex2bin(bufp, &databuf[MAX_PACKET_LEN], ptr+6);
+ if (len == -1)
+ {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("dct3trace: record length %d too long", rec->rec_header.packet_header.caplen);
+ return FALSE;
+ }
+ }
+ }
+ else if( !have_data && memcmp(dct3trace_magic_l2_start, line, strlen(dct3trace_magic_l2_start)) == 0 )
+ {
+ /* For uplink packets we might not get the raw L1, so have to recreate it from the L2 */
+ /* Parse L2 header if didn't get data from L1 <l2 ...> */
+ int data_len;
+ char *ptr = strstr(line, "data=\"");
+
+ if( !ptr )
+ {
+ continue;
+ }
+
+ have_data = TRUE;
+
+ /*
+ * We know we have no data already, so we know
+ * we have enough room for the header.
+ */
+ if( rec->rec_header.packet_header.pseudo_header.gsm_um.channel == GSM_UM_CHANNEL_SACCH || rec->rec_header.packet_header.pseudo_header.gsm_um.channel == GSM_UM_CHANNEL_FACCH || rec->rec_header.packet_header.pseudo_header.gsm_um.channel == GSM_UM_CHANNEL_SDCCH )
+ {
+ /* Add LAPDm B header */
+ memset(bufp, 0x1, 2);
+ len = 3;
+ }
+ else
+ {
+ /* Add LAPDm Bbis header */
+ len = 1;
+ }
+ bufp += len;
+
+ data_len = hex2bin(bufp, &databuf[MAX_PACKET_LEN], ptr+6);
+ if (data_len == -1)
+ {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("dct3trace: record length %d too long", rec->rec_header.packet_header.caplen);
+ return FALSE;
+ }
+ len += data_len;
+
+ /* Add LAPDm length byte */
+ *(bufp - 1) = data_len << 2 | 0x1;
+ }
+ }
+
+ *err = file_error(fh, err_info);
+ if (*err == 0)
+ {
+ *err = WTAP_ERR_SHORT_READ;
+ }
+ return FALSE;
+}
+
+
+/* Find the next packet and parse it; called from wtap_read(). */
+static gboolean dct3trace_read(wtap *wth, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info, gint64 *data_offset)
+{
+ *data_offset = file_tell(wth->fh);
+
+ return dct3trace_get_packet(wth->fh, rec, buf, err, err_info);
+}
+
+
+/* Used to read packets in random-access fashion */
+static gboolean dct3trace_seek_read(wtap *wth, gint64 seek_off,
+ wtap_rec *rec, Buffer *buf, int *err, gchar **err_info)
+{
+ if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
+ {
+ return FALSE;
+ }
+
+ return dct3trace_get_packet(wth->random_fh, rec, buf, err, err_info);
+}
+
+static const struct supported_block_type dct3trace_blocks_supported[] = {
+ /*
+ * We support packet blocks, with no comments or other options.
+ */
+ { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
+};
+
+static const struct file_type_subtype_info dct3trace_info = {
+ "Gammu DCT3 trace", "dct3trace", "xml", NULL,
+ FALSE, BLOCKS_SUPPORTED(dct3trace_blocks_supported),
+ NULL, NULL, NULL
+};
+
+void register_dct3trace(void)
+{
+ dct3trace_file_type_subtype = wtap_register_file_type_subtype(&dct3trace_info);
+
+ /*
+ * Register name for backwards compatibility with the
+ * wtap_filetypes table in Lua.
+ */
+ wtap_register_backwards_compatibility_lua_name("DCT3TRACE",
+ dct3trace_file_type_subtype);
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/wiretap/dct3trace.h b/wiretap/dct3trace.h
new file mode 100644
index 00000000..297b88bf
--- /dev/null
+++ b/wiretap/dct3trace.h
@@ -0,0 +1,17 @@
+/** @file
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __W_DCT3TRACE_H__
+#define __W_DCT3TRACE_H__
+#include <glib.h>
+#include "wtap.h"
+#include "ws_symbol_export.h"
+
+wtap_open_return_val dct3trace_open(wtap *wth, int *err, gchar **err_info);
+
+#endif
diff --git a/wiretap/dpa400.c b/wiretap/dpa400.c
new file mode 100644
index 00000000..671dbdff
--- /dev/null
+++ b/wiretap/dpa400.c
@@ -0,0 +1,289 @@
+/* dpa400.c
+ *
+ * Unigraf DisplayPort AUX channel monitor output parser
+ * Copyright 2018, Dirk Eibach, Guntermann & Drunck GmbH <dirk.eibach@gdsys.cc>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include "wtap-int.h"
+#include "file_wrappers.h"
+#include "dpa400.h"
+
+enum {
+ DPA400_DATA = 0x00,
+ DPA400_DATA_END = 0x01,
+ DPA400_EVENT = 0x02,
+ DPA400_START = 0x03,
+ DPA400_STOP = 0x04,
+ DPA400_TS_OVERFLOW = 0x84,
+};
+
+struct dpa400_header {
+ guint8 t0;
+ guint8 sb0;
+ guint8 t1;
+ guint8 sb1;
+ guint8 t2;
+ guint8 sb2;
+};
+
+static int dpa400_file_type_subtype = -1;
+
+void register_dpa400(void);
+
+static gboolean dpa400_read_header(FILE_T fh, struct dpa400_header *hdr, int *err, gchar **err_info)
+{
+ if (!wtap_read_bytes_or_eof(fh, hdr, sizeof(struct dpa400_header), err, err_info))
+ return FALSE;
+
+ if (hdr->sb0 || hdr->sb1 || hdr->sb2) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("dpa400: malformed packet header");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void get_ts(struct dpa400_header *hdr, nstime_t *ts)
+{
+ guint32 val;
+
+ val = (hdr->t0 | (hdr->t1 << 8) | ((hdr->t2 & 0x7f) << 16)) << 5;
+
+ ts->secs = val / 1000000;
+ ts->nsecs = (val % 1000000) * 1000;
+}
+
+static void get_ts_overflow(nstime_t *ts)
+{
+ guint32 val = 0x7fffff << 5;
+
+ ts->secs = val / 1000000;
+ ts->nsecs = (val % 1000000) * 1000;
+}
+
+static guint8 get_from(struct dpa400_header *hdr)
+{
+ return hdr->t2 & 0x80;
+}
+
+static gboolean dpa400_read_packet(wtap *wth, FILE_T fh, wtap_rec *rec,
+ Buffer *buf, int *err, gchar **err_info)
+{
+ guint8 chunk[2];
+ guint32 ctr = 0;
+
+ if (!wth || !rec || !buf)
+ return FALSE;
+
+ if (!wtap_read_bytes_or_eof(fh, chunk, sizeof(chunk), err, err_info))
+ return FALSE;
+
+ if (chunk[1] != 1) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("dpa400: malformed packet framing");
+ return FALSE;
+ }
+
+ ws_buffer_clean(buf);
+
+ ws_buffer_append(buf, &chunk[0], 1);
+ ctr++;
+
+ switch (chunk[0]) {
+ case DPA400_STOP: {
+ struct dpa400_header hdr;
+
+ if (!dpa400_read_header(fh, &hdr, err, err_info))
+ return FALSE;
+
+ get_ts(&hdr, &rec->ts);
+
+ rec->rec_type = REC_TYPE_PACKET;
+ rec->block = wtap_block_create(WTAP_BLOCK_PACKET);
+ rec->presence_flags = WTAP_HAS_TS;
+ rec->rec_header.packet_header.caplen = rec->rec_header.packet_header.len = 0;
+
+ break;
+ }
+
+ case DPA400_START:
+ case DPA400_EVENT: {
+ struct dpa400_header hdr;
+
+ if (!dpa400_read_header(fh, &hdr, err, err_info))
+ return FALSE;
+
+ get_ts(&hdr, &rec->ts);
+
+ if (!wtap_read_bytes_or_eof(fh, chunk, sizeof(chunk), err, err_info))
+ return FALSE;
+
+ if (chunk[1]) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("dpa400: malformed packet");
+ return FALSE;
+ }
+
+ ws_buffer_append(buf, &chunk[0], 1);
+ ctr++;
+
+ rec->rec_type = REC_TYPE_PACKET;
+ rec->block = wtap_block_create(WTAP_BLOCK_PACKET);
+ rec->presence_flags = WTAP_HAS_TS;
+ rec->rec_header.packet_header.caplen = rec->rec_header.packet_header.len = ctr;
+
+ break;
+ }
+
+ case DPA400_DATA: {
+ struct dpa400_header hdr;
+ guint8 from_source;
+
+ if (!dpa400_read_header(fh, &hdr, err, err_info))
+ return FALSE;
+
+ get_ts(&hdr, &rec->ts);
+
+ from_source = !get_from(&hdr);
+ ws_buffer_append(buf, &from_source, 1);
+ ctr++;
+
+ while (1) {
+ if (!wtap_read_bytes_or_eof(fh, chunk, sizeof(chunk), err, err_info))
+ return FALSE;
+
+ if (chunk[1])
+ break;
+
+ if (++ctr > WTAP_MAX_PACKET_SIZE_STANDARD) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("dpa400: File has data record bigger than maximum of %u",
+ WTAP_MAX_PACKET_SIZE_STANDARD);
+ return FALSE;
+ }
+
+ ws_buffer_append(buf, &chunk[0], 1);
+ }
+
+ rec->rec_type = REC_TYPE_PACKET;
+ rec->block = wtap_block_create(WTAP_BLOCK_PACKET);
+ rec->presence_flags = WTAP_HAS_TS;
+ rec->rec_header.packet_header.caplen = rec->rec_header.packet_header.len = ctr;
+
+ break;
+ }
+
+ case DPA400_TS_OVERFLOW: {
+ get_ts_overflow(&rec->ts);
+
+ rec->rec_type = REC_TYPE_PACKET;
+ rec->block = wtap_block_create(WTAP_BLOCK_PACKET);
+ rec->presence_flags = WTAP_HAS_TS;
+ rec->rec_header.packet_header.caplen = rec->rec_header.packet_header.len = ctr;
+
+ break;
+ }
+
+ default:
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("dpa400: unknown packet type %02x", chunk[0]);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean dpa400_seek_read(wtap *wth,gint64 seek_off, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info)
+{
+ if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
+ return FALSE;
+
+ return dpa400_read_packet(wth, wth->random_fh, rec, buf, err, err_info);
+}
+
+static gboolean dpa400_read(wtap *wth, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info, gint64 *data_offset)
+{
+ *data_offset = file_tell(wth->fh);
+
+ return dpa400_read_packet(wth, wth->fh, rec, buf, err, err_info);
+}
+
+wtap_open_return_val dpa400_open(wtap *wth, int *err, gchar **err_info)
+{
+ char magic[4];
+ const char dpa_magic[] = { 'D', 'B', 'F', 'R' };
+
+ /* Read in the number that should be at the start of a "dpa-400" file */
+ if (!wtap_read_bytes(wth->fh, &magic, sizeof magic, err, err_info)) {
+ if (*err != WTAP_ERR_SHORT_READ)
+ return WTAP_OPEN_ERROR;
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ if (memcmp(magic, dpa_magic, sizeof(dpa_magic)))
+ return WTAP_OPEN_NOT_MINE;
+
+ wth->file_type_subtype = dpa400_file_type_subtype;
+ wth->file_encap = WTAP_ENCAP_DPAUXMON;
+ wth->file_tsprec = WTAP_TSPREC_USEC;
+ wth->subtype_read = dpa400_read;
+ wth->subtype_seek_read = dpa400_seek_read;
+ wth->snapshot_length = 0;
+
+ /*
+ * Add an IDB; we don't know how many interfaces were
+ * involved, so we just say one interface, about which
+ * we only know the link-layer type, snapshot length,
+ * and time stamp resolution.
+ */
+ wtap_add_generated_idb(wth);
+
+ return WTAP_OPEN_MINE;
+}
+
+static const struct supported_block_type dpa400_blocks_supported[] = {
+ /*
+ * We support packet blocks, with no comments or other options.
+ */
+ { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
+};
+
+static const struct file_type_subtype_info dpa400_info = {
+ "Unigraf DPA-400 capture", "dpa400", "bin", NULL,
+ FALSE, BLOCKS_SUPPORTED(dpa400_blocks_supported),
+ NULL, NULL, NULL
+};
+
+void register_dpa400(void)
+{
+ dpa400_file_type_subtype = wtap_register_file_type_subtype(&dpa400_info);
+
+ /*
+ * Register name for backwards compatibility with the
+ * wtap_filetypes table in Lua.
+ */
+ wtap_register_backwards_compatibility_lua_name("DPA400",
+ dpa400_file_type_subtype);
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/wiretap/dpa400.h b/wiretap/dpa400.h
new file mode 100644
index 00000000..2e78cf29
--- /dev/null
+++ b/wiretap/dpa400.h
@@ -0,0 +1,30 @@
+/** @file
+ *
+ * Copyright 2018, Dirk Eibach, Guntermann & Drunck GmbH <dirk.eibach@gdsys.cc>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __DPA400_H__
+#define __DPA400_H__
+
+#include <glib.h>
+
+#include "wtap.h"
+
+wtap_open_return_val dpa400_open(wtap *wth, int *err, gchar **err_info);
+
+#endif
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * vi: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */
diff --git a/wiretap/erf-common.h b/wiretap/erf-common.h
new file mode 100644
index 00000000..d70bf2a2
--- /dev/null
+++ b/wiretap/erf-common.h
@@ -0,0 +1,41 @@
+/** @file
+ *
+ * Copyright (c) 2003 Endace Technology Ltd, Hamilton, New Zealand.
+ * All rights reserved.
+ *
+ * This software and documentation has been developed by Endace Technology Ltd.
+ * along with the DAG PCI network capture cards. For further information please
+ * visit https://www.endace.com/.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef __W_ERF_COMMON_H__
+#define __W_ERF_COMMON_H__
+
+/*
+ * Declarations of functions exported to file readers that handle
+ * LINKTYPE_ERF packets.
+ */
+
+typedef struct erf_private erf_t;
+
+erf_t* erf_priv_create(void);
+erf_t* erf_priv_free(erf_t* erf_priv);
+
+int erf_populate_interface_from_header(erf_t* erf_priv, wtap *wth, union wtap_pseudo_header *pseudo_header, int *err, gchar **err_info);
+
+#endif /* __W_ERF_COMMON_H__ */
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/wiretap/erf.c b/wiretap/erf.c
new file mode 100644
index 00000000..c4770ff3
--- /dev/null
+++ b/wiretap/erf.c
@@ -0,0 +1,3640 @@
+/*
+ * Copyright (c) 2003 Endace Technology Ltd, Hamilton, New Zealand.
+ * All rights reserved.
+ *
+ * This software and documentation has been developed by Endace Technology Ltd.
+ * along with the DAG PCI network capture cards. For further information please
+ * visit http://www.endace.com/.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+/*
+ * erf - Endace ERF (Extensible Record Format)
+ *
+ * Rev A:
+ * http://web.archive.org/web/20050829051042/http://www.endace.com/support/EndaceRecordFormat.pdf
+ * Version 1:
+ * http://web.archive.org/web/20061111014023/http://www.endace.com/support/EndaceRecordFormat.pdf
+ * Version 8:
+ * https://gitlab.com/wireshark/wireshark/uploads/f694bfee494784425b6545892180a8b2/Endace_ERF_Types.pdf
+ * (bug #4484)
+ * Current version (version 21, as of 2023-03-28):
+ * https://www.endace.com/erf-extensible-record-format-types.pdf
+ *
+ * Note that version 17 drops descriptions of records for no-longer-supported
+ * DAG cards. Version 16 is probably the best version to use for those
+ * older record types, but it's not in any obvious location in the Wayback
+ * Machine.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <glib.h>
+
+#include <wsutil/crc32.h>
+#include <wsutil/strtoi.h>
+#include <wsutil/glib-compat.h>
+
+#include "wtap-int.h"
+#include "file_wrappers.h"
+#include "erf.h"
+#include "erf_record.h"
+#include "erf-common.h"
+
+struct erf_anchor_mapping {
+ guint64 host_id;
+ guint64 anchor_id;
+ guint64 gen_time;
+ gchar *comment;
+};
+
+static const guint erf_header_size = (guint)sizeof(erf_header_t);
+static const guint erf_mc_header_size = (guint)sizeof(erf_mc_header_t);
+static const guint erf_eth_hdr_size = (guint)sizeof(erf_eth_header_t);
+
+
+static gboolean erf_read_header(wtap *wth, FILE_T fh,
+ wtap_rec *rec,
+ erf_header_t *erf_header,
+ int *err,
+ gchar **err_info,
+ guint32 *bytes_read,
+ guint32 *packet_size,
+ GPtrArray *anchor_mappings_to_update);
+static gboolean erf_read(wtap *wth, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info, gint64 *data_offset);
+static gboolean erf_seek_read(wtap *wth, gint64 seek_off,
+ wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info);
+static void erf_close(wtap *wth);
+
+static int populate_summary_info(erf_t *erf_priv, wtap *wth, union wtap_pseudo_header *pseudo_header, Buffer *buf, guint32 packet_size, GPtrArray *anchor_mappings_to_update, int *err, gchar **err_info);
+static int erf_update_anchors_from_header(erf_t *erf_priv, wtap_rec *rec, union wtap_pseudo_header *pseudo_header, guint64 host_id, GPtrArray *anchor_mappings_to_update);
+static int erf_get_source_from_header(union wtap_pseudo_header *pseudo_header, guint64 *host_id, guint8 *source_id);
+static int erf_populate_interface(erf_t* erf_priv, wtap *wth, union wtap_pseudo_header *pseudo_header, guint64 host_id, guint8 source_id, guint8 if_num, int *err, gchar **err_info);
+
+typedef struct {
+ gboolean write_next_extra_meta;
+ gboolean last_meta_periodic;
+ guint64 host_id;
+ guint64 implicit_host_id;
+ guint64 prev_frame_ts;
+ guint8 prev_erf_type;
+ guint64 gen_time;
+ time_t first_frame_time_sec;
+ time_t prev_inserted_time_sec;
+ gchar* user_comment_ptr;
+ GPtrArray* periodic_sections;
+ GArray *periodic_extra_ehdrs;
+ GRand *rand;
+} erf_dump_t;
+
+static erf_dump_t* erf_dump_priv_create(void);
+static void erf_dump_priv_free(erf_dump_t *dump_priv);
+static gboolean erf_dump_priv_compare_capture_comment(wtap_dumper *wdh, erf_dump_t *dump_priv,const union wtap_pseudo_header *pseudo_header, const guint8 *pd);
+static gboolean erf_comment_to_sections(wtap_dumper *wdh, guint16 section_type, guint16 section_id, gchar *comment, GPtrArray *sections);
+static gboolean erf_wtap_info_to_sections(wtap_dumper *wdh, GPtrArray *sections);
+static gboolean get_user_comment_string(wtap_dumper *wdh, gchar** user_comment_ptr);
+
+static gboolean erf_write_meta_record(wtap_dumper *wdh, erf_dump_t *dump_priv, guint64 timestamp, GPtrArray *sections, GArray *extra_ehdrs, int *err);
+
+static const struct {
+ int erf_encap_value;
+ int wtap_encap_value;
+} erf_to_wtap_map[] = {
+ { ERF_TYPE_HDLC_POS, WTAP_ENCAP_CHDLC },
+ { ERF_TYPE_HDLC_POS, WTAP_ENCAP_HHDLC },
+ { ERF_TYPE_HDLC_POS, WTAP_ENCAP_CHDLC_WITH_PHDR },
+ { ERF_TYPE_HDLC_POS, WTAP_ENCAP_PPP },
+ { ERF_TYPE_HDLC_POS, WTAP_ENCAP_FRELAY },
+ { ERF_TYPE_HDLC_POS, WTAP_ENCAP_MTP2 },
+ { ERF_TYPE_ETH, WTAP_ENCAP_ETHERNET },
+ { 99, WTAP_ENCAP_ERF }, /*this type added so WTAP_ENCAP_ERF will work and then be treated at ERF->ERF*/
+};
+
+#define NUM_ERF_ENCAPS (sizeof erf_to_wtap_map / sizeof erf_to_wtap_map[0])
+
+#define ERF_META_TAG_HEADERLEN 4
+#define ERF_META_TAG_TOTAL_ALIGNED_LENGTH(taglength) ((((guint32)taglength + 0x3U) & ~0x3U) + ERF_META_TAG_HEADERLEN)
+#define ERF_META_TAG_ALIGNED_LENGTH(taglength) ((((guint32)taglength + 0x3U) & ~0x3U))
+#define ERF_PADDING_TO_8(len) ((8 - len % 8) % 8)
+
+struct erf_if_info {
+ int if_index;
+ gchar *name;
+ gchar *descr;
+ int stream_num;
+ struct {
+ guint filter:1;
+ guint fcs_len:1;
+ guint snaplen:1;
+ } set_flags;
+};
+
+struct erf_if_mapping {
+ guint64 host_id;
+ guint8 source_id;
+ struct erf_if_info interfaces[4];
+
+ gchar *module_filter_str;
+ /*here because we could have captures from multiple hosts in the file*/
+ gchar *capture_filter_str;
+ gint8 module_fcs_len;
+ guint32 module_snaplen;
+ int interface_metadata;
+ guint64 interface_gentime;
+ guint64 module_gentime;
+};
+
+struct erf_meta_section {
+ guint16 type;
+ guint16 section_id;
+ guint16 section_length;
+ GPtrArray *tags;
+};
+
+struct erf_meta_tag {
+ guint16 type;
+ guint16 length;
+ guint8 *value;
+};
+
+struct erf_meta_read_state {
+ guint8 *tag_ptr;
+ guint32 remaining_len;
+
+ struct erf_if_mapping *if_map;
+
+ guint16 sectiontype;
+ guint16 sectionid;
+ guint16 parentsectiontype;
+ guint16 parentsectionid;
+
+ guint64 gen_time;
+
+ int interface_metadata;
+};
+
+static gboolean erf_wtap_blocks_to_erf_sections(wtap_block_t block, GPtrArray *sections, guint16 section_type, guint16 section_id, wtap_block_foreach_func func);
+
+static guint32 erf_meta_read_tag(struct erf_meta_tag*, guint8*, guint32);
+
+static int erf_file_type_subtype = -1;
+
+void register_erf(void);
+
+static guint erf_anchor_mapping_hash(gconstpointer key) {
+ const struct erf_anchor_mapping *anchor_map = (const struct erf_anchor_mapping*) key;
+
+ return ((guint32)anchor_map->host_id ^ (guint32)anchor_map->anchor_id);
+
+}
+
+static gboolean erf_anchor_mapping_equal(gconstpointer a, gconstpointer b) {
+ const struct erf_anchor_mapping *anchor_map_a = (const struct erf_anchor_mapping*) a ;
+ const struct erf_anchor_mapping *anchor_map_b = (const struct erf_anchor_mapping*) b ;
+
+ return (anchor_map_a->host_id) == (anchor_map_b->host_id) &&
+ (anchor_map_a->anchor_id & ERF_EXT_HDR_TYPE_ANCHOR_ID) == (anchor_map_b->anchor_id & ERF_EXT_HDR_TYPE_ANCHOR_ID);
+}
+
+static void erf_anchor_mapping_destroy(gpointer key) {
+ struct erf_anchor_mapping *anchor_map = (struct erf_anchor_mapping*) key;
+
+ if(anchor_map->comment != NULL) {
+ g_free(anchor_map->comment);
+ anchor_map->comment = NULL;
+ }
+ g_free(anchor_map);
+ anchor_map = NULL;
+}
+
+static gboolean erf_if_mapping_equal(gconstpointer a, gconstpointer b)
+{
+ const struct erf_if_mapping *if_map_a = (const struct erf_if_mapping*) a;
+ const struct erf_if_mapping *if_map_b = (const struct erf_if_mapping*) b;
+
+ return if_map_a->source_id == if_map_b->source_id && if_map_a->host_id == if_map_b->host_id;
+}
+
+static guint erf_if_mapping_hash(gconstpointer key)
+{
+ const struct erf_if_mapping *if_map = (const struct erf_if_mapping*) key;
+
+ return (((guint) if_map->host_id) << 16) | if_map->source_id;
+}
+
+static void erf_if_mapping_destroy(gpointer key)
+{
+ int i = 0;
+ struct erf_if_mapping *if_map = (struct erf_if_mapping*) key;
+
+ for (i = 0; i < 4; i++) {
+ g_free(if_map->interfaces[i].name);
+ g_free(if_map->interfaces[i].descr);
+ }
+
+ g_free(if_map->module_filter_str);
+ g_free(if_map);
+}
+
+static struct erf_if_mapping* erf_if_mapping_create(guint64 host_id, guint8 source_id)
+{
+ int i = 0;
+ struct erf_if_mapping *if_map = NULL;
+
+ if_map = g_new0(struct erf_if_mapping, 1);
+
+ if_map->host_id = host_id;
+ if_map->source_id = source_id;
+
+ for (i = 0; i < 4; i++) {
+ if_map->interfaces[i].if_index = -1;
+ if_map->interfaces[i].stream_num = -1;
+ }
+
+ if_map->module_fcs_len = -1;
+ if_map->module_snaplen = (guint32) -1;
+ /* everything else 0 by g_malloc0*/
+
+ return if_map;
+}
+
+
+erf_t *erf_priv_create(void)
+{
+ erf_t *erf_priv;
+
+ erf_priv = g_new(erf_t, 1);
+ erf_priv->anchor_map = g_hash_table_new_full(erf_anchor_mapping_hash, erf_anchor_mapping_equal, erf_anchor_mapping_destroy, NULL);
+ erf_priv->if_map = g_hash_table_new_full(erf_if_mapping_hash, erf_if_mapping_equal, erf_if_mapping_destroy, NULL);
+ erf_priv->implicit_host_id = ERF_META_HOST_ID_IMPLICIT;
+ erf_priv->capture_gentime = 0;
+ erf_priv->host_gentime = 0;
+
+ return erf_priv;
+}
+
+erf_t* erf_priv_free(erf_t* erf_priv)
+{
+ if (erf_priv)
+ {
+ g_hash_table_destroy(erf_priv->anchor_map);
+ g_hash_table_destroy(erf_priv->if_map);
+ g_free(erf_priv);
+ }
+
+ return NULL;
+}
+
+static void erf_dump_priv_free(erf_dump_t *dump_priv) {
+ if(dump_priv) {
+ if(dump_priv->periodic_sections) {
+ g_ptr_array_free(dump_priv->periodic_sections, TRUE);
+ }
+ if(dump_priv->periodic_extra_ehdrs) {
+ g_array_free(dump_priv->periodic_extra_ehdrs, TRUE);
+ }
+ if(dump_priv->user_comment_ptr) {
+ g_free(dump_priv->user_comment_ptr);
+ }
+
+ g_free(dump_priv->rand);
+
+ g_free(dump_priv);
+ }
+
+}
+
+static void erf_meta_section_free(gpointer data) {
+ struct erf_meta_section *section_ptr = (struct erf_meta_section*) data;
+ if (section_ptr) {
+ g_ptr_array_free(section_ptr->tags, TRUE);
+ section_ptr->tags = NULL;
+ }
+ g_free(section_ptr);
+}
+
+static void erf_meta_tag_free(gpointer data) {
+ struct erf_meta_tag *tag_ptr = (struct erf_meta_tag*) data;
+ if (tag_ptr) {
+ g_free(tag_ptr->value);
+ tag_ptr->value = NULL;
+ }
+ g_free(tag_ptr);
+}
+
+
+static gboolean erf_dump_finish(struct wtap_dumper *wdh, int *err, gchar **err_info _U_) {
+ erf_dump_t *dump_priv = (erf_dump_t*)wdh->priv;
+ gboolean ret = TRUE;
+
+ /* Write final metadata record. There are some corner cases where we should
+ * do this (file <1 second, last record was ERF_TYPE_META with an out of date
+ * comment) and there is no harm doing this always if we have already written
+ * some metadata. */
+ if(dump_priv->write_next_extra_meta) {
+ if (!dump_priv->periodic_sections) {
+ dump_priv->periodic_sections = g_ptr_array_new_with_free_func(erf_meta_section_free);
+ if (dump_priv->prev_erf_type == ERF_TYPE_META && dump_priv->last_meta_periodic) {
+ erf_comment_to_sections(wdh, ERF_META_SECTION_CAPTURE, 0, dump_priv->user_comment_ptr, dump_priv->periodic_sections);
+ } else {
+ /* If we get here, metadata record was not found in the first ~1 sec
+ * but we have either a capture comment or a non-ERF file (see
+ * erf_dump_open) */
+ erf_wtap_info_to_sections(wdh, dump_priv->periodic_sections);
+ }
+ }
+
+ if (!erf_write_meta_record(wdh, dump_priv, dump_priv->prev_frame_ts, dump_priv->periodic_sections, dump_priv->periodic_extra_ehdrs, err)) ret = FALSE;
+ }
+
+ /* Clean up */
+ erf_dump_priv_free(dump_priv);
+ /* Avoid double freeing by setting it to NULL*/
+ wdh->priv = NULL;
+
+ return ret;
+
+}
+
+static void
+erf_free_data(gpointer data, gpointer user_data _U_)
+{
+ g_free(data);
+}
+
+
+extern wtap_open_return_val erf_open(wtap *wth, int *err, gchar **err_info)
+{
+ int i, n, records_for_erf_check = RECORDS_FOR_ERF_CHECK;
+ int valid_prev = 0;
+ char *s;
+ erf_timestamp_t prevts,ts;
+ erf_header_t header;
+ guint32 mc_hdr;
+ struct erf_eth_hdr eth_hdr;
+ guint32 packet_size;
+ guint16 rlen;
+ guint64 erf_ext_header;
+ guint erf_ext_header_size = (guint)sizeof(erf_ext_header);
+ guint8 type;
+
+ prevts = 0;
+
+ /* number of records to scan before deciding if this really is ERF */
+ if ((s = getenv("ERF_RECORDS_TO_CHECK")) != NULL) {
+ if (ws_strtoi32(s, NULL, &n) && n >= 0 && n < 101) {
+ records_for_erf_check = n;
+ }
+ }
+
+ /*
+ * ERF is a little hard because there's no magic number; we look at
+ * the first few records and see if they look enough like ERF
+ * records.
+ */
+
+ for (i = 0; i < records_for_erf_check; i++) { /* records_for_erf_check */
+
+ if (!wtap_read_bytes_or_eof(wth->fh,&header,erf_header_size,err,err_info)) {
+ if (*err == 0) {
+ /* EOF - all records have been successfully checked, accept the file */
+ break;
+ }
+ if (*err == WTAP_ERR_SHORT_READ) {
+ /* ERF header too short accept the file,
+ only if the very first records have been successfully checked */
+ if (i < MIN_RECORDS_FOR_ERF_CHECK) {
+ return WTAP_OPEN_NOT_MINE;
+ } else {
+ /* BREAK, the last record is too short, and will be ignored */
+ break;
+ }
+ } else {
+ return WTAP_OPEN_ERROR;
+ }
+ }
+
+ rlen=g_ntohs(header.rlen);
+
+ /* fail on invalid record type, invalid rlen, timestamps decreasing, or incrementing too far */
+
+ /* Test valid rlen >= 16 */
+ if (rlen < 16) {
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ packet_size = rlen - erf_header_size;
+ if (packet_size > WTAP_MAX_PACKET_SIZE_STANDARD) {
+ /*
+ * Probably a corrupt capture file or a file that's not an ERF file
+ * but that passed earlier tests.
+ */
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ /* Skip PAD records, timestamps may not be set */
+ if ((header.type & 0x7F) == ERF_TYPE_PAD) {
+ if (!wtap_read_bytes(wth->fh, NULL, packet_size, err, err_info)) {
+ if (*err != WTAP_ERR_SHORT_READ) {
+ /* A real error */
+ return WTAP_OPEN_ERROR;
+ }
+ /* ERF record too short, accept the file,
+ only if the very first records have been successfully checked */
+ if (i < MIN_RECORDS_FOR_ERF_CHECK) {
+ return WTAP_OPEN_NOT_MINE;
+ }
+ }
+ continue;
+ }
+
+ /* ERF Type 0 is reserved for ancient legacy records which are not supported, probably not ERF */
+ if ((header.type & 0x7F) == 0) {
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ /* fail on decreasing timestamps */
+ if ((ts = pletoh64(&header.ts)) < prevts) {
+ /* reassembled AALx records may not be in time order, also records are not in strict time order between physical interfaces, so allow 1 sec fudge */
+ if ( ((prevts-ts)>>32) > 1 ) {
+ return WTAP_OPEN_NOT_MINE;
+ }
+ }
+
+ /* Check to see if timestamp increment is > 1 year */
+ if ( (valid_prev) && (ts > prevts) && (((ts-prevts)>>32) > 3600*24*365) ) {
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ prevts = ts;
+
+ /* Read over the extension headers */
+ type = header.type;
+ while (type & 0x80){
+ if (!wtap_read_bytes(wth->fh,&erf_ext_header,erf_ext_header_size,err,err_info)) {
+ if (*err == WTAP_ERR_SHORT_READ) {
+ /* Extension header missing, not an ERF file */
+ return WTAP_OPEN_NOT_MINE;
+ }
+ return WTAP_OPEN_ERROR;
+ }
+ if (packet_size < erf_ext_header_size)
+ return WTAP_OPEN_NOT_MINE;
+ packet_size -= erf_ext_header_size;
+ memcpy(&type, &erf_ext_header, sizeof(type));
+ }
+
+
+ /* Read over MC or ETH subheader */
+ switch(header.type & 0x7F) {
+ case ERF_TYPE_MC_HDLC:
+ case ERF_TYPE_MC_RAW:
+ case ERF_TYPE_MC_ATM:
+ case ERF_TYPE_MC_RAW_CHANNEL:
+ case ERF_TYPE_MC_AAL5:
+ case ERF_TYPE_MC_AAL2:
+ case ERF_TYPE_COLOR_MC_HDLC_POS:
+ case ERF_TYPE_AAL2: /* not an MC type but has a similar 'AAL2 ext' header */
+ if (!wtap_read_bytes(wth->fh,&mc_hdr,erf_mc_header_size,err,err_info)) {
+ if (*err == WTAP_ERR_SHORT_READ) {
+ /* Subheader missing, not an ERF file */
+ return WTAP_OPEN_NOT_MINE;
+ }
+ return WTAP_OPEN_ERROR;
+ }
+ if (packet_size < erf_mc_header_size)
+ return WTAP_OPEN_NOT_MINE;
+ packet_size -= erf_mc_header_size;
+ break;
+ case ERF_TYPE_ETH:
+ case ERF_TYPE_COLOR_ETH:
+ case ERF_TYPE_DSM_COLOR_ETH:
+ case ERF_TYPE_COLOR_HASH_ETH:
+ if (!wtap_read_bytes(wth->fh,&eth_hdr,erf_eth_hdr_size,err,err_info)) {
+ if (*err == WTAP_ERR_SHORT_READ) {
+ /* Subheader missing, not an ERF file */
+ return WTAP_OPEN_NOT_MINE;
+ }
+ return WTAP_OPEN_ERROR;
+ }
+ if (packet_size < erf_eth_hdr_size)
+ return WTAP_OPEN_NOT_MINE;
+ packet_size -= erf_eth_hdr_size;
+ break;
+ default:
+ break;
+ }
+
+ if (!wtap_read_bytes(wth->fh, NULL, packet_size, err, err_info)) {
+ if (*err != WTAP_ERR_SHORT_READ) {
+ /* A real error */
+ return WTAP_OPEN_ERROR;
+ }
+ /* ERF record too short, accept the file,
+ only if the very first records have been successfully checked */
+ if (i < MIN_RECORDS_FOR_ERF_CHECK) {
+ return WTAP_OPEN_NOT_MINE;
+ }
+ }
+
+ valid_prev = 1;
+
+ } /* records_for_erf_check */
+
+ if (file_seek(wth->fh, 0L, SEEK_SET, err) == -1) { /* rewind */
+ return WTAP_OPEN_ERROR;
+ }
+
+ /* This is an ERF file */
+ wth->file_type_subtype = erf_file_type_subtype;
+ wth->snapshot_length = 0; /* not available in header, only in frame */
+
+ /*
+ * Use the encapsulation for ERF records.
+ */
+ wth->file_encap = WTAP_ENCAP_ERF;
+
+ wth->subtype_read = erf_read;
+ wth->subtype_seek_read = erf_seek_read;
+ wth->subtype_close = erf_close;
+ wth->file_tsprec = WTAP_TSPREC_NSEC;
+
+ wth->priv = erf_priv_create();
+
+ return WTAP_OPEN_MINE;
+}
+
+/* Read the next packet */
+static gboolean erf_read(wtap *wth, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info, gint64 *data_offset)
+{
+ erf_header_t erf_header;
+ guint32 packet_size, bytes_read;
+ GPtrArray *anchor_mappings_to_update;
+
+ *data_offset = file_tell(wth->fh);
+
+ anchor_mappings_to_update = g_ptr_array_new_with_free_func(erf_anchor_mapping_destroy);
+
+ do {
+ if (!erf_read_header(wth, wth->fh, rec, &erf_header,
+ err, err_info, &bytes_read, &packet_size,
+ anchor_mappings_to_update)) {
+ g_ptr_array_free(anchor_mappings_to_update, TRUE);
+ return FALSE;
+ }
+
+ if (!wtap_read_packet_bytes(wth->fh, buf, packet_size, err, err_info)) {
+ g_ptr_array_free(anchor_mappings_to_update, TRUE);
+ return FALSE;
+ }
+
+ /*
+ * If Provenance metadata record, frame buffer could hold the meta erf tags.
+ * It can also contain per packet comments which can be associated to another
+ * frame.
+ */
+ if ((erf_header.type & 0x7F) == ERF_TYPE_META && packet_size > 0)
+ {
+ if (populate_summary_info((erf_t*) wth->priv, wth, &rec->rec_header.packet_header.pseudo_header, buf, packet_size, anchor_mappings_to_update, err, err_info) < 0) {
+ g_ptr_array_free(anchor_mappings_to_update, TRUE);
+ return FALSE;
+ }
+ }
+
+ } while ( erf_header.type == ERF_TYPE_PAD );
+
+ g_ptr_array_free(anchor_mappings_to_update, TRUE);
+
+ return TRUE;
+}
+
+static gboolean erf_seek_read(wtap *wth, gint64 seek_off,
+ wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info)
+{
+ erf_header_t erf_header;
+ guint32 packet_size;
+ GPtrArray *anchor_mappings_to_update;
+
+ if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
+ return FALSE;
+
+ anchor_mappings_to_update = g_ptr_array_new_with_free_func(erf_anchor_mapping_destroy);
+
+ do {
+ if (!erf_read_header(wth, wth->random_fh, rec, &erf_header,
+ err, err_info, NULL, &packet_size, anchor_mappings_to_update)) {
+ g_ptr_array_free(anchor_mappings_to_update, TRUE);
+ return FALSE;
+ }
+ } while ( erf_header.type == ERF_TYPE_PAD );
+
+ g_ptr_array_free(anchor_mappings_to_update, TRUE);
+
+ return wtap_read_packet_bytes(wth->random_fh, buf, packet_size,
+ err, err_info);
+}
+
+static struct erf_anchor_mapping* erf_find_anchor_mapping(erf_t *priv,
+ guint64 host_id,
+ guint64 anchor_id)
+{
+ struct erf_anchor_mapping mapping = {
+ host_id,
+ anchor_id,
+ 0,
+ NULL
+ };
+
+ if (!priv) {
+ return NULL;
+ }
+
+ return (struct erf_anchor_mapping*)g_hash_table_lookup(priv->anchor_map, &mapping);
+
+}
+
+static gboolean erf_read_header(wtap *wth, FILE_T fh,
+ wtap_rec *rec,
+ erf_header_t *erf_header,
+ int *err,
+ gchar **err_info,
+ guint32 *bytes_read,
+ guint32 *packet_size,
+ GPtrArray *anchor_mappings_to_update)
+{
+ union wtap_pseudo_header *pseudo_header = &rec->rec_header.packet_header.pseudo_header;
+ guint8 erf_exhdr[8];
+ guint64 erf_exhdr_sw;
+ guint8 type = 0;
+ guint32 mc_hdr;
+ guint32 aal2_hdr;
+ struct wtap_erf_eth_hdr eth_hdr;
+ guint32 skiplen = 0;
+ int i = 0;
+ int max = sizeof(pseudo_header->erf.ehdr_list)/sizeof(struct erf_ehdr);
+ erf_t *priv = (erf_t*)wth->priv;
+ int interface_id;
+
+ guint64 host_id = ERF_META_HOST_ID_IMPLICIT;
+ guint8 source_id = 0;
+ guint8 if_num = 0;
+ gboolean host_id_found = FALSE;
+
+ if (!wtap_read_bytes_or_eof(fh, erf_header, sizeof(*erf_header), err, err_info)) {
+ return FALSE;
+ }
+ if (bytes_read != NULL) {
+ *bytes_read = sizeof(*erf_header);
+ }
+
+ *packet_size = g_ntohs(erf_header->rlen) - (guint32)sizeof(*erf_header);
+
+ if (*packet_size > WTAP_MAX_PACKET_SIZE_STANDARD) {
+ /*
+ * Probably a corrupt capture file; don't blow up trying
+ * to allocate space for an immensely-large packet.
+ */
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("erf: File has %u-byte packet, bigger than maximum of %u",
+ *packet_size, WTAP_MAX_PACKET_SIZE_STANDARD);
+ return FALSE;
+ }
+
+ if (*packet_size == 0) {
+ /* If this isn't a pad record, it's a corrupt packet; bail out */
+ if ((erf_header->type & 0x7F) != ERF_TYPE_PAD) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("erf: File has 0 byte packet");
+
+ return FALSE;
+ }
+ }
+
+ {
+ guint64 ts = pletoh64(&erf_header->ts);
+
+ /*if ((erf_header->type & 0x7f) != ERF_TYPE_META || wth->file_type_subtype != file_type_subtype_erf) {*/
+ rec->rec_type = REC_TYPE_PACKET;
+ rec->block = wtap_block_create(WTAP_BLOCK_PACKET);
+ /*
+ * XXX: ERF_TYPE_META records should ideally be FT_SPECIFIC for display
+ * purposes, but currently ft_specific_record_phdr clashes with erf_mc_phdr
+ * and the pcapng dumper assumes it is a pcapng block type. Ideally we
+ * would register a block handler with pcapng and write out the closest
+ * pcapng block, or a custom block/Provenance record.
+ *
+ */
+#if 0
+ } else {
+ /*
+ * TODO: how to identify, distinguish and timestamp events?
+ * What to do about ENCAP_ERF in pcap/pcapng? Filetype dissector is
+ * chosen by wth->file_type_subtype?
+ */
+ /* For now just treat all Provenance records as reports */
+ rec->rec_type = REC_TYPE_FT_SPECIFIC_REPORT;
+ rec->block = wtap_block_create(WTAP_BLOCK_FT_SPECIFIC_REPORT);
+ /* XXX: phdr ft_specific_record_phdr? */
+ }
+#endif
+ rec->presence_flags = WTAP_HAS_TS|WTAP_HAS_CAP_LEN|WTAP_HAS_INTERFACE_ID;
+ rec->ts.secs = (long) (ts >> 32);
+ ts = ((ts & 0xffffffff) * 1000 * 1000 * 1000);
+ ts += (ts & 0x80000000) << 1; /* rounding */
+ rec->ts.nsecs = ((int) (ts >> 32));
+ if (rec->ts.nsecs >= 1000000000) {
+ rec->ts.nsecs -= 1000000000;
+ rec->ts.secs += 1;
+ }
+
+ if_num = erf_header->flags & 0x03;
+ }
+
+ /* Copy the ERF pseudo header */
+ memset(&pseudo_header->erf, 0, sizeof(pseudo_header->erf));
+ pseudo_header->erf.phdr.ts = pletoh64(&erf_header->ts);
+ pseudo_header->erf.phdr.type = erf_header->type;
+ pseudo_header->erf.phdr.flags = erf_header->flags;
+ pseudo_header->erf.phdr.rlen = g_ntohs(erf_header->rlen);
+ pseudo_header->erf.phdr.lctr = g_ntohs(erf_header->lctr);
+ pseudo_header->erf.phdr.wlen = g_ntohs(erf_header->wlen);
+
+ /* Copy the ERF extension header into the pseudo header */
+ type = erf_header->type;
+ while (type & 0x80){
+ if (!wtap_read_bytes(fh, &erf_exhdr, sizeof(erf_exhdr),
+ err, err_info))
+ return FALSE;
+ if (bytes_read != NULL)
+ *bytes_read += (guint32)sizeof(erf_exhdr);
+ *packet_size -= (guint32)sizeof(erf_exhdr);
+ skiplen += (guint32)sizeof(erf_exhdr);
+ erf_exhdr_sw = pntoh64(erf_exhdr);
+ if (i < max)
+ memcpy(&pseudo_header->erf.ehdr_list[i].ehdr, &erf_exhdr_sw, sizeof(erf_exhdr_sw));
+ type = erf_exhdr[0];
+
+ /*
+ * XXX: Only want first Source ID and Host ID, and want to preserve HID n SID 0 (see
+ * erf_populate_interface)
+ */
+ switch (type & 0x7FU) {
+ case ERF_EXT_HDR_TYPE_HOST_ID:
+ if (!host_id_found)
+ host_id = erf_exhdr_sw & ERF_EHDR_HOST_ID_MASK;
+
+ host_id_found = TRUE;
+ /* Fall through */
+ case ERF_EXT_HDR_TYPE_FLOW_ID:
+ /* Source ID is present in both Flow ID and Host ID extension headers */
+ if (!source_id)
+ source_id = (erf_exhdr_sw >> 48) & 0xff;
+ break;
+ case ERF_EXT_HDR_TYPE_ANCHOR_ID:
+ /* handled below*/
+ break;
+ }
+ i++;
+ }
+
+ interface_id = erf_populate_interface((erf_t*) wth->priv, wth, pseudo_header, host_id, source_id, if_num, err, err_info);
+ if (interface_id < 0) {
+ return FALSE;
+ }
+ rec->rec_header.packet_header.interface_id = (guint) interface_id;
+
+ /* Try to find comment links using Anchor ID. Done here after we found the first Host ID and have updated the implicit Host ID. */
+ erf_update_anchors_from_header(priv, rec, pseudo_header, host_id, anchor_mappings_to_update);
+
+ switch (erf_header->type & 0x7F) {
+ case ERF_TYPE_IPV4:
+ case ERF_TYPE_IPV6:
+ case ERF_TYPE_RAW_LINK:
+ case ERF_TYPE_INFINIBAND:
+ case ERF_TYPE_INFINIBAND_LINK:
+ case ERF_TYPE_META:
+ case ERF_TYPE_OPA_SNC:
+ case ERF_TYPE_OPA_9B:
+#if 0
+ {
+ rec->rec_header.packet_header.len = g_htons(erf_header->wlen);
+ rec->rec_header.packet_header.caplen = g_htons(erf_header->wlen);
+ }
+ return TRUE;
+#endif
+ break;
+ case ERF_TYPE_PAD:
+ case ERF_TYPE_HDLC_POS:
+ case ERF_TYPE_COLOR_HDLC_POS:
+ case ERF_TYPE_DSM_COLOR_HDLC_POS:
+ case ERF_TYPE_COLOR_HASH_POS:
+ case ERF_TYPE_ATM:
+ case ERF_TYPE_AAL5:
+ break;
+
+ case ERF_TYPE_ETH:
+ case ERF_TYPE_COLOR_ETH:
+ case ERF_TYPE_DSM_COLOR_ETH:
+ case ERF_TYPE_COLOR_HASH_ETH:
+ if (!wtap_read_bytes(fh, &eth_hdr, sizeof(eth_hdr), err, err_info))
+ return FALSE;
+ if (bytes_read != NULL)
+ *bytes_read += (guint32)sizeof(eth_hdr);
+ *packet_size -= (guint32)sizeof(eth_hdr);
+ skiplen += (guint32)sizeof(eth_hdr);
+ pseudo_header->erf.subhdr.eth_hdr = eth_hdr;
+ break;
+
+ case ERF_TYPE_MC_HDLC:
+ case ERF_TYPE_MC_RAW:
+ case ERF_TYPE_MC_ATM:
+ case ERF_TYPE_MC_RAW_CHANNEL:
+ case ERF_TYPE_MC_AAL5:
+ case ERF_TYPE_MC_AAL2:
+ case ERF_TYPE_COLOR_MC_HDLC_POS:
+ if (!wtap_read_bytes(fh, &mc_hdr, sizeof(mc_hdr), err, err_info))
+ return FALSE;
+ if (bytes_read != NULL)
+ *bytes_read += (guint32)sizeof(mc_hdr);
+ *packet_size -= (guint32)sizeof(mc_hdr);
+ skiplen += (guint32)sizeof(mc_hdr);
+ pseudo_header->erf.subhdr.mc_hdr = g_ntohl(mc_hdr);
+ break;
+
+ case ERF_TYPE_AAL2:
+ if (!wtap_read_bytes(fh, &aal2_hdr, sizeof(aal2_hdr), err, err_info))
+ return FALSE;
+ if (bytes_read != NULL)
+ *bytes_read += (guint32)sizeof(aal2_hdr);
+ *packet_size -= (guint32)sizeof(aal2_hdr);
+ skiplen += (guint32)sizeof(aal2_hdr);
+ pseudo_header->erf.subhdr.aal2_hdr = g_ntohl(aal2_hdr);
+ break;
+
+ case ERF_TYPE_IP_COUNTER:
+ case ERF_TYPE_TCP_FLOW_COUNTER:
+ /* unsupported, continue with default: */
+ default:
+ /* let the dissector dissect as unknown record type for forwards compatibility */
+ break;
+ }
+
+ {
+ rec->rec_header.packet_header.len = g_ntohs(erf_header->wlen);
+ rec->rec_header.packet_header.caplen = MIN( g_ntohs(erf_header->wlen),
+ g_ntohs(erf_header->rlen) - (guint32)sizeof(*erf_header) - skiplen );
+ }
+
+ if (*packet_size > WTAP_MAX_PACKET_SIZE_STANDARD) {
+ /*
+ * Probably a corrupt capture file; don't blow up trying
+ * to allocate space for an immensely-large packet.
+ */
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("erf: File has %u-byte packet, bigger than maximum of %u",
+ *packet_size, WTAP_MAX_PACKET_SIZE_STANDARD);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static int wtap_wtap_encap_to_erf_encap(int encap)
+{
+ unsigned int i;
+ for(i = 0; i < NUM_ERF_ENCAPS; i++){
+ if(erf_to_wtap_map[i].wtap_encap_value == encap)
+ return erf_to_wtap_map[i].erf_encap_value;
+ }
+ return -1;
+}
+
+static gboolean erf_write_phdr(wtap_dumper *wdh, int encap, const union wtap_pseudo_header *pseudo_header, int * err)
+{
+ guint8 erf_hdr[sizeof(struct erf_mc_phdr)];
+ guint8 erf_subhdr[sizeof(union erf_subhdr)];
+ guint8 ehdr[8*MAX_ERF_EHDR];
+ size_t size = 0;
+ size_t subhdr_size = 0;
+ int i = 0;
+ guint8 has_more = 0;
+
+ switch(encap){
+ case WTAP_ENCAP_ERF:
+ memset(&erf_hdr, 0, sizeof(erf_hdr));
+ phtolell(&erf_hdr[0], pseudo_header->erf.phdr.ts);
+ erf_hdr[8] = pseudo_header->erf.phdr.type;
+ erf_hdr[9] = pseudo_header->erf.phdr.flags;
+ phtons(&erf_hdr[10], pseudo_header->erf.phdr.rlen);
+ phtons(&erf_hdr[12], pseudo_header->erf.phdr.lctr);
+ phtons(&erf_hdr[14], pseudo_header->erf.phdr.wlen);
+ size = sizeof(struct erf_phdr);
+
+ switch(pseudo_header->erf.phdr.type & 0x7F) {
+ case ERF_TYPE_MC_HDLC:
+ case ERF_TYPE_MC_RAW:
+ case ERF_TYPE_MC_ATM:
+ case ERF_TYPE_MC_RAW_CHANNEL:
+ case ERF_TYPE_MC_AAL5:
+ case ERF_TYPE_MC_AAL2:
+ case ERF_TYPE_COLOR_MC_HDLC_POS:
+ phtonl(&erf_subhdr[0], pseudo_header->erf.subhdr.mc_hdr);
+ subhdr_size += (int)sizeof(struct erf_mc_hdr);
+ break;
+ case ERF_TYPE_AAL2:
+ phtonl(&erf_subhdr[0], pseudo_header->erf.subhdr.aal2_hdr);
+ subhdr_size += (int)sizeof(struct erf_aal2_hdr);
+ break;
+ case ERF_TYPE_ETH:
+ case ERF_TYPE_COLOR_ETH:
+ case ERF_TYPE_DSM_COLOR_ETH:
+ case ERF_TYPE_COLOR_HASH_ETH:
+ memcpy(&erf_subhdr[0], &pseudo_header->erf.subhdr.eth_hdr, sizeof pseudo_header->erf.subhdr.eth_hdr);
+ subhdr_size += erf_eth_hdr_size;
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ return FALSE;
+
+ }
+ if (!wtap_dump_file_write(wdh, erf_hdr, size, err))
+ return FALSE;
+
+ /*write out up to MAX_ERF_EHDR extension headers*/
+ has_more = pseudo_header->erf.phdr.type & 0x80;
+ if(has_more){ /*we have extension headers*/
+ do{
+ phtonll(ehdr+(i*8), pseudo_header->erf.ehdr_list[i].ehdr);
+ if(i == MAX_ERF_EHDR-1) ehdr[i*8] = ehdr[i*8] & 0x7F;
+ has_more = ehdr[i*8] & 0x80;
+ i++;
+ }while(has_more && i < MAX_ERF_EHDR);
+ if (!wtap_dump_file_write(wdh, ehdr, 8*i, err))
+ return FALSE;
+ }
+
+ if(!wtap_dump_file_write(wdh, erf_subhdr, subhdr_size, err))
+ return FALSE;
+
+ return TRUE;
+}
+
+
+static void erf_dump_priv_init_gen_time(erf_dump_t *dump_priv) {
+ gint64 real_time;
+
+ real_time = g_get_real_time();
+ /* Convert TimeVal to ERF timestamp */
+ dump_priv->gen_time = ((real_time / G_USEC_PER_SEC) << 32) + ((real_time % G_USEC_PER_SEC) << 32) / 1000 / 1000;
+}
+
+
+static gboolean erf_write_wtap_option_to_capture_tag(wtap_block_t block _U_,
+ guint option_id,
+ wtap_opttype_e option_type _U_,
+ wtap_optval_t *optval,
+ void* user_data) {
+
+ struct erf_meta_section *section_ptr = (struct erf_meta_section*) user_data;
+ struct erf_meta_tag *tag_ptr = NULL;
+
+ tag_ptr = g_new0(struct erf_meta_tag, 1);
+
+ switch(option_id) {
+ case OPT_SHB_USERAPPL:
+ tag_ptr->type = ERF_META_TAG_app_name;
+ tag_ptr->value = (guint8*)g_strdup(optval->stringval);
+ tag_ptr->length = (guint16)strlen((char*)tag_ptr->value);
+ break;
+ case OPT_COMMENT:
+ tag_ptr->type = ERF_META_TAG_comment;
+ tag_ptr->value = (guint8*)g_strdup(optval->stringval);
+ tag_ptr->length = (guint16)strlen((char*)tag_ptr->value);
+ break;
+ default:
+ erf_meta_tag_free(tag_ptr);
+ tag_ptr = NULL;
+ break;
+ }
+
+ if (tag_ptr)
+ g_ptr_array_add(section_ptr->tags, tag_ptr);
+
+ return TRUE; /* we always succeed */
+}
+
+static gboolean erf_write_wtap_option_to_host_tag(wtap_block_t block _U_,
+ guint option_id,
+ wtap_opttype_e option_type _U_,
+ wtap_optval_t *optval,
+ void* user_data) {
+
+ struct erf_meta_section *section_ptr = (struct erf_meta_section*) user_data;
+ struct erf_meta_tag *tag_ptr = NULL;
+
+ tag_ptr = g_new0(struct erf_meta_tag, 1);
+
+ switch(option_id) {
+ case OPT_SHB_HARDWARE:
+ tag_ptr->type = ERF_META_TAG_cpu;
+ tag_ptr->value = (guint8*)g_strdup(optval->stringval);
+ tag_ptr->length = (guint16)strlen((char*)tag_ptr->value);
+ break;
+ case OPT_SHB_OS:
+ tag_ptr->type = ERF_META_TAG_os;
+ tag_ptr->value = (guint8*)g_strdup(optval->stringval);
+ tag_ptr->length = (guint16)strlen((char*)tag_ptr->value);
+ break;
+ default:
+ erf_meta_tag_free(tag_ptr);
+ tag_ptr = NULL;
+ break;
+ }
+
+ if (tag_ptr)
+ g_ptr_array_add(section_ptr->tags, tag_ptr);
+
+ return TRUE; /* we always succeed */
+}
+
+static gboolean erf_write_wtap_option_to_interface_tag(wtap_block_t block _U_,
+ guint option_id,
+ wtap_opttype_e option_type _U_,
+ wtap_optval_t *optval,
+ void* user_data) {
+
+ struct erf_meta_section *section_ptr = (struct erf_meta_section*) user_data;
+ struct erf_meta_tag *tag_ptr = NULL;
+
+ tag_ptr = g_new0(struct erf_meta_tag, 1);
+
+ switch(option_id) {
+ case OPT_COMMENT:
+ tag_ptr->type = ERF_META_TAG_comment;
+ tag_ptr->value = (guint8*)g_strdup(optval->stringval);
+ tag_ptr->length = (guint16)strlen((char*)tag_ptr->value);
+ break;
+ case OPT_IDB_NAME:
+ tag_ptr->type = ERF_META_TAG_name;
+ tag_ptr->value = (guint8*)g_strdup(optval->stringval);
+ tag_ptr->length = (guint16)strlen((char*)tag_ptr->value);
+ break;
+ case OPT_IDB_DESCRIPTION:
+ tag_ptr->type = ERF_META_TAG_descr;
+ tag_ptr->value = (guint8*)g_strdup(optval->stringval);
+ tag_ptr->length = (guint16)strlen((char*)tag_ptr->value);
+ break;
+ case OPT_IDB_OS:
+ tag_ptr->type = ERF_META_TAG_os;
+ tag_ptr->value = (guint8*)g_strdup(optval->stringval);
+ tag_ptr->length = (guint16)strlen((char*)tag_ptr->value);
+ break;
+ case OPT_IDB_TSOFFSET:
+ tag_ptr->type = ERF_META_TAG_ts_offset;
+ tag_ptr->length = 8;
+ tag_ptr->value = (guint8*)g_malloc(sizeof(optval->uint64val));
+ /* convert to relative ERF timestamp */
+ phtolell(tag_ptr->value, optval->uint64val << 32);
+ break;
+ case OPT_IDB_SPEED:
+ tag_ptr->type = ERF_META_TAG_if_speed;
+ tag_ptr->length = 8;
+ tag_ptr->value = (guint8*)g_malloc(sizeof(optval->uint64val));
+ phtonll(tag_ptr->value, optval->uint64val);
+ break;
+ case OPT_IDB_IP4ADDR:
+ tag_ptr->type = ERF_META_TAG_if_ipv4;
+ tag_ptr->length = 4;
+ tag_ptr->value = (guint8*)g_malloc(sizeof(optval->ipv4val));
+ memcpy(tag_ptr->value, &optval->ipv4val, sizeof(optval->ipv4val));
+ break;
+ case OPT_IDB_IP6ADDR:
+ tag_ptr->type = ERF_META_TAG_if_ipv6;
+ tag_ptr->length = 16;
+ tag_ptr->value = (guint8*)g_malloc(sizeof(optval->ipv6val));
+ memcpy(tag_ptr->value, &optval->ipv6val, sizeof(optval->ipv6val));
+ break;
+ case OPT_IDB_FILTER:
+ {
+ if_filter_opt_t *filter;
+ filter = &optval->if_filterval;
+ tag_ptr->type = 0xF800;
+ if(filter->type == if_filter_pcap) {
+ tag_ptr->type = ERF_META_TAG_filter;
+ tag_ptr->value = (guint8*)g_strdup(filter->data.filter_str);
+ tag_ptr->length = (guint16)strlen((char*)tag_ptr->value);
+ }
+ }
+ break;
+ case OPT_IDB_FCSLEN:
+ tag_ptr->type = ERF_META_TAG_fcs_len;
+ tag_ptr->length = 4;
+ tag_ptr->value = (guint8*)g_malloc(tag_ptr->length);
+ phtonl(tag_ptr->value, (guint32)optval->uint8val);
+ break;
+ /* TODO: Don't know what to do with these yet */
+ case OPT_IDB_EUIADDR:
+#if 0
+ tag_ptr->type = ERF_META_TAG_if_eui;
+ tag_ptr->length = 8;
+ tag_ptr->value = (guint8*)g_malloc(sizeof(optval->eui64val));
+ memcpy(tag_ptr->value, &optval->euival, sizeof(optval->eui64val));
+ break;
+#endif
+ case OPT_IDB_MACADDR:
+#if 0
+ tag_ptr->type = ERF_META_TAG_if_mac;
+ tag_ptr->length = 6;
+ /*value same format as pcapng (6-byte canonical, padded by write
+ * function automatically to 32-bit boundary)*/
+ tag_ptr->value = (guint8*)g_malloc(sizeof(optval->macval));
+ memcpy(tag_ptr->value, &optval->macval, sizeof(optval->macval));
+ break;
+#endif
+ case OPT_IDB_TSRESOL:
+ case OPT_IDB_TZONE:
+ /* Fall through */
+ default:
+ erf_meta_tag_free(tag_ptr);
+ tag_ptr = NULL;
+ break;
+ }
+
+ if (tag_ptr)
+ g_ptr_array_add(section_ptr->tags, tag_ptr);
+
+ return TRUE; /* we always succeed */
+}
+
+static void erf_populate_section_length_by_tags(struct erf_meta_section *section_ptr) {
+ guint i = 0;
+ struct erf_meta_tag *tag_ptr;
+
+ section_ptr->section_length = 8;
+
+ for(;i < section_ptr->tags->len; i++) {
+ tag_ptr = (struct erf_meta_tag*)g_ptr_array_index(section_ptr->tags, i);
+ section_ptr->section_length += ERF_META_TAG_TOTAL_ALIGNED_LENGTH(tag_ptr->length);
+ }
+}
+
+/**
+ * @brief Converts a wtap_block_t block to ERF metadata sections
+ * @param block a wtap_block_t block
+ * @param sections pointer to a GPtrArray containing pointers to sections
+ * @param section_type the pre-specified section_type
+ * @param section_id Section ID to assign
+ * @param func a wtap_block_foreach_func call back function to specify
+ * what needs to be done on the block
+ * @return TRUE if success, FALSE if failed
+ */
+static gboolean erf_wtap_blocks_to_erf_sections(wtap_block_t block, GPtrArray *sections, guint16 section_type, guint16 section_id, wtap_block_foreach_func func) {
+
+ if(!block || !sections || !func) {
+ return FALSE;
+ }
+
+ struct erf_meta_section *section_ptr;
+
+ section_ptr = g_new(struct erf_meta_section, 1);
+ section_ptr->tags = g_ptr_array_new_with_free_func(erf_meta_tag_free);
+ section_ptr->type = section_type;
+ section_ptr->section_id = section_id;
+
+ wtap_block_foreach_option(block, func, (void*)section_ptr);
+ erf_populate_section_length_by_tags(section_ptr);
+ g_ptr_array_add(sections, section_ptr);
+
+ return TRUE;
+}
+
+
+static gboolean erf_meta_write_tag(wtap_dumper *wdh, struct erf_meta_tag *tag_ptr, int *err) {
+
+ guint16 data[2];
+ guint pad = 0;
+ /* we only need to pad up to 32 bits*/
+ guint32 padbuf = 0;
+
+ pad = ERF_META_TAG_ALIGNED_LENGTH(tag_ptr->length) - tag_ptr->length;
+ data[0] = g_htons(tag_ptr->type);
+ data[1] = g_htons(tag_ptr->length);
+
+ if(!wtap_dump_file_write(wdh, data, sizeof(data), err)) return FALSE;
+
+ if(!wtap_dump_file_write(wdh, tag_ptr->value, tag_ptr->length, err)) return FALSE;
+
+ if(pad) {
+ if(!wtap_dump_file_write(wdh, &padbuf, pad, err)) return FALSE;
+ }
+
+ return TRUE;
+
+}
+
+static gboolean erf_meta_write_section(wtap_dumper *wdh, struct erf_meta_section *section_ptr, int *err) {
+
+ struct erf_meta_tag *tag_ptr;
+ guint i;
+ guint16 data[4];
+
+ data[0] = g_htons(section_ptr->type);
+ data[1] = g_htons(4); /*section header length*/
+ data[2] = g_htons(section_ptr->section_id);
+ data[3] = g_htons(section_ptr->section_length);
+
+ if(!wtap_dump_file_write(wdh, data, sizeof(data), err)) return FALSE;
+
+ for(i = 0; i < section_ptr->tags->len; i++) {
+ tag_ptr = (struct erf_meta_tag*)g_ptr_array_index(section_ptr->tags, i);
+ if(!erf_meta_write_tag(wdh, tag_ptr, err)) return FALSE;
+ }
+
+ return TRUE;
+
+}
+
+static gboolean erf_wtap_info_to_sections(wtap_dumper *wdh, GPtrArray *sections) {
+ wtap_block_t block;
+ guint i = 0;
+
+ block = g_array_index(wdh->shb_hdrs, wtap_block_t, 0);
+ erf_wtap_blocks_to_erf_sections(block, sections, ERF_META_SECTION_CAPTURE, 0, erf_write_wtap_option_to_capture_tag);
+
+ block = g_array_index(wdh->shb_hdrs, wtap_block_t, 0);
+ erf_wtap_blocks_to_erf_sections(block, sections, ERF_META_SECTION_HOST, 0, erf_write_wtap_option_to_host_tag);
+
+ /*TODO: support >4 interfaces by using more Source IDs. Affects more than this
+ * function as need more metadata records. Just dump them all out for now. */
+ for(i = 0; i < wdh->interface_data->len; i++) {
+ block = g_array_index(wdh->interface_data, wtap_block_t, i);
+ erf_wtap_blocks_to_erf_sections(block, sections, ERF_META_SECTION_INTERFACE, (gint16)i+1, erf_write_wtap_option_to_interface_tag);
+ }
+
+ return TRUE;
+}
+
+static gboolean erf_comment_to_sections(wtap_dumper *wdh _U_, guint16 section_type, guint16 section_id, gchar *comment, GPtrArray *sections){
+ struct erf_meta_section *section_ptr;
+ struct erf_meta_tag *comment_tag_ptr = NULL;
+ struct erf_meta_tag *user_tag_ptr = NULL;
+ const gchar *user = NULL;
+
+ /* Generate the section */
+ section_ptr = g_new(struct erf_meta_section, 1);
+ section_ptr->type = section_type;
+ section_ptr->section_id = section_id;
+ section_ptr->tags = g_ptr_array_new_with_free_func(erf_meta_tag_free);
+
+ /* Generate the comment tag */
+ comment_tag_ptr = g_new(struct erf_meta_tag, 1);
+ comment_tag_ptr->type = ERF_META_TAG_comment;
+ /* XXX: if the comment has been cleared write the empty string (which
+ * conveniently is all a zero length tag which means the value is
+ * invalidated) */
+ comment_tag_ptr->value = (guint8*)g_strdup(comment ? comment : "");
+ comment_tag_ptr->length = (guint16)strlen((char*)comment_tag_ptr->value);
+ g_ptr_array_add(section_ptr->tags, comment_tag_ptr);
+
+ user = g_get_user_name();
+ if (user) {
+ /* Generate username tag */
+ user_tag_ptr = g_new(struct erf_meta_tag, 1);
+ user_tag_ptr->type = ERF_META_TAG_user;
+ user_tag_ptr->value = (guint8*)g_strdup(user);
+ user_tag_ptr->length = (guint16)strlen((char*)user_tag_ptr->value);
+ g_ptr_array_add(section_ptr->tags, user_tag_ptr);
+ }
+
+ erf_populate_section_length_by_tags(section_ptr);
+
+ g_ptr_array_add(sections, section_ptr);
+
+ return TRUE;
+}
+
+static guint64 erf_get_random_anchor_id(erf_dump_t *dump_priv) {
+ return (((guint64)g_rand_int(dump_priv->rand) << 32) | (guint64)g_rand_int(dump_priv->rand)) >> 16;
+}
+
+static guint64 erf_metaid_ext_hdr(guint8 exthdr_type, guint64 id, guint8 srcid_flags) {
+ guint64 ext_hdr;
+
+ ext_hdr = id & ERF_EHDR_HOST_ID_MASK;
+ ext_hdr |= ((guint64)srcid_flags) << 48;
+ ext_hdr |= ((guint64)exthdr_type) << 56;
+
+ return ext_hdr;
+}
+#define erf_host_id_ext_hdr(host_id, source_id) erf_metaid_ext_hdr(ERF_EXT_HDR_TYPE_HOST_ID, host_id, source_id)
+#define erf_anchor_id_ext_hdr(anchor_id, flags) erf_metaid_ext_hdr(ERF_EXT_HDR_TYPE_ANCHOR_ID, anchor_id, flags)
+
+static inline gboolean erf_add_ext_hdr_to_list(guint64 ext_hdr, guint64 comparison_mask, GArray *extra_ehdrs) {
+ /* check for existing Host ID in set and add */
+ guint i = 0;
+ struct erf_ehdr ehdr_tmp;
+ struct erf_ehdr *ehdr_ptr = NULL;
+
+ if (!extra_ehdrs)
+ return FALSE;
+
+ ext_hdr = ext_hdr & ~ERF_EHDR_MORE_EXTHDR_MASK;
+ if (comparison_mask == 0)
+ comparison_mask = G_MAXUINT64;
+
+ comparison_mask &= ~ERF_EHDR_MORE_EXTHDR_MASK;
+
+ for (i = 0; i < extra_ehdrs->len; i++) {
+ ehdr_ptr = &g_array_index(extra_ehdrs, struct erf_ehdr, i);
+ /* Check if we already have this Host ID extension header */
+ if (ext_hdr == (ehdr_ptr->ehdr & comparison_mask)) {
+ return TRUE;
+ }
+ }
+
+ /* set more flag on last extension header */
+ if (ehdr_ptr) {
+ ehdr_ptr->ehdr |= ERF_EHDR_MORE_EXTHDR_MASK;
+ }
+
+ ehdr_tmp.ehdr = ext_hdr; /*more flag already cleared above*/
+ g_array_append_val(extra_ehdrs, ehdr_tmp);
+
+ return TRUE;
+}
+
+static inline gboolean erf_append_ext_hdr_to_list(guint64 ext_hdr, GArray *extra_ehdrs) {
+ struct erf_ehdr ehdr_tmp;
+
+ if (!extra_ehdrs)
+ return FALSE;
+
+ ehdr_tmp.ehdr = ext_hdr & ~ERF_EHDR_MORE_EXTHDR_MASK;
+
+ /* set more flag on last extension header */
+ if (extra_ehdrs->len) {
+ g_array_index(extra_ehdrs, struct erf_ehdr, extra_ehdrs->len - 1).ehdr |= ERF_EHDR_MORE_EXTHDR_MASK;
+ }
+
+ g_array_append_val(extra_ehdrs, ehdr_tmp);
+
+ return TRUE;
+}
+
+static gboolean erf_update_host_id_ext_hdrs_list(erf_dump_t *dump_priv, const union wtap_pseudo_header *pseudo_header, GArray *extra_ehdrs) {
+ guint8 type;
+ guint8 erf_type;
+ int has_more;
+ guint64 hdr;
+ int i = 0;
+ guint8 source_id = 0;
+ guint64 host_id = 0;
+ gboolean host_id_found = FALSE;
+
+ if (!extra_ehdrs)
+ return FALSE;
+
+ erf_type = pseudo_header->erf.phdr.type & 0x7f;
+ has_more = pseudo_header->erf.phdr.type & 0x80;
+
+ while (has_more && i < MAX_ERF_EHDR) {
+ hdr = pseudo_header->erf.ehdr_list[i].ehdr;
+ type = (guint8) (hdr >> 56);
+
+ switch (type & 0x7f) {
+ case ERF_EXT_HDR_TYPE_HOST_ID:
+ host_id = hdr & ERF_EHDR_HOST_ID_MASK;
+ source_id = (hdr >> 48) & 0xff;
+
+ /* Don't add the wireshark Host ID Source ID 0 twice since we already add it to metadata records */
+ if (host_id != dump_priv->host_id || source_id != 0)
+ if (!erf_add_ext_hdr_to_list(hdr, 0, extra_ehdrs)) return FALSE;
+
+ if (!host_id_found) {
+ /* XXX: Take the opportunity to update the implicit Host ID if we
+ * don't know it yet. Ideally we should pass this through from the
+ * reader as a custom option or similar. */
+ if (erf_type == ERF_TYPE_META && ((hdr >> 48) & 0xff) > 0) {
+ if (dump_priv->implicit_host_id == ERF_META_HOST_ID_IMPLICIT) {
+ dump_priv->implicit_host_id = host_id;
+ }
+ }
+ }
+
+ host_id_found = TRUE;
+ break;
+ case ERF_EXT_HDR_TYPE_FLOW_ID:
+ if (source_id == 0) /* If no Host ID extension header use the first Source ID only */
+ source_id = (hdr >> 48) & 0xff;
+ break;
+ }
+
+ has_more = type & 0x80;
+ i++;
+ }
+
+ /* Add Source ID with implicit Host ID if not found */
+ if (!host_id_found) {
+ guint64 implicit_host_id = dump_priv->implicit_host_id == ERF_META_HOST_ID_IMPLICIT ? 0 : dump_priv->implicit_host_id;
+ /* Don't add the wireshark Host ID Source ID 0 twice since we already add it to metadata records */
+ if (implicit_host_id != dump_priv->host_id || source_id != 0)
+ if (!erf_add_ext_hdr_to_list(erf_host_id_ext_hdr(implicit_host_id, source_id), 0, extra_ehdrs)) return FALSE;
+ }
+
+ return TRUE;
+}
+
+/**
+ * Writes a metadata record with a randomly generated Anchor ID with the
+ * user comment attached to its comment section, also updates the
+ * modified frame header to include a Host ID extension header and
+ * a Anchor ID extension header to link the records together.
+ * @param wdh the wtap_dumper structure
+ * @param dump_priv private data for the dump stream
+ * @param rec record metadata from which to get user comment
+ * @param mutable_hdr pseudo_header to update with Anchor ID for comment record
+ * @param err the error value
+ * @return A gboolean value to indicate whether the dump was successful
+ */
+static gboolean erf_write_anchor_meta_update_phdr(wtap_dumper *wdh, erf_dump_t *dump_priv, const wtap_rec *rec, union wtap_pseudo_header *mutable_hdr, int *err) {
+ GArray *meta_ehdrs;
+ GPtrArray* sections = NULL;
+ guint8 has_more;
+ guint8 i = 0;
+ guint8 ext_hdr_count = 0;
+ guint8 j = 0;
+ guint64 host_id_src_hdr = ERF_META_HOST_ID_IMPLICIT;
+ guint64 host_id_own_hdr = erf_host_id_ext_hdr(dump_priv->host_id, 0);
+ guint64 flow_id_hdr = 0;
+ guint64 anchor_id_hdr = 0;
+ gboolean found_host_id = FALSE;
+ gboolean found_own_host_id = FALSE;
+ gboolean found_flow_id = FALSE;
+ gint new_ext_hdrs = 0;
+ guint8 insert_idx = 0;
+ guint8 source_id = 0;
+ gboolean ret = FALSE;
+ guint64 implicit_host_id = dump_priv->implicit_host_id == ERF_META_HOST_ID_IMPLICIT ? 0 : dump_priv->implicit_host_id;
+ gchar *pkt_comment;
+
+
+ /*
+ * There are 3 possible scenarios:
+ * a. The record has a source Host ID but not our Host ID. We need to add our
+ * Host ID extension header then our Anchor ID extension header.
+ * b. The record already has our Host ID extension header on it. We should
+ * insert the Anchor ID at the end of the list for that Host ID just
+ * before the next Host ID extension header.
+ * c. The record has no Host ID extension header at all. We need to add the Host ID
+ * extension header making the Implicit Host ID explicit before we add our
+ * one to avoid claiming the packet was captured by us.
+ */
+
+ /*
+ * Extract information from the packet extension header stack
+ * 1. original source Host ID extension header.
+ * 2. Anchor ID extension header insertion point (see b., above).
+ * 3. Flow ID extension header so we can add it for reference to the metadata
+ * record.
+ * 4. Enough information to generate an explicit Host ID extension header if
+ * there wasn't one (see erf_get_source_from_header).
+ */
+
+ has_more = mutable_hdr->erf.phdr.type & 0x80;
+
+ while (has_more && (i < MAX_ERF_EHDR)) {
+ guint64 hdr = mutable_hdr->erf.ehdr_list[i].ehdr;
+ guint8 type = (guint8) (hdr >> 56);
+
+ switch (type & 0x7f) {
+ case ERF_EXT_HDR_TYPE_HOST_ID:
+ /* Set insertion point of anchor ID to be at end of Host ID list (i.e.
+ * just before the next one). */
+ if (found_own_host_id && !insert_idx)
+ insert_idx = i;
+
+ if ((hdr & ERF_EHDR_HOST_ID_MASK) == dump_priv->host_id){
+ found_own_host_id = TRUE;
+ }
+
+ if (!found_host_id)
+ host_id_src_hdr = hdr;
+
+ found_host_id = TRUE;
+ break;
+
+ case ERF_EXT_HDR_TYPE_FLOW_ID:
+ /*XXX: we only use this when making the implicit host id explicit,
+ * otherwise we'd need to check the one in Host ID header too*/
+ if (source_id == 0)
+ source_id = (guint8)(hdr >> 48);
+
+ if (!found_flow_id)
+ flow_id_hdr = hdr;
+
+ found_flow_id = TRUE;
+ break;
+ }
+
+ has_more = type & 0x80;
+ i += 1;
+ }
+
+ ext_hdr_count = i;
+
+ if (!insert_idx)
+ insert_idx = i;
+
+ /* Don't need to add our own Host ID twice if it is the same as the implicit*/
+ if (!found_host_id && implicit_host_id == dump_priv->host_id) {
+ found_own_host_id = TRUE;
+ }
+
+ /*
+ * Update the packet record pseudo_header with Anchor ID and extension header(s)
+ */
+ new_ext_hdrs = 1 /*anchor id*/ + (found_own_host_id?0:1) + (found_host_id?0:1);
+
+ if(ext_hdr_count + new_ext_hdrs > MAX_ERF_EHDR
+ || mutable_hdr->erf.phdr.rlen + new_ext_hdrs * 8 > 65535) {
+ /* Not enough extension header slots to add Anchor ID */
+ *err = WTAP_ERR_PACKET_TOO_LARGE;
+ return FALSE;
+ }
+
+ mutable_hdr->erf.phdr.rlen += new_ext_hdrs * 8;
+
+ /* Set the more extension headers flag */
+ mutable_hdr->erf.phdr.type |= 0x80;
+ if (insert_idx > 0) {
+ mutable_hdr->erf.ehdr_list[insert_idx-1].ehdr |= ERF_EHDR_MORE_EXTHDR_MASK;
+ }
+
+ /* Generate the Anchor ID extension header */
+ anchor_id_hdr = erf_anchor_id_ext_hdr(erf_get_random_anchor_id(dump_priv), 0);
+
+ /* Either we can insert Anchor ID at the end of the list for our Host ID or we
+ * need to append the Host ID(s) and Anchor ID */
+ if (insert_idx < ext_hdr_count) {
+ /* shuffle up any following extension headers FIRST - we know we have room now */
+ for (j = ext_hdr_count; j > insert_idx; j--) {
+ mutable_hdr->erf.ehdr_list[j].ehdr = mutable_hdr->erf.ehdr_list[j-1].ehdr;
+ }
+
+ /* copy more extension headers bit from previous extension header */
+ anchor_id_hdr |= ERF_EHDR_MORE_EXTHDR_MASK;
+ }
+
+ if(!found_host_id) {
+ /* No Host ID extension header found and we have an implicit Host ID which
+ * we want to make explicit */
+
+ /* XXX: it is important that we know the implicit Host ID here or we end
+ * up semi-permentantly associating the packet with Host 0 (unknown), we should
+ * pass it through from the reader. In theory we should be on the
+ * original capture machine if we have no Host ID extension headers. */
+ host_id_src_hdr = erf_host_id_ext_hdr(implicit_host_id, source_id);
+ mutable_hdr->erf.ehdr_list[insert_idx++].ehdr = ERF_EHDR_SET_MORE_EXTHDR(host_id_src_hdr);
+ }
+
+ if(!found_own_host_id) {
+ /* Add our Host ID extension header */
+ mutable_hdr->erf.ehdr_list[insert_idx++].ehdr = ERF_EHDR_SET_MORE_EXTHDR(host_id_own_hdr);
+ }
+
+ /*Add the Anchor ID extension header */
+ mutable_hdr->erf.ehdr_list[insert_idx].ehdr = anchor_id_hdr;
+
+
+ /*
+ * Now construct the metadata Anchor record with the same Anchor ID
+ */
+
+ meta_ehdrs = g_array_new(FALSE, FALSE, sizeof(struct erf_ehdr));
+
+ /* We need up to 4 extension headers on the Provenance metadata record */
+ /*Required*/
+ /* 1. Added by erf_write_meta_record: HostID exthdr to indicate this Anchor
+ * record was generated by this host. Source ID 0 to avoid changing the
+ * implicit Host ID. */
+
+ /* 2. AnchorID exthdr with 'unique' per-host Anchor ID assigned by this host
+ * (in this case Wireshark). Anchor definition flag set to 1 to indicate this
+ * record contains a definition of the ID, in this case a comment on a single
+ * packet. Tied to above extension header by ordering like a list */
+ erf_append_ext_hdr_to_list(anchor_id_hdr | ERF_EHDR_ANCHOR_ID_DEFINITION_MASK, meta_ehdrs);
+
+ /*Helpful for indexing*/
+ /* 3. HostID exthdr with the original Source (first Host ID extension header) of the packet record */
+ erf_append_ext_hdr_to_list(host_id_src_hdr, meta_ehdrs);
+
+ /* Flow ID extension header from the packet record if we have one */
+ if (found_flow_id) {
+ /* 4. FlowID exthdr with Flow ID from the packet so a flow search will find the comment
+ * record too. Must come here so the (redundant here) Source ID is scoped to the
+ * correct Host ID. */
+ /* Clear the stack type just in case something tries to assume we're an IP
+ * packet without looking at the ERF type. Clear Source ID too just in case
+ * we're trying to associate with the wrong Host ID. */
+ erf_append_ext_hdr_to_list(flow_id_hdr & ~(ERF_EHDR_FLOW_ID_STACK_TYPE_MASK|ERF_EHDR_FLOW_ID_SOURCE_ID_MASK), meta_ehdrs);
+ }
+
+ /* Generate the metadata payload with the packet comment */
+ /* XXX - can ERF have more than one comment? */
+ sections = g_ptr_array_new_with_free_func(erf_meta_section_free);
+ if (WTAP_OPTTYPE_SUCCESS != wtap_block_get_nth_string_option_value(rec->block, OPT_COMMENT, 0, &pkt_comment)) {
+ pkt_comment = NULL;
+ }
+ erf_comment_to_sections(wdh, ERF_META_SECTION_INFO, 0x8000 /*local to record*/, pkt_comment, sections);
+
+ /* Write the metadata record, but not the packet record as what we do depends
+ * on the WTAP_ENCAP */
+ ret = erf_write_meta_record(wdh, dump_priv, mutable_hdr->erf.phdr.ts, sections, meta_ehdrs, err);
+ g_ptr_array_free(sections, TRUE);
+ g_array_free(meta_ehdrs, TRUE);
+
+ return ret;
+}
+
+static gboolean erf_write_meta_record(wtap_dumper *wdh, erf_dump_t *dump_priv, guint64 timestamp, GPtrArray *sections, GArray *extra_ehdrs, int *err) {
+ union wtap_pseudo_header other_header;
+ struct erf_meta_tag gen_time_tag;
+ struct erf_meta_section *section_ptr;
+ guint total_wlen = 0;
+ guint total_rlen = 0;
+ gint64 alignbytes = 0;
+ guint i;
+ guint num_extra_ehdrs = 0;
+
+ if(!sections || sections->len <= 0)
+ return FALSE;
+
+ for(i = 0; i < sections->len; i++) {
+ section_ptr = (struct erf_meta_section*)g_ptr_array_index(sections, i);
+ total_wlen += section_ptr->section_length;
+ }
+
+ gen_time_tag.type = ERF_META_TAG_gen_time;
+ gen_time_tag.length = 8U;
+ gen_time_tag.value = (guint8*)&dump_priv->gen_time;
+ total_wlen += gen_time_tag.length + 4;
+
+ total_rlen = total_wlen + 24; /* 24 is the header + extension header length */
+ if (extra_ehdrs) {
+ /*
+ * These will be appended to the first extension header in
+ * other_header.erf.ehdr_list. There are a total of MAX_ERF_EHDR
+ * extension headers in that array, so we can append no more than
+ * MAX_ERF_EHDR - 1 extension headeers.
+ */
+ num_extra_ehdrs = MIN(extra_ehdrs->len, MAX_ERF_EHDR - 1);
+ total_rlen += num_extra_ehdrs * 8;
+ }
+ /*padding to 8 byte alignment*/
+ total_rlen += ERF_PADDING_TO_8(total_rlen);
+
+ if(total_rlen > 65535) {
+ *err = WTAP_ERR_PACKET_TOO_LARGE;
+ return FALSE;
+ }
+
+ other_header.erf.phdr.ts = timestamp;
+ other_header.erf.phdr.type = ERF_TYPE_META | 0x80;
+ other_header.erf.phdr.flags = 0x04; /* Varying record length */
+ other_header.erf.phdr.lctr = 0;
+ other_header.erf.phdr.wlen = (guint16)total_wlen;
+ other_header.erf.phdr.rlen = (guint16)total_rlen;
+ /*Add our Host ID in Host ID extension header indicating we generated this
+ * record. Source ID 0 to avoid affecting implicit Host ID. */
+ other_header.erf.ehdr_list[0].ehdr = erf_host_id_ext_hdr(dump_priv->host_id, 0);
+ /*Additional extension headers*/
+ /*XXX: If we end up cutting the list short, erf_write_phdr will correct the
+ * unterminated extension header list*/
+ if (num_extra_ehdrs > 0) {
+ other_header.erf.ehdr_list[0].ehdr |= ERF_EHDR_MORE_EXTHDR_MASK;
+ memcpy(&other_header.erf.ehdr_list[1], extra_ehdrs->data, sizeof(struct erf_ehdr) * num_extra_ehdrs);
+ }
+
+ /* Make sure we always write out rlen, regardless of what happens */
+ alignbytes = wdh->bytes_dumped + other_header.erf.phdr.rlen;
+
+ if(!erf_write_phdr(wdh, WTAP_ENCAP_ERF, &other_header, err)) return FALSE;
+
+ /* Generation time */
+ erf_meta_write_tag(wdh, &gen_time_tag, err);
+
+ /* Section(s) */
+ for(i = 0; i < sections->len; i++) {
+ section_ptr = (struct erf_meta_section*)g_ptr_array_index(sections, i);
+ erf_meta_write_section(wdh, section_ptr, err);
+ }
+
+ while(wdh->bytes_dumped < alignbytes){
+ if(!wtap_dump_file_write(wdh, "", 1, err)) return FALSE;
+ }
+
+ /* We wrote new packets, reloading is required */
+ wdh->needs_reload = TRUE;
+
+ return TRUE;
+
+}
+
+static erf_dump_t *erf_dump_priv_create(void) {
+ erf_dump_t *dump_priv;
+
+ dump_priv = g_new(erf_dump_t, 1);
+ dump_priv->write_next_extra_meta = FALSE;
+ dump_priv->last_meta_periodic = FALSE;
+ dump_priv->gen_time = 0;
+ dump_priv->host_id = ERF_WS_DEFAULT_HOST_ID;
+ dump_priv->implicit_host_id = ERF_META_HOST_ID_IMPLICIT;
+ dump_priv->first_frame_time_sec = 0;
+ dump_priv->prev_inserted_time_sec = 0;
+ dump_priv->prev_frame_ts = 0;
+ dump_priv->prev_erf_type = 0;
+ dump_priv->user_comment_ptr = NULL;
+ dump_priv->periodic_sections = NULL;
+ dump_priv->periodic_extra_ehdrs = g_array_new(FALSE, FALSE, sizeof(struct erf_ehdr));
+ dump_priv->rand = g_rand_new();
+
+ return dump_priv;
+}
+
+static gboolean erf_dump(
+ wtap_dumper *wdh,
+ const wtap_rec *rec,
+ const guint8 *pd,
+ int *err,
+ gchar **err_info _U_)
+{
+ const union wtap_pseudo_header *pseudo_header = &rec->rec_header.packet_header.pseudo_header;
+ union wtap_pseudo_header other_phdr;
+ int erf_type;
+ gint64 alignbytes = 0;
+ guint padbytes = 0;
+ int round_down = 0;
+ gboolean must_add_crc = FALSE;
+ guint32 crc32 = 0x00000000;
+ erf_dump_t *dump_priv = (erf_dump_t*)wdh->priv;
+ /* Host ID extension header with Host ID 0 (unknown). For now use Source ID 1. */
+ /* TODO: How to know if record was captured by this Wireshark? */
+ guint64 non_erf_host_id_ehdr = erf_host_id_ext_hdr(0, 1);
+
+ /* Don't write anything bigger than we're willing to read. */
+ if(rec->rec_header.packet_header.caplen > WTAP_MAX_PACKET_SIZE_STANDARD) {
+ *err = WTAP_ERR_PACKET_TOO_LARGE;
+ return FALSE;
+ }
+
+ if(!dump_priv->gen_time) {
+ erf_dump_priv_init_gen_time(dump_priv);
+ dump_priv->first_frame_time_sec = rec->ts.secs;
+ }
+
+ /*
+ * ERF doesn't have a per-file encapsulation type, and it
+ * doesn't have a pcapng-style notion of interfaces that
+ * support a per-interface encapsulation type. Therefore,
+ * we can just use this particular packet's encapsulation
+ * without checking whether it's the encapsulation for the
+ * dump file (as there isn't an encapsulation for an ERF
+ * file, unless you count WTAP_ENCAP_ERF as the encapsulation
+ * for all files, but we add ERF metadata if a packet is
+ * written with an encapsulation other than WTAP_ENCAP_ERF).
+ *
+ * We will check whether the encapsulation is something we
+ * support later.
+ */
+ if (rec->rec_header.packet_header.pkt_encap != WTAP_ENCAP_ERF) {
+ unsigned int total_rlen;;
+ unsigned int total_wlen;
+
+ /* Non-ERF encapsulation; generate ERF metadata */
+
+ total_rlen = rec->rec_header.packet_header.caplen+16;
+ total_wlen = rec->rec_header.packet_header.len;
+
+ /* We can only convert packet records. */
+ if (rec->rec_type != REC_TYPE_PACKET) {
+ *err = WTAP_ERR_UNWRITABLE_REC_TYPE;
+ return FALSE;
+ }
+
+ erf_type = wtap_wtap_encap_to_erf_encap(rec->rec_header.packet_header.pkt_encap);
+ if (erf_type == -1) {
+ *err = WTAP_ERR_UNWRITABLE_ENCAP;
+ return FALSE;
+ }
+
+ /* Generate a fake header in other_phdr using data that we know*/
+ memset(&other_phdr, 0, sizeof(union wtap_pseudo_header));
+ /* Convert time erf timestamp format*/
+ other_phdr.erf.phdr.ts = ((guint64) rec->ts.secs << 32) + (((guint64) rec->ts.nsecs <<32) / 1000 / 1000 / 1000);
+ other_phdr.erf.phdr.type = (guint8)erf_type;
+ /* Support up to 4 interfaces */
+ /* TODO: use multiple Source IDs and metadata records to support >4 interfaces */
+ other_phdr.erf.phdr.flags = rec->rec_header.packet_header.interface_id % ERF_MAX_INTERFACES;
+ other_phdr.erf.phdr.flags |= 0x4; /*vlen flag set because we're creating variable length records*/
+
+ other_phdr.erf.phdr.lctr = 0;
+
+ /*now we work out rlen, accounting for all the different headers and missing fcs(eth)*/
+ switch(other_phdr.erf.phdr.type & 0x7F){
+ case ERF_TYPE_ETH:
+ total_rlen += 2; /*2 bytes for erf eth_type*/
+ if (pseudo_header->eth.fcs_len != 4) {
+ /* Either this packet doesn't include the FCS
+ (pseudo_header->eth.fcs_len = 0), or we don't
+ know whether it has an FCS (= -1). We have to
+ synthesize an FCS.*/
+ if(!(rec->rec_header.packet_header.caplen < rec->rec_header.packet_header.len)){ /*don't add FCS if packet has been snapped off*/
+ crc32 = crc32_ccitt_seed(pd, rec->rec_header.packet_header.caplen, 0xFFFFFFFF);
+ total_rlen += 4; /*4 bytes for added checksum*/
+ total_wlen += 4;
+ must_add_crc = TRUE;
+ }
+ }
+ break;
+ case ERF_TYPE_HDLC_POS:
+ /*we assume that it's missing a FCS checksum, make one up*/
+ if(!(rec->rec_header.packet_header.caplen < rec->rec_header.packet_header.len)){ /*unless of course, the packet has been snapped off*/
+ crc32 = crc32_ccitt_seed(pd, rec->rec_header.packet_header.caplen, 0xFFFFFFFF);
+ total_rlen += 4; /*4 bytes for added checksum*/
+ total_wlen += 4;
+ must_add_crc = TRUE; /* XXX - these never have an FCS? */
+ }
+ break;
+ default:
+ break;
+ }
+
+ /* Add Host ID extension header with Host ID 0 (unknown). For now use Source ID 1. */
+ other_phdr.erf.phdr.type |= 0x80;
+ other_phdr.erf.ehdr_list[0].ehdr = non_erf_host_id_ehdr;
+ total_rlen += 8;
+
+ padbytes = ERF_PADDING_TO_8(total_rlen); /*calculate how much padding will be required */
+ if(rec->rec_header.packet_header.caplen < rec->rec_header.packet_header.len){ /*if packet has been snapped, we need to round down what we output*/
+ round_down = (8 - padbytes) % 8;
+ total_rlen -= round_down;
+ }else{
+ total_rlen += padbytes;
+ }
+
+ if (total_rlen > G_MAXUINT16 || total_wlen > G_MAXUINT16) {
+ *err = WTAP_ERR_PACKET_TOO_LARGE;
+ return FALSE;
+ }
+
+ other_phdr.erf.phdr.rlen = (guint16)total_rlen;
+ other_phdr.erf.phdr.wlen = (guint16)total_wlen;
+
+ pseudo_header = &other_phdr;
+ } else if (rec->presence_flags & WTAP_HAS_TS) {
+ // Update timestamp if changed.
+ time_t secs;
+ int nsecs;
+ guint64 ts = pseudo_header->erf.phdr.ts;
+
+ secs = (long) (ts >> 32);
+ ts = ((ts & 0xffffffff) * 1000 * 1000 * 1000);
+ ts += (ts & 0x80000000) << 1; /* rounding */
+ nsecs = ((int) (ts >> 32));
+ if (nsecs >= 1000000000) {
+ nsecs -= 1000000000;
+ secs += 1;
+ }
+
+ if (secs != rec->ts.secs || nsecs != rec->ts.nsecs) {
+ other_phdr = *pseudo_header;
+ other_phdr.erf.phdr.ts = ((guint64) rec->ts.secs << 32) + (((guint64) rec->ts.nsecs <<32) / 1000 / 1000 / 1000);
+ pseudo_header = &other_phdr;
+ }
+ }
+
+ /* We now have a (real or fake) ERF record */
+ erf_type = pseudo_header->erf.phdr.type & 0x7FU;
+
+ /* Accumulate Host ID/Source ID to put in updated periodic metadata */
+ /* TODO: pass these through from read interface list instead? */
+ /* Note: this includes the one we made for the fake ERF header */
+ erf_update_host_id_ext_hdrs_list(dump_priv, pseudo_header, dump_priv->periodic_extra_ehdrs);
+
+ /* Insert new metadata record depending on whether the capture comment has
+ * changed. Write metadata each second at boundaries. If there is metadata
+ * write at the end of each of metadata records so we update the metadata. */
+ if (erf_type == ERF_TYPE_META) {
+ /* Check whether the capture comment string has changed */
+ /* Updates write_next_extra_meta */
+ dump_priv->last_meta_periodic = erf_dump_priv_compare_capture_comment(wdh, dump_priv, pseudo_header, pd);
+ } else { /* don't want to insert a new metadata record while looking at another */
+ if (dump_priv->prev_erf_type == ERF_TYPE_META && dump_priv->last_meta_periodic) {
+ /* Last frame was a periodic (non-comment) metadata record (and this frame is not), check if we
+ * need to insert one to update metadata. */
+
+ if(dump_priv->write_next_extra_meta) {
+ if (!dump_priv->periodic_sections) {
+ /* If we've seen metadata just insert the capture comment and not the
+ * rest of the metadata */
+ dump_priv->periodic_sections = g_ptr_array_new_with_free_func(erf_meta_section_free);
+ erf_comment_to_sections(wdh, ERF_META_SECTION_CAPTURE, 0, dump_priv->user_comment_ptr, dump_priv->periodic_sections);
+ }
+
+ if (!erf_write_meta_record(wdh, dump_priv, dump_priv->prev_frame_ts, dump_priv->periodic_sections, dump_priv->periodic_extra_ehdrs, err)) return FALSE;
+ dump_priv->prev_inserted_time_sec = rec->ts.secs;
+ /*TODO: clear accumulated existing extension headers here?*/
+ }
+
+ /* If we have seen a metadata record in the first ~1 second it
+ * means that we are dealing with an ERF file with metadata already in them.
+ * We don't want to write extra metadata if nothing has changed. We can't
+ * trust the Wireshark representation since we massage the fields on
+ * read. */
+ /* restart searching for next meta record to update capture comment at */
+ dump_priv->write_next_extra_meta = FALSE;
+ } else if (rec->ts.secs > dump_priv->first_frame_time_sec + 1
+ && dump_priv->prev_inserted_time_sec != rec->ts.secs) {
+ /* For compatibility, don't insert metadata for older ERF files with no changed metadata */
+ if (dump_priv->write_next_extra_meta) {
+ if (!dump_priv->periodic_sections) {
+ /* If we get here, metadata record was not found in the first ~1 sec
+ * but we have either a capture comment or a non-ERF file (see
+ * erf_dump_open) */
+ /* Start inserting metadata records from wtap data at second boundaries */
+ dump_priv->periodic_sections = g_ptr_array_new_with_free_func(erf_meta_section_free);
+ erf_wtap_info_to_sections(wdh, dump_priv->periodic_sections);
+ }
+ }
+
+ /* At second boundaries insert either the updated comment (if we've seen some metadata records
+ * already) or the full metadata */
+ if (dump_priv->periodic_sections) {
+ if (!erf_write_meta_record(wdh, dump_priv, (guint64)(rec->ts.secs) << 32, dump_priv->periodic_sections, dump_priv->periodic_extra_ehdrs, err)) return FALSE;
+ dump_priv->prev_inserted_time_sec = rec->ts.secs;
+ }
+ }
+ }
+
+ /* If the packet user comment has changed, we need to
+ * construct a new header with additional Host ID and Anchor ID
+ * and insert a metadata record before that frame */
+ /*XXX: The user may have changed the comment to cleared! */
+ if(rec->block_was_modified) {
+ if (rec->rec_header.packet_header.pkt_encap == WTAP_ENCAP_ERF) {
+ /* XXX: What about ERF-in-pcapng with existing comment (that wasn't
+ * modified)? */
+ if(rec->block_was_modified) {
+ memmove(&other_phdr, pseudo_header, sizeof(union wtap_pseudo_header));
+ if(!erf_write_anchor_meta_update_phdr(wdh, dump_priv, rec, &other_phdr, err)) return FALSE;
+ pseudo_header = &other_phdr;
+ }
+ } else {
+ /* Always write the comment if non-ERF */
+ if(!erf_write_anchor_meta_update_phdr(wdh, dump_priv, rec, &other_phdr, err)) return FALSE;
+ }
+ }
+
+ /* Make sure we always write out rlen, regardless of what happens */
+ alignbytes = wdh->bytes_dumped + pseudo_header->erf.phdr.rlen;
+
+ if(!erf_write_phdr(wdh, WTAP_ENCAP_ERF, pseudo_header, err)) return FALSE;
+
+ if(!wtap_dump_file_write(wdh, pd, rec->rec_header.packet_header.caplen - round_down, err)) return FALSE;
+
+ /*add the 4 byte CRC if necessary*/
+ if(must_add_crc){
+ if(!wtap_dump_file_write(wdh, &crc32, 4, err)) return FALSE;
+ }
+
+ /*XXX: In the case of ENCAP_ERF, this pads the record to its original length, which is fine in most
+ * cases. However with >MAX_ERF_EHDR unnecessary padding will be added, and
+ * if the record was truncated this will be incorrectly treated as payload.
+ * More than 8 extension headers is unusual though, only the first 8 are
+ * written out anyway and fixing properly would require major refactor.*/
+ /*records should be 8byte aligned, so we add padding to our calculated rlen */
+ while(wdh->bytes_dumped < alignbytes){
+ if(!wtap_dump_file_write(wdh, "", 1, err)) return FALSE;
+ }
+
+ dump_priv->prev_erf_type = pseudo_header->erf.phdr.type & 0x7FU;
+ dump_priv->prev_frame_ts = pseudo_header->erf.phdr.ts;
+
+ return TRUE;
+}
+
+static int erf_dump_can_write_encap(int encap)
+{
+
+ if(encap == WTAP_ENCAP_PER_PACKET)
+ return 0;
+
+ if (wtap_wtap_encap_to_erf_encap(encap) == -1)
+ return WTAP_ERR_UNWRITABLE_ENCAP;
+
+ return 0;
+}
+
+static int erf_dump_open(wtap_dumper *wdh, int *err _U_, gchar **err_info _U_)
+{
+ erf_dump_t *dump_priv;
+ gchar *s;
+ guint64 host_id;
+ char *first_shb_comment = NULL;
+
+ dump_priv = erf_dump_priv_create();
+
+ wdh->subtype_write = erf_dump;
+ wdh->priv = dump_priv;
+ wdh->subtype_finish = erf_dump_finish;
+
+ /* Get the first capture comment string */
+ get_user_comment_string(wdh, &first_shb_comment);
+ /* Save a copy of it */
+ dump_priv->user_comment_ptr = g_strdup(first_shb_comment);
+ /* XXX: If we have a capture comment or a non-ERF file assume we need to
+ * write metadata unless we see existing metadata in the first second. */
+ if (dump_priv->user_comment_ptr || wdh->file_encap != WTAP_ENCAP_ERF)
+ dump_priv->write_next_extra_meta = TRUE;
+
+ /* Read Host ID from environment variable */
+ /* TODO: generate one from MAC address? */
+ if ((s = getenv("ERF_HOST_ID")) != NULL) {
+ /* TODO: support both decimal and hex strings (base 0)? */
+ if (ws_hexstrtou64(s, NULL, &host_id)) {
+ dump_priv->host_id = host_id & ERF_EHDR_HOST_ID_MASK;
+ }
+ }
+
+ return TRUE;
+}
+
+static int erf_get_source_from_header(union wtap_pseudo_header *pseudo_header, guint64 *host_id, guint8 *source_id)
+{
+ guint8 type;
+ guint8 has_more;
+ guint64 hdr;
+ int i = 0;
+ gboolean host_id_found = FALSE;
+
+ if (!pseudo_header || !host_id || !source_id)
+ return -1;
+
+ *host_id = ERF_META_HOST_ID_IMPLICIT;
+ *source_id = 0;
+
+ has_more = pseudo_header->erf.phdr.type & 0x80;
+
+ while (has_more && (i < MAX_ERF_EHDR)) {
+ hdr = pseudo_header->erf.ehdr_list[i].ehdr;
+ type = (guint8) (hdr >> 56);
+
+ /*
+ * XXX: Only want first Source ID and Host ID, and want to preserve HID n SID 0 (see
+ * erf_populate_interface)
+ */
+ switch (type & 0x7f) {
+ case ERF_EXT_HDR_TYPE_HOST_ID:
+ if (!host_id_found)
+ *host_id = hdr & ERF_EHDR_HOST_ID_MASK;
+
+ host_id_found = TRUE;
+ /* Fall through */
+ case ERF_EXT_HDR_TYPE_FLOW_ID:
+ if (*source_id == 0)
+ *source_id = (hdr >> 48) & 0xff;
+ break;
+ }
+
+ if (host_id_found)
+ break;
+
+ has_more = type & 0x80;
+ i += 1;
+ }
+
+ return 0;
+}
+
+int erf_populate_interface_from_header(erf_t *erf_priv, wtap *wth, union wtap_pseudo_header *pseudo_header, int *err, gchar **err_info)
+{
+ guint64 host_id;
+ guint8 source_id;
+ guint8 if_num;
+
+ if (!pseudo_header)
+ return -1;
+
+ if_num = pseudo_header->erf.phdr.flags & 0x03;
+
+ erf_get_source_from_header(pseudo_header, &host_id, &source_id);
+
+ return erf_populate_interface(erf_priv, wth, pseudo_header, host_id, source_id, if_num, err, err_info);
+}
+
+static struct erf_if_mapping* erf_find_interface_mapping(erf_t *erf_priv, guint64 host_id, guint8 source_id)
+{
+ struct erf_if_mapping if_map_lookup;
+
+ /* XXX: erf_priv should never be NULL here */
+ if (!erf_priv)
+ return NULL;
+
+ if_map_lookup.host_id = host_id;
+ if_map_lookup.source_id = source_id;
+
+ return (struct erf_if_mapping*) g_hash_table_lookup(erf_priv->if_map, &if_map_lookup);
+}
+
+static void erf_set_interface_descr(wtap_block_t block, guint option_id, guint64 host_id, guint8 source_id, guint8 if_num, const gchar *descr)
+{
+ /* Source XXX,*/
+ char sourceid_buf[16];
+ /* Host XXXXXXXXXXXX,*/
+ char hostid_buf[24];
+
+ sourceid_buf[0] = '\0';
+ hostid_buf[0] = '\0';
+
+ /* Implicit Host ID defaults to 0 */
+ if (host_id == ERF_META_HOST_ID_IMPLICIT) {
+ host_id = 0;
+ }
+
+ if (host_id > 0) {
+ snprintf(hostid_buf, sizeof(hostid_buf), " Host %012" PRIx64 ",", host_id);
+ }
+
+ if (source_id > 0) {
+ snprintf(sourceid_buf, sizeof(sourceid_buf), " Source %u,", source_id);
+ }
+
+ if (descr) {
+ wtap_block_set_string_option_value_format(block, option_id, "%s (ERF%s%s Interface %d)", descr, hostid_buf, sourceid_buf, if_num);
+ } else {
+ wtap_block_set_string_option_value_format(block, option_id, "Port %c (ERF%s%s Interface %d)", 'A'+if_num, hostid_buf, sourceid_buf, if_num);
+ }
+}
+
+static int erf_update_anchors_from_header(erf_t *erf_priv, wtap_rec *rec, union wtap_pseudo_header *pseudo_header, guint64 host_id, GPtrArray *anchor_mappings_to_update)
+{
+ guint8 type;
+ guint8 has_more;
+ guint64 hdr;
+ guint64 comment_gen_time = 0;
+ guint64 host_id_current;
+ guint64 anchor_id_current = 0;
+ int i = 0;
+ gchar *comment = NULL;
+
+ if (!rec || !pseudo_header)
+ return -1;
+
+ /* Start with the first Host ID that was found on the record
+ * as the Anchor ID isn't required to be the first extension header' */
+ host_id_current = host_id == ERF_META_HOST_ID_IMPLICIT ? erf_priv->implicit_host_id : host_id;
+
+ has_more = pseudo_header->erf.phdr.type & 0x80;
+
+ while (has_more && (i < MAX_ERF_EHDR)) {
+ hdr = pseudo_header->erf.ehdr_list[i].ehdr;
+ type = (guint8) (hdr >> 56);
+
+ switch (type & 0x7f) {
+ case ERF_EXT_HDR_TYPE_HOST_ID:
+ host_id_current = hdr & ERF_EHDR_HOST_ID_MASK;
+ break;
+
+ case ERF_EXT_HDR_TYPE_ANCHOR_ID:
+ {
+ anchor_id_current = hdr & ERF_EHDR_ANCHOR_ID_MASK;
+ if (!(ERF_ANCHOR_ID_IS_DEFINITION(hdr))) {
+ /*
+ * Anchor definition flag is 0, attempt to associate a comment with this record
+ * XXX: currently the comment count may be wrong on the first pass!
+ */
+ /* We may not have found the implicit Host ID yet, if so we are unlikely to find anything */
+ struct erf_anchor_mapping* lookup_result;
+ lookup_result = erf_find_anchor_mapping(erf_priv, host_id_current, anchor_id_current);
+ if (lookup_result) {
+ if (lookup_result->gen_time > comment_gen_time) {
+ /* XXX: we might have a comment that clears the comment (i.e.
+ * empty string)! */
+ if (lookup_result->comment && lookup_result->comment[0] != '\0') {
+ comment = lookup_result->comment;
+ }
+ comment_gen_time = lookup_result->gen_time;
+ }
+ }
+ }
+ else {
+ if (anchor_mappings_to_update && (pseudo_header->erf.phdr.type & 0x7f) == ERF_TYPE_META) {
+ /*
+ * Anchor definition flag is 1, put the mapping in an array
+ * which we will later update when we walk through
+ * the metadata tags
+ */
+ /* Only Provenance record can contain the information we need */
+ struct erf_anchor_mapping *mapping_ptr =
+ g_new0(struct erf_anchor_mapping, 1);
+ /* May be ERF_META_HOST_ID_IMPLICIT */
+ mapping_ptr->host_id = host_id_current;
+ mapping_ptr->anchor_id = anchor_id_current;
+ g_ptr_array_add(anchor_mappings_to_update, mapping_ptr);
+ }
+ }
+ break;
+ }
+ }
+
+ has_more = type & 0x80;
+ i += 1;
+ }
+
+ if (comment) {
+ wtap_block_add_string_option(rec->block, OPT_COMMENT, comment, strlen(comment));
+ }
+
+ return 0;
+}
+
+/**
+ * @brief Update the implicit Host ID and Anchor Mapping information
+ */
+static int erf_update_implicit_host_id(erf_t *erf_priv, wtap *wth, guint64 implicit_host_id)
+{
+ GHashTableIter iter;
+ gpointer iter_value;
+ GList* implicit_list = NULL;
+ GList* item = NULL;
+ wtap_block_t int_data;
+ struct erf_if_mapping* if_map = NULL;
+ struct erf_if_mapping* if_map_other = NULL;
+ struct erf_if_info* if_info = NULL;
+ struct erf_anchor_mapping* anchor_mapping = NULL;
+ struct erf_anchor_mapping* anchor_mapping_other = NULL;
+ gchar *oldstr = NULL;
+ char portstr_buf[16];
+ int i;
+
+ if (!erf_priv)
+ return -1;
+
+ erf_priv->implicit_host_id = implicit_host_id;
+
+ /*
+ * We need to update the descriptions of all the interfaces with no Host
+ * ID to the correct Host ID.
+ */
+ g_hash_table_iter_init(&iter, erf_priv->if_map);
+
+ /* Remove the implicit mappings from the mapping table */
+ while (g_hash_table_iter_next(&iter, &iter_value, NULL)) {
+ if_map = (struct erf_if_mapping*) iter_value;
+
+ if (if_map->host_id == ERF_META_HOST_ID_IMPLICIT) {
+ /* Check we don't have an existing interface that matches */
+ if_map_other = erf_find_interface_mapping(erf_priv, implicit_host_id, if_map->source_id);
+
+ if (!if_map_other) {
+ /* Pull mapping for update */
+ /* XXX: Can't add while iterating hash table so use list instead */
+ g_hash_table_iter_steal(&iter);
+ implicit_list = g_list_prepend(implicit_list, if_map);
+ } else {
+ /*
+ * XXX: We have duplicate interfaces in this case, but not much else we
+ * can do since we have already dissected the earlier packets. Expected
+ * to be unusual as it reqires a mix of explicit and implicit Host ID
+ * (e.g. FlowID extension header only) packets with the same effective
+ * Host ID before the first ERF_TYPE_META record.
+ */
+
+ /*
+ * Update the description of the ERF_META_HOST_ID_IMPLICIT interface(s)
+ * for the first records in one pass mode. In 2 pass mode (Wireshark
+ * initial open, TShark in 2 pass mode) we will update the interface
+ * mapping for the frames on the second pass. Relatively consistent
+ * with the dissector behaviour.
+ *
+ * TODO: Can we delete this interface on the second (or even first)
+ * pass? Should we try to merge in other metadata?
+ * Needs a wtap_block_copy() that supports overwriting and/or expose
+ * custom option copy and do with wtap_block_foreach_option().
+ */
+ for (i = 0; i < 4; i++) {
+ if_info = &if_map->interfaces[i];
+
+ if (if_info->if_index >= 0) {
+ /* XXX: this is a pointer! */
+ int_data = g_array_index(wth->interface_data, wtap_block_t, if_info->if_index);
+
+ snprintf(portstr_buf, sizeof(portstr_buf), "Port %c", 'A'+i);
+
+ oldstr = if_info->name;
+ if_info->name = g_strconcat(oldstr ? oldstr : portstr_buf, " [unmatched implicit]", NULL);
+ g_free(oldstr); /* probably null, but g_free doesn't care */
+
+ oldstr = if_info->descr;
+ if_info->descr = g_strconcat(oldstr ? oldstr : portstr_buf, " [unmatched implicit]", NULL);
+ g_free(oldstr);
+
+ erf_set_interface_descr(int_data, OPT_IDB_NAME, implicit_host_id, if_map->source_id, (guint8) i, if_info->name);
+ erf_set_interface_descr(int_data, OPT_IDB_DESCRIPTION, implicit_host_id, if_map->source_id, (guint8) i, if_info->descr);
+ }
+ }
+ }
+ }
+ }
+
+ /* Re-add the non-clashing items under the real implicit Host ID */
+ if (implicit_list) {
+ item = implicit_list;
+ do {
+ if_map = (struct erf_if_mapping*) item->data;
+
+ for (i = 0; i < 4; i++) {
+ if_info = &if_map->interfaces[i];
+
+ if (if_info->if_index >= 0) {
+ /* XXX: this is a pointer! */
+ int_data = g_array_index(wth->interface_data, wtap_block_t, if_info->if_index);
+ erf_set_interface_descr(int_data, OPT_IDB_NAME, implicit_host_id, if_map->source_id, (guint8) i, if_info->name);
+ erf_set_interface_descr(int_data, OPT_IDB_DESCRIPTION, implicit_host_id, if_map->source_id, (guint8) i, if_info->descr);
+ }
+ }
+
+ if_map->host_id = implicit_host_id;
+ /* g_hash_table_add() only exists since 2.32. */
+ g_hash_table_replace(erf_priv->if_map, if_map, if_map);
+ } while ((item = g_list_next(item)));
+
+ g_list_free(implicit_list);
+ implicit_list = NULL;
+ }
+
+ /*
+ * We also need to update the anchor comment mappings
+ * to the correct Host ID.
+ */
+ g_hash_table_iter_init(&iter, erf_priv->anchor_map);
+
+ /* Remove the implicit mappings from the mapping table */
+ while (g_hash_table_iter_next(&iter, &iter_value, NULL)) {
+ anchor_mapping = (struct erf_anchor_mapping*) iter_value;
+
+ if (anchor_mapping->host_id == ERF_META_HOST_ID_IMPLICIT) {
+ /* Check we don't have an existing anchor that matches */
+ anchor_mapping_other = erf_find_anchor_mapping(erf_priv, implicit_host_id,
+ anchor_mapping->anchor_id);
+
+ if (anchor_mapping_other && anchor_mapping_other->gen_time >= anchor_mapping->gen_time) {
+ /*
+ * XXX: Duplicate entry of anchor mapping, keep the one with newer
+ * gen_time.
+ */
+ g_hash_table_iter_remove(&iter);
+ } else {
+ /* Pull mapping for update */
+ /* XXX: Can't add while iterating hash table so use list instead */
+ g_hash_table_iter_steal(&iter);
+ implicit_list = g_list_prepend(implicit_list, anchor_mapping);
+ /* existing entry (if any) will be removed by g_hash_table_replace */
+ }
+ }
+ }
+
+ /* Re-add the non-clashing items under the real implicit Host ID */
+ if (implicit_list) {
+ item = implicit_list;
+ do {
+ anchor_mapping = (struct erf_anchor_mapping*) item->data;
+ anchor_mapping->host_id = implicit_host_id;
+ g_hash_table_replace(erf_priv->anchor_map, anchor_mapping, anchor_mapping);
+ } while ((item = g_list_next(item)));
+
+ g_list_free(implicit_list);
+ implicit_list = NULL;
+ }
+
+ return 0;
+}
+
+static int erf_populate_interface(erf_t *erf_priv, wtap *wth, union wtap_pseudo_header *pseudo_header, guint64 host_id, guint8 source_id, guint8 if_num, int *err, gchar **err_info)
+{
+ wtap_block_t int_data;
+ wtapng_if_descr_mandatory_t* int_data_mand;
+ struct erf_if_mapping* if_map = NULL;
+
+ if (!wth) {
+ *err = WTAP_ERR_INTERNAL;
+ *err_info = ws_strdup_printf("erf: erf_populate_interface called with wth NULL");
+ return -1;
+ }
+ if (!pseudo_header) {
+ *err = WTAP_ERR_INTERNAL;
+ *err_info = ws_strdup_printf("erf: erf_populate_interface called with pseudo_header NULL");
+ return -1;
+ }
+ if (!erf_priv) {
+ *err = WTAP_ERR_INTERNAL;
+ *err_info = ws_strdup_printf("erf: erf_populate_interface called with erf_priv NULL");
+ return -1;
+ }
+ if (if_num > 3) {
+ *err = WTAP_ERR_INTERNAL;
+ *err_info = ws_strdup_printf("erf: erf_populate_interface called with if_num %u > 3",
+ if_num);
+ return -1;
+ }
+
+ if (host_id == ERF_META_HOST_ID_IMPLICIT) {
+ /* Defaults to ERF_META_HOST_ID_IMPLICIT so we can update mapping later */
+ host_id = erf_priv->implicit_host_id;
+ } else if ((pseudo_header->erf.phdr.type & 0x7f) == ERF_TYPE_META) {
+ /*
+ * XXX: We assume there is only one Implicit Host ID. As a special case a first
+ * Host ID extension header with Source ID 0 on a record does not change
+ * the implicit Host ID. We respect this even though we support only one
+ * Implicit Host ID.
+ */
+ if (erf_priv->implicit_host_id == ERF_META_HOST_ID_IMPLICIT && source_id > 0) {
+ erf_update_implicit_host_id(erf_priv, wth, host_id);
+ }
+ }
+
+ if_map = erf_find_interface_mapping(erf_priv, host_id, source_id);
+
+ if (!if_map) {
+ if_map = erf_if_mapping_create(host_id, source_id);
+ /* g_hash_table_add() only exists since 2.32. */
+ g_hash_table_replace(erf_priv->if_map, if_map, if_map);
+
+ }
+
+ /* Return the existing interface if we have it */
+ if (if_map->interfaces[if_num].if_index >= 0) {
+ return if_map->interfaces[if_num].if_index;
+ }
+
+ int_data = wtap_block_create(WTAP_BLOCK_IF_ID_AND_INFO);
+ int_data_mand = (wtapng_if_descr_mandatory_t*)wtap_block_get_mandatory_data(int_data);
+
+ int_data_mand->wtap_encap = WTAP_ENCAP_ERF;
+ /* int_data.time_units_per_second = (1LL<<32); ERF format resolution is 2^-32, capture resolution is unknown */
+ int_data_mand->time_units_per_second = 1000000000; /* XXX Since Wireshark only supports down to nanosecond resolution we have to dilute to this */
+ int_data_mand->tsprecision = WTAP_TSPREC_NSEC;
+ int_data_mand->snap_len = 65535; /* ERF max length */
+
+ /* XXX: if_IPv4addr opt 4 Interface network address and netmask.*/
+ /* XXX: if_IPv6addr opt 5 Interface network address and prefix length (stored in the last byte).*/
+ /* XXX: if_MACaddr opt 6 Interface Hardware MAC address (48 bits).*/
+ /* XXX: if_EUIaddr opt 7 Interface Hardware EUI address (64 bits)*/
+ /* XXX: if_speed opt 8 Interface speed (in bits per second)*/
+ /* int_data.if_tsresol = 0xa0; ERF format resolution is 2^-32 = 0xa0, capture resolution is unknown */
+ wtap_block_add_uint8_option(int_data, OPT_IDB_TSRESOL, 0x09); /* XXX Since Wireshark only supports down to nanosecond resolution we have to dilute to this */
+ /* XXX: if_tzone 10 Time zone for GMT support (TODO: specify better). */
+ /* XXX if_tsoffset; opt 14 A 64 bits integer value that specifies an offset (in seconds)...*/
+ /* Interface statistics */
+ int_data_mand->num_stat_entries = 0;
+ int_data_mand->interface_statistics = NULL;
+
+ erf_set_interface_descr(int_data, OPT_IDB_NAME, host_id, source_id, if_num, NULL);
+ erf_set_interface_descr(int_data, OPT_IDB_DESCRIPTION, host_id, source_id, if_num, NULL);
+
+ if_map->interfaces[if_num].if_index = (int) wth->interface_data->len;
+ wtap_add_idb(wth, int_data);
+
+ return if_map->interfaces[if_num].if_index;
+}
+
+static guint32 erf_meta_read_tag(struct erf_meta_tag* tag, guint8 *tag_ptr, guint32 remaining_len)
+{
+ guint16 tagtype;
+ guint16 taglength;
+ guint32 tagtotallength;
+
+ if (!tag_ptr || !tag || remaining_len < ERF_META_TAG_HEADERLEN)
+ return 0;
+
+ /* tagtype (2 bytes) */
+ tagtype = pntoh16(&tag_ptr[0]);
+
+ /* length (2 bytes) */
+ taglength = pntoh16(&tag_ptr[2]);
+
+ tagtotallength = ERF_META_TAG_TOTAL_ALIGNED_LENGTH(taglength);
+
+ if (remaining_len < tagtotallength) {
+ return 0;
+ }
+
+ tag->type = tagtype;
+ tag->length = taglength;
+ tag->value = &tag_ptr[4];
+
+ return tagtotallength;
+}
+
+static int populate_capture_host_info(erf_t *erf_priv, wtap *wth, union wtap_pseudo_header *pseudo_header _U_, struct erf_meta_read_state *state, int *err, gchar **err_info)
+{
+ struct erf_meta_tag tag = {0, 0, NULL};
+
+ wtap_block_t shb_hdr;
+ char* tmp;
+ gchar* app_name = NULL;
+ gchar* app_version = NULL;
+ gchar* model = NULL;
+ gchar* descr = NULL;
+ gchar* cpu = NULL;
+ gchar* modelcpu = NULL;
+ guint32 tagtotallength;
+
+ if (!wth) {
+ *err = WTAP_ERR_INTERNAL;
+ *err_info = ws_strdup_printf("erf: populate_capture_host_info called with wth NULL");
+ return -1;
+ }
+ if (!state) {
+ *err = WTAP_ERR_INTERNAL;
+ *err_info = ws_strdup_printf("erf: populate_capture_host_info called with state NULL");
+ return -1;
+ }
+ if (!wth->shb_hdrs) {
+ *err = WTAP_ERR_INTERNAL;
+ *err_info = ws_strdup_printf("erf: populate_capture_host_info called with wth->shb_hdrs NULL");
+ return -1;
+ }
+ if (wth->shb_hdrs->len == 0) {
+ *err = WTAP_ERR_INTERNAL;
+ *err_info = ws_strdup_printf("erf: populate_capture_host_info called with wth->shb_hdrs->len 0");
+ return -1;
+ }
+
+ /* XXX: wth->shb_hdr is already created by different layer, using directly for now. */
+ /* XXX: Only one section header is supported at this time */
+ shb_hdr = g_array_index(wth->shb_hdrs, wtap_block_t, 0);
+
+ while ((tagtotallength = erf_meta_read_tag(&tag, state->tag_ptr, state->remaining_len)) && !ERF_META_IS_SECTION(tag.type)) {
+ switch (state->sectiontype) {
+ case ERF_META_SECTION_CAPTURE:
+ {
+ if (erf_priv->capture_gentime > state->gen_time) {
+ return 0;
+ }
+
+ switch (tag.type) {
+ case ERF_META_TAG_comment:
+ {
+ gchar *existing_comment = NULL;
+ /*XXX: hack to make changing capture comment work since Wireshark only
+ * displays one. For now just overwrite the comment as we won't
+ * pick up all of them yet due to the gen_time check above */
+ if (wtap_block_get_nth_string_option_value(shb_hdr, OPT_COMMENT, 0, &existing_comment) == WTAP_OPTTYPE_SUCCESS) {
+ wtap_block_set_nth_string_option_value(shb_hdr, OPT_COMMENT, 0, tag.value, tag.length);
+ } else {
+ wtap_block_add_string_option(shb_hdr, OPT_COMMENT, tag.value, tag.length);
+ }
+ break;
+ }
+ }
+ }
+ /* Fall through */
+ case ERF_META_SECTION_HOST:
+ {
+ if (erf_priv->host_gentime > state->gen_time) {
+ return 0;
+ }
+
+ switch (tag.type) {
+ case ERF_META_TAG_model:
+ g_free(model);
+ model = g_strndup((gchar*) tag.value, tag.length);
+ break;
+ case ERF_META_TAG_cpu:
+ g_free(cpu);
+ cpu = g_strndup((gchar*) tag.value, tag.length);
+ break;
+ case ERF_META_TAG_descr:
+ g_free(descr);
+ descr = g_strndup((gchar*) tag.value, tag.length);
+ break;
+ case ERF_META_TAG_os:
+ wtap_block_set_string_option_value(shb_hdr, OPT_SHB_OS, tag.value, tag.length);
+ break;
+ case ERF_META_TAG_app_name:
+ g_free(app_name);
+ app_name = g_strndup((gchar*) tag.value, tag.length);
+ break;
+ case ERF_META_TAG_app_version:
+ g_free(app_version);
+ app_version = g_strndup((gchar*) tag.value, tag.length);
+ break;
+ /* TODO: dag_version? */
+ /* TODO: could concatenate comment(s)? */
+ case ERF_META_TAG_filter:
+ g_free(state->if_map->capture_filter_str);
+ state->if_map->capture_filter_str = g_strndup((gchar*) tag.value, tag.length);
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+ }
+
+ state->tag_ptr += tagtotallength;
+ state->remaining_len -= tagtotallength;
+ }
+
+ /* Post processing */
+
+ if (app_name || app_version) {
+ /*
+ * If we have no app_name, we use "(Unknown applicaton)".
+ *
+ * If we have no app_version, this will just use app_name.
+ */
+ tmp = g_strjoin(" ", app_name ? app_name : "(Unknown application)", app_version, NULL);
+ wtap_block_set_string_option_value(shb_hdr, OPT_SHB_USERAPPL, tmp, strlen(tmp));
+ g_free(tmp);
+
+ g_free(app_name);
+ g_free(app_version);
+ app_name = NULL;
+ app_version = NULL;
+ }
+
+ /* For the hardware field show description followed by (model; cpu) */
+ /* Build "Model; CPU" part */
+ if (model || cpu) {
+ /* g_strjoin() would be nice to use here if the API didn't stop on the first NULL... */
+ if (model && cpu) {
+ modelcpu = g_strconcat(model, "; ", cpu, NULL);
+ } else if (cpu) {
+ modelcpu = cpu;
+ /* avoid double-free */
+ cpu = NULL;
+ } else {
+ modelcpu = model;
+ /* avoid double-free */
+ model = NULL;
+ }
+ }
+
+ /* Combine into "Description (Model; CPU)" */
+ if (state->sectiontype == ERF_META_SECTION_HOST && descr) {
+ if (modelcpu) {
+ wtap_block_set_string_option_value_format(shb_hdr, OPT_SHB_HARDWARE, "%s (%s)", descr, modelcpu);
+ } else {
+ wtap_block_set_string_option_value(shb_hdr, OPT_SHB_HARDWARE, descr, strlen(descr));
+ /*descr = NULL;*/
+ }
+ } else if (modelcpu) {
+ wtap_block_set_string_option_value(shb_hdr, OPT_SHB_HARDWARE, modelcpu, strlen(modelcpu));
+ /*modelcpu = NULL;*/
+ }
+
+ /* Free the fields we didn't end up using */
+ g_free(modelcpu);
+ g_free(model);
+ g_free(descr);
+ g_free(cpu);
+
+ if (state->sectiontype == ERF_META_SECTION_CAPTURE) {
+ erf_priv->capture_gentime = state->gen_time;
+ } else {
+ erf_priv->host_gentime = state->gen_time;
+ }
+
+ return 1;
+}
+
+static int populate_module_info(erf_t *erf_priv _U_, wtap *wth, union wtap_pseudo_header *pseudo_header _U_, struct erf_meta_read_state *state, int *err, gchar **err_info)
+{
+ struct erf_meta_tag tag = {0, 0, NULL};
+
+ guint32 tagtotallength;
+
+ if (!wth) {
+ *err = WTAP_ERR_INTERNAL;
+ *err_info = ws_strdup_printf("erf: populate_module_info called with wth NULL");
+ return -1;
+ }
+ if (!state) {
+ *err = WTAP_ERR_INTERNAL;
+ *err_info = ws_strdup_printf("erf: populate_module_info called with stat NULL");
+ return -1;
+ }
+
+ if (state->if_map->module_gentime > state->gen_time) {
+ return 0;
+ }
+
+ while ((tagtotallength = erf_meta_read_tag(&tag, state->tag_ptr, state->remaining_len)) && !ERF_META_IS_SECTION(tag.type)) {
+ switch (tag.type) {
+ case ERF_META_TAG_fcs_len:
+ if (tag.length >= 4) {
+ state->if_map->module_fcs_len = (gint8) pntoh32(tag.value);
+ }
+ break;
+ case ERF_META_TAG_snaplen:
+ /* XXX: this is generally per stream */
+ if (tag.length >= 4) {
+ state->if_map->module_snaplen = pntoh32(tag.value);
+ }
+ break;
+ case ERF_META_TAG_filter:
+ g_free(state->if_map->module_filter_str);
+ state->if_map->module_filter_str = g_strndup((gchar*) tag.value, tag.length);
+ break;
+ }
+
+ state->tag_ptr += tagtotallength;
+ state->remaining_len -= tagtotallength;
+ }
+
+ state->if_map->module_gentime = state->gen_time;
+
+ return 1;
+}
+
+static int populate_interface_info(erf_t *erf_priv, wtap *wth, union wtap_pseudo_header *pseudo_header, struct erf_meta_read_state *state, int *err, gchar **err_info)
+{
+ struct erf_meta_tag tag = {0, 0, NULL};
+ guint32 tagtotallength;
+ int interface_index = -1;
+ wtap_block_t int_data = NULL;
+ wtapng_if_descr_mandatory_t* int_data_mand = NULL;
+ if_filter_opt_t if_filter;
+ guint32 if_num = 0;
+ struct erf_if_info* if_info = NULL;
+
+ if (!wth) {
+ *err = WTAP_ERR_INTERNAL;
+ *err_info = ws_strdup_printf("erf: populate_interface_info called with wth NULL");
+ return -1;
+ }
+ if (!state) {
+ *err = WTAP_ERR_INTERNAL;
+ *err_info = ws_strdup_printf("erf: populate_interface_info called with state NULL");
+ return -1;
+ }
+ if (!pseudo_header) {
+ *err = WTAP_ERR_INTERNAL;
+ *err_info = ws_strdup_printf("erf: populate_interface_info called with pseudo_header NULL");
+ return -1;
+ }
+ if (!state->if_map) {
+ *err = WTAP_ERR_INTERNAL;
+ *err_info = ws_strdup_printf("erf: populate_interface_info called with state->if_map NULL");
+ return -1;
+ }
+
+ /* Section ID of interface is defined to match ERF interface id. */
+ if_num = state->sectionid - 1;
+ /*
+ * Get or create the interface (there can be multiple interfaces in
+ * a Provenance record).
+ */
+ if (if_num < 4) { /* Note: -1u > 4*/
+ if_info = &state->if_map->interfaces[if_num];
+ interface_index = if_info->if_index;
+
+ /* Check if the interface information is still uninitialized */
+ if (interface_index == -1) {
+ guint8 *tag_ptr_tmp = state->tag_ptr;
+ guint32 remaining_len_tmp = state->remaining_len;
+
+ /* First iterate tags, checking we aren't looking at a timing port */
+ /*
+ * XXX: we deliberately only do this logic here rather than the per-packet
+ * population function so that if somehow we do see packets for an
+ * 'invalid' port the interface will be created at that time.
+ */
+ while ((tagtotallength = erf_meta_read_tag(&tag, tag_ptr_tmp, remaining_len_tmp)) && !ERF_META_IS_SECTION(tag.type)) {
+ if (tag.type == ERF_META_TAG_if_port_type) {
+ if (tag.length >= 4 && pntoh32(tag.value) == 2) {
+ /* This is a timing port, skip it from now on */
+ /* XXX: should we skip all non-capture ports instead? */
+
+ if_info->if_index = -2;
+ interface_index = -2;
+ }
+ } else if (tag.type == ERF_META_TAG_stream_num) {
+ if (tag.length >= 4) {
+ if_info->stream_num = (gint32) pntoh32(tag.value);
+ }
+ }
+
+ tag_ptr_tmp += tagtotallength;
+ remaining_len_tmp -= tagtotallength;
+ }
+
+ /* If the interface is valid but uninitialized, create it */
+ if (interface_index == -1) {
+ interface_index = erf_populate_interface(erf_priv, wth, pseudo_header, state->if_map->host_id, state->if_map->source_id, (guint8) if_num, err, err_info);
+ if (interface_index == -1) {
+ return -1;
+ }
+ }
+ }
+
+ /* Get the wiretap interface metadata */
+ if (interface_index >= 0) {
+ int_data = g_array_index(wth->interface_data, wtap_block_t, interface_index);
+ int_data_mand = (wtapng_if_descr_mandatory_t*)wtap_block_get_mandatory_data(int_data);
+ } else if (interface_index == -2) {
+ /* timing/unknown port */
+ return 0;
+ } else {
+ *err = WTAP_ERR_INTERNAL;
+ *err_info = ws_strdup_printf("erf: populate_interface_info got interface_index %d < 0 and != -2", interface_index);
+ return -1;
+ }
+ }
+
+ /*
+ * Bail if already have interface metadata or no interface to associate with.
+ * We also don't support metadata for >4 interfaces per Host + Source
+ * as we only use interface ID.
+ */
+ if (!int_data)
+ return 0;
+
+ if (state->if_map->interface_gentime > state->gen_time && state->if_map->interface_metadata & (1 << if_num))
+ return 0;
+
+ while ((tagtotallength = erf_meta_read_tag(&tag, state->tag_ptr, state->remaining_len)) && !ERF_META_IS_SECTION(tag.type)) {
+ switch (tag.type) {
+ case ERF_META_TAG_name:
+ /* TODO: fall back to module "dev_name Port N"? */
+ if (!if_info->name) {
+ if_info->name = g_strndup((gchar*) tag.value, tag.length);
+ erf_set_interface_descr(int_data, OPT_IDB_NAME, state->if_map->host_id, state->if_map->source_id, (guint8) if_num, if_info->name);
+
+ /* If we have no description, also copy to wtap if_description */
+ if (!if_info->descr) {
+ erf_set_interface_descr(int_data, OPT_IDB_DESCRIPTION, state->if_map->host_id, state->if_map->source_id, (guint8) if_num, if_info->name);
+ }
+ }
+ break;
+ case ERF_META_TAG_descr:
+ if (!if_info->descr) {
+ if_info->descr = g_strndup((gchar*) tag.value, tag.length);
+ erf_set_interface_descr(int_data, OPT_IDB_DESCRIPTION, state->if_map->host_id, state->if_map->source_id, (guint8) if_num, if_info->descr);
+
+ /* If we have no name, also copy to wtap if_name */
+ if (!if_info->name) {
+ erf_set_interface_descr(int_data, OPT_IDB_NAME, state->if_map->host_id, state->if_map->source_id, (guint8) if_num, if_info->descr);
+ }
+ }
+ break;
+ case ERF_META_TAG_if_speed:
+ if (tag.length >= 8)
+ wtap_block_add_uint64_option(int_data, OPT_IDB_SPEED, pntoh64(tag.value));
+ break;
+ case ERF_META_TAG_if_num:
+ /*
+ * XXX: We ignore this as Section ID must match the ERF ifid and
+ * that is all we care about/have space for at the moment. if_num
+ * is only really useful with >4 interfaces.
+ */
+ /* TODO: might want to put this number in description */
+ break;
+ case ERF_META_TAG_fcs_len:
+ if (tag.length >= 4) {
+ wtap_block_add_uint8_option(int_data, OPT_IDB_FCSLEN, (guint8) pntoh32(tag.value));
+ if_info->set_flags.fcs_len = 1;
+ }
+ break;
+ case ERF_META_TAG_snaplen:
+ /* XXX: this generally per stream */
+ if (tag.length >= 4) {
+ int_data_mand->snap_len = pntoh32(tag.value);
+ if_info->set_flags.snaplen = 1;
+ }
+ break;
+ case ERF_META_TAG_comment:
+ wtap_block_add_string_option(int_data, OPT_COMMENT, tag.value, tag.length);
+ break;
+ case ERF_META_TAG_filter:
+ if_filter.type = if_filter_pcap;
+ if_filter.data.filter_str = g_strndup((gchar*) tag.value, tag.length);
+ wtap_block_add_if_filter_option(int_data, OPT_IDB_FILTER, &if_filter);
+ g_free(if_filter.data.filter_str);
+ if_info->set_flags.filter = 1;
+ break;
+ default:
+ break;
+ }
+
+ state->tag_ptr += tagtotallength;
+ state->remaining_len -= tagtotallength;
+ }
+
+ /* Post processing */
+ /*
+ * XXX: Assumes module defined first. It is higher in hierarchy so only set
+ * if not already.
+ */
+
+ /*
+ * XXX: Missing exposed existence/type-check. No way currently to check if
+ * been set in the optionblock.
+ */
+ if (!if_info->set_flags.filter) {
+ if (state->if_map->module_filter_str) {
+ /* Duplicate because might use with multiple interfaces */
+ if_filter.type = if_filter_pcap;
+ if_filter.data.filter_str = state->if_map->module_filter_str;
+ wtap_block_add_if_filter_option(int_data, OPT_IDB_FILTER, &if_filter);
+ /*
+ * Don't set flag because stream is more specific than module.
+ */
+ } else if (state->if_map->capture_filter_str) {
+ /* TODO: display separately? Note that we could have multiple captures
+ * from multiple hosts in the file */
+ if_filter.type = if_filter_pcap;
+ if_filter.data.filter_str = state->if_map->capture_filter_str;
+ wtap_block_add_if_filter_option(int_data, OPT_IDB_FILTER, &if_filter);
+ }
+ }
+
+ if (state->if_map->module_fcs_len != -1 && !if_info->set_flags.fcs_len) {
+ wtap_block_add_uint8_option(int_data, OPT_IDB_FCSLEN, (guint8) state->if_map->module_fcs_len);
+ if_info->set_flags.fcs_len = 1;
+ }
+
+ if (state->if_map->module_snaplen != (guint32) -1 && !if_info->set_flags.snaplen) {
+ int_data_mand->snap_len = pntoh32(tag.value);
+ if_info->set_flags.snaplen = 1;
+ }
+
+ state->interface_metadata |= 1 << if_num;
+
+ return 1;
+}
+
+static int populate_stream_info(erf_t *erf_priv _U_, wtap *wth, union wtap_pseudo_header *pseudo_header, struct erf_meta_read_state *state, int *err, gchar **err_info)
+{
+ struct erf_meta_tag tag = {0, 0, NULL};
+ guint32 tagtotallength;
+ int interface_index = -1;
+ wtap_block_t int_data = NULL;
+ wtapng_if_descr_mandatory_t* int_data_mand = NULL;
+ if_filter_opt_t if_filter;
+ guint32 if_num = 0;
+ gint32 stream_num = -1;
+ guint8 *tag_ptr_tmp;
+ guint32 remaining_len_tmp;
+ struct erf_if_info* if_info = NULL;
+
+ if (!wth) {
+ *err = WTAP_ERR_INTERNAL;
+ *err_info = ws_strdup_printf("erf: populate_stream_info called with wth NULL");
+ return -1;
+ }
+ if (!pseudo_header) {
+ *err = WTAP_ERR_INTERNAL;
+ *err_info = ws_strdup_printf("erf: populate_stream_info called with pseudo_header NULL");
+ return -1;
+ }
+ if (!state) {
+ *err = WTAP_ERR_INTERNAL;
+ *err_info = ws_strdup_printf("erf: populate_stream_info called with state NULL");
+ return -1;
+ }
+ if (!state->if_map) {
+ *err = WTAP_ERR_INTERNAL;
+ *err_info = ws_strdup_printf("erf: populate_stream_info called with state->if_map NULL");
+ return -1;
+ }
+
+ tag_ptr_tmp = state->tag_ptr;
+ remaining_len_tmp = state->remaining_len;
+
+ /*
+ * XXX: We ignore parent section ID because it doesn't represent the
+ * many-to-many relationship of interfaces and streams very well. The stream is
+ * associated with all interfaces in the record that don't have a stream_num
+ * that says otherwise.
+ */
+
+ if (state->sectionid > 0 && state->sectionid != 0x7fff) {
+ /* Section ID of stream is supposed to match stream_num. */
+ stream_num = state->sectionid - 1;
+ } else {
+ /* First iterate tags, looking for the stream number interfaces might associate with. */
+ while ((tagtotallength = erf_meta_read_tag(&tag, tag_ptr_tmp, remaining_len_tmp)) && !ERF_META_IS_SECTION(tag.type)) {
+ if (tag.type == ERF_META_TAG_stream_num) {
+ if (tag.length >= 4) {
+ stream_num = (gint32) pntoh32(tag.value);
+ }
+ }
+
+ tag_ptr_tmp += tagtotallength;
+ remaining_len_tmp -= tagtotallength;
+ }
+ }
+ /* Otherwise assume the stream applies to all interfaces in the record */
+
+ for (if_num = 0; if_num < 4; if_num++) {
+ tag_ptr_tmp = state->tag_ptr;
+ remaining_len_tmp = state->remaining_len;
+ if_info = &state->if_map->interfaces[if_num];
+
+ /* Check if we should be handling this interface */
+ /* XXX: currently skips interfaces that are not in the record. */
+ if (state->if_map->interface_metadata & (1 << if_num)
+ || !(state->interface_metadata & (1 << if_num))) {
+ continue;
+ }
+
+ if (if_info->stream_num != -1
+ && if_info->stream_num != stream_num) {
+ continue;
+ }
+
+ interface_index = if_info->if_index;
+ /* Get the wiretap interface metadata */
+ if (interface_index >= 0) {
+ int_data = g_array_index(wth->interface_data, wtap_block_t, interface_index);
+ int_data_mand = (wtapng_if_descr_mandatory_t*)wtap_block_get_mandatory_data(int_data);
+ }
+
+ if (!int_data) {
+ continue;
+ }
+
+ while ((tagtotallength = erf_meta_read_tag(&tag, tag_ptr_tmp, remaining_len_tmp)) && !ERF_META_IS_SECTION(tag.type)) {
+ switch (tag.type) {
+ case ERF_META_TAG_fcs_len:
+ if (tag.length >= 4) {
+ /* Use the largest fcslen of matching streams */
+ gint8 fcs_len = (gint8) pntoh32(tag.value);
+ guint8 old_fcs_len = 0;
+
+ switch (wtap_block_get_uint8_option_value(int_data, OPT_IDB_FCSLEN, &old_fcs_len)) {
+
+ case WTAP_OPTTYPE_SUCCESS:
+ /* We already have an FCS length option; update it. */
+ if (fcs_len > old_fcs_len || !if_info->set_flags.fcs_len) {
+ wtap_block_set_uint8_option_value(int_data, OPT_IDB_FCSLEN, (guint8) pntoh32(tag.value));
+ if_info->set_flags.fcs_len = 1;
+ }
+ break;
+
+ case WTAP_OPTTYPE_NOT_FOUND:
+ /* We don't have an FCS length option; add it. */
+ wtap_block_add_uint8_option(int_data, OPT_IDB_FCSLEN, (guint8) pntoh32(tag.value));
+ if_info->set_flags.fcs_len = 1;
+ break;
+
+ default:
+ /* "shouldn't happen" */
+ break;
+ }
+ }
+ break;
+ case ERF_META_TAG_snaplen:
+ if (tag.length >= 4) {
+ /* Use the largest snaplen of matching streams */
+ guint32 snaplen = pntoh32(tag.value);
+
+ if (snaplen > int_data_mand->snap_len || !if_info->set_flags.snaplen) {
+ int_data_mand->snap_len = pntoh32(tag.value);
+ if_info->set_flags.snaplen = 1;
+ }
+ }
+ break;
+ case ERF_META_TAG_filter:
+ /* Override only if not set */
+ if (!if_info->set_flags.filter) {
+ if_filter.type = if_filter_pcap;
+ if_filter.data.filter_str = g_strndup((gchar*) tag.value, tag.length);
+ wtap_block_add_if_filter_option(int_data, OPT_IDB_FILTER, &if_filter);
+ g_free(if_filter.data.filter_str);
+ if_info->set_flags.filter = 1;
+ }
+ break;
+ default:
+ break;
+ }
+
+ tag_ptr_tmp += tagtotallength;
+ remaining_len_tmp -= tagtotallength;
+ }
+ }
+ state->tag_ptr = tag_ptr_tmp;
+ state->remaining_len = remaining_len_tmp;
+
+ return 1;
+}
+
+static int populate_anchor_info(erf_t *erf_priv, wtap *wth, union wtap_pseudo_header *pseudo_header, struct erf_meta_read_state *state, GPtrArray *anchor_mappings_to_update, int *err, gchar **err_info) {
+ struct erf_meta_tag tag = {0, 0, NULL};
+ guint32 tagtotallength;
+ gchar *comment_ptr = NULL;
+ guint i = 0;
+
+ if (!wth) {
+ *err = WTAP_ERR_INTERNAL;
+ *err_info = ws_strdup_printf("erf: populate_anchor_info called with wth NULL");
+ return -1;
+ }
+ if (!state) {
+ *err = WTAP_ERR_INTERNAL;
+ *err_info = ws_strdup_printf("erf: populate_anchor_info called with state NULL");
+ return -1;
+ }
+ if (!pseudo_header) {
+ *err = WTAP_ERR_INTERNAL;
+ *err_info = ws_strdup_printf("erf: populate_anchor_info called with pseudo_header NULL");
+ return -1;
+ }
+
+ if (!anchor_mappings_to_update || anchor_mappings_to_update->len == 0)
+ return 0;
+
+ while ((tagtotallength = erf_meta_read_tag(&tag, state->tag_ptr, state->remaining_len)) && !ERF_META_IS_SECTION(tag.type)) {
+ /* XXX:Always gets the first comment tag in the section */
+ switch(tag.type) {
+ case ERF_META_TAG_comment:
+ if(!comment_ptr) {
+ comment_ptr = g_strndup((gchar*)tag.value, tag.length);
+ }
+ break;
+ default:
+ break;
+ }
+
+ state->tag_ptr += tagtotallength;
+ state->remaining_len -= tagtotallength;
+ }
+
+ if(comment_ptr) {
+ for(i = 0; i < anchor_mappings_to_update->len; i++) {
+ struct erf_anchor_mapping *mapping;
+ struct erf_anchor_mapping *lookup_result;
+
+ mapping = (struct erf_anchor_mapping*)g_ptr_array_index(anchor_mappings_to_update, i);
+ lookup_result = (struct erf_anchor_mapping*)g_hash_table_lookup(erf_priv->anchor_map, mapping);
+
+ /* Use the most recent comment, across all anchors associated with the
+ * record. */
+ if(lookup_result) {
+ if(lookup_result->gen_time < state->gen_time) {
+ lookup_result->gen_time = state->gen_time;
+ g_free(lookup_result->comment);
+ lookup_result->comment = g_strdup(comment_ptr);
+ }
+ }
+ else {
+ /* !lookup_result */
+ struct erf_anchor_mapping *new_mapping;
+ new_mapping = g_new0(struct erf_anchor_mapping, 1);
+ new_mapping->anchor_id = mapping->anchor_id;
+ new_mapping->host_id = mapping->host_id;
+ new_mapping->gen_time = state->gen_time;
+ new_mapping->comment = g_strdup(comment_ptr);
+ g_hash_table_replace(erf_priv->anchor_map, new_mapping, new_mapping);
+ }
+ }
+ }
+
+ g_free(comment_ptr);
+
+ return 1;
+}
+
+/* Populates the capture and interface information for display on the Capture File Properties */
+static int populate_summary_info(erf_t *erf_priv, wtap *wth, union wtap_pseudo_header *pseudo_header, Buffer *buf, guint32 packet_size, GPtrArray *anchor_mappings_to_update, int *err, gchar **err_info)
+{
+ struct erf_meta_read_state state = {0};
+ struct erf_meta_read_state *state_post = NULL;
+ guint64 host_id;
+ guint8 source_id;
+ GList *post_list = NULL;
+ GList *item = NULL;
+
+ struct erf_meta_tag tag = {0, 0, NULL};
+ guint32 tagtotallength;
+
+ if (!wth) {
+ *err = WTAP_ERR_INTERNAL;
+ *err_info = ws_strdup_printf("erf: populate_summary_info called with wth NULL");
+ return -1;
+ }
+ if (!pseudo_header) {
+ *err = WTAP_ERR_INTERNAL;
+ *err_info = ws_strdup_printf("erf: populate_summary_info called with pseudo_header NULL");
+ return -1;
+ }
+ if (!erf_priv) {
+ *err = WTAP_ERR_INTERNAL;
+ *err_info = ws_strdup_printf("erf: populate_summary_info called with erf_priv NULL");
+ return -1;
+ }
+
+ erf_get_source_from_header(pseudo_header, &host_id, &source_id);
+
+ if (host_id == 0) {
+ host_id = erf_priv->implicit_host_id;
+ }
+
+ state.if_map = erf_find_interface_mapping(erf_priv, host_id, source_id);
+
+ if (!state.if_map) {
+ state.if_map = erf_if_mapping_create(host_id, source_id);
+ /* g_hash_table_add() only exists since 2.32. */
+ g_hash_table_replace(erf_priv->if_map, state.if_map, state.if_map);
+
+ }
+
+
+ state.tag_ptr = buf->data;
+ state.remaining_len = packet_size;
+
+ /* Read until see next section tag */
+ while ((tagtotallength = erf_meta_read_tag(&tag, state.tag_ptr, state.remaining_len))) {
+ /*
+ * Obtain the gen_time from the non-section at the beginning of the record
+ */
+ if (!ERF_META_IS_SECTION(tag.type)) {
+ if(state.gen_time == 0U
+ && tag.type == ERF_META_TAG_gen_time
+ ) {
+ memcpy(&state.gen_time, tag.value, sizeof(state.gen_time));
+
+ /*
+ * Since wireshark doesn't have a concept of different summary metadata
+ * over time, skip the record if metadata is older than what we already have.
+ */
+ /* TODO: This doesn't work very well for some tags that map to
+ * pcapng options where the pcapng specification only allows one
+ * instance per block, which is the case for most options. The
+ * only current exxceptions are:
+ *
+ * comments;
+ * IPv4 and IPv6 addresses for an interface;
+ * hash values for a packet;
+ * custom options.
+ *
+ * For options where only one instance is allowed per block,
+ * wtap_block_add_XXX_option() is currently used to add a new
+ * instance of an option to a block that has no instance (it
+ * fails if there's already an instance), and
+ * wtap_block_set_XXX_optin() is currently used to change the
+ * value of an option in a block that has one instance (it fails
+ * if there isn't already an instance).
+ *
+ * For options where more than one instance is allowed per block,
+ * wtap_block_add_XXX_option() is used to add a new instance to
+ * a block, no matter how many instances it currently has, and
+ * wtap_block_set_nth_XXX_option() is used to change the value
+ * of the Nth instance of an option in a block (the block must
+ * *have* an Nth instance).
+ *
+ * Currently we only particularly care about updating the capture comment
+ * and a few counters anyway.
+ */
+ if ((state.if_map->interface_metadata & 0x03)
+ && state.gen_time < erf_priv->host_gentime && state.gen_time < erf_priv->capture_gentime
+ && (!anchor_mappings_to_update || !anchor_mappings_to_update->len)) {
+ return 0;
+ }
+ }
+ /*
+ * Skip until we get to the next section tag (which could be the current tag
+ * after an empty section or successful parsing).
+ */
+ /* adjust offset */
+ state.tag_ptr += tagtotallength;
+ state.remaining_len -= tagtotallength;
+ continue;
+ }
+
+ /*
+ * We are now looking at the next section (and would have exited the loop
+ * if we reached the end).
+ */
+
+ /* Update parent section. Implicit grouping is by a change in section except Interface and Stream. */
+ if (tag.type != state.sectiontype) {
+ if ((tag.type == ERF_META_SECTION_STREAM && state.sectiontype == ERF_META_SECTION_INTERFACE) ||
+ (tag.type == ERF_META_SECTION_INTERFACE && state.sectiontype == ERF_META_SECTION_STREAM)) {
+ /* do nothing */
+ } else {
+ state.parentsectiontype = state.sectiontype;
+ state.parentsectionid = state.sectionid;
+ }
+ }
+
+ /* Update with new sectiontype */
+ state.sectiontype = tag.type;
+ if (tag.length >= 4) {
+ state.sectionid = pntoh16(tag.value);
+ } else {
+ state.sectionid = 0;
+ }
+
+ /* Adjust offset to that of first tag in section */
+ state.tag_ptr += tagtotallength;
+ state.remaining_len -= tagtotallength;
+
+ if (erf_meta_read_tag(&tag, state.tag_ptr, state.remaining_len)) {
+ /*
+ * Process parent section tag if present (which must be the first tag in
+ * the section).
+ */
+ if (tag.type == ERF_META_TAG_parent_section && tag.length >= 4) {
+ state.parentsectiontype = pntoh16(tag.value);
+ state.parentsectionid = pntoh16(&tag.value[2]);
+ }
+ }
+
+ /* Skip empty sections (includes if above read fails) */
+ if (ERF_META_IS_SECTION(tag.type)) {
+ continue;
+ }
+
+ /*
+ * Skip sections that don't apply to the general set of records
+ * (extension point for per-packet/event metadata).
+ * Unless we need to update the anchor info
+ * in which case, read into it
+ */
+ if (state.sectionid & 0x8000) {
+ if(state.sectiontype & (ERF_META_SECTION_INFO)) {
+ /* TODO: do we care if it returns 0 or 1? */
+ if (populate_anchor_info(erf_priv, wth, pseudo_header, &state, anchor_mappings_to_update, err, err_info) < 0) {
+ return -1;
+ }
+ }
+ continue;
+ }
+
+ /*
+ * Start at first tag in section, makes loop
+ * simpler in called functions too. Also makes iterating after failure
+ * much simpler.
+ */
+ switch (state.sectiontype) {
+ case ERF_META_SECTION_CAPTURE:
+ case ERF_META_SECTION_HOST:
+ /* TODO: do we care if it returns 0 or 1? */
+ if (populate_capture_host_info(erf_priv, wth, pseudo_header, &state, err, err_info) < 0) {
+ return -1;
+ }
+ break;
+ case ERF_META_SECTION_MODULE:
+ /* TODO: do we care if it returns 0 or 1? */
+ if (populate_module_info(erf_priv, wth, pseudo_header, &state, err, err_info) < 0) {
+ return -1;
+ }
+ break;
+ case ERF_META_SECTION_INTERFACE:
+ /* TODO: do we care if it returns 0 or 1? */
+ if (populate_interface_info(erf_priv, wth, pseudo_header, &state, err, err_info) < 0) {
+ return -1;
+ }
+ break;
+ case ERF_META_SECTION_STREAM:
+ /*
+ * XXX: Treat streams specially in case the stream information appears
+ * before the interface information, as we associate them to interface
+ * data.
+ */
+ post_list = g_list_append(post_list, g_memdup2(&state, sizeof(struct erf_meta_read_state)));
+ break;
+ case ERF_META_SECTION_SOURCE:
+ case ERF_META_SECTION_DNS:
+ default:
+ /* TODO: Not yet implemented */
+ break;
+ }
+ }
+
+ /* Process streams last */
+ if (post_list) {
+ item = post_list;
+ do {
+ state_post = (struct erf_meta_read_state*) item->data;
+ switch (state_post->sectiontype) {
+ case ERF_META_SECTION_STREAM:
+ if (populate_stream_info(erf_priv, wth, pseudo_header, state_post, err, err_info) < 0) {
+ g_list_foreach(post_list, erf_free_data, NULL);
+ g_list_free(post_list);
+ return -1;
+ }
+ break;
+ }
+ } while ((item = g_list_next(item)));
+ /* g_list_free_full() only exists since 2.28. */
+ g_list_foreach(post_list, erf_free_data, NULL);
+ g_list_free(post_list);
+ }
+
+ /*
+ * Update known metadata so we only examine the first set of metadata. Need to
+ * do this here so can have interface and stream in same record.
+ */
+ if (state.interface_metadata) {
+ state.if_map->interface_metadata |= state.interface_metadata;
+ state.if_map->interface_gentime = state.gen_time;
+ }
+
+ return 0;
+}
+
+static gboolean get_user_comment_string(wtap_dumper *wdh, gchar** user_comment_ptr) {
+ wtap_block_t wtap_block;
+ wtap_opttype_return_val ret;
+
+ wtap_block = NULL;
+
+ if(wdh->shb_hdrs && (wdh->shb_hdrs->len > 0)) {
+ wtap_block = g_array_index(wdh->shb_hdrs, wtap_block_t, 0);
+ }
+
+ if(wtap_block != NULL) {
+ ret = wtap_block_get_nth_string_option_value(wtap_block, OPT_COMMENT, 0, user_comment_ptr);
+ if(ret != WTAP_OPTTYPE_SUCCESS) {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+static gboolean erf_dump_priv_compare_capture_comment(wtap_dumper *wdh _U_, erf_dump_t *dump_priv, const union wtap_pseudo_header *pseudo_header, const guint8 *pd){
+ struct erf_meta_read_state state = {0};
+ struct erf_meta_tag tag = {0, 0, NULL};
+ guint32 tagtotallength;
+ gboolean found_capture_section = FALSE;
+ gboolean found_normal_section = FALSE;
+ gchar* comment_ptr = NULL;
+
+ state.remaining_len = pseudo_header->erf.phdr.wlen;
+ memcpy(&(state.tag_ptr), &pd, sizeof(pd));
+
+ while((tagtotallength = erf_meta_read_tag(&tag, state.tag_ptr, state.remaining_len))) {
+ if (ERF_META_IS_SECTION(tag.type)) {
+ state.sectiontype = tag.type;
+ if (tag.length >= 4) {
+ state.sectionid = pntoh16(tag.value);
+ } else {
+ state.sectionid = 0;
+ }
+
+ /* Skip sections that don't apply to the general set of records */
+ if (!(state.sectionid & 0x8000)) {
+ found_normal_section = TRUE;
+
+ if(tag.type == ERF_META_SECTION_CAPTURE) {
+ /* Found the Capture Section */
+ found_capture_section = TRUE;
+ }
+ }
+ } else {
+ if (state.sectiontype == ERF_META_SECTION_CAPTURE && !(state.sectionid & 0x8000)) {
+ if (tag.type == ERF_META_TAG_comment) {
+ /* XXX: Only compare the first comment tag */
+ if(!comment_ptr) {
+ comment_ptr = g_strndup((char*)tag.value, tag.length);
+ }
+ break;
+ }
+ }
+ }
+
+ /* Read until we have the Capture section */
+ state.tag_ptr += tagtotallength;
+ state.remaining_len -= tagtotallength;
+ }
+
+ if(found_capture_section && (comment_ptr || dump_priv->user_comment_ptr)) {
+ if(g_strcmp0(comment_ptr, dump_priv->user_comment_ptr)
+ && !(dump_priv->user_comment_ptr == NULL && comment_ptr && comment_ptr[0] == '\0')) {
+ /* Also treat "" in ERF as equivalent to NULL as that is how we clear the comment on write. */
+
+ /* Comments are different, we should write extra metadata record at the end of the list */
+ dump_priv->write_next_extra_meta = TRUE;
+ g_free(comment_ptr);
+ return TRUE;
+ } else {
+ /* We have a capture comment but there is no change, we don't
+ * need to insert the 'changed' comment. This most likely happened
+ * because we were looking at list of periodic records and got up to the
+ * one where the comment was last set. */
+ dump_priv->write_next_extra_meta = FALSE;
+ }
+ /* Otherwise no effect on whether we need to write extra metadata record */
+ }
+ /* We didn't find a capture section (e.g. looking at a comment Anchor
+ * record), or the comment hadn't changed. */
+
+ g_free(comment_ptr);
+ /* Return whether we found any non-local metadata (i.e. whether the record has
+ * metadata that is more than just packet 'comments') */
+ return found_normal_section;
+}
+
+static void erf_close(wtap *wth)
+{
+ erf_t* erf_priv = (erf_t*)wth->priv;
+
+ erf_priv_free(erf_priv);
+ /* XXX: Prevent double free by wtap_close() */
+ wth->priv = NULL;
+}
+
+static const struct supported_option_type section_block_options_supported[] = {
+ { OPT_COMMENT, ONE_OPTION_SUPPORTED }, /* XXX - multiple? */
+ { OPT_SHB_USERAPPL, ONE_OPTION_SUPPORTED }
+};
+
+static const struct supported_option_type interface_block_options_supported[] = {
+ { OPT_COMMENT, ONE_OPTION_SUPPORTED }, /* XXX - multiple? */
+ { OPT_IDB_NAME, ONE_OPTION_SUPPORTED },
+ { OPT_IDB_DESCRIPTION, ONE_OPTION_SUPPORTED },
+ { OPT_IDB_OS, ONE_OPTION_SUPPORTED },
+ { OPT_IDB_TSOFFSET, ONE_OPTION_SUPPORTED },
+ { OPT_IDB_SPEED, ONE_OPTION_SUPPORTED },
+ { OPT_IDB_IP4ADDR, ONE_OPTION_SUPPORTED }, /* XXX - multiple? */
+ { OPT_IDB_IP6ADDR, ONE_OPTION_SUPPORTED }, /* XXX - multiple? */
+ { OPT_IDB_FILTER, ONE_OPTION_SUPPORTED },
+ { OPT_IDB_FCSLEN, ONE_OPTION_SUPPORTED }
+};
+
+static const struct supported_option_type packet_block_options_supported[] = {
+ { OPT_COMMENT, ONE_OPTION_SUPPORTED } /* XXX - multiple? */
+};
+
+static const struct supported_block_type erf_blocks_supported[] = {
+ /*
+ * Per-file comments and application supported; section blocks
+ * are used for that.
+ * ERF files have only one section. (XXX - true?)
+ */
+ { WTAP_BLOCK_SECTION, ONE_BLOCK_SUPPORTED, OPTION_TYPES_SUPPORTED(section_block_options_supported) },
+
+ /*
+ * ERF supports multiple interfaces, with information, and
+ * supports associating packets with interfaces. Interface
+ * description blocks are used for that.
+ */
+ { WTAP_BLOCK_IF_ID_AND_INFO, MULTIPLE_BLOCKS_SUPPORTED, OPTION_TYPES_SUPPORTED(interface_block_options_supported) },
+
+ /*
+ * Name resolution is supported, but we don't support comments.
+ */
+ { WTAP_BLOCK_NAME_RESOLUTION, ONE_BLOCK_SUPPORTED, NO_OPTIONS_SUPPORTED },
+
+ /*
+ * ERF is a capture format, so it obviously supports packets.
+ */
+ { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, OPTION_TYPES_SUPPORTED(packet_block_options_supported) }
+};
+
+static const struct file_type_subtype_info erf_info = {
+ "Endace ERF capture", "erf", "erf", NULL,
+ FALSE, BLOCKS_SUPPORTED(erf_blocks_supported),
+ erf_dump_can_write_encap, erf_dump_open, NULL
+};
+
+void register_erf(void)
+{
+ erf_file_type_subtype = wtap_register_file_type_subtype(&erf_info);
+
+ /*
+ * Register name for backwards compatibility with the
+ * wtap_filetypes table in Lua.
+ */
+ wtap_register_backwards_compatibility_lua_name("ERF", erf_file_type_subtype);
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local Variables:
+ * c-basic-offset: 2
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * vi: set shiftwidth=2 tabstop=8 expandtab:
+ * :indentSize=2:tabSize=8:noTabs=true:
+ */
diff --git a/wiretap/erf.h b/wiretap/erf.h
new file mode 100644
index 00000000..62e2144d
--- /dev/null
+++ b/wiretap/erf.h
@@ -0,0 +1,58 @@
+/** @file
+ *
+ * Copyright (c) 2003 Endace Technology Ltd, Hamilton, New Zealand.
+ * All rights reserved.
+ *
+ * This software and documentation has been developed by Endace Technology Ltd.
+ * along with the DAG PCI network capture cards. For further information please
+ * visit https://www.endace.com/.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef __W_ERF_H__
+#define __W_ERF_H__
+
+#include <glib.h>
+#include <wiretap/wtap.h>
+#include "ws_symbol_export.h"
+
+#define ERF_POPULATE_SUCCESS 1
+#define ERF_POPULATE_ALREADY_POPULATED 0
+#define ERF_POPULATE_FAILED -1
+
+#define ERF_MAX_INTERFACES 4
+
+/*
+ * Private data for ERF files and LINKTYPE_ERF packets in pcap and pcapng.
+ */
+struct erf_private {
+ GHashTable* if_map;
+ GHashTable* anchor_map;
+ guint64 implicit_host_id;
+ guint64 capture_gentime;
+ guint64 host_gentime;
+};
+
+#define MIN_RECORDS_FOR_ERF_CHECK 3
+#define RECORDS_FOR_ERF_CHECK 20
+#define FCS_BITS 32
+/*Configurable through ERF_HOST_ID environment variable */
+#define ERF_WS_DEFAULT_HOST_ID 0
+
+wtap_open_return_val erf_open(wtap *wth, int *err, gchar **err_info);
+
+#endif /* __W_ERF_H__ */
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/wiretap/erf_record.h b/wiretap/erf_record.h
new file mode 100644
index 00000000..81e35a0f
--- /dev/null
+++ b/wiretap/erf_record.h
@@ -0,0 +1,429 @@
+/** @file
+ *
+ * Copyright (c) 2003 Endace Technology Ltd, Hamilton, New Zealand.
+ * All rights reserved.
+ *
+ * This software and documentation has been developed by Endace Technology Ltd.
+ * along with the DAG PCI network capture cards. For further information please
+ * visit https://www.endace.com/.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef __W_ERF_RECORD_H__
+#define __W_ERF_RECORD_H__
+
+/*
+ * Declarations and definitions for ERF records; for use by the ERF
+ * file reader, code to handle LINKTYPE_ERF packets in pcap and
+ * pcapng files, ERF metadata dissectors, and protocol dissectors
+ * that register for particular ERF record types.
+ */
+
+/* Record type defines */
+#define ERF_TYPE_LEGACY 0
+#define ERF_TYPE_HDLC_POS 1
+#define ERF_TYPE_ETH 2
+#define ERF_TYPE_ATM 3
+#define ERF_TYPE_AAL5 4
+#define ERF_TYPE_MC_HDLC 5
+#define ERF_TYPE_MC_RAW 6
+#define ERF_TYPE_MC_ATM 7
+#define ERF_TYPE_MC_RAW_CHANNEL 8
+#define ERF_TYPE_MC_AAL5 9
+#define ERF_TYPE_COLOR_HDLC_POS 10
+#define ERF_TYPE_COLOR_ETH 11
+#define ERF_TYPE_MC_AAL2 12
+#define ERF_TYPE_IP_COUNTER 13
+#define ERF_TYPE_TCP_FLOW_COUNTER 14
+#define ERF_TYPE_DSM_COLOR_HDLC_POS 15
+#define ERF_TYPE_DSM_COLOR_ETH 16
+#define ERF_TYPE_COLOR_MC_HDLC_POS 17
+#define ERF_TYPE_AAL2 18
+#define ERF_TYPE_COLOR_HASH_POS 19
+#define ERF_TYPE_COLOR_HASH_ETH 20
+#define ERF_TYPE_INFINIBAND 21
+#define ERF_TYPE_IPV4 22
+#define ERF_TYPE_IPV6 23
+#define ERF_TYPE_RAW_LINK 24
+#define ERF_TYPE_INFINIBAND_LINK 25
+/* XXX - what about 26? */
+#define ERF_TYPE_META 27
+#define ERF_TYPE_OPA_SNC 28
+#define ERF_TYPE_OPA_9B 29
+
+/* 28-31 reserved for future public ERF types */
+
+/* Record types reserved for local and internal use */
+#define ERF_TYPE_INTERNAL0 32
+#define ERF_TYPE_INTERNAL1 33
+#define ERF_TYPE_INTERNAL2 34
+#define ERF_TYPE_INTERNAL3 35
+#define ERF_TYPE_INTERNAL4 36
+#define ERF_TYPE_INTERNAL5 37
+#define ERF_TYPE_INTERNAL6 38
+#define ERF_TYPE_INTERNAL7 39
+#define ERF_TYPE_INTERNAL8 40
+#define ERF_TYPE_INTERNAL9 41
+#define ERF_TYPE_INTERNAL10 42
+#define ERF_TYPE_INTERNAL11 43
+#define ERF_TYPE_INTERNAL12 44
+#define ERF_TYPE_INTERNAL13 45
+#define ERF_TYPE_INTERNAL14 46
+#define ERF_TYPE_INTERNAL15 47
+
+/* Pad records */
+#define ERF_TYPE_PAD 48
+
+#define ERF_EXT_HDR_TYPE_CLASSIFICATION 3
+#define ERF_EXT_HDR_TYPE_INTERCEPTID 4
+#define ERF_EXT_HDR_TYPE_RAW_LINK 5
+#define ERF_EXT_HDR_TYPE_BFS 6
+#define ERF_EXT_HDR_TYPE_CHANNELISED 12
+#define ERF_EXT_HDR_TYPE_SIGNATURE 14
+#define ERF_EXT_HDR_TYPE_PKT_ID 15
+#define ERF_EXT_HDR_TYPE_FLOW_ID 16
+#define ERF_EXT_HDR_TYPE_HOST_ID 17
+#define ERF_EXT_HDR_TYPE_ANCHOR_ID 18
+#define ERF_EXT_HDR_TYPE_ENTROPY 19
+
+/* Host ID and Anchor ID*/
+#define ERF_EHDR_HOST_ID_MASK G_GUINT64_CONSTANT(0xffffffffffff)
+#define ERF_EHDR_ANCHOR_ID_MASK G_GUINT64_CONSTANT(0xffffffffffff)
+#define ERF_EHDR_MORE_EXTHDR_MASK G_GUINT64_CONSTANT(0x8000000000000000)
+#define ERF_EHDR_ANCHOR_ID_DEFINITION_MASK G_GUINT64_CONSTANT(0x80000000000000)
+
+#define ERF_EHDR_FLOW_ID_STACK_TYPE_MASK G_GUINT64_CONSTANT(0xff00000000)
+#define ERF_EHDR_FLOW_ID_SOURCE_ID_MASK G_GUINT64_CONSTANT(0xff000000000000)
+
+/* ERF Provenance metadata */
+#define ERF_META_SECTION_MASK 0xFF00
+#define ERF_META_IS_SECTION(type) (type > 0 && (type & ERF_META_SECTION_MASK) == ERF_META_SECTION_MASK)
+#define ERF_META_HOST_ID_IMPLICIT G_MAXUINT64
+#define ERF_ANCHOR_ID_IS_DEFINITION(anchor_id) ((guint64)anchor_id & ERF_EHDR_ANCHOR_ID_DEFINITION_MASK)
+#define ERF_EHDR_SET_MORE_EXTHDR(ext_hdr) ((guint64)ext_hdr | ERF_EHDR_MORE_EXTHDR_MASK)
+
+#define ERF_META_SECTION_CAPTURE 0xFF00
+#define ERF_META_SECTION_HOST 0xFF01
+#define ERF_META_SECTION_MODULE 0xFF02
+#define ERF_META_SECTION_INTERFACE 0xFF03
+#define ERF_META_SECTION_FLOW 0xFF04
+#define ERF_META_SECTION_STATS 0xFF05
+#define ERF_META_SECTION_INFO 0xFF06
+#define ERF_META_SECTION_CONTEXT 0xFF07
+#define ERF_META_SECTION_STREAM 0xFF08
+#define ERF_META_SECTION_TRANSFORM 0xFF09
+#define ERF_META_SECTION_DNS 0xFF0A
+#define ERF_META_SECTION_SOURCE 0xFF0B
+#define ERF_META_SECTION_NETWORK 0xFF0C
+#define ERF_META_SECTION_ENDPOINT 0xFF0D
+#define ERF_META_SECTION_INPUT 0xFF0E
+#define ERF_META_SECTION_OUTPUT 0xFF0F
+
+#define ERF_META_TAG_padding 0
+#define ERF_META_TAG_comment 1
+#define ERF_META_TAG_gen_time 2
+#define ERF_META_TAG_parent_section 3
+#define ERF_META_TAG_reset 4
+#define ERF_META_TAG_event_time 5
+#define ERF_META_TAG_host_id 6
+#define ERF_META_TAG_attribute 7
+#define ERF_META_TAG_fcs_len 8
+#define ERF_META_TAG_mask_ipv4 9
+#define ERF_META_TAG_mask_cidr 10
+
+#define ERF_META_TAG_org_name 11
+#define ERF_META_TAG_name 12
+#define ERF_META_TAG_descr 13
+#define ERF_META_TAG_config 14
+#define ERF_META_TAG_datapipe 15
+#define ERF_META_TAG_app_name 16
+#define ERF_META_TAG_os 17
+#define ERF_META_TAG_hostname 18
+#define ERF_META_TAG_user 19
+#define ERF_META_TAG_model 20
+#define ERF_META_TAG_fw_version 21
+#define ERF_META_TAG_serial_no 22
+#define ERF_META_TAG_ts_offset 23
+#define ERF_META_TAG_ts_clock_freq 24
+#define ERF_META_TAG_tzone 25
+#define ERF_META_TAG_tzone_name 26
+#define ERF_META_TAG_loc_lat 27
+#define ERF_META_TAG_loc_long 28
+#define ERF_META_TAG_snaplen 29
+#define ERF_META_TAG_card_num 30
+#define ERF_META_TAG_module_num 31
+#define ERF_META_TAG_access_num 32
+#define ERF_META_TAG_stream_num 33
+#define ERF_META_TAG_loc_name 34
+#define ERF_META_TAG_parent_file 35
+#define ERF_META_TAG_filter 36
+#define ERF_META_TAG_flow_hash_mode 37
+#define ERF_META_TAG_tunneling_mode 38
+#define ERF_META_TAG_npb_format 39
+#define ERF_META_TAG_mem 40
+#define ERF_META_TAG_datamine_id 41
+#define ERF_META_TAG_rotfile_id 42
+#define ERF_META_TAG_rotfile_name 43
+#define ERF_META_TAG_dev_name 44
+#define ERF_META_TAG_dev_path 45
+#define ERF_META_TAG_loc_descr 46
+#define ERF_META_TAG_app_version 47
+#define ERF_META_TAG_cpu_affinity 48
+#define ERF_META_TAG_cpu 49
+#define ERF_META_TAG_cpu_phys_cores 50
+#define ERF_META_TAG_cpu_numa_nodes 51
+#define ERF_META_TAG_dag_attribute 52
+#define ERF_META_TAG_dag_version 53
+#define ERF_META_TAG_stream_flags 54
+#define ERF_META_TAG_entropy_threshold 55
+#define ERF_META_TAG_smart_trunc_default 56
+#define ERF_META_TAG_ext_hdrs_added 57
+#define ERF_META_TAG_ext_hdrs_removed 58
+#define ERF_META_TAG_relative_snaplen 59
+#define ERF_META_TAG_temperature 60
+#define ERF_META_TAG_power 61
+#define ERF_META_TAG_vendor 62
+#define ERF_META_TAG_cpu_threads 63
+
+#define ERF_META_TAG_if_num 64
+#define ERF_META_TAG_if_vc 65
+#define ERF_META_TAG_if_speed 66
+#define ERF_META_TAG_if_ipv4 67
+#define ERF_META_TAG_if_ipv6 68
+#define ERF_META_TAG_if_mac 69
+#define ERF_META_TAG_if_eui 70
+#define ERF_META_TAG_if_ib_gid 71
+#define ERF_META_TAG_if_ib_lid 72
+#define ERF_META_TAG_if_wwn 73
+#define ERF_META_TAG_if_fc_id 74
+#define ERF_META_TAG_if_tx_speed 75
+#define ERF_META_TAG_if_erf_type 76
+#define ERF_META_TAG_if_link_type 77
+#define ERF_META_TAG_if_sfp_type 78
+#define ERF_META_TAG_if_rx_power 79
+#define ERF_META_TAG_if_tx_power 80
+#define ERF_META_TAG_if_link_status 81
+#define ERF_META_TAG_if_phy_mode 82
+#define ERF_META_TAG_if_port_type 83
+#define ERF_META_TAG_if_rx_latency 84
+#define ERF_META_TAG_tap_mode 85
+#define ERF_META_TAG_tap_fail_mode 86
+#define ERF_META_TAG_watchdog_expired 87
+#define ERF_META_TAG_watchdog_interval 88
+
+#define ERF_META_TAG_src_ipv4 128
+#define ERF_META_TAG_dest_ipv4 129
+#define ERF_META_TAG_src_ipv6 130
+#define ERF_META_TAG_dest_ipv6 131
+#define ERF_META_TAG_src_mac 132
+#define ERF_META_TAG_dest_mac 133
+#define ERF_META_TAG_src_eui 134
+#define ERF_META_TAG_dest_eui 135
+#define ERF_META_TAG_src_ib_gid 136
+#define ERF_META_TAG_dest_ib_gid 137
+#define ERF_META_TAG_src_ib_lid 138
+#define ERF_META_TAG_dest_ib_lid 139
+#define ERF_META_TAG_src_wwn 140
+#define ERF_META_TAG_dest_wwn 141
+#define ERF_META_TAG_src_fc_id 142
+#define ERF_META_TAG_dest_fc_id 143
+#define ERF_META_TAG_src_port 144
+#define ERF_META_TAG_dest_port 145
+#define ERF_META_TAG_ip_proto 146
+#define ERF_META_TAG_flow_hash 147
+#define ERF_META_TAG_filter_match 148
+#define ERF_META_TAG_filter_match_name 149
+#define ERF_META_TAG_error_flags 150
+#define ERF_META_TAG_initiator_pkts 151
+#define ERF_META_TAG_responder_pkts 152
+#define ERF_META_TAG_initiator_bytes 153
+#define ERF_META_TAG_responder_bytes 154
+#define ERF_META_TAG_initiator_min_entropy 155
+#define ERF_META_TAG_responder_min_entropy 156
+#define ERF_META_TAG_initiator_avg_entropy 157
+#define ERF_META_TAG_responder_avg_entropy 158
+#define ERF_META_TAG_initiator_max_entropy 159
+#define ERF_META_TAG_responder_max_entropy 160
+#define ERF_META_TAG_dpi_application 161
+#define ERF_META_TAG_dpi_confidence 162
+#define ERF_META_TAG_dpi_state 163
+#define ERF_META_TAG_dpi_protocol_stack 164
+#define ERF_META_TAG_flow_state 165
+#define ERF_META_TAG_vlan_id 166
+#define ERF_META_TAG_mpls_label 167
+#define ERF_META_TAG_vlan_pcp 168
+#define ERF_META_TAG_mpls_tc 169
+#define ERF_META_TAG_dscp 170
+#define ERF_META_TAG_initiator_mpls_label 171
+#define ERF_META_TAG_responder_mpls_label 172
+#define ERF_META_TAG_initiator_mpls_tc 173
+#define ERF_META_TAG_responder_mpls_tc 174
+#define ERF_META_TAG_initiator_ipv4 175
+#define ERF_META_TAG_responder_ipv4 176
+#define ERF_META_TAG_initiator_ipv6 177
+#define ERF_META_TAG_responder_ipv6 178
+#define ERF_META_TAG_initiator_mac 179
+#define ERF_META_TAG_responder_mac 180
+#define ERF_META_TAG_initiator_port 181
+#define ERF_META_TAG_responder_port 182
+#define ERF_META_TAG_initiator_retx 183
+#define ERF_META_TAG_responder_retx 184
+#define ERF_META_TAG_initiator_zwin 185
+#define ERF_META_TAG_responder_zwin 186
+#define ERF_META_TAG_initiator_tcp_flags 187
+#define ERF_META_TAG_responder_tcp_flags 188
+#define ERF_META_TAG_tcp_irtt 189
+
+#define ERF_META_TAG_start_time 193
+#define ERF_META_TAG_end_time 194
+#define ERF_META_TAG_stat_if_drop 195
+#define ERF_META_TAG_stat_frames 196
+#define ERF_META_TAG_stat_bytes 197
+#define ERF_META_TAG_stat_cap 198
+#define ERF_META_TAG_stat_cap_bytes 199
+#define ERF_META_TAG_stat_os_drop 200
+#define ERF_META_TAG_stat_ds_lctr 201
+#define ERF_META_TAG_stat_filter_match 202
+#define ERF_META_TAG_stat_filter_drop 203
+#define ERF_META_TAG_stat_too_short 204
+#define ERF_META_TAG_stat_too_long 205
+#define ERF_META_TAG_stat_rx_error 206
+#define ERF_META_TAG_stat_fcs_error 207
+#define ERF_META_TAG_stat_aborted 208
+#define ERF_META_TAG_stat_proto_error 209
+#define ERF_META_TAG_stat_b1_error 210
+#define ERF_META_TAG_stat_b2_error 211
+#define ERF_META_TAG_stat_b3_error 212
+#define ERF_META_TAG_stat_rei_error 213
+#define ERF_META_TAG_stat_drop 214
+#define ERF_META_TAG_stat_buf_drop 215
+#define ERF_META_TAG_stream_drop 216
+#define ERF_META_TAG_stream_buf_drop 217
+#define ERF_META_TAG_pkt_drop 218
+#define ERF_META_TAG_record_drop 219
+#define ERF_META_TAG_bandwidth 220
+#define ERF_META_TAG_duration 221
+#define ERF_META_TAG_top_index 222
+#define ERF_META_TAG_concurrent_flows 223
+#define ERF_META_TAG_active_flows 224
+#define ERF_META_TAG_created_flows 225
+#define ERF_META_TAG_deleted_flows 226
+#define ERF_META_TAG_active_endpoints 227
+#define ERF_META_TAG_tx_pkts 228
+#define ERF_META_TAG_tx_bytes 229
+#define ERF_META_TAG_rx_bandwidth 230
+#define ERF_META_TAG_tx_bandwidth 231
+#define ERF_META_TAG_records 232
+#define ERF_META_TAG_record_bytes 233
+#define ERF_META_TAG_pkt_drop_bytes 234
+#define ERF_META_TAG_record_drop_bytes 235
+#define ERF_META_TAG_drop_bandwidth 236
+#define ERF_META_TAG_retx_pkts 237
+#define ERF_META_TAG_zwin_pkts 238
+
+#define ERF_META_TAG_ns_host_ipv4 256
+#define ERF_META_TAG_ns_host_ipv6 257
+#define ERF_META_TAG_ns_host_mac 258
+#define ERF_META_TAG_ns_host_eui 259
+#define ERF_META_TAG_ns_host_ib_gid 260
+#define ERF_META_TAG_ns_host_ib_lid 261
+#define ERF_META_TAG_ns_host_wwn 262
+#define ERF_META_TAG_ns_host_fc_id 263
+#define ERF_META_TAG_ns_dns_ipv4 264
+#define ERF_META_TAG_ns_dns_ipv6 265
+
+#define ERF_META_TAG_exthdr 321
+#define ERF_META_TAG_pcap_ng_block 322
+#define ERF_META_TAG_asn1 323
+#define ERF_META_TAG_section_ref 324
+
+#define ERF_META_TAG_clk_source 384
+#define ERF_META_TAG_clk_state 385
+#define ERF_META_TAG_clk_threshold 386
+#define ERF_META_TAG_clk_correction 387
+#define ERF_META_TAG_clk_failures 388
+#define ERF_META_TAG_clk_resyncs 389
+#define ERF_META_TAG_clk_phase_error 390
+#define ERF_META_TAG_clk_input_pulses 391
+#define ERF_META_TAG_clk_rejected_pulses 392
+#define ERF_META_TAG_clk_phc_index 393
+#define ERF_META_TAG_clk_phc_offset 394
+#define ERF_META_TAG_clk_timebase 395
+#define ERF_META_TAG_clk_descr 396
+#define ERF_META_TAG_clk_out_source 397
+#define ERF_META_TAG_clk_link_mode 398
+#define ERF_META_TAG_ptp_domain_num 399
+#define ERF_META_TAG_ptp_steps_removed 400
+#define ERF_META_TAG_ptp_offset_from_master 401
+#define ERF_META_TAG_ptp_mean_path_delay 402
+#define ERF_META_TAG_ptp_parent_identity 403
+#define ERF_META_TAG_ptp_parent_port_num 404
+#define ERF_META_TAG_ptp_gm_identity 405
+#define ERF_META_TAG_ptp_gm_clock_quality 406
+#define ERF_META_TAG_ptp_current_utc_offset 407
+#define ERF_META_TAG_ptp_time_properties 408
+#define ERF_META_TAG_ptp_time_source 409
+#define ERF_META_TAG_ptp_clock_identity 410
+#define ERF_META_TAG_ptp_port_num 411
+#define ERF_META_TAG_ptp_port_state 412
+#define ERF_META_TAG_ptp_delay_mechanism 413
+#define ERF_META_TAG_clk_port_proto 414
+#define ERF_META_TAG_ntp_status 415
+#define ERF_META_TAG_ntp_stratum 416
+#define ERF_META_TAG_ntp_rootdelay 417
+#define ERF_META_TAG_ntp_rootdisp 418
+#define ERF_META_TAG_ntp_offset 419
+#define ERF_META_TAG_ntp_frequency 420
+#define ERF_META_TAG_ntp_sys_jitter 421
+#define ERF_META_TAG_ntp_peer_remote 422
+#define ERF_META_TAG_ntp_peer_refid 423
+
+ /*
+ * The timestamp is 64bit unsigned fixed point little-endian value with
+ * 32 bits for second and 32 bits for fraction.
+ */
+typedef guint64 erf_timestamp_t;
+
+typedef struct erf_record {
+ erf_timestamp_t ts;
+ guint8 type;
+ guint8 flags;
+ guint16 rlen;
+ guint16 lctr;
+ guint16 wlen;
+} erf_header_t;
+
+typedef struct erf_mc_hdr {
+ guint32 mc;
+} erf_mc_header_t;
+
+typedef struct erf_aal2_hdr {
+ guint32 aal2;
+} erf_aal2_header_t;
+
+typedef struct erf_eth_hdr {
+ guint8 offset;
+ guint8 pad;
+} erf_eth_header_t;
+
+union erf_subhdr {
+ struct erf_mc_hdr mc_hdr;
+ struct erf_aal2_hdr aal2_hdr;
+ struct erf_eth_hdr eth_hdr;
+};
+
+#endif /* __W_ERF_RECORD_H__ */
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/wiretap/eri_enb_log.c b/wiretap/eri_enb_log.c
new file mode 100644
index 00000000..20abcd7a
--- /dev/null
+++ b/wiretap/eri_enb_log.c
@@ -0,0 +1,168 @@
+/* eri_enb_log.c
+ *
+ * Ericsson eNode-B raw log file format decoder for the Wiretap library.
+ *
+ * Wiretap Library
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "eri_enb_log.h"
+
+#include <string.h>
+
+#include "file_wrappers.h"
+#include "wtap-int.h"
+
+static const char eri_enb_log_magic[] = "com_ericsson";
+
+static int eri_enb_log_file_type_subtype = -1;
+
+void register_eri_enb_log(void);
+
+#define MAX_LINE_LENGTH 131072
+
+static gboolean eri_enb_log_get_packet(FILE_T fh, wtap_rec* rec,
+ Buffer* buf, int* err _U_, gchar** err_info _U_)
+{
+ static char line[MAX_LINE_LENGTH];
+ /* Read in a line */
+ gint64 pos_before = file_tell(fh);
+
+ while (file_gets(line, sizeof(line), fh) != NULL)
+ {
+ nstime_t packet_time;
+ gint length;
+ /* Set length (avoiding strlen()) and offset.. */
+ length = (gint)(file_tell(fh) - pos_before);
+
+ /* ...but don't want to include newline in line length */
+ if (length > 0 && line[length - 1] == '\n') {
+ line[length - 1] = '\0';
+ length = length - 1;
+ }
+ /* Nor do we want '\r' (as will be written when log is created on windows) */
+ if (length > 0 && line[length - 1] == '\r') {
+ line[length - 1] = '\0';
+ length = length - 1;
+ }
+
+ if (NULL != iso8601_to_nstime(&packet_time, line+1, ISO8601_DATETIME)) {
+ rec->ts.secs = packet_time.secs;
+ rec->ts.nsecs = packet_time.nsecs;
+ rec->presence_flags |= WTAP_HAS_TS;
+ } else {
+ rec->ts.secs = 0;
+ rec->ts.nsecs = 0;
+ rec->presence_flags = 0; /* no time stamp, no separate "on the wire" length */
+ }
+ /* We've got a full packet! */
+ rec->rec_type = REC_TYPE_PACKET;
+ rec->block = wtap_block_create(WTAP_BLOCK_PACKET);
+ rec->rec_header.packet_header.caplen = length;
+ rec->rec_header.packet_header.len = length;
+
+ *err = 0;
+
+ /* Make sure we have enough room for the packet */
+ ws_buffer_assure_space(buf, rec->rec_header.packet_header.caplen);
+ memcpy(ws_buffer_start_ptr(buf), line, rec->rec_header.packet_header.caplen);
+
+ return TRUE;
+
+ }
+ return FALSE;
+}
+
+/* Find the next packet and parse it; called from wtap_read(). */
+static gboolean eri_enb_log_read(wtap* wth, wtap_rec* rec, Buffer* buf,
+ int* err, gchar** err_info, gint64* data_offset)
+{
+ *data_offset = file_tell(wth->fh);
+
+ return eri_enb_log_get_packet(wth->fh, rec, buf, err, err_info);
+}
+
+/* Used to read packets in random-access fashion */
+static gboolean eri_enb_log_seek_read(wtap* wth, gint64 seek_off,
+ wtap_rec* rec, Buffer* buf, int* err, gchar** err_info)
+{
+ if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
+ {
+ return FALSE;
+ }
+
+ return eri_enb_log_get_packet(wth->random_fh, rec, buf, err, err_info);
+}
+
+wtap_open_return_val
+eri_enb_log_open(wtap *wth, int *err, gchar **err_info)
+{
+ char line1[64];
+
+ /* Look for Gammu DCT3 trace header */
+ if (file_gets(line1, sizeof(line1), wth->fh) == NULL)
+ {
+ *err = file_error(wth->fh, err_info);
+ if (*err != 0 && *err != WTAP_ERR_SHORT_READ)
+ return WTAP_OPEN_ERROR;
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ if (g_strstr_len(line1, sizeof(line1), eri_enb_log_magic) == NULL)
+ {
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ if (file_seek(wth->fh, 0, SEEK_SET, err) == -1)
+ return WTAP_OPEN_ERROR;
+
+ wth->file_type_subtype = eri_enb_log_file_type_subtype;
+ wth->file_encap = WTAP_ENCAP_ERI_ENB_LOG;
+ wth->file_tsprec = WTAP_TSPREC_NSEC;
+ wth->subtype_read = eri_enb_log_read;
+ wth->subtype_seek_read = eri_enb_log_seek_read;
+ wth->snapshot_length = 0;
+
+ return WTAP_OPEN_MINE;
+}
+
+static const struct supported_block_type eri_enb_log_blocks_supported[] = {
+ /*
+ * This is a file format that we dissect, so we provide
+ * only one "packet" with the file's contents, and don't
+ * support any options.
+ */
+ { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
+};
+
+static const struct file_type_subtype_info eri_enb_log_info = {
+ "Ericsson eNode-B raw log", "eri_enb_log", "eri_enb_log", NULL,
+ FALSE, BLOCKS_SUPPORTED(eri_enb_log_blocks_supported),
+ NULL, NULL, NULL
+};
+
+void register_eri_enb_log(void)
+{
+ eri_enb_log_file_type_subtype = wtap_register_file_type_subtype(&eri_enb_log_info);
+
+ /*
+ * Register name for backwards compatibility with the
+ * wtap_filetypes table in Lua.
+ */
+ //wtap_register_backwards_compatibility_lua_name("MP4",
+ // eri_enb_log_file_type_subtype);
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/wiretap/eri_enb_log.h b/wiretap/eri_enb_log.h
new file mode 100644
index 00000000..53c0ef36
--- /dev/null
+++ b/wiretap/eri_enb_log.h
@@ -0,0 +1,16 @@
+/** @file
+ *
+ * Ericsson eNode-B raw log file format decoder for the Wiretap library.
+ *
+ * Wiretap Library
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __W_ERI_ENB_LOG_H__
+#define __W_ERI_ENB_LOG_H__
+
+#include "wtap.h"
+
+wtap_open_return_val eri_enb_log_open(wtap *wth, int *err, gchar **err_info);
+
+#endif /* __W_ERI_ENB_LOG_H__*/
diff --git a/wiretap/eyesdn.c b/wiretap/eyesdn.c
new file mode 100644
index 00000000..4d22d6b7
--- /dev/null
+++ b/wiretap/eyesdn.c
@@ -0,0 +1,514 @@
+/* eyesdn.c
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "config.h"
+#include "wtap-int.h"
+#include "eyesdn.h"
+#include "file_wrappers.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+static int eyesdn_file_type_subtype = -1;
+
+void register_eyesdn(void);
+
+/* This module reads the output of the EyeSDN USB S0/E1 ISDN probes
+ * They store HDLC frames of D and B channels in a binary format
+ * The fileformat is
+ *
+ * 1-6 Byte: EyeSDN - Magic
+ * 7-n Byte: Frames
+ *
+ * Each Frame starts with the 0xff Flag byte
+ * - Bytes 0-2: timestamp (usec in network byte order)
+ * - Bytes 3-7: timestamp (40bits sec since 1970 in network byte order)
+ * - Byte 8: channel (0 for D channel, 1-30 for B1-B30)
+ * - Byte 9: Sender Bit 0(0 NT, 1 TE), Protocol in Bits 7:1, see enum
+ * - Byte 10-11: frame size in bytes
+ * - Byte 12-n: Frame Payload
+ *
+ * All multibyte values are represented in network byte order
+ * The frame is terminated with a flag character (0xff)
+ * bytes 0xff within a frame are escaped using the 0xfe escape character
+ * the byte following the escape character is decremented by two:
+ * so 0xfe 0xfd is actually a 0xff
+ * Characters that need to be escaped are 0xff and 0xfe
+ */
+
+
+static gboolean esc_read(FILE_T fh, guint8 *buf, int len, int *err, gchar **err_info)
+{
+ int i;
+ int value;
+
+ for(i=0; i<len; i++) {
+ value=file_getc(fh);
+ if(value==-1) {
+ /* EOF or error */
+ *err=file_error(fh, err_info);
+ if(*err==0)
+ *err=WTAP_ERR_SHORT_READ;
+ return FALSE;
+ }
+ if(value==0xff) {
+ /* error !!, read into next frame */
+ *err=WTAP_ERR_BAD_FILE;
+ *err_info=g_strdup("eyesdn: No flag character seen in frame");
+ return FALSE;
+ }
+ if(value==0xfe) {
+ /* we need to escape */
+ value=file_getc(fh);
+ if(value==-1) {
+ /* EOF or error */
+ *err=file_error(fh, err_info);
+ if(*err==0)
+ *err=WTAP_ERR_SHORT_READ;
+ return FALSE;
+ }
+ value+=2;
+ }
+ buf[i]=value;
+ }
+
+ return TRUE;
+}
+
+/* Magic text to check for eyesdn-ness of file */
+static const unsigned char eyesdn_hdr_magic[] =
+{ 'E', 'y', 'e', 'S', 'D', 'N'};
+#define EYESDN_HDR_MAGIC_SIZE sizeof(eyesdn_hdr_magic)
+
+/* Size of a record header */
+#define EYESDN_HDR_LENGTH 12
+
+static gboolean eyesdn_read(wtap *wth, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info, gint64 *data_offset);
+static gboolean eyesdn_seek_read(wtap *wth, gint64 seek_off,
+ wtap_rec *rec, Buffer *buf, int *err, gchar **err_info);
+static int read_eyesdn_rec(FILE_T fh, wtap_rec *rec, Buffer* buf,
+ int *err, gchar **err_info);
+
+/* Seeks to the beginning of the next packet, and returns the
+ byte offset. Returns -1 on failure, and sets "*err" to the error
+ and "*err_info" to null or an additional error string. */
+static gint64 eyesdn_seek_next_packet(wtap *wth, int *err, gchar **err_info)
+{
+ int byte;
+ gint64 cur_off;
+
+ while ((byte = file_getc(wth->fh)) != EOF) {
+ if (byte == 0xff) {
+ cur_off = file_tell(wth->fh);
+ if (cur_off == -1) {
+ /* Error. */
+ *err = file_error(wth->fh, err_info);
+ return -1;
+ }
+ return cur_off;
+ }
+ }
+ /* EOF or error. */
+ *err = file_error(wth->fh, err_info);
+ return -1;
+}
+
+wtap_open_return_val eyesdn_open(wtap *wth, int *err, gchar **err_info)
+{
+ char magic[EYESDN_HDR_MAGIC_SIZE];
+
+ /* Look for eyesdn header */
+ if (!wtap_read_bytes(wth->fh, &magic, sizeof magic, err, err_info)) {
+ if (*err != WTAP_ERR_SHORT_READ)
+ return WTAP_OPEN_ERROR;
+ return WTAP_OPEN_NOT_MINE;
+ }
+ if (memcmp(magic, eyesdn_hdr_magic, EYESDN_HDR_MAGIC_SIZE) != 0)
+ return WTAP_OPEN_NOT_MINE;
+
+ wth->file_encap = WTAP_ENCAP_PER_PACKET;
+ wth->file_type_subtype = eyesdn_file_type_subtype;
+ wth->snapshot_length = 0; /* not known */
+ wth->subtype_read = eyesdn_read;
+ wth->subtype_seek_read = eyesdn_seek_read;
+ wth->file_tsprec = WTAP_TSPREC_USEC;
+
+ return WTAP_OPEN_MINE;
+}
+
+/* Find the next record and parse it; called from wtap_read(). */
+static gboolean eyesdn_read(wtap *wth, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info, gint64 *data_offset)
+{
+ gint64 offset;
+
+ /* Find the next record */
+ offset = eyesdn_seek_next_packet(wth, err, err_info);
+ if (offset < 1)
+ return FALSE;
+ *data_offset = offset;
+
+ /* Parse the record */
+ return read_eyesdn_rec(wth->fh, rec, buf, err, err_info);
+}
+
+/* Used to read packets in random-access fashion */
+static gboolean
+eyesdn_seek_read(wtap *wth, gint64 seek_off, wtap_rec *rec,
+ Buffer *buf, int *err, gchar **err_info)
+{
+ if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
+ return FALSE;
+
+ return read_eyesdn_rec(wth->random_fh, rec, buf, err, err_info);
+}
+
+/* Parses a record. */
+static gboolean
+read_eyesdn_rec(FILE_T fh, wtap_rec *rec, Buffer *buf, int *err,
+ gchar **err_info)
+{
+ union wtap_pseudo_header *pseudo_header = &rec->rec_header.packet_header.pseudo_header;
+ guint8 hdr[EYESDN_HDR_LENGTH];
+ time_t secs;
+ int usecs;
+ guint pkt_len;
+ guint8 channel, direction;
+ guint8 *pd;
+
+ /* Our file pointer should be at the summary information header
+ * for a packet. Read in that header and extract the useful
+ * information.
+ */
+ if (!esc_read(fh, hdr, EYESDN_HDR_LENGTH, err, err_info))
+ return FALSE;
+
+ /* extract information from header */
+ usecs = pntoh24(&hdr[0]);
+#ifdef TV64BITS
+ secs = hdr[3];
+#else
+ secs = 0;
+#endif
+ secs = (secs << 8) | hdr[4];
+ secs = (secs << 8) | hdr[5];
+ secs = (secs << 8) | hdr[6];
+ secs = (secs << 8) | hdr[7];
+
+ channel = hdr[8];
+ direction = hdr[9];
+ pkt_len = pntoh16(&hdr[10]);
+
+ switch(direction >> 1) {
+
+ default:
+ case EYESDN_ENCAP_ISDN: /* ISDN */
+ pseudo_header->isdn.uton = direction & 1;
+ pseudo_header->isdn.channel = channel;
+ if(channel) { /* bearer channels */
+ rec->rec_header.packet_header.pkt_encap = WTAP_ENCAP_ISDN; /* recognises PPP */
+ pseudo_header->isdn.uton=!pseudo_header->isdn.uton; /* bug */
+ } else { /* D channel */
+ rec->rec_header.packet_header.pkt_encap = WTAP_ENCAP_ISDN;
+ }
+ break;
+
+ case EYESDN_ENCAP_MSG: /* Layer 1 message */
+ rec->rec_header.packet_header.pkt_encap = WTAP_ENCAP_LAYER1_EVENT;
+ pseudo_header->l1event.uton = (direction & 1);
+ break;
+
+ case EYESDN_ENCAP_LAPB: /* X.25 via LAPB */
+ rec->rec_header.packet_header.pkt_encap = WTAP_ENCAP_LAPB;
+ pseudo_header->dte_dce.flags = (direction & 1) ? 0 : 0x80;
+ break;
+
+ case EYESDN_ENCAP_ATM: { /* ATM cells */
+#define CELL_LEN 53
+ unsigned char cell[CELL_LEN];
+ gint64 cur_off;
+
+ if(pkt_len != CELL_LEN) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf(
+ "eyesdn: ATM cell has a length != 53 (%u)",
+ pkt_len);
+ return FALSE;
+ }
+
+ cur_off = file_tell(fh);
+ if (!esc_read(fh, cell, CELL_LEN, err, err_info))
+ return FALSE;
+ if (file_seek(fh, cur_off, SEEK_SET, err) == -1)
+ return FALSE;
+ rec->rec_header.packet_header.pkt_encap = WTAP_ENCAP_ATM_PDUS_UNTRUNCATED;
+ pseudo_header->atm.flags=ATM_RAW_CELL;
+ pseudo_header->atm.aal=AAL_UNKNOWN;
+ pseudo_header->atm.type=TRAF_UMTS_FP;
+ pseudo_header->atm.subtype=TRAF_ST_UNKNOWN;
+ pseudo_header->atm.vpi=((cell[0]&0xf)<<4) + (cell[0]&0xf);
+ pseudo_header->atm.vci=((cell[0]&0xf)<<4) + cell[0]; /* from cell */
+ pseudo_header->atm.channel=direction & 1;
+ }
+ break;
+
+ case EYESDN_ENCAP_MTP2: /* SS7 frames */
+ pseudo_header->mtp2.sent = direction & 1;
+ pseudo_header->mtp2.annex_a_used = MTP2_ANNEX_A_USED_UNKNOWN;
+ pseudo_header->mtp2.link_number = channel;
+ rec->rec_header.packet_header.pkt_encap = WTAP_ENCAP_MTP2_WITH_PHDR;
+ break;
+
+ case EYESDN_ENCAP_DPNSS: /* DPNSS */
+ pseudo_header->isdn.uton = direction & 1;
+ pseudo_header->isdn.channel = channel;
+ rec->rec_header.packet_header.pkt_encap = WTAP_ENCAP_DPNSS;
+ break;
+
+ case EYESDN_ENCAP_DASS2: /* DASS2 frames */
+ pseudo_header->isdn.uton = direction & 1;
+ pseudo_header->isdn.channel = channel;
+ rec->rec_header.packet_header.pkt_encap = WTAP_ENCAP_DPNSS;
+ break;
+
+ case EYESDN_ENCAP_BACNET: /* BACNET async over HDLC frames */
+ pseudo_header->isdn.uton = direction & 1;
+ pseudo_header->isdn.channel = channel;
+ rec->rec_header.packet_header.pkt_encap = WTAP_ENCAP_BACNET_MS_TP_WITH_PHDR;
+ break;
+
+ case EYESDN_ENCAP_V5_EF: /* V5EF */
+ pseudo_header->isdn.uton = direction & 1;
+ pseudo_header->isdn.channel = channel;
+ rec->rec_header.packet_header.pkt_encap = WTAP_ENCAP_V5_EF;
+ break;
+ }
+
+ if(pkt_len > WTAP_MAX_PACKET_SIZE_STANDARD) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("eyesdn: File has %u-byte packet, bigger than maximum of %u",
+ pkt_len, WTAP_MAX_PACKET_SIZE_STANDARD);
+ return FALSE;
+ }
+
+ rec->rec_type = REC_TYPE_PACKET;
+ rec->block = wtap_block_create(WTAP_BLOCK_PACKET);
+ rec->presence_flags = WTAP_HAS_TS;
+ rec->ts.secs = secs;
+ rec->ts.nsecs = usecs * 1000;
+ rec->rec_header.packet_header.caplen = pkt_len;
+ rec->rec_header.packet_header.len = pkt_len;
+
+ /* Make sure we have enough room for the packet */
+ ws_buffer_assure_space(buf, pkt_len);
+
+ pd = ws_buffer_start_ptr(buf);
+ if (!esc_read(fh, pd, pkt_len, err, err_info))
+ return FALSE;
+ return TRUE;
+}
+
+
+static gboolean
+esc_write(wtap_dumper *wdh, const guint8 *buf, int len, int *err)
+{
+ int i;
+ guint8 byte;
+ static const guint8 esc = 0xfe;
+
+ for(i=0; i<len; i++) {
+ byte=buf[i];
+ if(byte == 0xff || byte == 0xfe) {
+ /*
+ * Escape the frame delimiter and escape byte.
+ */
+ if (!wtap_dump_file_write(wdh, &esc, sizeof esc, err))
+ return FALSE;
+ byte-=2;
+ }
+ if (!wtap_dump_file_write(wdh, &byte, sizeof byte, err))
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static gboolean eyesdn_dump(wtap_dumper *wdh,
+ const wtap_rec *rec,
+ const guint8 *pd, int *err, gchar **err_info);
+
+static gboolean eyesdn_dump_open(wtap_dumper *wdh, int *err, gchar **err_info _U_)
+{
+ wdh->subtype_write=eyesdn_dump;
+
+ if (!wtap_dump_file_write(wdh, eyesdn_hdr_magic,
+ EYESDN_HDR_MAGIC_SIZE, err))
+ return FALSE;
+ *err=0;
+ return TRUE;
+}
+
+static int eyesdn_dump_can_write_encap(int encap)
+{
+ switch (encap) {
+ case WTAP_ENCAP_ISDN:
+ case WTAP_ENCAP_LAYER1_EVENT:
+ case WTAP_ENCAP_DPNSS:
+ case WTAP_ENCAP_ATM_PDUS_UNTRUNCATED:
+ case WTAP_ENCAP_LAPB:
+ case WTAP_ENCAP_MTP2_WITH_PHDR:
+ case WTAP_ENCAP_BACNET_MS_TP_WITH_PHDR:
+ case WTAP_ENCAP_PER_PACKET:
+ return 0;
+
+ default:
+ return WTAP_ERR_UNWRITABLE_ENCAP;
+ }
+}
+
+/* Write a record for a packet to a dump file.
+ * Returns TRUE on success, FALSE on failure. */
+static gboolean eyesdn_dump(wtap_dumper *wdh,
+ const wtap_rec *rec,
+ const guint8 *pd, int *err, gchar **err_info _U_)
+{
+ static const guint8 start_flag = 0xff;
+ const union wtap_pseudo_header *pseudo_header = &rec->rec_header.packet_header.pseudo_header;
+ guint8 buf[EYESDN_HDR_LENGTH];
+ int usecs;
+ time_t secs;
+ int channel;
+ int origin;
+ int protocol;
+ int size;
+
+ /* We can only write packet records. */
+ if (rec->rec_type != REC_TYPE_PACKET) {
+ *err = WTAP_ERR_UNWRITABLE_REC_TYPE;
+ return FALSE;
+ }
+
+ /* Don't write out anything bigger than we can read.
+ * (The length field in packet headers is 16 bits, which
+ * imposes a hard limit.) */
+ if (rec->rec_header.packet_header.caplen > 65535) {
+ *err = WTAP_ERR_PACKET_TOO_LARGE;
+ return FALSE;
+ }
+
+ usecs=rec->ts.nsecs/1000;
+ secs=rec->ts.secs;
+ size=rec->rec_header.packet_header.caplen;
+ origin = pseudo_header->isdn.uton;
+ channel = pseudo_header->isdn.channel;
+
+ switch(rec->rec_header.packet_header.pkt_encap) {
+
+ case WTAP_ENCAP_ISDN:
+ protocol=EYESDN_ENCAP_ISDN; /* set depending on decoder format and mode */
+ break;
+
+ case WTAP_ENCAP_LAYER1_EVENT:
+ protocol=EYESDN_ENCAP_MSG;
+ break;
+
+ case WTAP_ENCAP_DPNSS:
+ protocol=EYESDN_ENCAP_DPNSS;
+ break;
+
+#if 0
+ case WTAP_ENCAP_DASS2:
+ protocol=EYESDN_ENCAP_DASS2;
+ break;
+#endif
+
+ case WTAP_ENCAP_ATM_PDUS_UNTRUNCATED:
+ protocol=EYESDN_ENCAP_ATM;
+ channel=0x80;
+ break;
+
+ case WTAP_ENCAP_LAPB:
+ protocol=EYESDN_ENCAP_LAPB;
+ break;
+
+ case WTAP_ENCAP_MTP2_WITH_PHDR:
+ protocol=EYESDN_ENCAP_MTP2;
+ break;
+
+ case WTAP_ENCAP_BACNET_MS_TP_WITH_PHDR:
+ protocol=EYESDN_ENCAP_BACNET;
+ break;
+
+ case WTAP_ENCAP_V5_EF:
+ protocol=EYESDN_ENCAP_V5_EF;
+ break;
+
+ default:
+ *err=WTAP_ERR_UNWRITABLE_ENCAP;
+ return FALSE;
+ }
+
+ phton24(&buf[0], usecs);
+
+ buf[3] = (guint8)0;
+ buf[4] = (guint8)(0xff & (secs >> 24));
+ buf[5] = (guint8)(0xff & (secs >> 16));
+ buf[6] = (guint8)(0xff & (secs >> 8));
+ buf[7] = (guint8)(0xff & (secs >> 0));
+
+ buf[8] = (guint8) channel;
+ buf[9] = (guint8) (origin?1:0) + (protocol << 1);
+ phtons(&buf[10], size);
+
+ /* start flag */
+ if (!wtap_dump_file_write(wdh, &start_flag, sizeof start_flag, err))
+ return FALSE;
+ if (!esc_write(wdh, buf, 12, err))
+ return FALSE;
+ if (!esc_write(wdh, pd, size, err))
+ return FALSE;
+ return TRUE;
+}
+
+static const struct supported_block_type eyesdn_blocks_supported[] = {
+ /*
+ * We support packet blocks, with no comments or other options.
+ */
+ { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
+};
+
+static const struct file_type_subtype_info eyesdn_info = {
+ "EyeSDN USB S0/E1 ISDN trace format", "eyesdn", "trc", NULL,
+ FALSE, BLOCKS_SUPPORTED(eyesdn_blocks_supported),
+ eyesdn_dump_can_write_encap, eyesdn_dump_open, NULL
+};
+
+void register_eyesdn(void)
+{
+ eyesdn_file_type_subtype = wtap_register_file_type_subtype(&eyesdn_info);
+
+ /*
+ * Register name for backwards compatibility with the
+ * wtap_filetypes table in Lua.
+ */
+ wtap_register_backwards_compatibility_lua_name("EYESDN",
+ eyesdn_file_type_subtype);
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/wiretap/eyesdn.h b/wiretap/eyesdn.h
new file mode 100644
index 00000000..28dfec82
--- /dev/null
+++ b/wiretap/eyesdn.h
@@ -0,0 +1,31 @@
+/** @file
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ */
+
+#ifndef __W_EYESDN_H__
+#define __W_EYESDN_H__
+
+#include <glib.h>
+#include "wtap.h"
+#include "ws_symbol_export.h"
+
+wtap_open_return_val eyesdn_open(wtap *wth, int *err, gchar **err_info);
+
+enum EyeSDN_TYPES {
+ EYESDN_ENCAP_ISDN=0,
+ EYESDN_ENCAP_MSG,
+ EYESDN_ENCAP_LAPB,
+ EYESDN_ENCAP_ATM,
+ EYESDN_ENCAP_MTP2,
+ EYESDN_ENCAP_DPNSS,
+ EYESDN_ENCAP_DASS2,
+ EYESDN_ENCAP_BACNET,
+ EYESDN_ENCAP_V5_EF
+};
+
+#endif
diff --git a/wiretap/file_access.c b/wiretap/file_access.c
new file mode 100644
index 00000000..01317da0
--- /dev/null
+++ b/wiretap/file_access.c
@@ -0,0 +1,2994 @@
+/* file_access.c
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "config.h"
+#define WS_LOG_DOMAIN LOG_DOMAIN_WIRETAP
+
+#include <string.h>
+#include <stdlib.h>
+
+#include <errno.h>
+
+#include <wsutil/file_util.h>
+#include <wsutil/tempfile.h>
+#ifdef HAVE_PLUGINS
+#include <wsutil/plugins.h>
+#endif
+#include <wsutil/ws_assert.h>
+#include <wsutil/wslog.h>
+
+#include "wtap-int.h"
+#include "wtap_modules.h"
+#include "file_wrappers.h"
+#include "required_file_handlers.h"
+#include <wsutil/buffer.h>
+#include <wsutil/str_util.h>
+
+#include "lanalyzer.h"
+#include "ngsniffer.h"
+#include "radcom.h"
+#include "ascendtext.h"
+#include "nettl.h"
+#include "libpcap.h"
+#include "snoop.h"
+#include "iptrace.h"
+#include "iseries.h"
+#include "netmon.h"
+#include "netxray.h"
+#include "toshiba.h"
+#include "eyesdn.h"
+#include "i4btrace.h"
+#include "csids.h"
+#include "pppdump.h"
+#include "peekclassic.h"
+#include "peektagged.h"
+#include "vms.h"
+#include "dbs-etherwatch.h"
+#include "visual.h"
+#include "cosine.h"
+#include "5views.h"
+#include "erf.h"
+#include "hcidump.h"
+#include "logcat.h"
+#include "logcat_text.h"
+#include "json.h"
+#include "observer.h"
+#include "k12.h"
+#include "ber.h"
+#include "catapult_dct2000.h"
+#include "mp4.h"
+#include "mp2t.h"
+#include "mpeg.h"
+#include "netscreen.h"
+#include "commview.h"
+#include "pcapng.h"
+#include "aethra.h"
+#include "btsnoop.h"
+#include "tnef.h"
+#include "dct3trace.h"
+#include "packetlogger.h"
+#include "daintree-sna.h"
+#include "netscaler.h"
+#include "mime_file.h"
+#include "ipfix.h"
+#include "vwr.h"
+#include "camins.h"
+#include "stanag4607.h"
+#include "capsa.h"
+#include "nettrace_3gpp_32_423.h"
+#include "mplog.h"
+#include "dpa400.h"
+#include "rfc7468.h"
+#include "ruby_marshal.h"
+#include "systemd_journal.h"
+#include "log3gpp.h"
+#include "candump.h"
+#include "busmaster.h"
+#include "blf.h"
+#include "eri_enb_log.h"
+#include "autosar_dlt.h"
+#include "rtpdump.h"
+
+
+/*
+ * Add an extension, and all compressed versions thereof if requested,
+ * to a GSList of extensions.
+ */
+static GSList *
+add_extensions(GSList *extensions, const gchar *extension,
+ GSList *compression_type_extensions)
+{
+ /*
+ * Add the specified extension.
+ */
+ extensions = g_slist_prepend(extensions, g_strdup(extension));
+
+ /*
+ * Add whatever compressed versions we were supplied.
+ */
+ for (GSList *compression_type_extension = compression_type_extensions;
+ compression_type_extension != NULL;
+ compression_type_extension = g_slist_next(compression_type_extension)) {
+ extensions = g_slist_prepend(extensions,
+ ws_strdup_printf("%s.%s", extension,
+ (const char *)compression_type_extension->data));
+ }
+
+ return extensions;
+}
+
+/*
+ * File types that can be identified by file extensions.
+ *
+ * These are used in file open dialogs to offer choices of extensions
+ * for which to filter. Note that the first field can list more than
+ * one type of file, because, for example, ".cap" is a popular
+ * extension used by a number of capture file types.
+ *
+ * File types that *don't* have a file extension used for them should
+ * *not* be placed here; if there's nothing to put in the last field
+ * of the structure, don't put an entry here, not even one with an
+ * empty string for the extensions list.
+ *
+ * All added file types, regardless of extension or lack thereof,
+ * must also be added open_info_base[] below.
+ */
+static const struct file_extension_info file_type_extensions_base[] = {
+ { "Wireshark/tcpdump/... - pcap", TRUE, "pcap;cap;dmp" },
+ { "Wireshark/... - pcapng", TRUE, "pcapng;ntar;scap" },
+ { "Network Monitor, Surveyor, NetScaler", TRUE, "cap" },
+ { "Sun snoop", TRUE, "snoop" },
+ { "InfoVista 5View capture", TRUE, "5vw" },
+ { "Sniffer (DOS)", TRUE, "cap;enc;trc;fdc;syc" },
+ { "Cinco NetXRay, Sniffer (Windows)", TRUE, "cap;caz" },
+ { "Endace ERF capture", TRUE, "erf" },
+ { "EyeSDN USB S0/E1 ISDN trace format", TRUE, "trc" },
+ { "HP-UX nettl trace", TRUE, "trc0;trc1" },
+ { "Viavi Observer", TRUE, "bfr" },
+ { "Colasoft Capsa", TRUE, "cscpkt" },
+ { "Novell LANalyzer", TRUE, "tr1" },
+ { "Tektronix K12xx 32-bit .rf5 format", TRUE, "rf5" },
+ { "Savvius *Peek", TRUE, "pkt;tpc;apc;wpz" },
+ { "Catapult DCT2000 trace (.out format)", TRUE, "out" },
+ { "Micropross mplog", TRUE, "mplog" },
+ { "TamoSoft CommView NCF", TRUE, "ncf" },
+ { "TamoSoft CommView NCFX", TRUE, "ncfx" },
+ { "Symbian OS btsnoop", TRUE, "log" },
+ { "XML files (including Gammu DCT3 traces)", TRUE, "xml" },
+ { "macOS PacketLogger", TRUE, "pklg" },
+ { "Daintree SNA", TRUE, "dcf" },
+ { "IPFIX File Format", TRUE, "pfx;ipfix" },
+ { "Aethra .aps file", TRUE, "aps" },
+ { "MPEG2 transport stream", TRUE, "mp2t;ts;mpg" },
+ { "Ixia IxVeriWave .vwr Raw 802.11 Capture", TRUE, "vwr" },
+ { "CAM Inspector file", TRUE, "camins" },
+ { "BLF file", TRUE, "blf" },
+ { "AUTOSAR DLT file", TRUE, "dlt" },
+ { "MPEG files", FALSE, "mpeg;mpg;mp3" },
+ { "Transport-Neutral Encapsulation Format", FALSE, "tnef" },
+ { "JPEG/JFIF files", FALSE, "jpg;jpeg;jfif" },
+ { "JavaScript Object Notation file", FALSE, "json" },
+ { "MP4 file", FALSE, "mp4" },
+ { "RTPDump file", FALSE, "rtp;rtpdump" },
+};
+
+#define N_FILE_TYPE_EXTENSIONS (sizeof file_type_extensions_base / sizeof file_type_extensions_base[0])
+
+static const struct file_extension_info* file_type_extensions = NULL;
+
+static GArray* file_type_extensions_arr = NULL;
+
+/* initialize the extensions array if it has not been initialized yet */
+static void
+init_file_type_extensions(void)
+{
+
+ if (file_type_extensions_arr) return;
+
+ file_type_extensions_arr = g_array_new(FALSE,TRUE,sizeof(struct file_extension_info));
+
+ g_array_append_vals(file_type_extensions_arr,file_type_extensions_base,N_FILE_TYPE_EXTENSIONS);
+
+ file_type_extensions = (struct file_extension_info*)(void *)file_type_extensions_arr->data;
+}
+
+void
+wtap_register_file_type_extension(const struct file_extension_info *ei)
+{
+ init_file_type_extensions();
+
+ g_array_append_val(file_type_extensions_arr,*ei);
+
+ file_type_extensions = (const struct file_extension_info*)(void *)file_type_extensions_arr->data;
+}
+
+int
+wtap_get_num_file_type_extensions(void)
+{
+ return file_type_extensions_arr->len;
+}
+
+const char *
+wtap_get_file_extension_type_name(int extension_type)
+{
+ return file_type_extensions[extension_type].name;
+}
+
+static GSList *
+add_extensions_for_file_extensions_type(int extension_type, GSList *extensions,
+ GSList *compression_type_extensions)
+{
+ gchar **extensions_set, **extensionp, *extension;
+
+ /*
+ * Split the extension-list string into a set of extensions.
+ */
+ extensions_set = g_strsplit(file_type_extensions[extension_type].extensions,
+ ";", 0);
+
+ /*
+ * Add each of those extensions to the list.
+ */
+ for (extensionp = extensions_set; *extensionp != NULL; extensionp++) {
+ extension = *extensionp;
+
+ /*
+ * Add the extension, and all compressed variants
+ * of it.
+ */
+ extensions = add_extensions(extensions, extension,
+ compression_type_extensions);
+ }
+
+ g_strfreev(extensions_set);
+ return extensions;
+}
+
+/* Return a list of file extensions that are used by the specified file
+ * extension type.
+ *
+ * All strings in the list are allocated with g_malloc() and must be freed
+ * with g_free().
+ */
+GSList *
+wtap_get_file_extension_type_extensions(guint extension_type)
+{
+ GSList *extensions, *compression_type_extensions;
+
+ if (extension_type >= file_type_extensions_arr->len)
+ return NULL; /* not a valid extension type */
+
+ extensions = NULL; /* empty list, to start with */
+
+ /*
+ * Get compression-type extensions, if any.
+ */
+ compression_type_extensions = wtap_get_all_compression_type_extensions_list();
+
+ /*
+ * Add all this file extension type's extensions, with compressed
+ * variants.
+ */
+ extensions = add_extensions_for_file_extensions_type(extension_type,
+ extensions, compression_type_extensions);
+
+ g_slist_free(compression_type_extensions);
+
+ return extensions;
+}
+
+/*
+ * The open_file_* routines must return:
+ *
+ * WTAP_OPEN_ERROR on an I/O error;
+ *
+ * WTAP_OPEN_MINE if the file they're reading is one of the types
+ * it handles;
+ *
+ * WTAP_OPEN_NOT_MINE if the file they're reading isn't the type
+ * they're checking for.
+ *
+ * If the routine handles this type of file, it must set the "file_type"
+ * field in the "struct wtap" to the type of the file.
+ *
+ * Note that the routine does *not* have to free the private data pointer on
+ * error. The caller takes care of that by calling wtap_close on error.
+ * (See https://gitlab.com/wireshark/wireshark/-/issues/8518)
+ *
+ * However, the caller *does* have to free the private data pointer when
+ * returning WTAP_OPEN_NOT_MINE, since the next file type will be called
+ * and will likely just overwrite the pointer.
+ *
+ * The names are used in file open dialogs to select, for files that
+ * don't have magic numbers and that could potentially be files of
+ * more than one type based on the heuristics, a particular file
+ * type to interpret it as, if the file name has no extension, the
+ * extension isn't sufficient to determine the appropriate file type,
+ * or the extension is wrong.
+ *
+ * NOTE: when adding file formats to this list you may also want to add them
+ * to the following files so that the various desktop environments will
+ * know that Wireshark can open the file:
+ * 1) resources/freedesktop/org.wireshark.Wireshark-mime.xml (for freedesktop.org environments)
+ * 2) packaging/macosx/WiresharkInfo.plist.in (for macOS)
+ *
+ * If your file format has a commonly-used extension (e.g., ".pcap") then you
+ * should probably also add it to file_type_extensions_base[] (in this file),
+ * to the list of "<glob pattern=...>" entries for this file format in
+ * resources/freedesktop/org.wireshark.Wireshark-mime.xml, to the
+ * CFBundleTypeExtensions array for this file format in
+ * packaging/macosx/WiresharkInfo.plist, and to the PushFileExtensions macro
+ * in packaging/nsis/wireshark-common.nsh and the File Associations in
+ * packaging/wix/ComponentGroups.wxi (for Windows).
+ */
+static const struct open_info open_info_base[] = {
+ /* Open routines that look for magic numbers */
+ { "Wireshark/tcpdump/... - pcap", OPEN_INFO_MAGIC, libpcap_open, NULL, NULL, NULL },
+ { "Wireshark/... - pcapng", OPEN_INFO_MAGIC, pcapng_open, NULL, NULL, NULL },
+ { "Sniffer (DOS)", OPEN_INFO_MAGIC, ngsniffer_open, NULL, NULL, NULL },
+ { "Snoop, Shomiti/Finisar Surveyor", OPEN_INFO_MAGIC, snoop_open, NULL, NULL, NULL },
+ { "AIX iptrace", OPEN_INFO_MAGIC, iptrace_open, NULL, NULL, NULL },
+ { "Microsoft Network Monitor", OPEN_INFO_MAGIC, netmon_open, NULL, NULL, NULL },
+ { "Cinco NetXray/Sniffer (Windows)", OPEN_INFO_MAGIC, netxray_open, NULL, NULL, NULL },
+ { "RADCOM WAN/LAN analyzer", OPEN_INFO_MAGIC, radcom_open, NULL, NULL, NULL },
+ { "HP-UX nettl trace", OPEN_INFO_MAGIC, nettl_open, NULL, NULL, NULL },
+ { "Visual Networks traffic capture", OPEN_INFO_MAGIC, visual_open, NULL, NULL, NULL },
+ { "InfoVista 5View capture", OPEN_INFO_MAGIC, _5views_open, NULL, NULL, NULL },
+ { "Viavi Observer", OPEN_INFO_MAGIC, observer_open, NULL, NULL, NULL },
+ { "Savvius tagged", OPEN_INFO_MAGIC, peektagged_open, NULL, NULL, NULL },
+ { "Colasoft Capsa", OPEN_INFO_MAGIC, capsa_open, NULL, NULL, NULL },
+ { "DBS Etherwatch (VMS)", OPEN_INFO_MAGIC, dbs_etherwatch_open, NULL, NULL, NULL },
+ { "Tektronix K12xx 32-bit .rf5 format", OPEN_INFO_MAGIC, k12_open, NULL, NULL, NULL },
+ { "Catapult DCT2000 trace (.out format)", OPEN_INFO_MAGIC, catapult_dct2000_open, NULL, NULL, NULL },
+ { "Aethra .aps file", OPEN_INFO_MAGIC, aethra_open, NULL, NULL, NULL },
+ { "Symbian OS btsnoop", OPEN_INFO_MAGIC, btsnoop_open, "log", NULL, NULL },
+ { "EyeSDN USB S0/E1 ISDN trace format", OPEN_INFO_MAGIC, eyesdn_open, NULL, NULL, NULL },
+ { "Transport-Neutral Encapsulation Format", OPEN_INFO_MAGIC, tnef_open, NULL, NULL, NULL },
+ /* 3GPP TS 32.423 Trace must come before MIME Files as it's XML based*/
+ { "3GPP TS 32.423 Trace format", OPEN_INFO_MAGIC, nettrace_3gpp_32_423_file_open, NULL, NULL, NULL },
+ /* Gammu DCT3 trace must come before MIME files as it's XML based*/
+ { "Gammu DCT3 trace", OPEN_INFO_MAGIC, dct3trace_open, NULL, NULL, NULL },
+ { "BLF Logfile", OPEN_INFO_MAGIC, blf_open, NULL, NULL, NULL },
+ { "AUTOSAR DLT Logfile", OPEN_INFO_MAGIC, autosar_dlt_open, NULL, NULL, NULL },
+ { "RTPDump files", OPEN_INFO_MAGIC, rtpdump_open, NULL, NULL, NULL },
+ { "MIME Files Format", OPEN_INFO_MAGIC, mime_file_open, NULL, NULL, NULL },
+ { "Micropross mplog", OPEN_INFO_MAGIC, mplog_open, NULL, NULL, NULL },
+ { "Unigraf DPA-400 capture", OPEN_INFO_MAGIC, dpa400_open, NULL, NULL, NULL },
+ { "RFC 7468 files", OPEN_INFO_MAGIC, rfc7468_open, NULL, NULL, NULL },
+
+ /* Open routines that have no magic numbers and require heuristics. */
+ { "Novell LANalyzer", OPEN_INFO_HEURISTIC, lanalyzer_open, "tr1", NULL, NULL },
+ /*
+ * PacketLogger must come before MPEG, because its files
+ * are sometimes grabbed by mpeg_open.
+ */
+ { "macOS PacketLogger", OPEN_INFO_HEURISTIC, packetlogger_open, "pklg", NULL, NULL },
+ /* Some MPEG files have magic numbers, others just have heuristics. */
+ { "MPEG", OPEN_INFO_HEURISTIC, mpeg_open, "mpeg;mpg;mp3", NULL, NULL },
+ { "Daintree SNA", OPEN_INFO_HEURISTIC, daintree_sna_open, "dcf", NULL, NULL },
+ { "STANAG 4607 Format", OPEN_INFO_HEURISTIC, stanag4607_open, NULL, NULL, NULL },
+ { "ASN.1 Basic Encoding Rules", OPEN_INFO_HEURISTIC, ber_open, NULL, NULL, NULL },
+ /*
+ * I put NetScreen *before* erf, because there were some
+ * false positives with my test-files (Sake Blok, July 2007)
+ *
+ * I put VWR *after* ERF, because there were some cases where
+ * ERF files were misidentified as vwr files (Stephen
+ * Donnelly, August 2013; see bug 9054)
+ *
+ * I put VWR *after* Peek Classic, CommView, iSeries text,
+ * Toshiba text, K12 text, VMS tcpiptrace text, and NetScaler,
+ * because there were some cases where files of those types were
+ * misidentified as vwr files (Guy Harris, December 2013)
+ */
+ { "NetScreen snoop text file", OPEN_INFO_HEURISTIC, netscreen_open, "txt", NULL, NULL },
+ { "Endace ERF capture", OPEN_INFO_HEURISTIC, erf_open, "erf", NULL, NULL },
+ { "IPFIX File Format", OPEN_INFO_HEURISTIC, ipfix_open, "pfx;ipfix",NULL, NULL },
+ { "K12 text file", OPEN_INFO_HEURISTIC, k12text_open, "txt", NULL, NULL },
+ { "Savvius classic", OPEN_INFO_HEURISTIC, peekclassic_open, "pkt;tpc;apc;wpz", NULL, NULL },
+ { "pppd log (pppdump format)", OPEN_INFO_HEURISTIC, pppdump_open, NULL, NULL, NULL },
+ { "IBM iSeries comm. trace", OPEN_INFO_HEURISTIC, iseries_open, "txt", NULL, NULL },
+ { "I4B ISDN trace", OPEN_INFO_HEURISTIC, i4btrace_open, NULL, NULL, NULL },
+ { "MPEG2 transport stream", OPEN_INFO_HEURISTIC, mp2t_open, "mp2t;ts;mpg", NULL, NULL },
+ { "CSIDS IPLog", OPEN_INFO_HEURISTIC, csids_open, NULL, NULL, NULL },
+ { "TCPIPtrace (VMS)", OPEN_INFO_HEURISTIC, vms_open, "txt", NULL, NULL },
+ { "CoSine IPSX L2 capture", OPEN_INFO_HEURISTIC, cosine_open, "txt", NULL, NULL },
+ { "Bluetooth HCI dump", OPEN_INFO_HEURISTIC, hcidump_open, NULL, NULL, NULL },
+ { "TamoSoft CommView NCF", OPEN_INFO_HEURISTIC, commview_ncf_open, "ncf", NULL, NULL },
+ { "TamoSoft CommView NCFX", OPEN_INFO_HEURISTIC, commview_ncfx_open, "ncfx", NULL, NULL },
+ { "NetScaler", OPEN_INFO_HEURISTIC, nstrace_open, "cap", NULL, NULL },
+ { "Android Logcat Binary format", OPEN_INFO_HEURISTIC, logcat_open, "logcat", NULL, NULL },
+ { "Android Logcat Text formats", OPEN_INFO_HEURISTIC, logcat_text_open, "txt", NULL, NULL },
+ { "Candump log", OPEN_INFO_HEURISTIC, candump_open, NULL, NULL, NULL },
+ { "Busmaster log", OPEN_INFO_HEURISTIC, busmaster_open, NULL, NULL, NULL },
+ { "Ericsson eNode-B raw log", OPEN_INFO_MAGIC, eri_enb_log_open, NULL, NULL, NULL },
+ { "Systemd Journal", OPEN_INFO_HEURISTIC, systemd_journal_open, "log;jnl;journal", NULL, NULL },
+
+ /* ASCII trace files from Telnet sessions. */
+ { "Lucent/Ascend access server trace", OPEN_INFO_HEURISTIC, ascend_open, "txt", NULL, NULL },
+ { "Toshiba Compact ISDN Router snoop", OPEN_INFO_HEURISTIC, toshiba_open, "txt", NULL, NULL },
+ /* Extremely weak heuristics - put them at the end. */
+ { "Ixia IxVeriWave .vwr Raw Capture", OPEN_INFO_HEURISTIC, vwr_open, "vwr", NULL, NULL },
+ { "CAM Inspector file", OPEN_INFO_HEURISTIC, camins_open, "camins", NULL, NULL },
+ { "JavaScript Object Notation", OPEN_INFO_HEURISTIC, json_open, "json", NULL, NULL },
+ { "Ruby Marshal Object", OPEN_INFO_HEURISTIC, ruby_marshal_open, "", NULL, NULL },
+ { "3gpp phone log", OPEN_INFO_MAGIC, log3gpp_open, "log", NULL, NULL },
+ { "MP4 media file", OPEN_INFO_MAGIC, mp4_open, "mp4", NULL, NULL },
+
+};
+
+/* this is only used to build the dynamic array on load, do NOT use this
+ * for anything else, because the size of the actual array will change if
+ * Lua scripts register a new file reader.
+ */
+#define N_OPEN_INFO_ROUTINES ((sizeof open_info_base / sizeof open_info_base[0]))
+
+static GArray *open_info_arr = NULL;
+
+/* this always points to the top of the created array */
+struct open_info *open_routines = NULL;
+
+/* this points to the first OPEN_INFO_HEURISTIC type in the array */
+static guint heuristic_open_routine_idx = 0;
+
+static void
+set_heuristic_routine(void)
+{
+ guint i;
+ ws_assert(open_info_arr != NULL);
+
+ for (i = 0; i < open_info_arr->len; i++) {
+ if (open_routines[i].type == OPEN_INFO_HEURISTIC) {
+ heuristic_open_routine_idx = i;
+ break;
+ }
+ /* sanity check */
+ ws_assert(open_routines[i].type == OPEN_INFO_MAGIC);
+ }
+
+ ws_assert(heuristic_open_routine_idx > 0);
+}
+
+void
+init_open_routines(void)
+{
+ unsigned int i;
+ struct open_info *i_open;
+
+ if (open_info_arr)
+ return;
+
+ open_info_arr = g_array_new(TRUE,TRUE,sizeof(struct open_info));
+
+ g_array_append_vals(open_info_arr, open_info_base, N_OPEN_INFO_ROUTINES);
+
+ open_routines = (struct open_info *)(void*) open_info_arr->data;
+
+ /* Populate the extensions_set list now */
+ for (i = 0, i_open = open_routines; i < open_info_arr->len; i++, i_open++) {
+ if (i_open->extensions != NULL)
+ i_open->extensions_set = g_strsplit(i_open->extensions, ";", 0);
+ }
+
+ set_heuristic_routine();
+}
+
+/*
+ * Registers a new file reader - currently only called by wslua code for
+ * Lua readers and by compiled file reader plugins.
+ *
+ * If first_routine is true, the reader added before other readers of its
+ * type (magic or heuristic). This should be done only in cases where
+ * this reader's open test must be performed early, to avoid false
+ * positives for other readers' tests treating files for this reader
+ * as being for another reader.
+ *
+ * XXX - given that there is no guarantee that registration routines will
+ * be called in a given order, all this really does is divide readers for
+ * a given type (magic or heuristic) into two categories, with open routines
+ * for readers in the first category (first_routine true) all being called
+ * before readers in the second category; it does not guarantee a particular
+ * total order for open routines.
+ *
+ * Checks for an existing reader of the same name and errors if it finds one;
+ * if you want to handle that condition more gracefully, call
+ * wtap_has_open_info() first.
+ */
+void
+wtap_register_open_info(struct open_info *oi, const gboolean first_routine)
+{
+ if (!oi || !oi->name) {
+ ws_error("No open_info name given to register");
+ return;
+ }
+
+ /* verify name doesn't already exist */
+ if (wtap_has_open_info(oi->name)) {
+ ws_error("Name given to register_open_info already exists");
+ return;
+ }
+
+ if (oi->extensions != NULL)
+ oi->extensions_set = g_strsplit(oi->extensions, ";", 0);
+
+ /* if it's magic and first, prepend it; if it's heuristic and not first,
+ append it; if it's anything else, stick it in the middle */
+ if (first_routine && oi->type == OPEN_INFO_MAGIC) {
+ g_array_prepend_val(open_info_arr, *oi);
+ } else if (!first_routine && oi->type == OPEN_INFO_HEURISTIC) {
+ g_array_append_val(open_info_arr, *oi);
+ } else {
+ g_array_insert_val(open_info_arr, heuristic_open_routine_idx, *oi);
+ }
+
+ open_routines = (struct open_info *)(void*) open_info_arr->data;
+ set_heuristic_routine();
+}
+
+/* De-registers a file reader by removing it from the GArray based on its name.
+ * This function must NOT be called during wtap_open_offline(), since it changes the array.
+ * Note: this function will error if it doesn't find the given name; if you want to handle
+ * that condition more gracefully, call wtap_has_open_info() first.
+ */
+void
+wtap_deregister_open_info(const gchar *name)
+{
+ guint i;
+
+ if (!name) {
+ ws_error("Missing open_info name to de-register");
+ return;
+ }
+
+ for (i = 0; i < open_info_arr->len; i++) {
+ if (open_routines[i].name && strcmp(open_routines[i].name, name) == 0) {
+ g_strfreev(open_routines[i].extensions_set);
+ open_info_arr = g_array_remove_index(open_info_arr, i);
+ set_heuristic_routine();
+ return;
+ }
+ }
+
+ ws_error("deregister_open_info: name not found");
+}
+
+/* Determines if a open routine short name already exists
+ */
+gboolean
+wtap_has_open_info(const gchar *name)
+{
+ guint i;
+
+ if (!name) {
+ ws_error("No name given to wtap_has_open_info!");
+ return FALSE;
+ }
+
+
+ for (i = 0; i < open_info_arr->len; i++) {
+ if (open_routines[i].name && strcmp(open_routines[i].name, name) == 0) {
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+gboolean
+wtap_uses_lua_filehandler(const wtap* wth)
+{
+ if (wth && wth->wslua_data != NULL) {
+ /*
+ * Currently, wslua_data is set if and only if using a Lua
+ * file handler.
+ */
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/*
+ * Visual C++ on Win32 systems doesn't define these. (Old UNIX systems don't
+ * define them either.)
+ *
+ * Visual C++ on Win32 systems doesn't define S_IFIFO, it defines _S_IFIFO.
+ */
+#ifndef S_ISREG
+#define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG)
+#endif
+#ifndef S_IFIFO
+#define S_IFIFO _S_IFIFO
+#endif
+#ifndef S_ISFIFO
+#define S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFIFO)
+#endif
+#ifndef S_ISDIR
+#define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
+#endif
+
+/* returns the 'type' number to use for wtap_open_offline based on the
+ * passed-in name (the name in the open_info struct). It returns WTAP_TYPE_AUTO
+ * on failure, which is the number 0. The 'type' number is the entry's index+1,
+ * because that's what wtap_open_offline() expects it to be.
+ */
+unsigned int
+open_info_name_to_type(const char *name)
+{
+ unsigned int i;
+
+ if (!name)
+ return WTAP_TYPE_AUTO;
+
+ for (i = 0; i < open_info_arr->len; i++) {
+ if (open_routines[i].name != NULL &&
+ strcmp(name, open_routines[i].name) == 0)
+ return i+1;
+ }
+
+ return WTAP_TYPE_AUTO; /* no such file type */
+}
+
+static char *
+get_file_extension(const char *pathname)
+{
+ gchar *filename;
+ gchar **components;
+ size_t ncomponents;
+ gchar *extensionp;
+
+ /*
+ * Is the pathname empty?
+ */
+ if (strcmp(pathname, "") == 0)
+ return NULL; /* no extension */
+
+ /*
+ * Find the last component of the pathname.
+ */
+ filename = g_path_get_basename(pathname);
+
+ /*
+ * Does it have an extension?
+ */
+ if (strchr(filename, '.') == NULL) {
+ g_free(filename);
+ return NULL; /* no extension whatsoever */
+ }
+
+ /*
+ * Yes. Fold it to lowercase, since open_routines[] has
+ * its extensions in lowercase.
+ */
+ ascii_strdown_inplace(filename);
+
+ /*
+ * Split it into components separated by ".".
+ */
+ components = g_strsplit(filename, ".", 0);
+ g_free(filename);
+
+ /*
+ * Count the components.
+ */
+ for (ncomponents = 0; components[ncomponents] != NULL; ncomponents++)
+ ;
+
+ if (ncomponents == 0) {
+ g_strfreev(components);
+ return NULL; /* no components */
+ }
+ if (ncomponents == 1) {
+ g_strfreev(components);
+ return NULL; /* only one component, with no "." */
+ }
+
+ /*
+ * Get compression-type extensions, if any.
+ */
+ GSList *compression_type_extensions = wtap_get_all_compression_type_extensions_list();
+
+ /*
+ * Is the last component one of the extensions used for compressed
+ * files?
+ */
+ extensionp = components[ncomponents - 1];
+ for (GSList *compression_type_extension = compression_type_extensions;
+ compression_type_extension != NULL;
+ compression_type_extension = g_slist_next(compression_type_extension)) {
+ if (strcmp(extensionp, (const char *)compression_type_extension->data) == 0) {
+ /*
+ * Yes, so it's one of the compressed-file extensions.
+ * Is there an extension before that?
+ */
+ if (ncomponents == 2) {
+ g_slist_free(compression_type_extensions);
+ g_strfreev(components);
+ return NULL; /* no, only two components */
+ }
+
+ /*
+ * Yes, return that extension.
+ */
+ g_slist_free(compression_type_extensions);
+ extensionp = g_strdup(components[ncomponents - 2]);
+ g_strfreev(components);
+ return extensionp;
+ }
+ }
+
+ g_slist_free(compression_type_extensions);
+
+ /*
+ * The extension isn't one of the compressed-file extensions;
+ * return it.
+ */
+ extensionp = g_strdup(extensionp);
+ g_strfreev(components);
+ return extensionp;
+}
+
+/*
+ * Check if file extension is used in this heuristic
+ */
+static gboolean
+heuristic_uses_extension(unsigned int i, const char *extension)
+{
+ gchar **extensionp;
+
+ /*
+ * Does this file type *have* any extensions?
+ */
+ if (open_routines[i].extensions == NULL)
+ return FALSE; /* no */
+
+ /*
+ * Check each of them against the specified extension.
+ */
+ for (extensionp = open_routines[i].extensions_set; *extensionp != NULL;
+ extensionp++) {
+ if (strcmp(extension, *extensionp) == 0) {
+ return TRUE; /* it's one of them */
+ }
+ }
+
+ return FALSE; /* it's not one of them */
+}
+
+/* Opens a file and prepares a wtap struct.
+ * If "do_random" is TRUE, it opens the file twice; the second open
+ * allows the application to do random-access I/O without moving
+ * the seek offset for sequential I/O, which is used by Wireshark
+ * so that it can do sequential I/O to a capture file that's being
+ * written to as new packets arrive independently of random I/O done
+ * to display protocol trees for packets when they're selected.
+ */
+wtap *
+wtap_open_offline(const char *filename, unsigned int type, int *err, char **err_info,
+ gboolean do_random)
+{
+ int fd;
+ ws_statb64 statb;
+ gboolean ispipe = FALSE;
+ wtap *wth;
+ unsigned int i;
+ gboolean use_stdin = FALSE;
+ gchar *extension;
+ wtap_block_t shb;
+
+ *err = 0;
+ *err_info = NULL;
+
+ /* open standard input if filename is '-' */
+ if (strcmp(filename, "-") == 0)
+ use_stdin = TRUE;
+
+ /* First, make sure the file is valid */
+ if (use_stdin) {
+ if (ws_fstat64(0, &statb) < 0) {
+ *err = errno;
+ return NULL;
+ }
+ } else {
+ if (ws_stat64(filename, &statb) < 0) {
+ *err = errno;
+ return NULL;
+ }
+ }
+ if (S_ISFIFO(statb.st_mode)) {
+ /*
+ * Opens of FIFOs are allowed only when not opening
+ * for random access.
+ *
+ * Currently, we do seeking when trying to find out
+ * the file type, but our I/O routines do some amount
+ * of buffering, and do backward seeks within the buffer
+ * if possible, so at least some file types can be
+ * opened from pipes, so we don't completely disallow opens
+ * of pipes.
+ */
+ if (do_random) {
+ *err = WTAP_ERR_RANDOM_OPEN_PIPE;
+ return NULL;
+ }
+ ispipe = TRUE;
+ } else if (S_ISDIR(statb.st_mode)) {
+ /*
+ * Return different errors for "this is a directory"
+ * and "this is some random special file type", so
+ * the user can get a potentially more helpful error.
+ */
+ *err = EISDIR;
+ return NULL;
+ } else if (! S_ISREG(statb.st_mode)) {
+ *err = WTAP_ERR_NOT_REGULAR_FILE;
+ return NULL;
+ }
+
+ /*
+ * We need two independent descriptors for random access, so
+ * they have different file positions. If we're opening the
+ * standard input, we can only dup it to get additional
+ * descriptors, so we can't have two independent descriptors,
+ * and thus can't do random access.
+ */
+ if (use_stdin && do_random) {
+ *err = WTAP_ERR_RANDOM_OPEN_STDIN;
+ return NULL;
+ }
+
+ errno = ENOMEM;
+ wth = g_new0(wtap, 1);
+
+ /* Open the file */
+ errno = WTAP_ERR_CANT_OPEN;
+ if (use_stdin) {
+ /*
+ * We dup FD 0, so that we don't have to worry about
+ * a file_close of wth->fh closing the standard
+ * input of the process.
+ */
+ fd = ws_dup(0);
+ if (fd < 0) {
+ *err = errno;
+ g_free(wth);
+ return NULL;
+ }
+#ifdef _WIN32
+ if (_setmode(fd, O_BINARY) == -1) {
+ /* "Shouldn't happen" */
+ *err = errno;
+ g_free(wth);
+ return NULL;
+ }
+#endif
+ if (!(wth->fh = file_fdopen(fd))) {
+ *err = errno;
+ ws_close(fd);
+ g_free(wth);
+ return NULL;
+ }
+ } else {
+ if (!(wth->fh = file_open(filename))) {
+ *err = errno;
+ g_free(wth);
+ return NULL;
+ }
+ }
+
+ if (do_random) {
+ if (!(wth->random_fh = file_open(filename))) {
+ *err = errno;
+ file_close(wth->fh);
+ g_free(wth);
+ return NULL;
+ }
+ } else
+ wth->random_fh = NULL;
+
+ /* initialization */
+ wth->ispipe = ispipe;
+ wth->file_encap = WTAP_ENCAP_UNKNOWN;
+ wth->subtype_sequential_close = NULL;
+ wth->subtype_close = NULL;
+ wth->file_tsprec = WTAP_TSPREC_USEC;
+ wth->pathname = g_strdup(filename);
+ wth->priv = NULL;
+ wth->wslua_data = NULL;
+ wth->shb_hdrs = g_array_new(FALSE, FALSE, sizeof(wtap_block_t));
+ shb = wtap_block_create(WTAP_BLOCK_SECTION);
+ if (shb)
+ g_array_append_val(wth->shb_hdrs, shb);
+
+ /* Initialize the array containing a list of interfaces. pcapng_open and
+ * erf_open needs this (and libpcap_open for ERF encapsulation types).
+ * Always initing it here saves checking for a NULL ptr later. */
+ wth->interface_data = g_array_new(FALSE, FALSE, sizeof(wtap_block_t));
+ /*
+ * Next interface data that wtap_get_next_interface_description()
+ * will return.
+ */
+ wth->next_interface_data = 0;
+
+ if (wth->random_fh) {
+ wth->fast_seek = g_ptr_array_new();
+
+ file_set_random_access(wth->fh, FALSE, wth->fast_seek);
+ file_set_random_access(wth->random_fh, TRUE, wth->fast_seek);
+ }
+
+ /* 'type' is 1 greater than the array index */
+ if (type != WTAP_TYPE_AUTO && type <= open_info_arr->len) {
+ int result;
+
+ if (file_seek(wth->fh, 0, SEEK_SET, err) == -1) {
+ /* I/O error - give up */
+ wtap_close(wth);
+ return NULL;
+ }
+
+ /* Set wth with wslua data if any - this is how we pass the data
+ * to the file reader, kinda like the priv member but not free'd later.
+ * It's ok for this to copy a NULL.
+ */
+ wth->wslua_data = open_routines[type - 1].wslua_data;
+
+ result = (*open_routines[type - 1].open_routine)(wth, err, err_info);
+
+ switch (result) {
+ case WTAP_OPEN_ERROR:
+ /* Error - give up */
+ wtap_close(wth);
+ return NULL;
+
+ case WTAP_OPEN_NOT_MINE:
+ /* No error, but not that type of file */
+ goto fail;
+
+ case WTAP_OPEN_MINE:
+ /* We found the file type */
+ goto success;
+ }
+ }
+
+ /* Try all file types that support magic numbers */
+ for (i = 0; i < heuristic_open_routine_idx; i++) {
+ /* Seek back to the beginning of the file; the open routine
+ * for the previous file type may have left the file
+ * position somewhere other than the beginning, and the
+ * open routine for this file type will probably want
+ * to start reading at the beginning.
+ *
+ * Initialize the data offset while we're at it.
+ */
+ if (file_seek(wth->fh, 0, SEEK_SET, err) == -1) {
+ /* Error - give up */
+ wtap_close(wth);
+ return NULL;
+ }
+
+ /* Set wth with wslua data if any - this is how we pass the data
+ * to the file reader, kinda like the priv member but not free'd later.
+ * It's ok for this to copy a NULL.
+ */
+ wth->wslua_data = open_routines[i].wslua_data;
+
+ switch ((*open_routines[i].open_routine)(wth, err, err_info)) {
+
+ case WTAP_OPEN_ERROR:
+ /* Error - give up */
+ wtap_close(wth);
+ return NULL;
+
+ case WTAP_OPEN_NOT_MINE:
+ /* No error, but not that type of file */
+ break;
+
+ case WTAP_OPEN_MINE:
+ /* We found the file type */
+ goto success;
+ }
+ }
+
+
+ /* Does this file's name have an extension? */
+ extension = get_file_extension(filename);
+ if (extension != NULL) {
+ /* Yes - try the heuristic types that use that extension first. */
+ for (i = heuristic_open_routine_idx; i < open_info_arr->len; i++) {
+ /* Does this type use that extension? */
+ if (heuristic_uses_extension(i, extension)) {
+ /* Yes. */
+ if (file_seek(wth->fh, 0, SEEK_SET, err) == -1) {
+ /* Error - give up */
+ g_free(extension);
+ wtap_close(wth);
+ return NULL;
+ }
+
+ /* Set wth with wslua data if any - this is how we pass the data
+ * to the file reader, kind of like priv but not free'd later.
+ */
+ wth->wslua_data = open_routines[i].wslua_data;
+
+ switch ((*open_routines[i].open_routine)(wth,
+ err, err_info)) {
+
+ case WTAP_OPEN_ERROR:
+ /* Error - give up */
+ g_free(extension);
+ wtap_close(wth);
+ return NULL;
+
+ case WTAP_OPEN_NOT_MINE:
+ /* No error, but not that type of file */
+ break;
+
+ case WTAP_OPEN_MINE:
+ /* We found the file type */
+ g_free(extension);
+ goto success;
+ }
+ }
+ }
+
+ /*
+ * Now try the heuristic types that have no extensions
+ * to check; we try those before the ones that have
+ * extensions that *don't* match this file's extension,
+ * on the theory that files of those types generally
+ * have one of the type's extensions, and, as this file
+ * *doesn't* have one of those extensions, it's probably
+ * *not* one of those files.
+ */
+ for (i = heuristic_open_routine_idx; i < open_info_arr->len; i++) {
+ /* Does this type have any extensions? */
+ if (open_routines[i].extensions == NULL) {
+ /* No. */
+ if (file_seek(wth->fh, 0, SEEK_SET, err) == -1) {
+ /* Error - give up */
+ g_free(extension);
+ wtap_close(wth);
+ return NULL;
+ }
+
+ /* Set wth with wslua data if any - this is how we pass the data
+ * to the file reader, kind of like priv but not free'd later.
+ */
+ wth->wslua_data = open_routines[i].wslua_data;
+
+ switch ((*open_routines[i].open_routine)(wth,
+ err, err_info)) {
+
+ case WTAP_OPEN_ERROR:
+ /* Error - give up */
+ g_free(extension);
+ wtap_close(wth);
+ return NULL;
+
+ case WTAP_OPEN_NOT_MINE:
+ /* No error, but not that type of file */
+ break;
+
+ case WTAP_OPEN_MINE:
+ /* We found the file type */
+ g_free(extension);
+ goto success;
+ }
+ }
+ }
+
+ /*
+ * Now try the ones that have extensions where none of
+ * them matches this file's extensions.
+ */
+ for (i = heuristic_open_routine_idx; i < open_info_arr->len; i++) {
+ /*
+ * Does this type have extensions and is this file's
+ * extension one of them?
+ */
+ if (open_routines[i].extensions != NULL &&
+ !heuristic_uses_extension(i, extension)) {
+ /* Yes and no. */
+ if (file_seek(wth->fh, 0, SEEK_SET, err) == -1) {
+ /* Error - give up */
+ g_free(extension);
+ wtap_close(wth);
+ return NULL;
+ }
+
+ /* Set wth with wslua data if any - this is how we pass the data
+ * to the file reader, kind of like priv but not free'd later.
+ */
+ wth->wslua_data = open_routines[i].wslua_data;
+
+ switch ((*open_routines[i].open_routine)(wth,
+ err, err_info)) {
+
+ case WTAP_OPEN_ERROR:
+ /* Error - give up */
+ g_free(extension);
+ wtap_close(wth);
+ return NULL;
+
+ case WTAP_OPEN_NOT_MINE:
+ /* No error, but not that type of file */
+ break;
+
+ case WTAP_OPEN_MINE:
+ /* We found the file type */
+ g_free(extension);
+ goto success;
+ }
+ }
+ }
+ g_free(extension);
+ } else {
+ /* No - try all the heuristics types in order. */
+ for (i = heuristic_open_routine_idx; i < open_info_arr->len; i++) {
+
+ if (file_seek(wth->fh, 0, SEEK_SET, err) == -1) {
+ /* Error - give up */
+ wtap_close(wth);
+ return NULL;
+ }
+
+ /* Set wth with wslua data if any - this is how we pass the data
+ * to the file reader, kind of like priv but not free'd later.
+ */
+ wth->wslua_data = open_routines[i].wslua_data;
+
+ switch ((*open_routines[i].open_routine)(wth, err, err_info)) {
+
+ case WTAP_OPEN_ERROR:
+ /* Error - give up */
+ wtap_close(wth);
+ return NULL;
+
+ case WTAP_OPEN_NOT_MINE:
+ /* No error, but not that type of file */
+ break;
+
+ case WTAP_OPEN_MINE:
+ /* We found the file type */
+ goto success;
+ }
+ }
+ }
+
+fail:
+
+ /* Well, it's not one of the types of file we know about. */
+ wtap_close(wth);
+ *err = WTAP_ERR_FILE_UNKNOWN_FORMAT;
+ return NULL;
+
+success:
+ return wth;
+}
+
+/*
+ * Given the pathname of the file we just closed with wtap_fdclose(), attempt
+ * to reopen that file and assign the new file descriptor(s) to the sequential
+ * stream and, if do_random is TRUE, to the random stream. Used on Windows
+ * after the rename of a file we had open was done or if the rename of a
+ * file on top of a file we had open failed.
+ *
+ * This is only required by Wireshark, not TShark, and, at the point that
+ * Wireshark is doing this, the sequential stream is closed, and the
+ * random stream is open, so this refuses to open pipes, and only
+ * reopens the random stream.
+ */
+gboolean
+wtap_fdreopen(wtap *wth, const char *filename, int *err)
+{
+ ws_statb64 statb;
+
+ /*
+ * We need two independent descriptors for random access, so
+ * they have different file positions. If we're opening the
+ * standard input, we can only dup it to get additional
+ * descriptors, so we can't have two independent descriptors,
+ * and thus can't do random access.
+ */
+ if (strcmp(filename, "-") == 0) {
+ *err = WTAP_ERR_RANDOM_OPEN_STDIN;
+ return FALSE;
+ }
+
+ /* First, make sure the file is valid */
+ if (ws_stat64(filename, &statb) < 0) {
+ *err = errno;
+ return FALSE;
+ }
+ if (S_ISFIFO(statb.st_mode)) {
+ /*
+ * Opens of FIFOs are not allowed; see above.
+ */
+ *err = WTAP_ERR_RANDOM_OPEN_PIPE;
+ return FALSE;
+ } else if (S_ISDIR(statb.st_mode)) {
+ /*
+ * Return different errors for "this is a directory"
+ * and "this is some random special file type", so
+ * the user can get a potentially more helpful error.
+ */
+ *err = EISDIR;
+ return FALSE;
+ } else if (! S_ISREG(statb.st_mode)) {
+ *err = WTAP_ERR_NOT_REGULAR_FILE;
+ return FALSE;
+ }
+
+ /* Open the file */
+ errno = WTAP_ERR_CANT_OPEN;
+ if (!file_fdreopen(wth->random_fh, filename)) {
+ *err = errno;
+ return FALSE;
+ }
+ if (strcmp(filename, wth->pathname) != 0) {
+ g_free(wth->pathname);
+ wth->pathname = g_strdup(filename);
+ }
+ return TRUE;
+}
+
+/* Table of the file types and subtypes for which we have support. */
+
+/*
+ * Pointer to the GArray holding the registered file types.
+ */
+static GArray* file_type_subtype_table_arr;
+
+/*
+ * Pointer to the table of registered file types in that GArray.
+ */
+static const struct file_type_subtype_info* file_type_subtype_table;
+
+/*
+ * Number of elements in the table for builtin file types/subtypes.
+ */
+static guint wtap_num_builtin_file_types_subtypes;
+
+/*
+ * Required builtin types.
+ */
+int pcap_file_type_subtype = -1;
+int pcap_nsec_file_type_subtype = -1;
+int pcapng_file_type_subtype = -1;
+
+/*
+ * Table for mapping old file type/subtype names to new ones for
+ * backwards compatibility.
+ */
+static GHashTable *type_subtype_name_map;
+
+/*
+ * Initialize the table of file types/subtypes with all the builtin
+ * types/subtypes.
+ */
+void
+wtap_init_file_type_subtypes(void)
+{
+ /* Don't do this twice. */
+ ws_assert(file_type_subtype_table_arr == NULL);
+
+ /*
+ * Estimate the number of file types/subtypes as twice the
+ * number of modules; that's probably an overestimate, as
+ * the average number of file types/subtypes registered by
+ * a module is > 1 but probably < 2, but that shouldn't
+ * waste too much memory.
+ *
+ * Add on 7 more for pcapng, pcap, nanosecond pcap, and the
+ * extra modified flavors of pcap.
+ */
+ file_type_subtype_table_arr = g_array_sized_new(FALSE, TRUE,
+ sizeof(struct file_type_subtype_info), wtap_module_count*2 + 7);
+ file_type_subtype_table = (const struct file_type_subtype_info*)(void *)file_type_subtype_table_arr->data;
+
+ /*
+ * Initialize the hash table for mapping old file type/subtype
+ * names to the corresponding new names.
+ */
+ type_subtype_name_map = g_hash_table_new_full(g_str_hash,
+ g_str_equal, g_free, g_free);
+
+ /* No entries yet, so no builtin entries yet. */
+ wtap_num_builtin_file_types_subtypes = 0;
+
+ /*
+ * Register the builtin entries that aren't in the table.
+ * First, do the required ones; register pcapng first, then
+ * pcap, so, at the beginning of the table, we have pcapng,
+ * pcap, nanosecond pcap, and the weird modified pcaps, so
+ * searches for file types that can write a file format
+ * start with pcapng, pcap, and nanosecond pcap.
+ */
+ register_pcapng();
+ register_pcap();
+
+ /* Now register the ones found by the build process */
+ for (guint i = 0; i < wtap_module_count; i++)
+ wtap_module_reg[i].cb_func();
+
+ /* Update the number of builtin entries. */
+ wtap_num_builtin_file_types_subtypes = file_type_subtype_table_arr->len;
+}
+
+/*
+ * Attempt to register a new file type/subtype; fails if a type/subtype
+ * with that name is already registered.
+ */
+int
+wtap_register_file_type_subtype(const struct file_type_subtype_info* fi)
+{
+ struct file_type_subtype_info* finfo;
+ guint file_type_subtype;
+
+ /*
+ * Check for required fields (description and name).
+ */
+ if (!fi || !fi->description || !fi->name) {
+ ws_warning("no file type info");
+ return -1;
+ }
+
+ /*
+ * There must be at least one block type that this file
+ * type/subtype supports.
+ */
+ if (fi->num_supported_blocks == 0 || fi->supported_blocks == NULL) {
+ ws_warning("no blocks supported by file type \"%s\"", fi->name);
+ return -1;
+ }
+
+ /*
+ * Is this type already registered?
+ */
+ if (wtap_name_to_file_type_subtype(fi->name) != -1) {
+ /*
+ * Yes. You don't get to replace an existing handler.
+ */
+ ws_warning("file type \"%s\" is already registered", fi->name);
+ return -1;
+ }
+
+ /*
+ * Is there a freed entry in the array, due to a file type
+ * being de-registered?
+ *
+ * Skip the built-in entries, as they're never deregistered.
+ */
+ for (file_type_subtype = wtap_num_builtin_file_types_subtypes;
+ file_type_subtype < file_type_subtype_table_arr->len;
+ file_type_subtype++) {
+ if (file_type_subtype_table[file_type_subtype].name == NULL) {
+ /*
+ * We found such an entry.
+ *
+ * Get the pointer from the GArray, so that we get a
+ * non-const pointer.
+ */
+ finfo = &g_array_index(file_type_subtype_table_arr, struct file_type_subtype_info, file_type_subtype);
+
+ /*
+ * Fill in the entry with the new values.
+ */
+ *finfo = *fi;
+
+ return (gint)file_type_subtype;
+ }
+ }
+
+ /*
+ * There aren't any free slots, so add a new entry.
+ * Get the number of current number of entries, which will
+ * be the index of the new entry, then append this entry
+ * to the end of the array, change file_type_subtype_table
+ * in case the array had to get reallocated, and return
+ * the index of the new entry.
+ */
+ file_type_subtype = file_type_subtype_table_arr->len;
+ g_array_append_val(file_type_subtype_table_arr, *fi);
+ file_type_subtype_table = (const struct file_type_subtype_info*)(void *)file_type_subtype_table_arr->data;
+ return file_type_subtype;
+}
+
+/* De-registers a file writer - they can never be removed from the GArray, but we can "clear" an entry.
+ */
+void
+wtap_deregister_file_type_subtype(const int subtype)
+{
+ struct file_type_subtype_info* finfo;
+
+ if (subtype < 0 || subtype >= (int)file_type_subtype_table_arr->len) {
+ ws_error("invalid file type to de-register");
+ return;
+ }
+ if ((guint)subtype < wtap_num_builtin_file_types_subtypes) {
+ ws_error("built-in file types cannot be de-registered");
+ return;
+ }
+
+ /*
+ * Get the pointer from the GArray, so that we get a non-const
+ * pointer.
+ */
+ finfo = &g_array_index(file_type_subtype_table_arr, struct file_type_subtype_info, subtype);
+ /*
+ * Clear out this entry.
+ */
+ finfo->description = NULL;
+ finfo->name = NULL;
+ finfo->default_file_extension = NULL;
+ finfo->additional_file_extensions = NULL;
+ finfo->writing_must_seek = FALSE;
+ finfo->num_supported_blocks = 0;
+ finfo->supported_blocks = NULL;
+ finfo->can_write_encap = NULL;
+ finfo->dump_open = NULL;
+ finfo->wslua_info = NULL;
+}
+
+/*
+ * Given a GArray of WTAP_ENCAP_ types, return the per-file encapsulation
+ * type that would be needed to write out a file with those types. If
+ * there's only one type, it's that type, otherwise it's
+ * WTAP_ENCAP_PER_PACKET.
+ */
+int
+wtap_dump_required_file_encap_type(const GArray *file_encaps)
+{
+ int encap;
+
+ encap = WTAP_ENCAP_PER_PACKET;
+ if (file_encaps->len == 1) {
+ /* OK, use the one-and-only encapsulation type. */
+ encap = g_array_index(file_encaps, gint, 0);
+ }
+ return encap;
+}
+
+gboolean
+wtap_dump_can_write_encap(int file_type_subtype, int encap)
+{
+ int result = 0;
+
+ if (file_type_subtype < 0 ||
+ file_type_subtype >= (int)file_type_subtype_table_arr->len ||
+ file_type_subtype_table[file_type_subtype].can_write_encap == NULL)
+ return FALSE;
+
+ result = (*file_type_subtype_table[file_type_subtype].can_write_encap)(encap);
+
+ if (result != 0) {
+ /* if the err said to check wslua's can_write_encap, try that */
+ if (result == WTAP_ERR_CHECK_WSLUA
+ && file_type_subtype_table[file_type_subtype].wslua_info != NULL
+ && file_type_subtype_table[file_type_subtype].wslua_info->wslua_can_write_encap != NULL) {
+
+ result = (*file_type_subtype_table[file_type_subtype].wslua_info->wslua_can_write_encap)(encap, file_type_subtype_table[file_type_subtype].wslua_info->wslua_data);
+
+ }
+
+ if (result != 0)
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/*
+ * Return TRUE if a capture with a given GArray of encapsulation types
+ * and a given bitset of comment types can be written in a specified
+ * format, and FALSE if it can't.
+ */
+static gboolean
+wtap_dump_can_write_format(int ft, const GArray *file_encaps,
+ guint32 required_comment_types)
+{
+ guint i;
+
+ /*
+ * Can we write in this format?
+ */
+ if (!wtap_dump_can_open(ft)) {
+ /* No. */
+ return FALSE;
+ }
+
+ /*
+ * Yes. Can we write out all the required comments in this
+ * format?
+ */
+ if (required_comment_types & WTAP_COMMENT_PER_SECTION) {
+ if (wtap_file_type_subtype_supports_option(ft,
+ WTAP_BLOCK_SECTION, OPT_COMMENT) == OPTION_NOT_SUPPORTED) {
+ /* Not section comments. */
+ return FALSE;
+ }
+ }
+ if (required_comment_types & WTAP_COMMENT_PER_INTERFACE) {
+ if (wtap_file_type_subtype_supports_option(ft,
+ WTAP_BLOCK_IF_ID_AND_INFO, OPT_COMMENT) == OPTION_NOT_SUPPORTED) {
+ /* Not interface comments. */
+ return FALSE;
+ }
+ }
+ if (required_comment_types & WTAP_COMMENT_PER_PACKET) {
+ if (wtap_file_type_subtype_supports_option(ft,
+ WTAP_BLOCK_PACKET, OPT_COMMENT) == OPTION_NOT_SUPPORTED) {
+ /* Not packet comments. */
+ return FALSE;
+ }
+ }
+
+ /*
+ * Yes. Is the required per-file encapsulation type supported?
+ * This might be WTAP_ENCAP_PER_PACKET.
+ */
+ if (!wtap_dump_can_write_encap(ft, wtap_dump_required_file_encap_type(file_encaps))) {
+ /* No. */
+ return FALSE;
+ }
+
+ /*
+ * Yes. Are all the individual encapsulation types supported?
+ */
+ for (i = 0; i < file_encaps->len; i++) {
+ if (!wtap_dump_can_write_encap(ft,
+ g_array_index(file_encaps, int, i))) {
+ /* No - one of them isn't. */
+ return FALSE;
+ }
+ }
+
+ /* Yes - we're OK. */
+ return TRUE;
+}
+
+/*
+ * Return TRUE if we can write a file with the given GArray of
+ * encapsulation types and the given bitmask of comment types.
+ */
+gboolean
+wtap_dump_can_write(const GArray *file_encaps, guint32 required_comment_types)
+{
+ int ft;
+
+ for (ft = 0; ft < (int)file_type_subtype_table_arr->len; ft++) {
+ /* To save a file with Wiretap, Wiretap has to handle that format,
+ * and its code to handle that format must be able to write a file
+ * with this file's encapsulation types.
+ */
+ if (wtap_dump_can_write_format(ft, file_encaps, required_comment_types)) {
+ /* OK, we can write it out in this type. */
+ return TRUE;
+ }
+ }
+
+ /* No, we couldn't save it in any format. */
+ return FALSE;
+}
+
+/*
+ * Sort by file type/subtype name.
+ */
+static int
+compare_file_type_subtypes_by_name(gconstpointer a, gconstpointer b)
+{
+ int file_type_subtype_a = *(const int *)a;
+ int file_type_subtype_b = *(const int *)b;
+
+ return strcmp(wtap_file_type_subtype_name(file_type_subtype_a),
+ wtap_file_type_subtype_name(file_type_subtype_b));
+}
+
+/*
+ * Sort by file type/subtype description.
+ */
+static int
+compare_file_type_subtypes_by_description(gconstpointer a, gconstpointer b)
+{
+ int file_type_subtype_a = *(const int *)a;
+ int file_type_subtype_b = *(const int *)b;
+
+ return strcmp(wtap_file_type_subtype_description(file_type_subtype_a),
+ wtap_file_type_subtype_description(file_type_subtype_b));
+}
+
+/*
+ * Get a GArray of file type/subtype values for file types/subtypes
+ * that can be used to save a file of a given type/subtype with a given
+ * GArray of encapsulation types and the given bitmask of comment types.
+ */
+GArray *
+wtap_get_savable_file_types_subtypes_for_file(int file_type_subtype,
+ const GArray *file_encaps, guint32 required_comment_types,
+ ft_sort_order sort_order)
+{
+ GArray *savable_file_types_subtypes;
+ int ft;
+ int default_file_type_subtype = -1;
+ int other_file_type_subtype = -1;
+
+ /* Can we save this file in its own file type/subtype? */
+ if (wtap_dump_can_write_format(file_type_subtype, file_encaps,
+ required_comment_types)) {
+ /* Yes - make that the default file type/subtype. */
+ default_file_type_subtype = file_type_subtype;
+ } else if (wtap_dump_can_write_format(pcap_file_type_subtype,
+ file_encaps,
+ required_comment_types)) {
+ /*
+ * No, but we can write it as a pcap file; make that
+ * the default file type/subtype.
+ */
+ default_file_type_subtype = pcap_file_type_subtype;
+ } else if (wtap_dump_can_write_format(pcapng_file_type_subtype,
+ file_encaps,
+ required_comment_types)) {
+ /*
+ * No, but we can write it as a pcapng file; make that
+ * the default file type/subtype.
+ */
+ default_file_type_subtype = pcapng_file_type_subtype;
+ } else {
+ /* OK, find the first file type/subtype we *can* save it as. */
+ default_file_type_subtype = -1;
+ for (ft = 0; ft < (int)file_type_subtype_table_arr->len; ft++) {
+ if (wtap_dump_can_write_format(ft, file_encaps,
+ required_comment_types)) {
+ /* OK, got it. */
+ default_file_type_subtype = ft;
+ break;
+ }
+ }
+ }
+
+ if (default_file_type_subtype == -1) {
+ /* We don't support writing this file as any file type/subtype. */
+ return NULL;
+ }
+
+ /*
+ * If the default is pcap, put pcapng right after it if we can
+ * also write it in pcapng format; otherwise, if the default is
+ * pcapng, put pcap right after it if we can also write it in
+ * pcap format.
+ */
+ if (default_file_type_subtype == pcap_file_type_subtype) {
+ if (wtap_dump_can_write_format(pcapng_file_type_subtype,
+ file_encaps,
+ required_comment_types))
+ other_file_type_subtype = pcapng_file_type_subtype;
+ } else if (default_file_type_subtype == pcapng_file_type_subtype) {
+ if (wtap_dump_can_write_format(pcap_file_type_subtype,
+ file_encaps,
+ required_comment_types))
+ other_file_type_subtype = pcap_file_type_subtype;
+ }
+
+ /* Allocate the array. */
+ savable_file_types_subtypes = g_array_new(FALSE, FALSE,
+ sizeof (int));
+
+ /*
+ * First, add the types we don't want to force to the
+ * beginning of the list.
+ */
+ for (ft = 0; ft < (int)file_type_subtype_table_arr->len; ft++) {
+ if (ft == default_file_type_subtype ||
+ ft == other_file_type_subtype)
+ continue; /* we will done this one later */
+ if (wtap_dump_can_write_format(ft, file_encaps,
+ required_comment_types)) {
+ /* OK, we can write it out in this type. */
+ g_array_append_val(savable_file_types_subtypes, ft);
+ }
+ }
+
+ /* Now, sort the list. */
+ g_array_sort(savable_file_types_subtypes,
+ (sort_order == FT_SORT_BY_NAME) ? compare_file_type_subtypes_by_name :
+ compare_file_type_subtypes_by_description);
+
+ /*
+ * If we have a type/subtype to put above the default one,
+ * do so.
+ *
+ * We put this type at the beginning before putting the
+ * default there, so the default is at the top.
+ */
+ if (other_file_type_subtype != -1)
+ g_array_prepend_val(savable_file_types_subtypes,
+ other_file_type_subtype);
+
+ /* Put the default file type/subtype first in the list. */
+ g_array_prepend_val(savable_file_types_subtypes,
+ default_file_type_subtype);
+
+ return savable_file_types_subtypes;
+}
+
+/*
+ * Get a GArray of all writable file type/subtype values.
+ */
+GArray *
+wtap_get_writable_file_types_subtypes(ft_sort_order sort_order)
+{
+ GArray *writable_file_types_subtypes;
+ int ft;
+
+ /*
+ * Allocate the array.
+ * Pre-allocate room enough for all types.
+ * XXX - that's overkill; just scan the table to find all the
+ * writable types and count them.
+ */
+ writable_file_types_subtypes = g_array_sized_new(FALSE, FALSE,
+ sizeof (int), file_type_subtype_table_arr->len);
+
+ /*
+ * First, add the types we don't want to force to the
+ * beginning of the list.
+ */
+ for (ft = 0; ft < (int)file_type_subtype_table_arr->len; ft++) {
+ if (ft == pcap_file_type_subtype ||
+ ft == pcapng_file_type_subtype)
+ continue; /* we've already done these two */
+ if (wtap_dump_can_open(ft)) {
+ /* OK, we can write this type. */
+ g_array_append_val(writable_file_types_subtypes, ft);
+ }
+ }
+
+ /* Now, sort the list. */
+ g_array_sort(writable_file_types_subtypes,
+ (sort_order == FT_SORT_BY_NAME) ? compare_file_type_subtypes_by_name :
+ compare_file_type_subtypes_by_description);
+
+ /*
+ * Now, put pcap and pcapng at the beginning, as they're
+ * our "native" formats. Put pcapng there first, and
+ * pcap before it.
+ */
+ if (pcapng_file_type_subtype != -1 &&
+ wtap_dump_can_open(pcapng_file_type_subtype)) {
+ /*
+ * We can write pcapng. (If we can't, that's a huge
+ * mistake.)
+ */
+ g_array_prepend_val(writable_file_types_subtypes,
+ pcapng_file_type_subtype);
+ }
+ if (pcap_file_type_subtype != -1 &&
+ wtap_dump_can_open(pcap_file_type_subtype)) {
+ /*
+ * We can write pcap. (If we can't, that's a huge
+ * mistake.)
+ */
+ g_array_prepend_val(writable_file_types_subtypes,
+ pcap_file_type_subtype);
+ }
+
+ return writable_file_types_subtypes;
+}
+
+/*
+ * String describing the file type/subtype.
+ */
+const char *
+wtap_file_type_subtype_description(int file_type_subtype)
+{
+ if (file_type_subtype < 0 ||
+ file_type_subtype >= (int)file_type_subtype_table_arr->len)
+ return NULL;
+ else
+ return file_type_subtype_table[file_type_subtype].description;
+}
+
+/*
+ * Name to use in, say, a command-line flag specifying the type/subtype.
+ */
+const char *
+wtap_file_type_subtype_name(int file_type_subtype)
+{
+ if (file_type_subtype < 0 ||
+ file_type_subtype >= (int)file_type_subtype_table_arr->len)
+ return NULL;
+ else
+ return file_type_subtype_table[file_type_subtype].name;
+}
+
+/*
+ * Register a backwards-compatibility name.
+ */
+void
+wtap_register_compatibility_file_subtype_name(const char *old_name,
+ const char *new_name)
+{
+ g_hash_table_insert(type_subtype_name_map, g_strdup(old_name),
+ g_strdup(new_name));
+}
+
+/*
+ * Translate a name to a capture file type/subtype.
+ */
+int
+wtap_name_to_file_type_subtype(const char *name)
+{
+ char *new_name;
+ int file_type_subtype;
+
+ /*
+ * Is this name a backwards-compatibility name?
+ */
+ new_name = (char *)g_hash_table_lookup(type_subtype_name_map,
+ (gpointer)name);
+ if (new_name != NULL) {
+ /*
+ * Yes, and new_name is the name to which it should
+ * be mapped.
+ */
+ name = new_name;
+ }
+ for (file_type_subtype = 0;
+ file_type_subtype < (int)file_type_subtype_table_arr->len;
+ file_type_subtype++) {
+ if (file_type_subtype_table[file_type_subtype].name != NULL &&
+ strcmp(name, file_type_subtype_table[file_type_subtype].name) == 0)
+ return file_type_subtype;
+ }
+
+ return -1; /* no such file type, or we can't write it */
+}
+
+/*
+ * Provide the file type/subtype for pcap.
+ */
+int
+wtap_pcap_file_type_subtype(void)
+{
+ /*
+ * Make sure pcap was registered as a file type/subtype;
+ * it's one of our "native" formats.
+ */
+ ws_assert(pcap_file_type_subtype != -1);
+ return pcap_file_type_subtype;
+}
+
+/*
+ * Provide the file type/subtype for nanosecond-resolution pcap.
+ */
+int
+wtap_pcap_nsec_file_type_subtype(void)
+{
+ /*
+ * Make sure nanosecond-resolution pcap was registered
+ * as a file type/subtype; it's one of our "native" formats.
+ */
+ ws_assert(pcap_nsec_file_type_subtype != -1);
+ return pcap_nsec_file_type_subtype;
+}
+
+/*
+ * Provide the file type/subtype for pcapng.
+ */
+int
+wtap_pcapng_file_type_subtype(void)
+{
+ /*
+ * Make sure pcapng was registered as a file type/subtype;
+ * it's one of our "native" formats.
+ */
+ ws_assert(pcapng_file_type_subtype != -1);
+ return pcapng_file_type_subtype;
+}
+
+/*
+ * Determine if a file type/subtype can write a block of the given type.
+ */
+block_support_t
+wtap_file_type_subtype_supports_block(int file_type_subtype,
+ wtap_block_type_t type)
+{
+ size_t num_supported_blocks;
+ const struct supported_block_type *supported_blocks;
+
+ if (file_type_subtype < 0 ||
+ file_type_subtype >= (int)file_type_subtype_table_arr->len) {
+ /*
+ * There's no such file type, so it can't support any
+ * blocks.
+ */
+ return BLOCK_NOT_SUPPORTED;
+ }
+
+ num_supported_blocks = file_type_subtype_table[file_type_subtype].num_supported_blocks;
+ supported_blocks = file_type_subtype_table[file_type_subtype].supported_blocks;
+
+ for (size_t block_idx = 0; block_idx < num_supported_blocks;
+ block_idx++) {
+ if (supported_blocks[block_idx].type == type)
+ return supported_blocks[block_idx].support;
+ }
+
+ /*
+ * Not found, which means not supported.
+ */
+ return BLOCK_NOT_SUPPORTED;
+}
+
+/*
+ * Determine if a file type/subtype, when writing a block of the given type,
+ * can support adding the given option to the block.
+ */
+option_support_t
+wtap_file_type_subtype_supports_option(int file_type_subtype,
+ wtap_block_type_t type, guint option)
+{
+ size_t num_supported_blocks;
+ const struct supported_block_type *supported_blocks;
+
+ if (file_type_subtype < 0 ||
+ file_type_subtype >= (int)file_type_subtype_table_arr->len) {
+ /*
+ * There's no such file type, so it can't support any
+ * blocks, and thus can't support any options.
+ */
+ return OPTION_NOT_SUPPORTED;
+ }
+
+ num_supported_blocks = file_type_subtype_table[file_type_subtype].num_supported_blocks;
+ supported_blocks = file_type_subtype_table[file_type_subtype].supported_blocks;
+
+ for (size_t block_idx = 0; block_idx < num_supported_blocks;
+ block_idx++) {
+ if (supported_blocks[block_idx].type == type) {
+ /*
+ * OK, that block is known.
+ * Is it supported?
+ */
+ if (supported_blocks[block_idx].support == BLOCK_NOT_SUPPORTED) {
+ /*
+ * No, so clearly the option isn't
+ * supported in that block.
+ */
+ return OPTION_NOT_SUPPORTED;
+ }
+
+ /*
+ * Yes, so check the options.
+ */
+ size_t num_supported_options;
+ const struct supported_option_type *supported_options;
+
+ num_supported_options = supported_blocks[block_idx].num_supported_options;
+ supported_options = supported_blocks[block_idx].supported_options;
+ for (size_t opt_idx = 0; opt_idx < num_supported_options;
+ opt_idx++) {
+ if (supported_options[opt_idx].opt == option)
+ return supported_options[opt_idx].support;
+ }
+
+ /*
+ * Not found, which means not supported.
+ */
+ return OPTION_NOT_SUPPORTED;
+ }
+ }
+
+ /*
+ * The block type wasn't found, which means it's not supported,
+ * which means the option isn't supported in that block.
+ */
+ return OPTION_NOT_SUPPORTED;
+}
+
+static GSList *
+add_extensions_for_file_type_subtype(int file_type_subtype, GSList *extensions,
+ GSList *compression_type_extensions)
+{
+ gchar **extensions_set, **extensionp;
+ gchar *extension;
+
+ if (file_type_subtype < 0 ||
+ file_type_subtype >= (int)file_type_subtype_table_arr->len) {
+ /*
+ * There's no such file type, so it has no extensions
+ * to add.
+ */
+ return extensions;
+ }
+
+ /*
+ * Add the default extension, and all of the compressed variants
+ * from the list of compressed-file extensions, if there is a
+ * default extension.
+ */
+ if (file_type_subtype_table[file_type_subtype].default_file_extension != NULL) {
+ extensions = add_extensions(extensions,
+ file_type_subtype_table[file_type_subtype].default_file_extension,
+ compression_type_extensions);
+ }
+
+ if (file_type_subtype_table[file_type_subtype].additional_file_extensions != NULL) {
+ /*
+ * We have additional extensions; add them.
+ *
+ * First, split the extension-list string into a set of
+ * extensions.
+ */
+ extensions_set = g_strsplit(file_type_subtype_table[file_type_subtype].additional_file_extensions,
+ ";", 0);
+
+ /*
+ * Add each of those extensions to the list.
+ */
+ for (extensionp = extensions_set; *extensionp != NULL;
+ extensionp++) {
+ extension = *extensionp;
+
+ /*
+ * Add the extension, and all compressed variants
+ * of it if requested.
+ */
+ extensions = add_extensions(extensions, extension,
+ compression_type_extensions);
+ }
+
+ g_strfreev(extensions_set);
+ }
+ return extensions;
+}
+
+/* Return a list of file extensions that are used by the specified file
+ * type/subtype.
+ *
+ * If include_compressed is TRUE, the list will include compressed
+ * extensions, e.g. not just "pcap" but also "pcap.gz" if we can read
+ * gzipped files.
+ *
+ * All strings in the list are allocated with g_malloc() and must be freed
+ * with g_free().
+ */
+GSList *
+wtap_get_file_extensions_list(int file_type_subtype, gboolean include_compressed)
+{
+ GSList *extensions, *compression_type_extensions;
+
+ if (file_type_subtype < 0 ||
+ file_type_subtype >= (int)file_type_subtype_table_arr->len)
+ return NULL; /* not a valid file type */
+
+ if (file_type_subtype_table[file_type_subtype].default_file_extension == NULL)
+ return NULL; /* valid, but no extensions known */
+
+ extensions = NULL; /* empty list, to start with */
+
+ /*
+ * Add all this file type's extensions, with compressed
+ * variants if include_compressed is true.
+ */
+ if (include_compressed) {
+ /*
+ * Get compression-type extensions, if any.
+ */
+ compression_type_extensions = wtap_get_all_compression_type_extensions_list();
+ } else {
+ /*
+ * We don't want the compressed file extensions.
+ */
+ compression_type_extensions = NULL;
+ }
+ extensions = add_extensions_for_file_type_subtype(file_type_subtype, extensions,
+ compression_type_extensions);
+
+ g_slist_free(compression_type_extensions);
+
+ return extensions;
+}
+
+/* Return a list of all extensions that are used by all capture file
+ * types, including compressed extensions, e.g. not just "pcap" but
+ * also "pcap.gz" if we can read gzipped files.
+ *
+ * "Capture files" means "include file types that correspond to
+ * collections of network packets, but not file types that
+ * store data that just happens to be transported over protocols
+ * such as HTTP but that aren't collections of network packets",
+ * so that it could be used for "All Capture Files" without picking
+ * up JPEG files or files such as that - those aren't capture files,
+ * and we *do* have them listed in the long list of individual file
+ * types, so omitting them from "All Capture Files" is the right
+ * thing to do.
+ *
+ * All strings in the list are allocated with g_malloc() and must be freed
+ * with g_free().
+ *
+ * This is used to generate a list of extensions to look for if the user
+ * chooses "All Capture Files" in a file open dialog.
+ */
+GSList *
+wtap_get_all_capture_file_extensions_list(void)
+{
+ GSList *extensions, *compression_type_extensions;
+ unsigned int i;
+
+ init_file_type_extensions();
+
+ extensions = NULL; /* empty list, to start with */
+
+ /*
+ * Get compression-type extensions, if any.
+ */
+ compression_type_extensions = wtap_get_all_compression_type_extensions_list();
+
+ for (i = 0; i < file_type_extensions_arr->len; i++) {
+ /*
+ * Is this a capture file, rather than one of the
+ * other random file types we can read?
+ */
+ if (file_type_extensions[i].is_capture_file) {
+ /*
+ * Yes. Add all this file extension type's
+ * extensions, with compressed variants.
+ */
+ extensions = add_extensions_for_file_extensions_type(i,
+ extensions, compression_type_extensions);
+ }
+ }
+
+ g_slist_free(compression_type_extensions);
+
+ return extensions;
+}
+
+/* Return a list of all extensions that are used by all file types that
+ * we can read, including compressed extensions, e.g. not just "pcap" but
+ * also "pcap.gz" if we can read gzipped files.
+ *
+ * "File type" means "include file types that correspond to collections
+ * of network packets, as well as file types that store data that just
+ * happens to be transported over protocols such as HTTP but that aren't
+ * collections of network packets, and plain text files".
+ *
+ * All strings in the list are allocated with g_malloc() and must be freed
+ * with g_free().
+ *
+ * This is used to get the "base name" for a file, by stripping off
+ * compressed-file extensions and extensions that correspond to file
+ * types that we know about.
+ */
+GSList *
+wtap_get_all_file_extensions_list(void)
+{
+ GSList *extensions, *compression_type_extensions;
+
+ extensions = NULL; /* empty list, to start with */
+
+ /*
+ * Get compression-type extensions, if any.
+ */
+ compression_type_extensions = wtap_get_all_compression_type_extensions_list();
+
+ for (int ft = 0; ft < (int)file_type_subtype_table_arr->len; ft++) {
+ extensions = add_extensions_for_file_type_subtype(ft, extensions,
+ compression_type_extensions);
+ }
+
+ g_slist_free(compression_type_extensions);
+
+ return extensions;
+}
+
+/*
+ * Free a list returned by wtap_get_file_extension_type_extensions(),
+ * wtap_get_all_capture_file_extensions_list, wtap_get_file_extensions_list(),
+ * or wtap_get_all_file_extensions_list().
+ */
+void
+wtap_free_extensions_list(GSList *extensions)
+{
+ GSList *extension;
+
+ for (extension = extensions; extension != NULL;
+ extension = g_slist_next(extension)) {
+ g_free(extension->data);
+ }
+ g_slist_free(extensions);
+}
+
+/*
+ * Return the default file extension to use with the specified file type
+ * and subtype; that's just the extension, without any ".".
+ */
+const char *
+wtap_default_file_extension(int file_type_subtype)
+{
+ if (file_type_subtype < 0 ||
+ file_type_subtype >= (int)file_type_subtype_table_arr->len)
+ return NULL;
+ else
+ return file_type_subtype_table[file_type_subtype].default_file_extension;
+}
+
+/*
+ * Return whether we know how to write the specified file type.
+ */
+gboolean
+wtap_dump_can_open(int file_type_subtype)
+{
+ if (file_type_subtype < 0 ||
+ file_type_subtype >= (int)file_type_subtype_table_arr->len ||
+ file_type_subtype_table[file_type_subtype].dump_open == NULL)
+ return FALSE;
+
+ return TRUE;
+}
+
+/*
+ * Return whether we know how to write a compressed file of the specified
+ * file type.
+ */
+#ifdef HAVE_ZLIB
+gboolean
+wtap_dump_can_compress(int file_type_subtype)
+{
+ /*
+ * If this is an unknown file type, or if we have to
+ * seek when writing out a file with this file type,
+ * return FALSE.
+ */
+ if (file_type_subtype < 0 ||
+ file_type_subtype >= (int)file_type_subtype_table_arr->len ||
+ file_type_subtype_table[file_type_subtype].writing_must_seek)
+ return FALSE;
+
+ return TRUE;
+}
+#else
+gboolean
+wtap_dump_can_compress(int file_type_subtype _U_)
+{
+ return FALSE;
+}
+#endif
+
+static gboolean wtap_dump_open_finish(wtap_dumper *wdh, int *err,
+ gchar **err_info);
+
+static WFILE_T wtap_dump_file_open(wtap_dumper *wdh, const char *filename);
+static WFILE_T wtap_dump_file_fdopen(wtap_dumper *wdh, int fd);
+static int wtap_dump_file_close(wtap_dumper *wdh);
+
+static wtap_dumper *
+wtap_dump_init_dumper(int file_type_subtype, wtap_compression_type compression_type,
+ const wtap_dump_params *params, int *err)
+{
+ wtap_dumper *wdh;
+ wtap_block_t descr, file_int_data;
+ wtapng_if_descr_mandatory_t *descr_mand, *file_int_data_mand;
+ GArray *interfaces = params->idb_inf ? params->idb_inf->interface_data : NULL;
+
+ /* Can we write files of this file type/subtype?
+ *
+ * This will fail if file_type_subtype isn't a valid
+ * file type/subtype value, so, if it doesn't fail,
+ * we know file_type_subtype is within the bounds of
+ * the table of file types/subtypes.
+ */
+ if (!wtap_dump_can_open(file_type_subtype)) {
+ /* Invalid type, or type we don't know how to write. */
+ *err = WTAP_ERR_UNWRITABLE_FILE_TYPE;
+ return FALSE;
+ }
+
+ /* OK, we know how to write that file type/subtype; can we write
+ * the specified encapsulation type in that file type/subtype?
+ */
+ *err = (*file_type_subtype_table[file_type_subtype].can_write_encap)(params->encap);
+ /* if the err said to check wslua's can_write_encap, try that */
+ if (*err == WTAP_ERR_CHECK_WSLUA
+ && file_type_subtype_table[file_type_subtype].wslua_info != NULL
+ && file_type_subtype_table[file_type_subtype].wslua_info->wslua_can_write_encap != NULL) {
+
+ *err = (*file_type_subtype_table[file_type_subtype].wslua_info->wslua_can_write_encap)(params->encap, file_type_subtype_table[file_type_subtype].wslua_info->wslua_data);
+ }
+
+ if (*err != 0) {
+ /* No, we can't. */
+ return NULL;
+ }
+
+ /* Check whether we can open a capture file with that file type
+ * and that encapsulation, and, if the compression type isn't
+ * "uncompressed", whether we can write a *compressed* file
+ * of that file type.
+ * If we're doing compression, can this file type/subtype be
+ written in compressed form?
+ *
+ * (The particular type doesn't matter - if the file can't
+ * be written 100% sequentially, we can't compress it,
+ * because we can't go back and overwrite something we've
+ * already written.
+ */
+ if (compression_type != WTAP_UNCOMPRESSED &&
+ !wtap_dump_can_compress(file_type_subtype)) {
+ *err = WTAP_ERR_COMPRESSION_NOT_SUPPORTED;
+ return NULL;
+ }
+
+ /* Allocate a data structure for the output stream. */
+ wdh = g_new0(wtap_dumper, 1);
+ if (wdh == NULL) {
+ *err = errno;
+ return NULL;
+ }
+
+ wdh->file_type_subtype = file_type_subtype;
+ wdh->snaplen = params->snaplen;
+ wdh->file_encap = params->encap;
+ wdh->compression_type = compression_type;
+ wdh->wslua_data = NULL;
+ wdh->interface_data = g_array_new(FALSE, FALSE, sizeof(wtap_block_t));
+
+ /* Set Section Header Block data */
+ wdh->shb_hdrs = params->shb_hdrs;
+ /* Set Name Resolution Block data */
+ wdh->nrbs_growing = params->nrbs_growing;
+ /* Set Interface Description Block data */
+ if (interfaces && interfaces->len) {
+ if (!params->dont_copy_idbs) { /* XXX */
+ guint itf_count;
+
+ /* Note: this memory is owned by wtap_dumper and will become
+ * invalid after wtap_dump_close. */
+ for (itf_count = 0; itf_count < interfaces->len; itf_count++) {
+ file_int_data = g_array_index(interfaces, wtap_block_t, itf_count);
+ file_int_data_mand = (wtapng_if_descr_mandatory_t*)wtap_block_get_mandatory_data(file_int_data);
+ descr = wtap_block_make_copy(file_int_data);
+ if ((params->encap != WTAP_ENCAP_PER_PACKET) && (params->encap != file_int_data_mand->wtap_encap)) {
+ descr_mand = (wtapng_if_descr_mandatory_t*)wtap_block_get_mandatory_data(descr);
+ descr_mand->wtap_encap = params->encap;
+ }
+ g_array_append_val(wdh->interface_data, descr);
+ }
+ }
+ } else if (params->encap != WTAP_ENCAP_NONE && params->encap != WTAP_ENCAP_PER_PACKET) {
+ /* Generate a fake IDB if we don't have one, unless the
+ * file encapsulation is none. (WTAP_ENCAP_NONE either
+ * means that there are no interfaces, or they will be
+ * provided later when reading the file in single-pass mode.)
+ *
+ * For WTAP_ENCAP_PER_PACKET, we'll have to generate IDBs
+ * from packet records as they come in. (pcapng does this now.)
+ *
+ * XXX File types should provide their own IDBs (possibly
+ * fake ones generated by wtap_add_generated_idb()), in
+ * order to support being used as inputs for mergecap where
+ * pcapng is the output.
+ */
+ descr = wtap_dump_params_generate_idb(params);
+ g_array_append_val(wdh->interface_data, descr);
+ }
+ /* Set Decryption Secrets Blocks */
+ wdh->dsbs_initial = params->dsbs_initial;
+ wdh->dsbs_growing = params->dsbs_growing;
+ /* Set Sysdig meta events */
+ wdh->sysdig_mev_growing = params->sysdig_mev_growing;
+ return wdh;
+}
+
+wtap_dumper *
+wtap_dump_open(const char *filename, int file_type_subtype,
+ wtap_compression_type compression_type, const wtap_dump_params *params,
+ int *err, gchar **err_info)
+{
+ wtap_dumper *wdh;
+ WFILE_T fh;
+
+ *err = 0;
+ *err_info = NULL;
+
+ /* Allocate and initialize a data structure for the output stream. */
+ wdh = wtap_dump_init_dumper(file_type_subtype, compression_type, params,
+ err);
+ if (wdh == NULL)
+ return NULL;
+
+ /* In case "fopen()" fails but doesn't set "errno", set "errno"
+ to a generic "the open failed" error. */
+ errno = WTAP_ERR_CANT_OPEN;
+ fh = wtap_dump_file_open(wdh, filename);
+ if (fh == NULL) {
+ *err = errno;
+ g_free(wdh);
+ return NULL; /* can't create file */
+ }
+ wdh->fh = fh;
+
+ if (!wtap_dump_open_finish(wdh, err, err_info)) {
+ /* Get rid of the file we created; we couldn't finish
+ opening it. */
+ wtap_dump_file_close(wdh);
+ ws_unlink(filename);
+ g_free(wdh);
+ return NULL;
+ }
+ return wdh;
+}
+
+wtap_dumper *
+wtap_dump_open_tempfile(const char *tmpdir, char **filenamep, const char *pfx,
+ int file_type_subtype, wtap_compression_type compression_type,
+ const wtap_dump_params *params, int *err, gchar **err_info)
+{
+ int fd;
+ const char *ext;
+ char sfx[16];
+ wtap_dumper *wdh;
+ WFILE_T fh;
+
+ /* No path name for the temporary file yet. */
+ *filenamep = NULL;
+
+ *err = 0;
+ *err_info = NULL;
+
+ /* Allocate and initialize a data structure for the output stream. */
+ wdh = wtap_dump_init_dumper(file_type_subtype, compression_type, params,
+ err);
+ if (wdh == NULL)
+ return NULL;
+
+ /* Choose an appropriate suffix for the file */
+ ext = wtap_default_file_extension(file_type_subtype);
+ if (ext == NULL)
+ ext = "tmp";
+ sfx[0] = '.';
+ sfx[1] = '\0';
+ (void) g_strlcat(sfx, ext, 16);
+
+ /* Choose a random name for the file */
+ fd = create_tempfile(tmpdir, filenamep, pfx, sfx, NULL);
+ if (fd == -1) {
+ *err = WTAP_ERR_CANT_OPEN;
+ g_free(wdh);
+ return NULL; /* can't create file */
+ }
+
+ /* In case "fopen()" fails but doesn't set "errno", set "errno"
+ to a generic "the open failed" error. */
+ errno = WTAP_ERR_CANT_OPEN;
+ fh = wtap_dump_file_fdopen(wdh, fd);
+ if (fh == NULL) {
+ *err = errno;
+ ws_close(fd);
+ g_free(wdh);
+ return NULL; /* can't create file */
+ }
+ wdh->fh = fh;
+
+ if (!wtap_dump_open_finish(wdh, err, err_info)) {
+ /* Get rid of the file we created; we couldn't finish
+ opening it. */
+ wtap_dump_file_close(wdh);
+ ws_unlink(*filenamep);
+ g_free(wdh);
+ return NULL;
+ }
+ return wdh;
+}
+
+wtap_dumper *
+wtap_dump_fdopen(int fd, int file_type_subtype, wtap_compression_type compression_type,
+ const wtap_dump_params *params, int *err, gchar **err_info)
+{
+ wtap_dumper *wdh;
+ WFILE_T fh;
+
+ *err = 0;
+ *err_info = NULL;
+
+ /* Allocate and initialize a data structure for the output stream. */
+ wdh = wtap_dump_init_dumper(file_type_subtype, compression_type, params,
+ err);
+ if (wdh == NULL)
+ return NULL;
+
+ /* In case "fopen()" fails but doesn't set "errno", set "errno"
+ to a generic "the open failed" error. */
+ errno = WTAP_ERR_CANT_OPEN;
+ fh = wtap_dump_file_fdopen(wdh, fd);
+ if (fh == NULL) {
+ *err = errno;
+ g_free(wdh);
+ return NULL; /* can't create standard I/O stream */
+ }
+ wdh->fh = fh;
+
+ if (!wtap_dump_open_finish(wdh, err, err_info)) {
+ wtap_dump_file_close(wdh);
+ g_free(wdh);
+ return NULL;
+ }
+ return wdh;
+}
+
+wtap_dumper *
+wtap_dump_open_stdout(int file_type_subtype, wtap_compression_type compression_type,
+ const wtap_dump_params *params, int *err, gchar **err_info)
+{
+ int new_fd;
+ wtap_dumper *wdh;
+
+ /*
+ * Duplicate the file descriptor, so that we can close the
+ * wtap_dumper handle the same way we close any other
+ * wtap_dumper handle, without closing the standard output.
+ */
+ new_fd = ws_dup(1);
+ if (new_fd == -1) {
+ /* dup failed */
+ *err = errno;
+ return NULL;
+ }
+#ifdef _WIN32
+ /*
+ * Put the new descriptor into binary mode.
+ *
+ * XXX - even if the file format we're writing is a text
+ * format?
+ */
+ if (_setmode(new_fd, O_BINARY) == -1) {
+ /* "Should not happen" */
+ *err = errno;
+ ws_close(new_fd);
+ return NULL;
+ }
+#endif
+
+ wdh = wtap_dump_fdopen(new_fd, file_type_subtype, compression_type,
+ params, err, err_info);
+ if (wdh == NULL) {
+ /* Failed; close the new FD */
+ ws_close(new_fd);
+ return NULL;
+ }
+ return wdh;
+}
+
+static gboolean
+wtap_dump_open_finish(wtap_dumper *wdh, int *err, gchar **err_info)
+{
+ int fd;
+ gboolean cant_seek;
+
+ /* Can we do a seek on the file descriptor?
+ If not, note that fact. */
+ if (wdh->compression_type != WTAP_UNCOMPRESSED) {
+ cant_seek = TRUE;
+ } else {
+ fd = ws_fileno((FILE *)wdh->fh);
+ if (ws_lseek64(fd, 1, SEEK_CUR) == (off_t) -1)
+ cant_seek = TRUE;
+ else {
+ /* Undo the seek. */
+ ws_lseek64(fd, 0, SEEK_SET);
+ cant_seek = FALSE;
+ }
+ }
+
+ /* If this file type requires seeking, and we can't seek, fail. */
+ if (file_type_subtype_table[wdh->file_type_subtype].writing_must_seek && cant_seek) {
+ *err = WTAP_ERR_CANT_WRITE_TO_PIPE;
+ return FALSE;
+ }
+
+ /* Set wdh with wslua data if any - this is how we pass the data
+ * to the file writer.
+ */
+ if (file_type_subtype_table[wdh->file_type_subtype].wslua_info)
+ wdh->wslua_data = file_type_subtype_table[wdh->file_type_subtype].wslua_info->wslua_data;
+
+ /* Now try to open the file for writing. */
+ if (!(*file_type_subtype_table[wdh->file_type_subtype].dump_open)(wdh, err,
+ err_info)) {
+ return FALSE;
+ }
+
+ return TRUE; /* success! */
+}
+
+gboolean
+wtap_dump_add_idb(wtap_dumper *wdh, wtap_block_t idb, int *err,
+ gchar **err_info)
+{
+ if (wdh->subtype_add_idb == NULL) {
+ /* Not supported. */
+ *err = WTAP_ERR_UNWRITABLE_REC_TYPE;
+ *err_info = g_strdup("Adding IDBs isn't supported by this file type");
+ return FALSE;
+ }
+ *err = 0;
+ *err_info = NULL;
+ return (wdh->subtype_add_idb)(wdh, idb, err, err_info);
+}
+
+gboolean
+wtap_dump(wtap_dumper *wdh, const wtap_rec *rec,
+ const guint8 *pd, int *err, gchar **err_info)
+{
+ *err = 0;
+ *err_info = NULL;
+ return (wdh->subtype_write)(wdh, rec, pd, err, err_info);
+}
+
+gboolean
+wtap_dump_flush(wtap_dumper *wdh, int *err)
+{
+#ifdef HAVE_ZLIB
+ if (wdh->compression_type == WTAP_GZIP_COMPRESSED) {
+ if (gzwfile_flush((GZWFILE_T)wdh->fh) == -1) {
+ *err = gzwfile_geterr((GZWFILE_T)wdh->fh);
+ return FALSE;
+ }
+ } else
+#endif
+ {
+ if (fflush((FILE *)wdh->fh) == EOF) {
+ *err = errno;
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+gboolean
+wtap_dump_close(wtap_dumper *wdh, gboolean *needs_reload,
+ int *err, gchar **err_info)
+{
+ gboolean ret = TRUE;
+
+ *err = 0;
+ *err_info = NULL;
+ if (wdh->subtype_finish != NULL) {
+ /* There's a finish routine for this dump stream. */
+ if (!(wdh->subtype_finish)(wdh, err, err_info))
+ ret = FALSE;
+ }
+ errno = WTAP_ERR_CANT_CLOSE;
+ if (wtap_dump_file_close(wdh) == EOF) {
+ if (ret) {
+ /* The per-format finish function succeeded,
+ but the stream close didn't. Save the
+ reason why, if our caller asked for it. */
+ if (err != NULL)
+ *err = errno;
+ }
+ ret = FALSE;
+ }
+ if (needs_reload != NULL)
+ *needs_reload = wdh->needs_reload;
+ g_free(wdh->priv);
+ wtap_block_array_free(wdh->interface_data);
+ wtap_block_array_free(wdh->dsbs_initial);
+ g_free(wdh);
+ return ret;
+}
+
+int
+wtap_dump_file_type_subtype(wtap_dumper *wdh)
+{
+ return wdh->file_type_subtype;
+}
+
+gint64
+wtap_get_bytes_dumped(wtap_dumper *wdh)
+{
+ return wdh->bytes_dumped;
+}
+
+void
+wtap_set_bytes_dumped(wtap_dumper *wdh, gint64 bytes_dumped)
+{
+ wdh->bytes_dumped = bytes_dumped;
+}
+
+gboolean
+wtap_addrinfo_list_empty(addrinfo_lists_t *addrinfo_lists)
+{
+ return (addrinfo_lists == NULL) ||
+ ((addrinfo_lists->ipv4_addr_list == NULL) &&
+ (addrinfo_lists->ipv6_addr_list == NULL));
+}
+
+gboolean
+wtap_dump_set_addrinfo_list(wtap_dumper *wdh, addrinfo_lists_t *addrinfo_lists)
+{
+ if (!wdh || wdh->file_type_subtype < 0 ||
+ wdh->file_type_subtype >= (int)file_type_subtype_table_arr->len ||
+ wtap_file_type_subtype_supports_block(wdh->file_type_subtype, WTAP_BLOCK_NAME_RESOLUTION) == BLOCK_NOT_SUPPORTED)
+ return FALSE;
+ wdh->addrinfo_lists = addrinfo_lists;
+ return TRUE;
+}
+
+void
+wtap_dump_discard_name_resolution(wtap_dumper *wdh)
+{
+ /* As below for DSBs. */
+ if (wdh->nrbs_growing) {
+ /*
+ * Pretend we've written all of them.
+ */
+ wdh->nrbs_growing_written = wdh->nrbs_growing->len;
+ }
+}
+
+void
+wtap_dump_discard_decryption_secrets(wtap_dumper *wdh)
+{
+ /*
+ * This doesn't free the data, as it might be pointed to
+ * from other structures; it merely marks all of them as
+ * having been written to the file, so that they don't
+ * get written by wtap_dump().
+ *
+ * XXX - our APIs for dealing with some metadata, such as
+ * resolved names, decryption secrets, and interface
+ * statistics is not very well oriented towards one-pass
+ * programs; this needs to be cleaned up. See bug 15502.
+ */
+ if (wdh->dsbs_growing) {
+ /*
+ * Pretend we've written all of them.
+ */
+ wdh->dsbs_growing_written = wdh->dsbs_growing->len;
+ }
+}
+
+void
+wtap_dump_discard_sysdig_meta_events(wtap_dumper *wdh)
+{
+ /* As above for DSBs. */
+ if (wdh->sysdig_mev_growing) {
+ /*
+ * Pretend we've written all of them.
+ */
+ wdh->sysdig_mev_growing_written = wdh->sysdig_mev_growing->len;
+ }
+}
+
+/* internally open a file for writing (compressed or not) */
+#ifdef HAVE_ZLIB
+static WFILE_T
+wtap_dump_file_open(wtap_dumper *wdh, const char *filename)
+{
+ if (wdh->compression_type == WTAP_GZIP_COMPRESSED) {
+ return gzwfile_open(filename);
+ } else {
+ return ws_fopen(filename, "wb");
+ }
+}
+#else
+static WFILE_T
+wtap_dump_file_open(wtap_dumper *wdh _U_, const char *filename)
+{
+ return ws_fopen(filename, "wb");
+}
+#endif
+
+/* internally open a file for writing (compressed or not) */
+#ifdef HAVE_ZLIB
+static WFILE_T
+wtap_dump_file_fdopen(wtap_dumper *wdh, int fd)
+{
+ if (wdh->compression_type == WTAP_GZIP_COMPRESSED) {
+ return gzwfile_fdopen(fd);
+ } else {
+ return ws_fdopen(fd, "wb");
+ }
+}
+#else
+static WFILE_T
+wtap_dump_file_fdopen(wtap_dumper *wdh _U_, int fd)
+{
+ return ws_fdopen(fd, "wb");
+}
+#endif
+
+/* internally writing raw bytes (compressed or not). Updates wdh->bytes_dumped on success */
+gboolean
+wtap_dump_file_write(wtap_dumper *wdh, const void *buf, size_t bufsize, int *err)
+{
+ size_t nwritten;
+
+#ifdef HAVE_ZLIB
+ if (wdh->compression_type == WTAP_GZIP_COMPRESSED) {
+ nwritten = gzwfile_write((GZWFILE_T)wdh->fh, buf, (unsigned int) bufsize);
+ /*
+ * gzwfile_write() returns 0 on error.
+ */
+ if (nwritten == 0) {
+ *err = gzwfile_geterr((GZWFILE_T)wdh->fh);
+ return FALSE;
+ }
+ } else
+#endif
+ {
+ errno = WTAP_ERR_CANT_WRITE;
+ nwritten = fwrite(buf, 1, bufsize, (FILE *)wdh->fh);
+ /*
+ * At least according to the macOS man page,
+ * this can return a short count on an error.
+ */
+ if (nwritten != bufsize) {
+ if (ferror((FILE *)wdh->fh))
+ *err = errno;
+ else
+ *err = WTAP_ERR_SHORT_WRITE;
+ return FALSE;
+ }
+ }
+ wdh->bytes_dumped += bufsize;
+ return TRUE;
+}
+
+/* internally close a file for writing (compressed or not) */
+static int
+wtap_dump_file_close(wtap_dumper *wdh)
+{
+#ifdef HAVE_ZLIB
+ if (wdh->compression_type == WTAP_GZIP_COMPRESSED)
+ return gzwfile_close((GZWFILE_T)wdh->fh);
+ else
+#endif
+ return fclose((FILE *)wdh->fh);
+}
+
+gint64
+wtap_dump_file_seek(wtap_dumper *wdh, gint64 offset, int whence, int *err)
+{
+#ifdef HAVE_ZLIB
+ if (wdh->compression_type != WTAP_UNCOMPRESSED) {
+ *err = WTAP_ERR_CANT_SEEK_COMPRESSED;
+ return -1;
+ } else
+#endif
+ {
+ if (-1 == ws_fseek64((FILE *)wdh->fh, offset, whence)) {
+ *err = errno;
+ return -1;
+ } else
+ {
+ return 0;
+ }
+ }
+}
+
+gint64
+wtap_dump_file_tell(wtap_dumper *wdh, int *err)
+{
+ gint64 rval;
+#ifdef HAVE_ZLIB
+ if (wdh->compression_type != WTAP_UNCOMPRESSED) {
+ *err = WTAP_ERR_CANT_SEEK_COMPRESSED;
+ return -1;
+ } else
+#endif
+ {
+ if (-1 == (rval = ws_ftell64((FILE *)wdh->fh))) {
+ *err = errno;
+ return -1;
+ } else
+ {
+ return rval;
+ }
+ }
+}
+
+void
+cleanup_open_routines(void)
+{
+ guint i;
+ struct open_info *i_open;
+
+ if (open_routines != NULL && open_info_arr) {
+ for (i = 0, i_open = open_routines; i < open_info_arr->len; i++, i_open++) {
+ if (i_open->extensions != NULL)
+ g_strfreev(i_open->extensions_set);
+ }
+
+ g_array_free(open_info_arr, TRUE);
+ open_info_arr = NULL;
+ }
+}
+
+/*
+ * Allow built-in file handlers (but *not* plugin file handlers!) to
+ * register a "backwards-compatibility" name and file type value, to
+ * put in the Lua wtap_filetypes table.
+ *
+ * This is only to be used as long as we have that table; new Lua
+ * code should use wtap_name_to_file_type_subtype() to look up
+ * file types by their name, just as C code should.
+ *
+ * The backwards-ccmpatibility names are the old WTAP_FILE_TYPE_SUBTYPE_
+ * #define name, with WTAP_FILE_TYPE_SUBTYPE_ removed.
+ */
+
+static GArray *backwards_compatibility_lua_names;
+
+void
+wtap_register_backwards_compatibility_lua_name(const char *name, int ft)
+{
+ struct backwards_compatibiliity_lua_name entry;
+
+ /*
+ * Create the table if it doesn't already exist.
+ * Use the same size as we do for the file type/subtype table.
+ */
+ if (backwards_compatibility_lua_names == NULL) {
+ backwards_compatibility_lua_names = g_array_sized_new(FALSE,
+ TRUE, sizeof(struct backwards_compatibiliity_lua_name),
+ wtap_module_count*2);
+
+ /*
+ * Extra backwards compatibility hack - add entries
+ * for time stamp precision values(!), as well as
+ * for "UNKNOWN" and types that don't yet register
+ * themselves.
+ *
+ * If new WS_TSPREC_ value are added, don't bother
+ * adding them to this table; any Lua program that
+ * would use them should use the wtap_tsprecs type.
+ *
+ * (Recursion: see "recursion".)
+ */
+ wtap_register_backwards_compatibility_lua_name("TSPREC_SEC",
+ WTAP_TSPREC_SEC);
+ wtap_register_backwards_compatibility_lua_name("TSPREC_DSEC",
+ WTAP_TSPREC_100_MSEC);
+ wtap_register_backwards_compatibility_lua_name("TSPREC_CSEC",
+ WTAP_TSPREC_10_MSEC);
+ wtap_register_backwards_compatibility_lua_name("TSPREC_MSEC",
+ WTAP_TSPREC_MSEC);
+ wtap_register_backwards_compatibility_lua_name("TSPREC_USEC",
+ WTAP_TSPREC_USEC);
+ wtap_register_backwards_compatibility_lua_name("TSPREC_NSEC",
+ WTAP_TSPREC_NSEC);
+ wtap_register_backwards_compatibility_lua_name("UNKNOWN",
+ WTAP_FILE_TYPE_SUBTYPE_UNKNOWN);
+ }
+ entry.name = name;
+ entry.ft = ft;
+ g_array_append_val(backwards_compatibility_lua_names, entry);
+}
+
+const GArray *
+get_backwards_compatibility_lua_table(void)
+{
+ return backwards_compatibility_lua_names;
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/wiretap/file_wrappers.c b/wiretap/file_wrappers.c
new file mode 100644
index 00000000..67979a5b
--- /dev/null
+++ b/wiretap/file_wrappers.c
@@ -0,0 +1,2236 @@
+/* file_wrappers.c
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+/* file_access interface based heavily on zlib gzread.c and gzlib.c from zlib
+ * Copyright (C) 1995-2010 Jean-loup Gailly and Mark Adler
+ * under licence:
+ *
+ * SPDX-License-Identifier: Zlib
+ *
+ */
+
+#include "config.h"
+#include "file_wrappers.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <string.h>
+#include "wtap-int.h"
+
+#include <wsutil/file_util.h>
+
+#ifdef HAVE_ZLIB
+#define ZLIB_CONST
+#include <zlib.h>
+#endif /* HAVE_ZLIB */
+
+#ifdef HAVE_ZSTD
+#include <zstd.h>
+#endif
+
+#ifdef HAVE_LZ4
+#include <lz4.h>
+
+#if LZ4_VERSION_NUMBER >= 10703
+#define USE_LZ4
+#include <lz4frame.h>
+#endif
+#endif
+
+/*
+ * See RFC 1952:
+ *
+ * https://tools.ietf.org/html/rfc1952
+ *
+ * for a description of the gzip file format.
+ *
+ * Some other compressed file formats we might want to support:
+ *
+ * XZ format: https://tukaani.org/xz/
+ *
+ * Bzip2 format: https://www.sourceware.org/bzip2/
+ *
+ * Lzip format: https://www.nongnu.org/lzip/
+ */
+
+/*
+ * List of compression types supported.
+ */
+static struct compression_type {
+ wtap_compression_type type;
+ const char *extension;
+ const char *description;
+} compression_types[] = {
+#ifdef HAVE_ZLIB
+ { WTAP_GZIP_COMPRESSED, "gz", "gzip compressed" },
+#endif
+#ifdef HAVE_ZSTD
+ { WTAP_ZSTD_COMPRESSED, "zst", "zstd compressed" },
+#endif
+#ifdef USE_LZ4
+ { WTAP_LZ4_COMPRESSED, "lz4", "lz4 compressed" },
+#endif
+ { WTAP_UNCOMPRESSED, NULL, NULL }
+};
+
+static wtap_compression_type file_get_compression_type(FILE_T stream);
+
+wtap_compression_type
+wtap_get_compression_type(wtap *wth)
+{
+ return file_get_compression_type((wth->fh == NULL) ? wth->random_fh : wth->fh);
+}
+
+const char *
+wtap_compression_type_description(wtap_compression_type compression_type)
+{
+ for (struct compression_type *p = compression_types;
+ p->type != WTAP_UNCOMPRESSED; p++) {
+ if (p->type == compression_type)
+ return p->description;
+ }
+ return NULL;
+}
+
+const char *
+wtap_compression_type_extension(wtap_compression_type compression_type)
+{
+ for (struct compression_type *p = compression_types;
+ p->type != WTAP_UNCOMPRESSED; p++) {
+ if (p->type == compression_type)
+ return p->extension;
+ }
+ return NULL;
+}
+
+GSList *
+wtap_get_all_compression_type_extensions_list(void)
+{
+ GSList *extensions;
+
+ extensions = NULL; /* empty list, to start with */
+
+ for (struct compression_type *p = compression_types;
+ p->type != WTAP_UNCOMPRESSED; p++)
+ extensions = g_slist_prepend(extensions, (gpointer)p->extension);
+
+ return extensions;
+}
+
+/* #define GZBUFSIZE 8192 */
+#define GZBUFSIZE 4096
+
+/* values for wtap_reader compression */
+typedef enum {
+ UNKNOWN, /* unknown - look for a compression header */
+ UNCOMPRESSED, /* uncompressed - copy input directly */
+ ZLIB, /* decompress a zlib stream */
+ GZIP_AFTER_HEADER,
+ ZSTD,
+ LZ4,
+} compression_t;
+
+/*
+ * We limit the size of our input and output buffers to 2^30 bytes,
+ * because:
+ *
+ * 1) on Windows with MSVC, the return value of _read() is int,
+ * so the biggest read you can do is INT_MAX, and the biggest
+ * power of 2 below that is 2^30;
+ *
+ * 2) the "avail_in" and "avail_out" values in a z_stream structure
+ * in zlib are uInts, and those are unsigned ints, and that
+ * imposes a limit on the buffer size when we're reading a
+ * gzipped file.
+ *
+ * Thus, we use guint for the buffer sizes, offsets, amount available
+ * from the buffer, etc.
+ *
+ * If we want an even bigger buffer for uncompressed data, or for
+ * some other form of compression, then the guint-sized values should
+ * be in structure values used only for reading gzipped files, and
+ * other values should be used for uncompressed data or data
+ * compressed using other algorithms (e.g., in a union).
+ */
+#define MAX_READ_BUF_SIZE (1U << 30)
+
+struct wtap_reader_buf {
+ guint8 *buf; /* buffer */
+ guint8 *next; /* next byte to deliver from buffer */
+ guint avail; /* number of bytes available to deliver at next */
+};
+
+struct wtap_reader {
+ int fd; /* file descriptor */
+ gint64 raw_pos; /* current position in file (just to not call lseek()) */
+ gint64 pos; /* current position in uncompressed data */
+ guint size; /* buffer size */
+
+ struct wtap_reader_buf in; /* input buffer, containing compressed data */
+ struct wtap_reader_buf out; /* output buffer, containing uncompressed data */
+
+ gboolean eof; /* TRUE if end of input file reached */
+ gint64 start; /* where the gzip data started, for rewinding */
+ gint64 raw; /* where the raw data started, for seeking */
+ compression_t compression; /* type of compression, if any */
+ compression_t last_compression; /* last known compression type */
+ gboolean is_compressed; /* FALSE if completely uncompressed, TRUE otherwise */
+
+ /* seek request */
+ gint64 skip; /* amount to skip (already rewound if backwards) */
+ gboolean seek_pending; /* TRUE if seek request pending */
+
+ /* error information */
+ int err; /* error code */
+ const char *err_info; /* additional error information string for some errors */
+
+#ifdef HAVE_ZLIB
+ /* zlib inflate stream */
+ z_stream strm; /* stream structure in-place (not a pointer) */
+ gboolean dont_check_crc; /* TRUE if we aren't supposed to check the CRC */
+#endif
+ /* fast seeking */
+ GPtrArray *fast_seek;
+ void *fast_seek_cur;
+#ifdef HAVE_ZSTD
+ ZSTD_DCtx *zstd_dctx;
+#endif
+#ifdef USE_LZ4
+ LZ4F_dctx *lz4_dctx;
+#endif
+};
+
+/* Current read offset within a buffer. */
+static guint
+offset_in_buffer(struct wtap_reader_buf *buf)
+{
+ /* buf->next points to the next byte to read, and buf->buf points
+ to the first byte in the buffer, so the difference between them
+ is the offset.
+
+ This will fit in an unsigned int, because it can't be bigger
+ than the size of the buffer, which is an unsigned int. */
+ return (guint)(buf->next - buf->buf);
+}
+
+/* Number of bytes of data that are in a buffer. */
+static guint
+bytes_in_buffer(struct wtap_reader_buf *buf)
+{
+ /* buf->next + buf->avail points just past the last byte of data in
+ the buffer.
+ Thus, (buf->next + buf->avail) - buf->buf is the number of bytes
+ of data in the buffer.
+
+ This will fit in an guint, because it can't be bigger
+ than the size of the buffer, which is a guint. */
+ return (guint)((buf->next + buf->avail) - buf->buf);
+}
+
+/* Reset a buffer, discarding all data in the buffer, so we read into
+ it starting at the beginning. */
+static void
+buf_reset(struct wtap_reader_buf *buf)
+{
+ buf->next = buf->buf;
+ buf->avail = 0;
+}
+
+static int
+buf_read(FILE_T state, struct wtap_reader_buf *buf)
+{
+ guint space_left, to_read;
+ unsigned char *read_ptr;
+ ssize_t ret;
+
+ /* How much space is left at the end of the buffer?
+ XXX - the output buffer actually has state->size * 2 bytes. */
+ space_left = state->size - bytes_in_buffer(buf);
+ if (space_left == 0) {
+ /* There's no space left, so we start fresh at the beginning
+ of the buffer. */
+ buf_reset(buf);
+
+ read_ptr = buf->buf;
+ to_read = state->size;
+ } else {
+ /* There's some space left; try to read as much data as we
+ can into that space. We may get less than that if we're
+ reading from a pipe or if we're near the end of the file. */
+ read_ptr = buf->next + buf->avail;
+ to_read = space_left;
+ }
+
+ ret = ws_read(state->fd, read_ptr, to_read);
+ if (ret < 0) {
+ state->err = errno;
+ state->err_info = NULL;
+ return -1;
+ }
+ if (ret == 0)
+ state->eof = TRUE;
+ state->raw_pos += ret;
+ buf->avail += (guint)ret;
+ return 0;
+}
+
+static int /* gz_avail */
+fill_in_buffer(FILE_T state)
+{
+ if (state->err != 0)
+ return -1;
+ if (!state->eof) {
+ if (buf_read(state, &state->in) < 0)
+ return -1;
+ }
+ return 0;
+}
+
+#define ZLIB_WINSIZE 32768
+
+struct fast_seek_point {
+ gint64 out; /* corresponding offset in uncompressed data */
+ gint64 in; /* offset in input file of first full byte */
+
+ compression_t compression;
+ union {
+ struct {
+#ifdef HAVE_INFLATEPRIME
+ int bits; /* number of bits (1-7) from byte at in - 1, or 0 */
+#endif
+ unsigned char window[ZLIB_WINSIZE]; /* preceding 32K of uncompressed data */
+
+ /* be gentle with Z_STREAM_END, 8 bytes more... Another solution would be to comment checks out */
+ guint32 adler;
+ guint32 total_out;
+ } zlib;
+ } data;
+};
+
+struct zlib_cur_seek_point {
+ unsigned char window[ZLIB_WINSIZE]; /* preceding 32K of uncompressed data */
+ unsigned int pos;
+ unsigned int have;
+};
+
+#define SPAN G_GINT64_CONSTANT(1048576)
+static struct fast_seek_point *
+fast_seek_find(FILE_T file, gint64 pos)
+{
+ struct fast_seek_point *smallest = NULL;
+ struct fast_seek_point *item;
+ guint low, i, max;
+
+ if (!file->fast_seek)
+ return NULL;
+
+ for (low = 0, max = file->fast_seek->len; low < max; ) {
+ i = (low + max) / 2;
+ item = (struct fast_seek_point *)file->fast_seek->pdata[i];
+
+ if (pos < item->out)
+ max = i;
+ else if (pos > item->out) {
+ smallest = item;
+ low = i + 1;
+ } else {
+ return item;
+ }
+ }
+ return smallest;
+}
+
+static void
+fast_seek_header(FILE_T file, gint64 in_pos, gint64 out_pos,
+ compression_t compression)
+{
+ struct fast_seek_point *item = NULL;
+
+ if (file->fast_seek->len != 0)
+ item = (struct fast_seek_point *)file->fast_seek->pdata[file->fast_seek->len - 1];
+
+ if (!item || item->out < out_pos) {
+ struct fast_seek_point *val = g_new(struct fast_seek_point,1);
+ val->in = in_pos;
+ val->out = out_pos;
+ val->compression = compression;
+
+ g_ptr_array_add(file->fast_seek, val);
+ }
+}
+
+static void
+fast_seek_reset(
+#ifdef HAVE_ZLIB
+ FILE_T state)
+#else
+ FILE_T state _U_)
+#endif
+{
+#ifdef HAVE_ZLIB
+ if (state->compression == ZLIB && state->fast_seek_cur != NULL) {
+ struct zlib_cur_seek_point *cur = (struct zlib_cur_seek_point *) state->fast_seek_cur;
+
+ cur->have = 0;
+ }
+#endif
+}
+
+#ifdef HAVE_ZLIB
+
+/* Get next byte from input, or -1 if end or error.
+ *
+ * Note:
+ *
+ * 1) errors from buf_read(), and thus from fill_in_buffer(), are
+ * "sticky", and fill_in_buffer() won't do any reading if there's
+ * an error;
+ *
+ * 2) GZ_GETC() returns -1 on an EOF;
+ *
+ * so it's safe to make multiple GZ_GETC() calls and only check the
+ * last one for an error. */
+#define GZ_GETC() ((state->in.avail == 0 && fill_in_buffer(state) == -1) ? -1 : \
+ (state->in.avail == 0 ? -1 : \
+ (state->in.avail--, *(state->in.next)++)))
+
+/* Get a one-byte integer and return 0 on success and the value in *ret.
+ Otherwise -1 is returned, state->err is set, and *ret is not modified. */
+static int
+gz_next1(FILE_T state, guint8 *ret)
+{
+ int ch;
+
+ ch = GZ_GETC();
+ if (ch == -1) {
+ if (state->err == 0) {
+ /* EOF */
+ state->err = WTAP_ERR_SHORT_READ;
+ state->err_info = NULL;
+ }
+ return -1;
+ }
+ *ret = ch;
+ return 0;
+}
+
+/* Get a two-byte little-endian integer and return 0 on success and the value
+ in *ret. Otherwise -1 is returned, state->err is set, and *ret is not
+ modified. */
+static int
+gz_next2(FILE_T state, guint16 *ret)
+{
+ guint16 val;
+ int ch;
+
+ val = GZ_GETC();
+ ch = GZ_GETC();
+ if (ch == -1) {
+ if (state->err == 0) {
+ /* EOF */
+ state->err = WTAP_ERR_SHORT_READ;
+ state->err_info = NULL;
+ }
+ return -1;
+ }
+ val += (guint16)ch << 8;
+ *ret = val;
+ return 0;
+}
+
+/* Get a four-byte little-endian integer and return 0 on success and the value
+ in *ret. Otherwise -1 is returned, state->err is set, and *ret is not
+ modified. */
+static int
+gz_next4(FILE_T state, guint32 *ret)
+{
+ guint32 val;
+ int ch;
+
+ val = GZ_GETC();
+ val += (unsigned)GZ_GETC() << 8;
+ val += (guint32)GZ_GETC() << 16;
+ ch = GZ_GETC();
+ if (ch == -1) {
+ if (state->err == 0) {
+ /* EOF */
+ state->err = WTAP_ERR_SHORT_READ;
+ state->err_info = NULL;
+ }
+ return -1;
+ }
+ val += (guint32)ch << 24;
+ *ret = val;
+ return 0;
+}
+
+/* Skip the specified number of bytes and return 0 on success. Otherwise -1
+ is returned. */
+static int
+gz_skipn(FILE_T state, size_t n)
+{
+ while (n != 0) {
+ if (GZ_GETC() == -1) {
+ if (state->err == 0) {
+ /* EOF */
+ state->err = WTAP_ERR_SHORT_READ;
+ state->err_info = NULL;
+ }
+ return -1;
+ }
+ n--;
+ }
+ return 0;
+}
+
+/* Skip a null-terminated string and return 0 on success. Otherwise -1
+ is returned. */
+static int
+gz_skipzstr(FILE_T state)
+{
+ int ch;
+
+ /* It's null-terminated, so scan until we read a byte with
+ the value 0 or get an error. */
+ while ((ch = GZ_GETC()) > 0)
+ ;
+ if (ch == -1) {
+ if (state->err == 0) {
+ /* EOF */
+ state->err = WTAP_ERR_SHORT_READ;
+ state->err_info = NULL;
+ }
+ return -1;
+ }
+ return 0;
+}
+
+static void
+zlib_fast_seek_add(FILE_T file, struct zlib_cur_seek_point *point, int bits, gint64 in_pos, gint64 out_pos)
+{
+ /* it's for sure after gzip header, so file->fast_seek->len != 0 */
+ struct fast_seek_point *item = (struct fast_seek_point *)file->fast_seek->pdata[file->fast_seek->len - 1];
+
+#ifndef HAVE_INFLATEPRIME
+ if (bits)
+ return;
+#endif
+
+ /* Glib has got Balanced Binary Trees (GTree) but I couldn't find a way to do quick search for nearest (and smaller) value to seek (It's what fast_seek_find() do)
+ * Inserting value in middle of sorted array is expensive, so we want to add only in the end.
+ * It's not big deal, cause first-read don't usually invoke seeking
+ */
+ if (item->out + SPAN < out_pos) {
+ struct fast_seek_point *val = g_new(struct fast_seek_point,1);
+ val->in = in_pos;
+ val->out = out_pos;
+ val->compression = ZLIB;
+#ifdef HAVE_INFLATEPRIME
+ val->data.zlib.bits = bits;
+#endif
+ if (point->pos != 0) {
+ unsigned int left = ZLIB_WINSIZE - point->pos;
+
+ memcpy(val->data.zlib.window, point->window + point->pos, left);
+ memcpy(val->data.zlib.window + left, point->window, point->pos);
+ } else
+ memcpy(val->data.zlib.window, point->window, ZLIB_WINSIZE);
+
+ /*
+ * XXX - strm.adler is a uLong in at least some versions
+ * of zlib, and uLong is an unsigned long in at least
+ * some of those versions, which means it's 64-bit
+ * on LP64 platforms, even though the checksum is
+ * 32-bit. We assume the actual Adler checksum
+ * is in the lower 32 bits of strm.adler; as the
+ * checksum in the file is only 32 bits, we save only
+ * those lower 32 bits, and cast away any additional
+ * bits to squelch warnings.
+ *
+ * The same applies to strm.total_out.
+ */
+ val->data.zlib.adler = (guint32) file->strm.adler;
+ val->data.zlib.total_out = (guint32) file->strm.total_out;
+ g_ptr_array_add(file->fast_seek, val);
+ }
+}
+
+static void /* gz_decomp */
+zlib_read(FILE_T state, unsigned char *buf, unsigned int count)
+{
+ int ret = 0; /* XXX */
+ guint32 crc, len;
+ z_streamp strm = &(state->strm);
+
+ unsigned char *buf2 = buf;
+ unsigned int count2 = count;
+
+ strm->avail_out = count;
+ strm->next_out = buf;
+
+ /* fill output buffer up to end of deflate stream or error */
+ do {
+ /* get more input for inflate() */
+ if (state->in.avail == 0 && fill_in_buffer(state) == -1)
+ break;
+ if (state->in.avail == 0) {
+ /* EOF */
+ state->err = WTAP_ERR_SHORT_READ;
+ state->err_info = NULL;
+ break;
+ }
+
+ strm->avail_in = state->in.avail;
+ strm->next_in = state->in.next;
+ /* decompress and handle errors */
+#ifdef Z_BLOCK
+ ret = inflate(strm, Z_BLOCK);
+#else
+ ret = inflate(strm, Z_NO_FLUSH);
+#endif
+ state->in.avail = strm->avail_in;
+#ifdef z_const
+DIAG_OFF(cast-qual)
+ state->in.next = (unsigned char *)strm->next_in;
+DIAG_ON(cast-qual)
+#else
+ state->in.next = strm->next_in;
+#endif
+ if (ret == Z_STREAM_ERROR) {
+ state->err = WTAP_ERR_DECOMPRESS;
+ state->err_info = strm->msg;
+ break;
+ }
+ if (ret == Z_NEED_DICT) {
+ state->err = WTAP_ERR_DECOMPRESS;
+ state->err_info = "preset dictionary needed";
+ break;
+ }
+ if (ret == Z_MEM_ERROR) {
+ /* This means "not enough memory". */
+ state->err = ENOMEM;
+ state->err_info = NULL;
+ break;
+ }
+ if (ret == Z_DATA_ERROR) { /* deflate stream invalid */
+ state->err = WTAP_ERR_DECOMPRESS;
+ state->err_info = strm->msg;
+ break;
+ }
+ /*
+ * XXX - Z_BUF_ERROR?
+ */
+
+ strm->adler = crc32(strm->adler, buf2, count2 - strm->avail_out);
+#ifdef Z_BLOCK
+ if (state->fast_seek_cur != NULL) {
+ struct zlib_cur_seek_point *cur = (struct zlib_cur_seek_point *) state->fast_seek_cur;
+ unsigned int ready = count2 - strm->avail_out;
+
+ if (ready < ZLIB_WINSIZE) {
+ guint left = ZLIB_WINSIZE - cur->pos;
+
+ if (ready >= left) {
+ memcpy(cur->window + cur->pos, buf2, left);
+ if (ready != left)
+ memcpy(cur->window, buf2 + left, ready - left);
+
+ cur->pos = ready - left;
+ cur->have += ready;
+ } else {
+ memcpy(cur->window + cur->pos, buf2, ready);
+ cur->pos += ready;
+ cur->have += ready;
+ }
+
+ if (cur->have >= ZLIB_WINSIZE)
+ cur->have = ZLIB_WINSIZE;
+
+ } else {
+ memcpy(cur->window, buf2 + (ready - ZLIB_WINSIZE), ZLIB_WINSIZE);
+ cur->pos = 0;
+ cur->have = ZLIB_WINSIZE;
+ }
+
+ if (cur->have >= ZLIB_WINSIZE && ret != Z_STREAM_END && (strm->data_type & 128) && !(strm->data_type & 64))
+ zlib_fast_seek_add(state, cur, (strm->data_type & 7), state->raw_pos - strm->avail_in, state->pos + (count - strm->avail_out));
+ }
+#endif
+ buf2 = (buf2 + count2 - strm->avail_out);
+ count2 = strm->avail_out;
+
+ } while (strm->avail_out && ret != Z_STREAM_END);
+
+ /* update available output and crc check value */
+ state->out.next = buf;
+ state->out.avail = count - strm->avail_out;
+
+ /* Check gzip trailer if at end of deflate stream.
+ We don't fail immediately here, we just set an error
+ indication, so that we try to process what data we
+ got before the error. The next attempt to read
+ something past that data will get the error. */
+ if (ret == Z_STREAM_END) {
+ if (gz_next4(state, &crc) != -1 &&
+ gz_next4(state, &len) != -1) {
+ if (crc != strm->adler && !state->dont_check_crc) {
+ state->err = WTAP_ERR_DECOMPRESS;
+ state->err_info = "bad CRC";
+ } else if (len != (strm->total_out & 0xffffffffUL)) {
+ state->err = WTAP_ERR_DECOMPRESS;
+ state->err_info = "length field wrong";
+ }
+ }
+ state->last_compression = state->compression;
+ state->compression = UNKNOWN; /* ready for next stream, once have is 0 */
+ g_free(state->fast_seek_cur);
+ state->fast_seek_cur = NULL;
+ }
+}
+#endif
+
+static int
+gz_head(FILE_T state)
+{
+ guint already_read;
+
+ /* get some data in the input buffer */
+ if (state->in.avail == 0) {
+ if (fill_in_buffer(state) == -1)
+ return -1;
+ if (state->in.avail == 0)
+ return 0;
+ }
+
+ /* look for the gzip magic header bytes 31 and 139 */
+ if (state->in.next[0] == 31) {
+ state->in.avail--;
+ state->in.next++;
+
+ /* Make sure the byte after the first byte is present */
+ if (state->in.avail == 0 && fill_in_buffer(state) == -1) {
+ /* Read error. */
+ return -1;
+ }
+ if (state->in.avail != 0) {
+ if (state->in.next[0] == 139) {
+ /*
+ * We have what looks like the ID1 and ID2 bytes of a gzip
+ * header.
+ * Continue processing the file.
+ *
+ * XXX - some capture file formats (I'M LOOKING AT YOU,
+ * ENDACE!) can have 31 in the first byte of the file
+ * and 139 in the second byte of the file. For now, in
+ * those cases, you lose.
+ */
+#ifdef HAVE_ZLIB
+ guint8 cm;
+ guint8 flags;
+ guint16 len;
+ guint16 hcrc;
+
+ state->in.avail--;
+ state->in.next++;
+
+ /* read rest of header */
+
+ /* compression method (CM) */
+ if (gz_next1(state, &cm) == -1)
+ return -1;
+ if (cm != 8) {
+ state->err = WTAP_ERR_DECOMPRESS;
+ state->err_info = "unknown compression method";
+ return -1;
+ }
+
+ /* flags (FLG) */
+ if (gz_next1(state, &flags) == -1) {
+ /* Read error. */
+ return -1;
+ }
+ if (flags & 0xe0) { /* reserved flag bits */
+ state->err = WTAP_ERR_DECOMPRESS;
+ state->err_info = "reserved flag bits set";
+ return -1;
+ }
+
+ /* modification time (MTIME) */
+ if (gz_skipn(state, 4) == -1) {
+ /* Read error. */
+ return -1;
+ }
+
+ /* extra flags (XFL) */
+ if (gz_skipn(state, 1) == -1) {
+ /* Read error. */
+ return -1;
+ }
+
+ /* operating system (OS) */
+ if (gz_skipn(state, 1) == -1) {
+ /* Read error. */
+ return -1;
+ }
+
+ if (flags & 4) {
+ /* extra field - get XLEN */
+ if (gz_next2(state, &len) == -1) {
+ /* Read error. */
+ return -1;
+ }
+
+ /* skip the extra field */
+ if (gz_skipn(state, len) == -1) {
+ /* Read error. */
+ return -1;
+ }
+ }
+ if (flags & 8) {
+ /* file name */
+ if (gz_skipzstr(state) == -1) {
+ /* Read error. */
+ return -1;
+ }
+ }
+ if (flags & 16) {
+ /* comment */
+ if (gz_skipzstr(state) == -1) {
+ /* Read error. */
+ return -1;
+ }
+ }
+ if (flags & 2) {
+ /* header crc */
+ if (gz_next2(state, &hcrc) == -1) {
+ /* Read error. */
+ return -1;
+ }
+ /* XXX - check the CRC? */
+ }
+
+ /* set up for decompression */
+ inflateReset(&(state->strm));
+ state->strm.adler = crc32(0L, Z_NULL, 0);
+ state->compression = ZLIB;
+ state->is_compressed = TRUE;
+#ifdef Z_BLOCK
+ if (state->fast_seek) {
+ struct zlib_cur_seek_point *cur = g_new(struct zlib_cur_seek_point,1);
+
+ cur->pos = cur->have = 0;
+ g_free(state->fast_seek_cur);
+ state->fast_seek_cur = cur;
+ fast_seek_header(state, state->raw_pos - state->in.avail, state->pos, GZIP_AFTER_HEADER);
+ }
+#endif /* Z_BLOCK */
+ return 0;
+#else /* HAVE_ZLIB */
+ state->err = WTAP_ERR_DECOMPRESSION_NOT_SUPPORTED;
+ state->err_info = "reading gzip-compressed files isn't supported";
+ return -1;
+#endif /* HAVE_ZLIB */
+ }
+
+ /*
+ * Not a gzip file. "Unget" the first character; either:
+ *
+ * 1) we read both of the first two bytes into the
+ * buffer with the first ws_read, so we can just back
+ * up by one byte;
+ *
+ * 2) we only read the first byte into the buffer with
+ * the first ws_read (e.g., because we're reading from
+ * a pipe and only the first byte had been written to
+ * the pipe at that point), and read the second byte
+ * into the buffer after the first byte in the
+ * fill_in_buffer call, so we now have two bytes in
+ * the buffer, and can just back up by one byte.
+ */
+ state->in.avail++;
+ state->in.next--;
+ }
+ }
+#ifdef HAVE_LIBXZ
+ /* { 0xFD, '7', 'z', 'X', 'Z', 0x00 } */
+ /* FD 37 7A 58 5A 00 */
+#endif
+
+ if (state->in.avail >= 4
+ && state->in.buf[0] == 0x28 && state->in.buf[1] == 0xb5
+ && state->in.buf[2] == 0x2f && state->in.buf[3] == 0xfd) {
+#ifdef HAVE_ZSTD
+ const size_t ret = ZSTD_initDStream(state->zstd_dctx);
+ if (ZSTD_isError(ret)) {
+ state->err = WTAP_ERR_DECOMPRESS;
+ state->err_info = ZSTD_getErrorName(ret);
+ return -1;
+ }
+
+ state->compression = ZSTD;
+ state->is_compressed = TRUE;
+ return 0;
+#else
+ state->err = WTAP_ERR_DECOMPRESSION_NOT_SUPPORTED;
+ state->err_info = "reading zstd-compressed files isn't supported";
+ return -1;
+#endif
+ }
+
+ if (state->in.avail >= 4
+ && state->in.buf[0] == 0x04 && state->in.buf[1] == 0x22
+ && state->in.buf[2] == 0x4d && state->in.buf[3] == 0x18) {
+#ifdef USE_LZ4
+#if LZ4_VERSION_NUMBER >= 10800
+ LZ4F_resetDecompressionContext(state->lz4_dctx);
+#else
+ LZ4F_freeDecompressionContext(state->lz4_dctx);
+ const LZ4F_errorCode_t ret = LZ4F_createDecompressionContext(&state->lz4_dctx, LZ4F_VERSION);
+ if (LZ4F_isError(ret)) {
+ state->err = WTAP_ERR_INTERNAL;
+ state->err_info = LZ4F_getErrorName(ret);
+ return -1;
+ }
+#endif
+ state->compression = LZ4;
+ state->is_compressed = TRUE;
+ return 0;
+#else
+ state->err = WTAP_ERR_DECOMPRESSION_NOT_SUPPORTED;
+ state->err_info = "reading lz4-compressed files isn't supported";
+ return -1;
+#endif
+ }
+
+ if (state->fast_seek)
+ fast_seek_header(state, state->raw_pos - state->in.avail - state->out.avail, state->pos, UNCOMPRESSED);
+
+ /* doing raw i/o, save start of raw data for seeking, copy any leftover
+ input to output -- this assumes that the output buffer is larger than
+ the input buffer, which also assures space for gzungetc() */
+ state->raw = state->pos;
+ state->out.next = state->out.buf;
+ /* not a compressed file -- copy everything we've read into the
+ input buffer to the output buffer and fall to raw i/o */
+ already_read = bytes_in_buffer(&state->in);
+ if (already_read != 0) {
+ memcpy(state->out.buf, state->in.buf, already_read);
+ state->out.avail = already_read;
+
+ /* Now discard everything in the input buffer */
+ buf_reset(&state->in);
+ }
+ state->compression = UNCOMPRESSED;
+ return 0;
+}
+
+static int /* gz_make */
+fill_out_buffer(FILE_T state)
+{
+ if (state->compression == UNKNOWN) { /* look for compression header */
+ if (gz_head(state) == -1)
+ return -1;
+ if (state->out.avail != 0) /* got some data from gz_head() */
+ return 0;
+ }
+ if (state->compression == UNCOMPRESSED) { /* straight copy */
+ if (buf_read(state, &state->out) < 0)
+ return -1;
+ }
+#ifdef HAVE_ZLIB
+ else if (state->compression == ZLIB) { /* decompress */
+ zlib_read(state, state->out.buf, state->size << 1);
+ }
+#endif
+#ifdef HAVE_ZSTD
+ else if (state->compression == ZSTD) {
+ ws_assert(state->out.avail == 0);
+
+ if (state->in.avail == 0 && fill_in_buffer(state) == -1)
+ return -1;
+
+ ZSTD_outBuffer output = {state->out.buf, state->size << 1, 0};
+ ZSTD_inBuffer input = {state->in.next, state->in.avail, 0};
+ const size_t ret = ZSTD_decompressStream(state->zstd_dctx, &output, &input);
+ if (ZSTD_isError(ret)) {
+ state->err = WTAP_ERR_DECOMPRESS;
+ state->err_info = ZSTD_getErrorName(ret);
+ return -1;
+ }
+
+ state->in.next = state->in.next + input.pos;
+ state->in.avail -= (guint)input.pos;
+
+ state->out.next = output.dst;
+ state->out.avail = (guint)output.pos;
+
+ if (ret == 0) {
+ state->last_compression = state->compression;
+ state->compression = UNKNOWN;
+ }
+ }
+#endif
+#ifdef USE_LZ4
+ else if (state->compression == LZ4) {
+ ws_assert(state->out.avail == 0);
+
+ if (state->in.avail == 0 && fill_in_buffer(state) == -1)
+ return -1;
+
+ size_t outBufSize = state->size << 1;
+ size_t inBufSize = state->in.avail;
+ const size_t ret = LZ4F_decompress(state->lz4_dctx, state->out.buf, &outBufSize, state->in.next, &inBufSize, NULL);
+ if (LZ4F_isError(ret)) {
+ state->err = WTAP_ERR_DECOMPRESS;
+ state->err_info = LZ4F_getErrorName(ret);
+ return -1;
+ }
+
+ /*
+ * We assume LZ4F_decompress() will not set inBufSize to a
+ * value > state->in.avail.
+ */
+ state->in.next = state->in.next + inBufSize;
+ state->in.avail -= (guint)inBufSize;
+
+ state->out.next = state->out.buf;
+ state->out.avail = (guint)outBufSize;
+
+ if (ret == 0) {
+ state->last_compression = state->compression;
+ state->compression = UNKNOWN;
+ }
+ }
+#endif
+ return 0;
+}
+
+static int
+gz_skip(FILE_T state, gint64 len)
+{
+ guint n;
+
+ /* skip over len bytes or reach end-of-file, whichever comes first */
+ while (len)
+ if (state->out.avail != 0) {
+ /* We have stuff in the output buffer; skip over
+ it. */
+ n = (gint64)state->out.avail > len ? (unsigned)len : state->out.avail;
+ state->out.avail -= n;
+ state->out.next += n;
+ state->pos += n;
+ len -= n;
+ } else if (state->err != 0) {
+ /* We have nothing in the output buffer, and
+ we have an error that may not have been
+ reported yet; that means we can't generate
+ any more data into the output buffer, so
+ return an error indication. */
+ return -1;
+ } else if (state->eof && state->in.avail == 0) {
+ /* We have nothing in the output buffer, and
+ we're at the end of the input; just return. */
+ break;
+ } else {
+ /* We have nothing in the output buffer, and
+ we can generate more data; get more output,
+ looking for header if required. */
+ if (fill_out_buffer(state) == -1)
+ return -1;
+ }
+ return 0;
+}
+
+static void
+gz_reset(FILE_T state)
+{
+ buf_reset(&state->out); /* no output data available */
+ state->eof = FALSE; /* not at end of file */
+ state->compression = UNKNOWN; /* look for compression header */
+
+ state->seek_pending = FALSE; /* no seek request pending */
+ state->err = 0; /* clear error */
+ state->err_info = NULL;
+ state->pos = 0; /* no uncompressed data yet */
+ buf_reset(&state->in); /* no input data yet */
+}
+
+FILE_T
+file_fdopen(int fd)
+{
+ /*
+ * XXX - we now check whether we have st_blksize in struct stat;
+ * it's not available on all platforms.
+ *
+ * I'm not sure why we're testing _STATBUF_ST_BLKSIZE; it's not
+ * set on all platforms that have st_blksize in struct stat.
+ * (Not all platforms have st_blksize in struct stat.)
+ *
+ * Is there some reason *not* to make the buffer size the maximum
+ * of GBUFSIZE and st_blksize? On most UN*Xes, the standard I/O
+ * library does I/O with st_blksize as the buffer size; on others,
+ * and on Windows, it's a 4K buffer size. If st_blksize is bigger
+ * than GBUFSIZE (which is currently 4KB), that's probably a
+ * hint that reading in st_blksize chunks is considered a good
+ * idea (e.g., an 8K/1K Berkeley fast file system with st_blksize
+ * being 8K, or APFS, where st_blksize is big on at least some
+ * versions of macOS).
+ */
+#ifdef _STATBUF_ST_BLKSIZE
+ ws_statb64 st;
+#endif
+#ifdef HAVE_ZSTD
+ size_t zstd_buf_size;
+#endif
+ guint want = GZBUFSIZE;
+ FILE_T state;
+#ifdef USE_LZ4
+ size_t ret;
+#endif
+
+ if (fd == -1)
+ return NULL;
+
+ /* allocate FILE_T structure to return */
+ state = (FILE_T)g_try_malloc0(sizeof *state);
+ if (state == NULL)
+ return NULL;
+
+ state->fast_seek_cur = NULL;
+ state->fast_seek = NULL;
+
+ /* open the file with the appropriate mode (or just use fd) */
+ state->fd = fd;
+
+ /* we don't yet know whether it's compressed */
+ state->is_compressed = FALSE;
+ state->last_compression = UNKNOWN;
+
+ /* save the current position for rewinding (only if reading) */
+ state->start = ws_lseek64(state->fd, 0, SEEK_CUR);
+ if (state->start == -1) state->start = 0;
+ state->raw_pos = state->start;
+
+ /* initialize stream */
+ gz_reset(state);
+
+#ifdef _STATBUF_ST_BLKSIZE
+ /*
+ * See what I/O size the file system recommends using, and if
+ * it's bigger than what we're using and isn't too big, use
+ * it.
+ */
+ if (ws_fstat64(fd, &st) >= 0) {
+ /*
+ * Yes, st_blksize can be bigger than an int; apparently,
+ * it's a long on LP64 Linux, for example.
+ *
+ * If the value is too big to fit into a guint,
+ * just use the maximum read buffer size.
+ *
+ * On top of that, the Single UNIX Speification says that
+ * st_blksize is of type blksize_t, which is a *signed*
+ * integer type, and, at minimum, macOS 11.6 and Linux 5.14.11's
+ * include/uapi/asm-generic/stat.h define it as such.
+ *
+ * However, other OSes might make it unsigned, and older versions
+ * of OSes that currently make it signed might make it unsigned,
+ * so we try to avoid warnings from that.
+ *
+ * We cast MAX_READ_BUF_SIZE to long in order to avoid the
+ * warning, although it might introduce warnings on platforms
+ * where st_blocksize is unsigned; we'll deal with that if
+ * it ever shows up as an issue.
+ *
+ * MAX_READ_BUF_SIZE is < the largest *signed* 32-bt integer,
+ * so casting it to long won't turn it into a negative number.
+ * (We only support 32-bit and 64-bit 2's-complement platforms.)
+ */
+ if (st.st_blksize <= (long)MAX_READ_BUF_SIZE)
+ want = (guint)st.st_blksize;
+ else
+ want = MAX_READ_BUF_SIZE;
+ /* XXX, verify result? */
+ }
+#endif
+#ifdef HAVE_ZSTD
+ /* we should have separate input and output buf sizes */
+ zstd_buf_size = ZSTD_DStreamInSize();
+ if (zstd_buf_size > want) {
+ if (zstd_buf_size <= MAX_READ_BUF_SIZE)
+ want = (guint)zstd_buf_size;
+ else
+ want = MAX_READ_BUF_SIZE;
+ }
+ zstd_buf_size = ZSTD_DStreamOutSize();
+ if (zstd_buf_size > want) {
+ if (zstd_buf_size <= MAX_READ_BUF_SIZE)
+ want = (guint)zstd_buf_size;
+ else
+ want = MAX_READ_BUF_SIZE;
+ }
+#endif
+ /* allocate buffers */
+ state->in.buf = (unsigned char *)g_try_malloc(want);
+ state->in.next = state->in.buf;
+ state->in.avail = 0;
+ state->out.buf = (unsigned char *)g_try_malloc(want << 1);
+ state->out.next = state->out.buf;
+ state->out.avail = 0;
+ state->size = want;
+ if (state->in.buf == NULL || state->out.buf == NULL) {
+ goto err;
+ }
+
+#ifdef HAVE_ZLIB
+ /* allocate inflate memory */
+ state->strm.zalloc = Z_NULL;
+ state->strm.zfree = Z_NULL;
+ state->strm.opaque = Z_NULL;
+ state->strm.avail_in = 0;
+ state->strm.next_in = Z_NULL;
+ if (inflateInit2(&(state->strm), -15) != Z_OK) { /* raw inflate */
+ goto err;
+ }
+
+ /* for now, assume we should check the crc */
+ state->dont_check_crc = FALSE;
+#endif
+
+#ifdef HAVE_ZSTD
+ state->zstd_dctx = ZSTD_createDCtx();
+ if (state->zstd_dctx == NULL) {
+ goto err;
+ }
+#endif
+
+#ifdef USE_LZ4
+ ret = LZ4F_createDecompressionContext(&state->lz4_dctx, LZ4F_VERSION);
+ if (LZ4F_isError(ret)) {
+ goto err;
+ }
+#endif
+
+ /* return stream */
+ return state;
+
+err:
+#ifdef HAVE_ZLIB
+ inflateEnd(&state->strm);
+#endif
+#ifdef HAVE_ZSTD
+ ZSTD_freeDCtx(state->zstd_dctx);
+#endif
+#ifdef USE_LZ4
+ LZ4F_freeDecompressionContext(state->lz4_dctx);
+#endif
+ g_free(state->out.buf);
+ g_free(state->in.buf);
+ g_free(state);
+ errno = ENOMEM;
+ return NULL;
+}
+
+FILE_T
+file_open(const char *path)
+{
+ int fd;
+ FILE_T ft;
+#ifdef HAVE_ZLIB
+ const char *suffixp;
+#endif
+
+ /* open file and do correct filename conversions.
+
+ XXX - do we need O_LARGEFILE? On UN*X, if we need to do
+ something special to get large file support, the configure
+ script should have set us up with the appropriate #defines,
+ so we should be getting a large-file-enabled file descriptor
+ here. Pre-Large File Summit UN*Xes, and possibly even some
+ post-LFS UN*Xes, might require O_LARGEFILE here, though.
+ If so, we should probably handle that in ws_open(). */
+ if ((fd = ws_open(path, O_RDONLY|O_BINARY, 0000)) == -1)
+ return NULL;
+
+ /* open file handle */
+ ft = file_fdopen(fd);
+ if (ft == NULL) {
+ ws_close(fd);
+ return NULL;
+ }
+
+#ifdef HAVE_ZLIB
+ /*
+ * If this file's name ends in ".caz", it's probably a compressed
+ * Windows Sniffer file. The compression is gzip, but if we
+ * process the CRC as specified by RFC 1952, the computed CRC
+ * doesn't match the stored CRC.
+ *
+ * Compressed Windows Sniffer files don't all have the same CRC
+ * value; is it just random crap, or are they running the CRC on
+ * a different set of data than you're supposed to (e.g., not
+ * CRCing some of the data), or something such as that?
+ *
+ * For now, we just set a flag to ignore CRC errors.
+ */
+ suffixp = strrchr(path, '.');
+ if (suffixp != NULL) {
+ if (g_ascii_strcasecmp(suffixp, ".caz") == 0)
+ ft->dont_check_crc = TRUE;
+ }
+#endif
+
+ return ft;
+}
+
+void
+file_set_random_access(FILE_T stream, gboolean random_flag _U_, GPtrArray *seek)
+{
+ stream->fast_seek = seek;
+}
+
+gint64
+file_seek(FILE_T file, gint64 offset, int whence, int *err)
+{
+ struct fast_seek_point *here;
+ guint n;
+
+ if (whence != SEEK_SET && whence != SEEK_CUR && whence != SEEK_END) {
+ ws_assert_not_reached();
+/*
+ *err = EINVAL;
+ return -1;
+*/
+ }
+
+ /* Normalize offset to a SEEK_CUR specification */
+ if (whence == SEEK_END) {
+ /* Seek relative to the end of the file; given that we might be
+ reading from a compressed file, we do that by seeking to the
+ end of the file, making an offset relative to the end of
+ the file an offset relative to the current position.
+
+ XXX - we don't actually use this yet, but, for uncompressed
+ files, we could optimize it, if desired, by directly using
+ ws_lseek64(). */
+ if (gz_skip(file, G_MAXINT64) == -1) {
+ *err = file->err;
+ return -1;
+ }
+ if (offset == 0) {
+ /* We are done */
+ return file->pos;
+ }
+ } else if (whence == SEEK_SET)
+ offset -= file->pos;
+ else if (file->seek_pending) {
+ /* There's a forward-skip pending, so file->pos doesn't reflect
+ the actual file position, it represents the position from
+ which we're skipping; update the offset to include that. */
+ offset += file->skip;
+ }
+ file->seek_pending = FALSE;
+
+ /*
+ * Are we moving at all?
+ */
+ if (offset == 0) {
+ /* No. Just return the current position. */
+ return file->pos;
+ }
+
+ /*
+ * Are we seeking backwards?
+ */
+ if (offset < 0) {
+ /*
+ * Yes.
+ *
+ * Do we have enough data before the current position in the
+ * buffer that we can seek backwards within the buffer?
+ */
+ if (-offset <= offset_in_buffer(&file->out)) {
+ /*
+ * Yes. Adjust appropriately.
+ *
+ * offset is negative, so -offset is non-negative, and
+ * -offset is <= an unsigned and thus fits in an unsigned.
+ * Get that value and adjust appropriately.
+ *
+ * (Casting offset to unsigned makes it positive, which
+ * is not what we would want, so we cast -offset instead.)
+ *
+ * XXX - this won't work with -offset = 2^63, as its
+ * negative isn't a valid 64-bit integer, but we are
+ * not at all likely to see files big enough to ever
+ * see a negative offset that large.
+ */
+ guint adjustment = (unsigned)(-offset);
+
+ file->out.avail += adjustment;
+ file->out.next -= adjustment;
+ file->pos -= adjustment;
+ return file->pos;
+ }
+ } else {
+ /*
+ * No. Offset is positive; we're seeking forwards.
+ *
+ * Do we have enough data after the current position in the
+ * buffer that we can seek forwards within the buffer?
+ */
+ if (offset < file->out.avail) {
+ /*
+ * Yes. Adjust appropriately.
+ *
+ * offset is < an unsigned and thus fits in an unsigned,
+ * so we can cast it to guint safely.
+ */
+ file->out.avail -= (guint)offset;
+ file->out.next += offset;
+ file->pos += offset;
+ return file->pos;
+ }
+ }
+
+ /*
+ * We're not seeking within the buffer. Do we have "fast seek" data
+ * for the location to which we will be seeking, and is the offset
+ * outside the span for compressed files or is this an uncompressed
+ * file?
+ *
+ * XXX, profile
+ */
+ if ((here = fast_seek_find(file, file->pos + offset)) &&
+ (offset < 0 || offset > SPAN || here->compression == UNCOMPRESSED)) {
+ gint64 off, off2;
+
+ /*
+ * Yes. Use that data to do the seek.
+ * Note that this will be true only if file_set_random_access()
+ * has been called on this file, which should never be the case
+ * for a pipe.
+ */
+#ifdef HAVE_ZLIB
+ if (here->compression == ZLIB) {
+#ifdef HAVE_INFLATEPRIME
+ off = here->in - (here->data.zlib.bits ? 1 : 0);
+#else
+ off = here->in;
+#endif
+ off2 = here->out;
+ } else if (here->compression == GZIP_AFTER_HEADER) {
+ off = here->in;
+ off2 = here->out;
+ } else
+#endif
+ {
+ off2 = (file->pos + offset);
+ off = here->in + (off2 - here->out);
+ }
+
+ if (ws_lseek64(file->fd, off, SEEK_SET) == -1) {
+ *err = errno;
+ return -1;
+ }
+ fast_seek_reset(file);
+
+ file->raw_pos = off;
+ buf_reset(&file->out);
+ file->eof = FALSE;
+ file->seek_pending = FALSE;
+ file->err = 0;
+ file->err_info = NULL;
+ buf_reset(&file->in);
+
+#ifdef HAVE_ZLIB
+ if (here->compression == ZLIB) {
+ z_stream *strm = &file->strm;
+
+ inflateReset(strm);
+ strm->adler = here->data.zlib.adler;
+ strm->total_out = here->data.zlib.total_out;
+#ifdef HAVE_INFLATEPRIME
+ if (here->data.zlib.bits) {
+ FILE_T state = file;
+ int ret = GZ_GETC();
+
+ if (ret == -1) {
+ if (state->err == 0) {
+ /* EOF */
+ *err = WTAP_ERR_SHORT_READ;
+ } else
+ *err = state->err;
+ return -1;
+ }
+ (void)inflatePrime(strm, here->data.zlib.bits, ret >> (8 - here->data.zlib.bits));
+ }
+#endif
+ (void)inflateSetDictionary(strm, here->data.zlib.window, ZLIB_WINSIZE);
+ file->compression = ZLIB;
+ } else if (here->compression == GZIP_AFTER_HEADER) {
+ z_stream *strm = &file->strm;
+
+ inflateReset(strm);
+ strm->adler = crc32(0L, Z_NULL, 0);
+ file->compression = ZLIB;
+ } else
+#endif
+ file->compression = here->compression;
+
+ offset = (file->pos + offset) - off2;
+ file->pos = off2;
+ /* g_print("OK! %ld\n", offset); */
+
+ if (offset) {
+ /* Don't skip forward yet, wait until we want to read from
+ the file; that way, if we do multiple seeks in a row,
+ all involving forward skips, they will be combined. */
+ file->seek_pending = TRUE;
+ file->skip = offset;
+ }
+ return file->pos + offset;
+ }
+
+ /*
+ * Is this an uncompressed file, are we within the raw area,
+ * are we either seeking backwards or seeking past the end
+ * of the buffer, and are we set up for random access with
+ * file_set_random_access()?
+ *
+ * Again, note that this will never be true on a pipe, as
+ * file_set_random_access() should never be called if we're
+ * reading from a pipe.
+ */
+ if (file->compression == UNCOMPRESSED && file->pos + offset >= file->raw
+ && (offset < 0 || offset >= file->out.avail)
+ && (file->fast_seek != NULL))
+ {
+ /*
+ * Yes. Just seek there within the file.
+ */
+ if (ws_lseek64(file->fd, offset - file->out.avail, SEEK_CUR) == -1) {
+ *err = errno;
+ return -1;
+ }
+ file->raw_pos += (offset - file->out.avail);
+ buf_reset(&file->out);
+ file->eof = FALSE;
+ file->seek_pending = FALSE;
+ file->err = 0;
+ file->err_info = NULL;
+ buf_reset(&file->in);
+ file->pos += offset;
+ return file->pos;
+ }
+
+ /*
+ * Are we seeking backwards?
+ */
+ if (offset < 0) {
+ /*
+ * Yes. We have no fast seek data, so we have to rewind and
+ * seek forward.
+ * XXX - true only for compressed files.
+ *
+ * Calculate the amount to skip forward after rewinding.
+ */
+ offset += file->pos;
+ if (offset < 0) { /* before start of file! */
+ *err = EINVAL;
+ return -1;
+ }
+ /* rewind, then skip to offset */
+
+ /* back up and start over */
+ if (ws_lseek64(file->fd, file->start, SEEK_SET) == -1) {
+ *err = errno;
+ return -1;
+ }
+ fast_seek_reset(file);
+ file->raw_pos = file->start;
+ gz_reset(file);
+ }
+
+ /*
+ * Either we're seeking backwards, but have rewound and now need to
+ * skip forwards, or we're seeking forwards.
+ *
+ * Skip what's in output buffer (one less gzgetc() check).
+ */
+ n = (gint64)file->out.avail > offset ? (unsigned)offset : file->out.avail;
+ file->out.avail -= n;
+ file->out.next += n;
+ file->pos += n;
+ offset -= n;
+
+ /* request skip (if not zero) */
+ if (offset) {
+ /* Don't skip forward yet, wait until we want to read from
+ the file; that way, if we do multiple seeks in a row,
+ all involving forward skips, they will be combined. */
+ file->seek_pending = TRUE;
+ file->skip = offset;
+ }
+ return file->pos + offset;
+}
+
+gint64
+file_tell(FILE_T stream)
+{
+ /* return position */
+ return stream->pos + (stream->seek_pending ? stream->skip : 0);
+}
+
+gint64
+file_tell_raw(FILE_T stream)
+{
+ return stream->raw_pos;
+}
+
+int
+file_fstat(FILE_T stream, ws_statb64 *statb, int *err)
+{
+ if (ws_fstat64(stream->fd, statb) == -1) {
+ if (err != NULL)
+ *err = errno;
+ return -1;
+ }
+ return 0;
+}
+
+gboolean
+file_iscompressed(FILE_T stream)
+{
+ return stream->is_compressed;
+}
+
+/* Returns a wtap compression type. If we don't know the compression type,
+ * return WTAP_UNCOMPRESSED, but if our compression state is temporarily
+ * UNKNOWN because we need to reread compression headers, return the last
+ * known compression type.
+ */
+static wtap_compression_type
+file_get_compression_type(FILE_T stream)
+{
+ if (stream->is_compressed) {
+ switch ((stream->compression == UNKNOWN) ? stream->last_compression : stream->compression) {
+
+ case ZLIB:
+ case GZIP_AFTER_HEADER:
+ return WTAP_GZIP_COMPRESSED;
+
+ case ZSTD:
+ return WTAP_ZSTD_COMPRESSED;
+
+ case LZ4:
+ return WTAP_LZ4_COMPRESSED;
+
+ case UNCOMPRESSED:
+ return WTAP_UNCOMPRESSED;
+
+ default: /* UNKNOWN, should never happen if is_compressed is set */
+ ws_assert_not_reached();
+ return WTAP_UNCOMPRESSED;
+ }
+ }
+ return WTAP_UNCOMPRESSED;
+}
+
+int
+file_read(void *buf, unsigned int len, FILE_T file)
+{
+ guint got, n;
+
+ /* if len is zero, avoid unnecessary operations */
+ if (len == 0)
+ return 0;
+
+ /* process a skip request */
+ if (file->seek_pending) {
+ file->seek_pending = FALSE;
+ if (gz_skip(file, file->skip) == -1)
+ return -1;
+ }
+
+ /*
+ * Get len bytes to buf, or less than len if at the end;
+ * if buf is null, just throw the bytes away.
+ */
+ got = 0;
+ do {
+ if (file->out.avail != 0) {
+ /* We have stuff in the output buffer; copy
+ what we have. */
+ n = file->out.avail > len ? len : file->out.avail;
+ if (buf != NULL) {
+ memcpy(buf, file->out.next, n);
+ buf = (char *)buf + n;
+ }
+ file->out.next += n;
+ file->out.avail -= n;
+ len -= n;
+ got += n;
+ file->pos += n;
+ } else if (file->err != 0) {
+ /* We have nothing in the output buffer, and
+ we have an error that may not have been
+ reported yet; that means we can't generate
+ any more data into the output buffer, so
+ return an error indication. */
+ return -1;
+ } else if (file->eof && file->in.avail == 0) {
+ /* We have nothing in the output buffer, and
+ we're at the end of the input; just return
+ with what we've gotten so far. */
+ break;
+ } else {
+ /* We have nothing in the output buffer, and
+ we can generate more data; get more output,
+ looking for header if required, and
+ keep looping to process the new stuff
+ in the output buffer. */
+ if (fill_out_buffer(file) == -1)
+ return -1;
+ }
+ } while (len);
+
+ return (int)got;
+}
+
+/*
+ * XXX - this *peeks* at next byte, not a character.
+ */
+int
+file_peekc(FILE_T file)
+{
+ int ret = 0;
+
+ /* check that we're reading and that there's no error */
+ if (file->err != 0)
+ return -1;
+
+ /* try output buffer (no need to check for skip request) */
+ if (file->out.avail != 0) {
+ return *(file->out.next);
+ }
+
+ /* process a skip request */
+ if (file->seek_pending) {
+ file->seek_pending = FALSE;
+ if (gz_skip(file, file->skip) == -1)
+ return -1;
+ }
+ /* if we processed a skip request, there may be data in the buffer,
+ * or an error could have occurred; likewise if we didn't do seek but
+ * now call fill_out_buffer, the errors can occur. So we do this while
+ * loop to check before and after - this is basically the logic from
+ * file_read() but only for peeking not consuming a byte
+ */
+ while (1) {
+ if (file->out.avail != 0) {
+ return *(file->out.next);
+ }
+ else if (file->err != 0) {
+ return -1;
+ }
+ else if (file->eof && file->in.avail == 0) {
+ return -1;
+ }
+ else if (fill_out_buffer(file) == -1) {
+ return -1;
+ }
+ }
+ /* it's actually impossible to get here */
+ return ret;
+}
+
+/*
+ * XXX - this gets a byte, not a character.
+ */
+int
+file_getc(FILE_T file)
+{
+ unsigned char buf[1];
+ int ret;
+
+ /* check that we're reading and that there's no error */
+ if (file->err != 0)
+ return -1;
+
+ /* try output buffer (no need to check for skip request) */
+ if (file->out.avail != 0) {
+ file->out.avail--;
+ file->pos++;
+ return *(file->out.next)++;
+ }
+
+ ret = file_read(buf, 1, file);
+ return ret < 1 ? -1 : buf[0];
+}
+
+/* Like file_gets, but returns a pointer to the terminating NUL. */
+char *
+file_getsp(char *buf, int len, FILE_T file)
+{
+ guint left, n;
+ char *str;
+ unsigned char *eol;
+
+ /* check parameters */
+ if (buf == NULL || len < 1)
+ return NULL;
+
+ /* check that there's no error */
+ if (file->err != 0)
+ return NULL;
+
+ /* process a skip request */
+ if (file->seek_pending) {
+ file->seek_pending = FALSE;
+ if (gz_skip(file, file->skip) == -1)
+ return NULL;
+ }
+
+ /* copy output bytes up to new line or len - 1, whichever comes first --
+ append a terminating zero to the string (we don't check for a zero in
+ the contents, let the user worry about that) */
+ str = buf;
+ left = (unsigned)len - 1;
+ if (left) do {
+ /* assure that something is in the output buffer */
+ if (file->out.avail == 0) {
+ /* We have nothing in the output buffer. */
+ if (file->err != 0) {
+ /* We have an error that may not have
+ been reported yet; that means we
+ can't generate any more data into
+ the output buffer, so return an
+ error indication. */
+ return NULL;
+ }
+ if (fill_out_buffer(file) == -1)
+ return NULL; /* error */
+ if (file->out.avail == 0) { /* end of file */
+ if (buf == str) /* got bupkus */
+ return NULL;
+ break; /* got something -- return it */
+ }
+ }
+
+ /* look for end-of-line in current output buffer */
+ n = file->out.avail > left ? left : file->out.avail;
+ eol = (unsigned char *)memchr(file->out.next, '\n', n);
+ if (eol != NULL)
+ n = (unsigned)(eol - file->out.next) + 1;
+
+ /* copy through end-of-line, or remainder if not found */
+ memcpy(buf, file->out.next, n);
+ file->out.avail -= n;
+ file->out.next += n;
+ file->pos += n;
+ left -= n;
+ buf += n;
+ } while (left && eol == NULL);
+
+ /* found end-of-line or out of space -- add a terminator and return
+ a pointer to it */
+ buf[0] = 0;
+ return buf;
+}
+
+char *
+file_gets(char *buf, int len, FILE_T file)
+{
+ if (!file_getsp(buf, len, file)) return NULL;
+ return buf;
+}
+
+int
+file_eof(FILE_T file)
+{
+ /* return end-of-file state */
+ return (file->eof && file->in.avail == 0 && file->out.avail == 0);
+}
+
+/*
+ * Routine to return a Wiretap error code (0 for no error, an errno
+ * for a file error, or a WTAP_ERR_ code for other errors) for an
+ * I/O stream. Also returns an error string for some errors.
+ */
+int
+file_error(FILE_T fh, gchar **err_info)
+{
+ if (fh->err!=0 && err_info) {
+ /* g_strdup() returns NULL for NULL argument */
+ *err_info = g_strdup(fh->err_info);
+ }
+ return fh->err;
+}
+
+void
+file_clearerr(FILE_T stream)
+{
+ /* clear error and end-of-file */
+ stream->err = 0;
+ stream->err_info = NULL;
+ stream->eof = FALSE;
+}
+
+void
+file_fdclose(FILE_T file)
+{
+ if (file->fd != -1)
+ ws_close(file->fd);
+ file->fd = -1;
+}
+
+gboolean
+file_fdreopen(FILE_T file, const char *path)
+{
+ int fd;
+
+ if ((fd = ws_open(path, O_RDONLY|O_BINARY, 0000)) == -1)
+ return FALSE;
+ file->fd = fd;
+ return TRUE;
+}
+
+void
+file_close(FILE_T file)
+{
+ int fd = file->fd;
+
+ /* free memory and close file */
+ if (file->size) {
+#ifdef HAVE_ZLIB
+ inflateEnd(&(file->strm));
+#endif
+#ifdef HAVE_ZSTD
+ ZSTD_freeDCtx(file->zstd_dctx);
+#endif
+#ifdef USE_LZ4
+ LZ4F_freeDecompressionContext(file->lz4_dctx);
+#endif
+ g_free(file->out.buf);
+ g_free(file->in.buf);
+ }
+ g_free(file->fast_seek_cur);
+ file->err = 0;
+ file->err_info = NULL;
+ g_free(file);
+ /*
+ * If fd is -1, somebody's done a file_closefd() on us, so
+ * we don't need to close the FD itself, and shouldn't do
+ * so.
+ */
+ if (fd != -1)
+ ws_close(fd);
+}
+
+#ifdef HAVE_ZLIB
+/* internal gzip file state data structure for writing */
+struct wtap_writer {
+ int fd; /* file descriptor */
+ gint64 pos; /* current position in uncompressed data */
+ guint size; /* buffer size, zero if not allocated yet */
+ guint want; /* requested buffer size, default is GZBUFSIZE */
+ unsigned char *in; /* input buffer */
+ unsigned char *out; /* output buffer (double-sized when reading) */
+ unsigned char *next; /* next output data to deliver or write */
+ int level; /* compression level */
+ int strategy; /* compression strategy */
+ int err; /* error code */
+ const char *err_info; /* additional error information string for some errors */
+ /* zlib deflate stream */
+ z_stream strm; /* stream structure in-place (not a pointer) */
+};
+
+GZWFILE_T
+gzwfile_open(const char *path)
+{
+ int fd;
+ GZWFILE_T state;
+ int save_errno;
+
+ fd = ws_open(path, O_BINARY|O_WRONLY|O_CREAT|O_TRUNC, 0666);
+ if (fd == -1)
+ return NULL;
+ state = gzwfile_fdopen(fd);
+ if (state == NULL) {
+ save_errno = errno;
+ ws_close(fd);
+ errno = save_errno;
+ }
+ return state;
+}
+
+GZWFILE_T
+gzwfile_fdopen(int fd)
+{
+ GZWFILE_T state;
+
+ /* allocate wtap_writer structure to return */
+ state = (GZWFILE_T)g_try_malloc(sizeof *state);
+ if (state == NULL)
+ return NULL;
+ state->fd = fd;
+ state->size = 0; /* no buffers allocated yet */
+ state->want = GZBUFSIZE; /* requested buffer size */
+
+ state->level = Z_DEFAULT_COMPRESSION;
+ state->strategy = Z_DEFAULT_STRATEGY;
+
+ /* initialize stream */
+ state->err = Z_OK; /* clear error */
+ state->err_info = NULL; /* clear additional error information */
+ state->pos = 0; /* no uncompressed data yet */
+ state->strm.avail_in = 0; /* no input data yet */
+
+ /* return stream */
+ return state;
+}
+
+/* Initialize state for writing a gzip file. Mark initialization by setting
+ state->size to non-zero. Return -1, and set state->err and possibly
+ state->err_info, on failure; return 0 on success. */
+static int
+gz_init(GZWFILE_T state)
+{
+ int ret;
+ z_streamp strm = &(state->strm);
+
+ /* allocate input and output buffers */
+ state->in = (unsigned char *)g_try_malloc(state->want);
+ state->out = (unsigned char *)g_try_malloc(state->want);
+ if (state->in == NULL || state->out == NULL) {
+ g_free(state->out);
+ g_free(state->in);
+ state->err = ENOMEM;
+ return -1;
+ }
+
+ /* allocate deflate memory, set up for gzip compression */
+ strm->zalloc = Z_NULL;
+ strm->zfree = Z_NULL;
+ strm->opaque = Z_NULL;
+ ret = deflateInit2(strm, state->level, Z_DEFLATED,
+ 15 + 16, 8, state->strategy);
+ if (ret != Z_OK) {
+ g_free(state->out);
+ g_free(state->in);
+ if (ret == Z_MEM_ERROR) {
+ /* This means "not enough memory". */
+ state->err = ENOMEM;
+ } else {
+ /* This "shouldn't happen". */
+ state->err = WTAP_ERR_INTERNAL;
+ state->err_info = "Unknown error from deflateInit2()";
+ }
+ return -1;
+ }
+
+ /* mark state as initialized */
+ state->size = state->want;
+
+ /* initialize write buffer */
+ strm->avail_out = state->size;
+ strm->next_out = state->out;
+ state->next = strm->next_out;
+ return 0;
+}
+
+/* Compress whatever is at avail_in and next_in and write to the output file.
+ Return -1, and set state->err and possibly state->err_info, if there is
+ an error writing to the output file; return 0 on success.
+ flush is assumed to be a valid deflate() flush value. If flush is Z_FINISH,
+ then the deflate() state is reset to start a new gzip stream. */
+static int
+gz_comp(GZWFILE_T state, int flush)
+{
+ int ret;
+ ssize_t got;
+ ptrdiff_t have;
+ z_streamp strm = &(state->strm);
+
+ /* allocate memory if this is the first time through */
+ if (state->size == 0 && gz_init(state) == -1)
+ return -1;
+
+ /* run deflate() on provided input until it produces no more output */
+ ret = Z_OK;
+ do {
+ /* write out current buffer contents if full, or if flushing, but if
+ doing Z_FINISH then don't write until we get to Z_STREAM_END */
+ if (strm->avail_out == 0 || (flush != Z_NO_FLUSH &&
+ (flush != Z_FINISH || ret == Z_STREAM_END))) {
+ have = strm->next_out - state->next;
+ if (have) {
+ got = ws_write(state->fd, state->next, (unsigned int)have);
+ if (got < 0) {
+ state->err = errno;
+ return -1;
+ }
+ if ((ptrdiff_t)got != have) {
+ state->err = WTAP_ERR_SHORT_WRITE;
+ return -1;
+ }
+ }
+ if (strm->avail_out == 0) {
+ strm->avail_out = state->size;
+ strm->next_out = state->out;
+ }
+ state->next = strm->next_out;
+ }
+
+ /* compress */
+ have = strm->avail_out;
+ ret = deflate(strm, flush);
+ if (ret == Z_STREAM_ERROR) {
+ /* This "shouldn't happen". */
+ state->err = WTAP_ERR_INTERNAL;
+ state->err_info = "Z_STREAM_ERROR from deflate()";
+ return -1;
+ }
+ have -= strm->avail_out;
+ } while (have);
+
+ /* if that completed a deflate stream, allow another to start */
+ if (flush == Z_FINISH)
+ deflateReset(strm);
+
+ /* all done, no errors */
+ return 0;
+}
+
+/* Write out len bytes from buf. Return 0, and set state->err, on
+ failure or on an attempt to write 0 bytes (in which case state->err
+ is Z_OK); return the number of bytes written on success. */
+unsigned
+gzwfile_write(GZWFILE_T state, const void *buf, guint len)
+{
+ guint put = len;
+ guint n;
+ z_streamp strm;
+
+ strm = &(state->strm);
+
+ /* check that there's no error */
+ if (state->err != Z_OK)
+ return 0;
+
+ /* if len is zero, avoid unnecessary operations */
+ if (len == 0)
+ return 0;
+
+ /* allocate memory if this is the first time through */
+ if (state->size == 0 && gz_init(state) == -1)
+ return 0;
+
+ /* for small len, copy to input buffer, otherwise compress directly */
+ if (len < state->size) {
+ /* copy to input buffer, compress when full */
+ do {
+ if (strm->avail_in == 0)
+ strm->next_in = state->in;
+ n = state->size - strm->avail_in;
+ if (n > len)
+ n = len;
+#ifdef z_const
+DIAG_OFF(cast-qual)
+ memcpy((Bytef *)strm->next_in + strm->avail_in, buf, n);
+DIAG_ON(cast-qual)
+#else
+ memcpy(strm->next_in + strm->avail_in, buf, n);
+#endif
+ strm->avail_in += n;
+ state->pos += n;
+ buf = (const char *)buf + n;
+ len -= n;
+ if (len && gz_comp(state, Z_NO_FLUSH) == -1)
+ return 0;
+ } while (len);
+ }
+ else {
+ /* consume whatever's left in the input buffer */
+ if (strm->avail_in != 0 && gz_comp(state, Z_NO_FLUSH) == -1)
+ return 0;
+
+ /* directly compress user buffer to file */
+ strm->avail_in = len;
+#ifdef z_const
+ strm->next_in = (z_const Bytef *)buf;
+#else
+DIAG_OFF(cast-qual)
+ strm->next_in = (Bytef *)buf;
+DIAG_ON(cast-qual)
+#endif
+ state->pos += len;
+ if (gz_comp(state, Z_NO_FLUSH) == -1)
+ return 0;
+ }
+
+ /* input was all buffered or compressed (put will fit in int) */
+ return (int)put;
+}
+
+/* Flush out what we've written so far. Returns -1, and sets state->err,
+ on failure; returns 0 on success. */
+int
+gzwfile_flush(GZWFILE_T state)
+{
+ /* check that there's no error */
+ if (state->err != Z_OK)
+ return -1;
+
+ /* compress remaining data with Z_SYNC_FLUSH */
+ gz_comp(state, Z_SYNC_FLUSH);
+ if (state->err != Z_OK)
+ return -1;
+ return 0;
+}
+
+/* Flush out all data written, and close the file. Returns a Wiretap
+ error on failure; returns 0 on success. */
+int
+gzwfile_close(GZWFILE_T state)
+{
+ int ret = 0;
+
+ /* flush, free memory, and close file */
+ if (gz_comp(state, Z_FINISH) == -1)
+ ret = state->err;
+ (void)deflateEnd(&(state->strm));
+ g_free(state->out);
+ g_free(state->in);
+ state->err = Z_OK;
+ if (ws_close(state->fd) == -1 && ret == 0)
+ ret = errno;
+ g_free(state);
+ return ret;
+}
+
+int
+gzwfile_geterr(GZWFILE_T state)
+{
+ return state->err;
+}
+#endif
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * vi: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */
diff --git a/wiretap/file_wrappers.h b/wiretap/file_wrappers.h
new file mode 100644
index 00000000..4c644f32
--- /dev/null
+++ b/wiretap/file_wrappers.h
@@ -0,0 +1,47 @@
+/** @file
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __WTAP_FILE_WRAPPERS_H__
+#define __WTAP_FILE_WRAPPERS_H__
+
+#include <wireshark.h>
+#include "wtap.h"
+#include <wsutil/file_util.h>
+
+extern FILE_T file_open(const char *path);
+extern FILE_T file_fdopen(int fildes);
+extern void file_set_random_access(FILE_T stream, gboolean random_flag, GPtrArray *seek);
+WS_DLL_PUBLIC gint64 file_seek(FILE_T stream, gint64 offset, int whence, int *err);
+WS_DLL_PUBLIC gint64 file_tell(FILE_T stream);
+extern gint64 file_tell_raw(FILE_T stream);
+extern int file_fstat(FILE_T stream, ws_statb64 *statb, int *err);
+WS_DLL_PUBLIC gboolean file_iscompressed(FILE_T stream);
+WS_DLL_PUBLIC int file_read(void *buf, unsigned int count, FILE_T file);
+WS_DLL_PUBLIC int file_peekc(FILE_T stream);
+WS_DLL_PUBLIC int file_getc(FILE_T stream);
+WS_DLL_PUBLIC char *file_gets(char *buf, int len, FILE_T stream);
+WS_DLL_PUBLIC char *file_getsp(char *buf, int len, FILE_T stream);
+WS_DLL_PUBLIC int file_eof(FILE_T stream);
+WS_DLL_PUBLIC int file_error(FILE_T fh, gchar **err_info);
+extern void file_clearerr(FILE_T stream);
+extern void file_fdclose(FILE_T file);
+extern int file_fdreopen(FILE_T file, const char *path);
+extern void file_close(FILE_T file);
+
+#ifdef HAVE_ZLIB
+typedef struct wtap_writer *GZWFILE_T;
+
+extern GZWFILE_T gzwfile_open(const char *path);
+extern GZWFILE_T gzwfile_fdopen(int fd);
+extern guint gzwfile_write(GZWFILE_T state, const void *buf, guint len);
+extern int gzwfile_flush(GZWFILE_T state);
+extern int gzwfile_close(GZWFILE_T state);
+extern int gzwfile_geterr(GZWFILE_T state);
+#endif /* HAVE_ZLIB */
+
+#endif /* __FILE_H__ */
diff --git a/wiretap/hcidump.c b/wiretap/hcidump.c
new file mode 100644
index 00000000..2755a86c
--- /dev/null
+++ b/wiretap/hcidump.c
@@ -0,0 +1,161 @@
+/* hcidump.c
+ *
+ * Copyright (c) 2003 by Marcel Holtmann <marcel@holtmann.org>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "config.h"
+
+#include "wtap-int.h"
+#include "file_wrappers.h"
+#include "hcidump.h"
+
+static int hcidump_file_type_subtype = -1;
+
+void register_hcidump(void);
+
+struct dump_hdr {
+ guint16 len;
+ guint8 in;
+ guint8 pad;
+ guint32 ts_sec;
+ guint32 ts_usec;
+};
+
+#define DUMP_HDR_SIZE (sizeof(struct dump_hdr))
+
+static gboolean hcidump_read_packet(FILE_T fh, wtap_rec *rec,
+ Buffer *buf, int *err, gchar **err_info)
+{
+ struct dump_hdr dh;
+ guint packet_size;
+
+ if (!wtap_read_bytes_or_eof(fh, &dh, DUMP_HDR_SIZE, err, err_info))
+ return FALSE;
+
+ packet_size = GUINT16_FROM_LE(dh.len);
+ if (packet_size > WTAP_MAX_PACKET_SIZE_STANDARD) {
+ /*
+ * Probably a corrupt capture file; don't blow up trying
+ * to allocate space for an immensely-large packet.
+ */
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("hcidump: File has %u-byte packet, bigger than maximum of %u",
+ packet_size, WTAP_MAX_PACKET_SIZE_STANDARD);
+ return FALSE;
+ }
+
+ rec->rec_type = REC_TYPE_PACKET;
+ rec->block = wtap_block_create(WTAP_BLOCK_PACKET);
+ rec->presence_flags = WTAP_HAS_TS;
+ rec->ts.secs = GUINT32_FROM_LE(dh.ts_sec);
+ rec->ts.nsecs = GUINT32_FROM_LE(dh.ts_usec) * 1000;
+ rec->rec_header.packet_header.caplen = packet_size;
+ rec->rec_header.packet_header.len = packet_size;
+
+ rec->rec_header.packet_header.pseudo_header.p2p.sent = (dh.in ? FALSE : TRUE);
+
+ return wtap_read_packet_bytes(fh, buf, packet_size, err, err_info);
+}
+
+static gboolean hcidump_read(wtap *wth, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info, gint64 *data_offset)
+{
+ *data_offset = file_tell(wth->fh);
+
+ return hcidump_read_packet(wth->fh, rec, buf, err, err_info);
+}
+
+static gboolean hcidump_seek_read(wtap *wth, gint64 seek_off,
+ wtap_rec *rec, Buffer *buf, int *err, gchar **err_info)
+{
+ if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
+ return FALSE;
+
+ return hcidump_read_packet(wth->random_fh, rec, buf, err, err_info);
+}
+
+wtap_open_return_val hcidump_open(wtap *wth, int *err, gchar **err_info)
+{
+ struct dump_hdr dh;
+ guint8 type;
+
+ if (!wtap_read_bytes(wth->fh, &dh, DUMP_HDR_SIZE, err, err_info)) {
+ if (*err != WTAP_ERR_SHORT_READ)
+ return WTAP_OPEN_ERROR;
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ if ((dh.in != 0 && dh.in != 1) || dh.pad != 0
+ || GUINT16_FROM_LE(dh.len) < 1)
+ return WTAP_OPEN_NOT_MINE;
+
+ if (!wtap_read_bytes(wth->fh, &type, 1, err, err_info)) {
+ if (*err != WTAP_ERR_SHORT_READ)
+ return WTAP_OPEN_ERROR;
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ if (type < 1 || type > 4)
+ return WTAP_OPEN_NOT_MINE;
+
+ if (file_seek(wth->fh, 0, SEEK_SET, err) == -1)
+ return WTAP_OPEN_ERROR;
+
+ wth->file_type_subtype = hcidump_file_type_subtype;
+ wth->file_encap = WTAP_ENCAP_BLUETOOTH_H4_WITH_PHDR;
+ wth->snapshot_length = 0;
+
+ wth->subtype_read = hcidump_read;
+ wth->subtype_seek_read = hcidump_seek_read;
+ wth->file_tsprec = WTAP_TSPREC_USEC;
+
+ /*
+ * Add an IDB; we don't know how many interfaces were
+ * involved, so we just say one interface, about which
+ * we only know the link-layer type, snapshot length,
+ * and time stamp resolution.
+ */
+ wtap_add_generated_idb(wth);
+
+ return WTAP_OPEN_MINE;
+}
+
+static const struct supported_block_type hcidummp_blocks_supported[] = {
+ /*
+ * We support packet blocks, with no comments or other options.
+ */
+ { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
+};
+
+static const struct file_type_subtype_info hcidump_info = {
+ "Bluetooth HCI dump", "hcidump", NULL, NULL,
+ FALSE, BLOCKS_SUPPORTED(hcidummp_blocks_supported),
+ NULL, NULL, NULL
+};
+
+void register_hcidump(void)
+{
+ hcidump_file_type_subtype = wtap_register_file_type_subtype(&hcidump_info);
+
+ /*
+ * Register name for backwards compatibility with the
+ * wtap_filetypes table in Lua.
+ */
+ wtap_register_backwards_compatibility_lua_name("HCIDUMP",
+ hcidump_file_type_subtype);
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/wiretap/hcidump.h b/wiretap/hcidump.h
new file mode 100644
index 00000000..b5685ed8
--- /dev/null
+++ b/wiretap/hcidump.h
@@ -0,0 +1,18 @@
+/** @file
+ *
+ * Copyright (c) 2003 by Marcel Holtmann <marcel@holtmann.org>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ */
+
+#ifndef __HCIDUMP_H__
+#define __HCIDUMP_H__
+
+#include <glib.h>
+#include "wtap.h"
+#include "ws_symbol_export.h"
+
+wtap_open_return_val hcidump_open(wtap *wth, int *err, gchar **err_info);
+
+#endif
diff --git a/wiretap/i4b_trace.h b/wiretap/i4b_trace.h
new file mode 100644
index 00000000..6b377be7
--- /dev/null
+++ b/wiretap/i4b_trace.h
@@ -0,0 +1,67 @@
+/** @file
+ *
+ * Copyright (c) 1997, 1999 Hellmuth Michaelis. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ *---------------------------------------------------------------------------*/
+
+#ifndef _I4B_TRACE_H_
+#define _I4B_TRACE_H_
+
+#include <glib.h>
+
+/*---------------------------------------------------------------------------*
+ * structure of the header at the beginning of every trace mbuf
+ *---------------------------------------------------------------------------*/
+typedef struct {
+ guint32 length; /* length of the following mbuf */
+ guint32 unit; /* controller unit number */
+ guint32 type; /* type of channel */
+#define TRC_CH_I 0 /* Layer 1 INFO's */
+#define TRC_CH_D 1 /* D channel */
+#define TRC_CH_B1 2 /* B1 channel */
+#define TRC_CH_B2 3 /* B2 channel */
+ gint32 dir; /* direction */
+#define FROM_TE 0 /* user -> network */
+#define FROM_NT 1 /* network -> user */
+ guint32 trunc; /* # of truncated bytes (frame > MCLBYTES) */
+ guint32 count; /* frame count for this unit/type */
+ guint32 ts_sec; /* timestamp seconds */
+ guint32 ts_usec; /* timestamp microseconds */
+} i4b_trace_hdr_t;
+
+#define INFO0 0 /* layer 1 */
+#define INFO1_8 1
+#define INFO1_10 2
+#define INFO2 3
+#define INFO3 4
+#define INFO4_8 5
+#define INFO4_10 6
+
+/*---------------------------------------------------------------------------*
+ * ioctl via /dev/i4btrc device(s):
+ * get/set current trace flag settings
+ *---------------------------------------------------------------------------*/
+
+#define I4B_TRC_GET _IOR('T', 0, int) /* get trace settings */
+#define I4B_TRC_SET _IOW('T', 1, int) /* set trace settings */
+
+#define TRACE_OFF 0x00 /* tracing off */
+#define TRACE_I 0x01 /* trace L1 INFO's on */
+#define TRACE_D_TX 0x02 /* trace D channel on */
+#define TRACE_D_RX 0x04 /* trace D channel on */
+#define TRACE_B_TX 0x08 /* trace B channel on */
+#define TRACE_B_RX 0x10 /* trace B channel on */
+
+typedef struct {
+ gint32 rxunit; /* unit # for rx frames */
+ gint32 rxflags; /* d and/or b channel */
+ gint32 txunit; /* unit # for tx frames */
+ gint32 txflags; /* d and/or b channel */
+} i4b_trace_setupa_t;
+
+#define I4B_TRC_SETA _IOW('T', 2, i4b_trace_setupa_t) /* set analyze mode */
+#define I4B_TRC_RESETA _IOW('T', 3, int) /* reset analyze mode */
+
+#endif /* _I4B_TRACE_H_ */
diff --git a/wiretap/i4btrace.c b/wiretap/i4btrace.c
new file mode 100644
index 00000000..1b15972b
--- /dev/null
+++ b/wiretap/i4btrace.c
@@ -0,0 +1,336 @@
+/* i4btrace.c
+ *
+ * Wiretap Library
+ * Copyright (c) 1999 by Bert Driehuis <driehuis@playbeing.org>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include "wtap-int.h"
+#include "file_wrappers.h"
+#include "i4b_trace.h"
+#include "i4btrace.h"
+
+typedef struct {
+ gboolean byte_swapped;
+} i4btrace_t;
+
+static gboolean i4btrace_read(wtap *wth, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info, gint64 *offset);
+static gboolean i4btrace_seek_read(wtap *wth, gint64 seek_off,
+ wtap_rec *rec, Buffer *buf, int *err, gchar **err_info);
+static int i4b_read_rec(wtap *wth, FILE_T fh, wtap_rec *rec,
+ Buffer *buf, int *err, gchar **err_info);
+
+static int i4btrace_file_type_subtype = -1;
+
+void register_i4btrace(void);
+
+/*
+ * Byte-swap the header.
+ */
+#define I4B_BYTESWAP_HEADER(hdr) \
+ { \
+ hdr.length = GUINT32_SWAP_LE_BE(hdr.length); \
+ hdr.unit = GUINT32_SWAP_LE_BE(hdr.unit); \
+ hdr.type = GUINT32_SWAP_LE_BE(hdr.type); \
+ hdr.dir = GUINT32_SWAP_LE_BE(hdr.dir); \
+ hdr.trunc = GUINT32_SWAP_LE_BE(hdr.trunc); \
+ hdr.count = GUINT32_SWAP_LE_BE(hdr.count); \
+ hdr.ts_sec = GUINT32_SWAP_LE_BE(hdr.ts_sec); \
+ hdr.ts_usec = GUINT32_SWAP_LE_BE(hdr.ts_usec); \
+ }
+
+/*
+ * Test some fields in the header to see if they make sense.
+ */
+#define I4B_HDR_IS_OK(hdr) \
+ (!(hdr.length < sizeof(hdr) || \
+ hdr.length > 16384 || \
+ hdr.unit > 4 || \
+ hdr.type > TRC_CH_B2 || \
+ hdr.dir > FROM_NT || \
+ hdr.trunc > 2048 || \
+ hdr.ts_usec >= 1000000))
+
+/*
+ * Number of packets to try reading.
+ */
+#define PACKETS_TO_CHECK 5
+
+wtap_open_return_val i4btrace_open(wtap *wth, int *err, gchar **err_info)
+{
+ i4b_trace_hdr_t hdr;
+ gboolean byte_swapped = FALSE;
+ i4btrace_t *i4btrace;
+
+ /* I4B trace files have no magic in the header... Sigh */
+ if (!wtap_read_bytes(wth->fh, &hdr, sizeof(hdr), err, err_info)) {
+ if (*err != WTAP_ERR_SHORT_READ)
+ return WTAP_OPEN_ERROR;
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ /* Silly heuristic... */
+ if (!I4B_HDR_IS_OK(hdr)) {
+ /*
+ * OK, try byte-swapping the header fields.
+ */
+ I4B_BYTESWAP_HEADER(hdr);
+ if (!I4B_HDR_IS_OK(hdr)) {
+ /*
+ * It doesn't look valid in either byte order.
+ */
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ /*
+ * It looks valid byte-swapped, so assume it's a
+ * trace written in the opposite byte order.
+ */
+ byte_swapped = TRUE;
+ }
+
+ /*
+ * Now try to read past the packet bytes; if that fails with
+ * a short read, we don't fail, so that we can report
+ * the file as a truncated I4B file.
+ */
+ if (!wtap_read_bytes(wth->fh, NULL, hdr.length - (guint32)sizeof(hdr),
+ err, err_info)) {
+ if (*err != WTAP_ERR_SHORT_READ)
+ return WTAP_OPEN_ERROR;
+ } else {
+ /*
+ * Now try reading a few more packets.
+ */
+ for (int i = 1; i < PACKETS_TO_CHECK; i++) {
+ /*
+ * Read and check the file header; we've already
+ * decided whether this would be a byte-swapped file
+ * or not, so we swap iff we decided it was.
+ */
+ if (!wtap_read_bytes_or_eof(wth->fh, &hdr, sizeof(hdr), err,
+ err_info)) {
+ if (*err == 0) {
+ /* EOF; no more packets to try. */
+ break;
+ }
+ if (*err != WTAP_ERR_SHORT_READ)
+ return WTAP_OPEN_ERROR;
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ if (byte_swapped)
+ I4B_BYTESWAP_HEADER(hdr);
+ if (!I4B_HDR_IS_OK(hdr)) {
+ /*
+ * It doesn't look valid.
+ */
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ /*
+ * Now try to read past the packet bytes; if that
+ * fails with a short read, we don't fail, so that
+ * we can report the file as a truncated I4B file.
+ */
+ if (!wtap_read_bytes(wth->fh, NULL,
+ hdr.length - (guint32)sizeof(hdr), err, err_info)) {
+ if (*err != WTAP_ERR_SHORT_READ)
+ return WTAP_OPEN_ERROR;
+
+ /*
+ * Probably a truncated file, so just quit.
+ */
+ break;
+ }
+ }
+ }
+
+ if (file_seek(wth->fh, 0, SEEK_SET, err) == -1)
+ return WTAP_OPEN_ERROR;
+
+ /* Get capture start time */
+
+ wth->file_type_subtype = i4btrace_file_type_subtype;
+ i4btrace = g_new(i4btrace_t, 1);
+ wth->priv = (void *)i4btrace;
+ wth->subtype_read = i4btrace_read;
+ wth->subtype_seek_read = i4btrace_seek_read;
+ wth->snapshot_length = 0; /* not known */
+
+ i4btrace->byte_swapped = byte_swapped;
+
+ wth->file_encap = WTAP_ENCAP_ISDN;
+ wth->file_tsprec = WTAP_TSPREC_USEC;
+
+ /*
+ * Add an IDB; we don't know how many interfaces were
+ * involved, so we just say one interface, about which
+ * we only know the link-layer type, snapshot length,
+ * and time stamp resolution.
+ */
+ wtap_add_generated_idb(wth);
+
+ return WTAP_OPEN_MINE;
+}
+
+/* Read the next packet */
+static gboolean i4btrace_read(wtap *wth, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info, gint64 *data_offset)
+{
+ *data_offset = file_tell(wth->fh);
+
+ return i4b_read_rec(wth, wth->fh, rec, buf, err, err_info);
+}
+
+static gboolean
+i4btrace_seek_read(wtap *wth, gint64 seek_off, wtap_rec *rec,
+ Buffer *buf, int *err, gchar **err_info)
+{
+ if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
+ return FALSE;
+
+ if (!i4b_read_rec(wth, wth->random_fh, rec, buf, err, err_info)) {
+ /* Read error or EOF */
+ if (*err == 0) {
+ /* EOF means "short read" in random-access mode */
+ *err = WTAP_ERR_SHORT_READ;
+ }
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static gboolean
+i4b_read_rec(wtap *wth, FILE_T fh, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info)
+{
+ i4btrace_t *i4btrace = (i4btrace_t *)wth->priv;
+ i4b_trace_hdr_t hdr;
+ guint32 length;
+
+ if (!wtap_read_bytes_or_eof(fh, &hdr, sizeof hdr, err, err_info))
+ return FALSE;
+
+ if (i4btrace->byte_swapped) {
+ /*
+ * Byte-swap the header.
+ */
+ I4B_BYTESWAP_HEADER(hdr);
+ }
+
+ if (hdr.length < sizeof(hdr)) {
+ *err = WTAP_ERR_BAD_FILE; /* record length < header! */
+ *err_info = ws_strdup_printf("i4btrace: record length %u < header length %lu",
+ hdr.length, (unsigned long)sizeof(hdr));
+ return FALSE;
+ }
+ length = hdr.length - (guint32)sizeof(hdr);
+ if (length > WTAP_MAX_PACKET_SIZE_STANDARD) {
+ /*
+ * Probably a corrupt capture file; don't blow up trying
+ * to allocate space for an immensely-large packet.
+ */
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("i4btrace: File has %u-byte packet, bigger than maximum of %u",
+ length, WTAP_MAX_PACKET_SIZE_STANDARD);
+ return FALSE;
+ }
+
+ rec->rec_type = REC_TYPE_PACKET;
+ rec->block = wtap_block_create(WTAP_BLOCK_PACKET);
+ rec->presence_flags = WTAP_HAS_TS;
+
+ rec->rec_header.packet_header.len = length;
+ rec->rec_header.packet_header.caplen = length;
+
+ rec->ts.secs = hdr.ts_sec;
+ rec->ts.nsecs = hdr.ts_usec * 1000;
+
+ switch (hdr.type) {
+
+ case TRC_CH_I:
+ /*
+ * XXX - what is it? It's probably not WTAP_ENCAP_NULL,
+ * as that means it has a 4-byte AF_ type as the
+ * encapsulation header.
+ */
+ rec->rec_header.packet_header.pkt_encap = WTAP_ENCAP_NULL;
+ break;
+
+ case TRC_CH_D:
+ /*
+ * D channel, so it's LAPD; set "p2p.sent".
+ */
+ rec->rec_header.packet_header.pkt_encap = WTAP_ENCAP_ISDN;
+ rec->rec_header.packet_header.pseudo_header.isdn.channel = 0;
+ break;
+
+ case TRC_CH_B1:
+ /*
+ * B channel 1.
+ */
+ rec->rec_header.packet_header.pkt_encap = WTAP_ENCAP_ISDN;
+ rec->rec_header.packet_header.pseudo_header.isdn.channel = 1;
+ break;
+
+ case TRC_CH_B2:
+ /*
+ * B channel 2.
+ */
+ rec->rec_header.packet_header.pkt_encap = WTAP_ENCAP_ISDN;
+ rec->rec_header.packet_header.pseudo_header.isdn.channel = 2;
+ break;
+ }
+
+ rec->rec_header.packet_header.pseudo_header.isdn.uton = (hdr.dir == FROM_TE);
+
+ /*
+ * Read the packet data.
+ */
+ return wtap_read_packet_bytes(fh, buf, length, err, err_info);
+}
+
+static const struct supported_block_type i4btrace_blocks_supported[] = {
+ /*
+ * We support packet blocks, with no comments or other options.
+ */
+ { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
+};
+
+static const struct file_type_subtype_info i4btrace_info = {
+ "I4B ISDN trace", "i4btrace", NULL, NULL,
+ FALSE, BLOCKS_SUPPORTED(i4btrace_blocks_supported),
+ NULL, NULL, NULL
+};
+
+void register_i4btrace(void)
+{
+ i4btrace_file_type_subtype = wtap_register_file_type_subtype(&i4btrace_info);
+
+ /*
+ * Register name for backwards compatibility with the
+ * wtap_filetypes table in Lua.
+ */
+ wtap_register_backwards_compatibility_lua_name("I4BTRACE",
+ i4btrace_file_type_subtype);
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/wiretap/i4btrace.h b/wiretap/i4btrace.h
new file mode 100644
index 00000000..455f0a65
--- /dev/null
+++ b/wiretap/i4btrace.h
@@ -0,0 +1,19 @@
+/** @file
+ *
+ * Wiretap Library
+ * Copyright (c) 1999 by Bert Driehuis <driehuis@playbeing.org>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ */
+
+#ifndef __I4BTRACE_H__
+#define __I4BTRACE_H__
+
+#include <glib.h>
+#include "wtap.h"
+#include "ws_symbol_export.h"
+
+wtap_open_return_val i4btrace_open(wtap *wth, int *err, gchar **err_info);
+
+#endif
diff --git a/wiretap/introspection-enums.c b/wiretap/introspection-enums.c
new file mode 100644
index 00000000..5ebd7b81
--- /dev/null
+++ b/wiretap/introspection-enums.c
@@ -0,0 +1,549 @@
+/*
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * Generated automatically from make-enums.py. It can be re-created by running
+ * "tools/make-enums.py" from the top source directory.
+ *
+ * It is fine to edit this file by hand. Particularly if a symbol
+ * disappears from the API it can just be removed here. There is no
+ * requirement to re-run the generator script.
+ *
+ */
+#include <wiretap/wtap.h>
+
+#define ENUM(arg) { #arg, arg }
+
+static ws_enum_t all_enums[] = {
+ ENUM(AAL_1),
+ ENUM(AAL_2),
+ ENUM(AAL_3_4),
+ ENUM(AAL_5),
+ ENUM(AAL_OAMCELL),
+ ENUM(AAL_SIGNALLING),
+ ENUM(AAL_UNKNOWN),
+ ENUM(AAL_USER),
+ ENUM(ASCEND_MAX_STR_LEN),
+ ENUM(ASCEND_PFX_ETHER),
+ ENUM(ASCEND_PFX_ISDN_R),
+ ENUM(ASCEND_PFX_ISDN_X),
+ ENUM(ASCEND_PFX_WDD),
+ ENUM(ASCEND_PFX_WDS_R),
+ ENUM(ASCEND_PFX_WDS_X),
+ ENUM(ATM_AAL2_NOPHDR),
+ ENUM(ATM_NO_HEC),
+ ENUM(ATM_RAW_CELL),
+ ENUM(ATM_REASSEMBLY_ERROR),
+ ENUM(BBLOG_TYPE_EVENT_BLOCK),
+ ENUM(BBLOG_TYPE_SKIPPED_BLOCK),
+ ENUM(BLOCK_NOT_SUPPORTED),
+ ENUM(BTHCI_CHANNEL_ACL),
+ ENUM(BTHCI_CHANNEL_COMMAND),
+ ENUM(BTHCI_CHANNEL_EVENT),
+ ENUM(BTHCI_CHANNEL_ISO),
+ ENUM(BTHCI_CHANNEL_SCO),
+ ENUM(COSINE_DIR_RX),
+ ENUM(COSINE_DIR_TX),
+ ENUM(COSINE_ENCAP_ATM),
+ ENUM(COSINE_ENCAP_ETH),
+ ENUM(COSINE_ENCAP_FR),
+ ENUM(COSINE_ENCAP_HDLC),
+ ENUM(COSINE_ENCAP_PPP),
+ ENUM(COSINE_ENCAP_PPoATM),
+ ENUM(COSINE_ENCAP_PPoFR),
+ ENUM(COSINE_ENCAP_TEST),
+ ENUM(COSINE_ENCAP_UNKNOWN),
+ ENUM(COSINE_MAX_IF_NAME_LEN),
+ ENUM(FROM_DCE),
+ ENUM(FT_SORT_BY_DESCRIPTION),
+ ENUM(FT_SORT_BY_NAME),
+ ENUM(GSM_UM_CHANNEL_AGCH),
+ ENUM(GSM_UM_CHANNEL_BCCH),
+ ENUM(GSM_UM_CHANNEL_CCCH),
+ ENUM(GSM_UM_CHANNEL_FACCH),
+ ENUM(GSM_UM_CHANNEL_PCH),
+ ENUM(GSM_UM_CHANNEL_RACH),
+ ENUM(GSM_UM_CHANNEL_SACCH),
+ ENUM(GSM_UM_CHANNEL_SDCCH),
+ ENUM(GSM_UM_CHANNEL_UNKNOWN),
+ ENUM(IRDA_CLASS_FRAME),
+ ENUM(IRDA_CLASS_LOG),
+ ENUM(IRDA_CLASS_MASK),
+ ENUM(IRDA_INCOMING),
+ ENUM(IRDA_LOG_MESSAGE),
+ ENUM(IRDA_MISSED_MSG),
+ ENUM(IRDA_OUTGOING),
+ ENUM(K12_PORT_ATMPVC),
+ ENUM(K12_PORT_DS0S),
+ ENUM(K12_PORT_DS1),
+ ENUM(LLCP_PHDR_FLAG_SENT),
+ ENUM(MAXNAMELEN),
+ ENUM(MAX_ERF_EHDR),
+ ENUM(MTP2_ANNEX_A_NOT_USED),
+ ENUM(MTP2_ANNEX_A_USED),
+ ENUM(MTP2_ANNEX_A_USED_UNKNOWN),
+ ENUM(MULTIPLE_BLOCKS_SUPPORTED),
+ ENUM(MULTIPLE_OPTIONS_SUPPORTED),
+ ENUM(ONE_BLOCK_SUPPORTED),
+ ENUM(ONE_OPTION_SUPPORTED),
+ ENUM(OPEN_INFO_HEURISTIC),
+ ENUM(OPEN_INFO_MAGIC),
+ ENUM(OPTION_NOT_SUPPORTED),
+ ENUM(PACK_FLAGS_CRC_ERROR),
+ ENUM(PACK_FLAGS_DIRECTION_INBOUND),
+ ENUM(PACK_FLAGS_DIRECTION_MASK),
+ ENUM(PACK_FLAGS_DIRECTION_OUTBOUND),
+ ENUM(PACK_FLAGS_DIRECTION_SHIFT),
+ ENUM(PACK_FLAGS_DIRECTION_UNKNOWN),
+ ENUM(PACK_FLAGS_FCS_LENGTH_MASK),
+ ENUM(PACK_FLAGS_FCS_LENGTH_SHIFT),
+ ENUM(PACK_FLAGS_PACKET_TOO_LONG),
+ ENUM(PACK_FLAGS_PACKET_TOO_SHORT),
+ ENUM(PACK_FLAGS_PREAMBLE_ERROR),
+ ENUM(PACK_FLAGS_RECEPTION_TYPE_BROADCAST),
+ ENUM(PACK_FLAGS_RECEPTION_TYPE_MASK),
+ ENUM(PACK_FLAGS_RECEPTION_TYPE_MULTICAST),
+ ENUM(PACK_FLAGS_RECEPTION_TYPE_PROMISCUOUS),
+ ENUM(PACK_FLAGS_RECEPTION_TYPE_SHIFT),
+ ENUM(PACK_FLAGS_RECEPTION_TYPE_UNICAST),
+ ENUM(PACK_FLAGS_RECEPTION_TYPE_UNSPECIFIED),
+ ENUM(PACK_FLAGS_RESERVED_MASK),
+ ENUM(PACK_FLAGS_START_FRAME_DELIMITER_ERROR),
+ ENUM(PACK_FLAGS_SYMBOL_ERROR),
+ ENUM(PACK_FLAGS_UNALIGNED_FRAME),
+ ENUM(PACK_FLAGS_WRONG_INTER_FRAME_GAP),
+ ENUM(PHDR_802_11AD_MAX_FREQUENCY),
+ ENUM(PHDR_802_11AD_MIN_FREQUENCY),
+ ENUM(PHDR_802_11A_CHANNEL_TYPE_HALF_CLOCKED),
+ ENUM(PHDR_802_11A_CHANNEL_TYPE_NORMAL),
+ ENUM(PHDR_802_11A_CHANNEL_TYPE_QUARTER_CLOCKED),
+ ENUM(PHDR_802_11A_TURBO_TYPE_DYNAMIC_TURBO),
+ ENUM(PHDR_802_11A_TURBO_TYPE_NORMAL),
+ ENUM(PHDR_802_11A_TURBO_TYPE_STATIC_TURBO),
+ ENUM(PHDR_802_11A_TURBO_TYPE_TURBO),
+ ENUM(PHDR_802_11G_MODE_NORMAL),
+ ENUM(PHDR_802_11G_MODE_SUPER_G),
+ ENUM(PHDR_802_11_0_LENGTH_PSDU_S1G_NDP),
+ ENUM(PHDR_802_11_0_LENGTH_PSDU_VENDOR_SPECIFIC),
+ ENUM(PHDR_802_11_A_MPDU_DELIM_CRC_ERROR),
+ ENUM(PHDR_802_11_BANDWIDTH_160_MHZ),
+ ENUM(PHDR_802_11_BANDWIDTH_20LL),
+ ENUM(PHDR_802_11_BANDWIDTH_20LLL),
+ ENUM(PHDR_802_11_BANDWIDTH_20LLU),
+ ENUM(PHDR_802_11_BANDWIDTH_20LU),
+ ENUM(PHDR_802_11_BANDWIDTH_20LUL),
+ ENUM(PHDR_802_11_BANDWIDTH_20LUU),
+ ENUM(PHDR_802_11_BANDWIDTH_20UL),
+ ENUM(PHDR_802_11_BANDWIDTH_20ULL),
+ ENUM(PHDR_802_11_BANDWIDTH_20ULU),
+ ENUM(PHDR_802_11_BANDWIDTH_20UU),
+ ENUM(PHDR_802_11_BANDWIDTH_20UUL),
+ ENUM(PHDR_802_11_BANDWIDTH_20UUU),
+ ENUM(PHDR_802_11_BANDWIDTH_20_20L),
+ ENUM(PHDR_802_11_BANDWIDTH_20_20U),
+ ENUM(PHDR_802_11_BANDWIDTH_20_MHZ),
+ ENUM(PHDR_802_11_BANDWIDTH_40LL),
+ ENUM(PHDR_802_11_BANDWIDTH_40LU),
+ ENUM(PHDR_802_11_BANDWIDTH_40UL),
+ ENUM(PHDR_802_11_BANDWIDTH_40UU),
+ ENUM(PHDR_802_11_BANDWIDTH_40_40L),
+ ENUM(PHDR_802_11_BANDWIDTH_40_40U),
+ ENUM(PHDR_802_11_BANDWIDTH_40_MHZ),
+ ENUM(PHDR_802_11_BANDWIDTH_80_80L),
+ ENUM(PHDR_802_11_BANDWIDTH_80_80U),
+ ENUM(PHDR_802_11_BANDWIDTH_80_MHZ),
+ ENUM(PHDR_802_11_DATA_NOT_CAPTURED),
+ ENUM(PHDR_802_11_LAST_PART_OF_A_MPDU),
+ ENUM(PHDR_802_11_PHY_11A),
+ ENUM(PHDR_802_11_PHY_11AC),
+ ENUM(PHDR_802_11_PHY_11AD),
+ ENUM(PHDR_802_11_PHY_11AH),
+ ENUM(PHDR_802_11_PHY_11AX),
+ ENUM(PHDR_802_11_PHY_11B),
+ ENUM(PHDR_802_11_PHY_11BE),
+ ENUM(PHDR_802_11_PHY_11G),
+ ENUM(PHDR_802_11_PHY_11N),
+ ENUM(PHDR_802_11_PHY_11_DSSS),
+ ENUM(PHDR_802_11_PHY_11_FHSS),
+ ENUM(PHDR_802_11_PHY_11_IR),
+ ENUM(PHDR_802_11_PHY_UNKNOWN),
+ ENUM(PHDR_802_11_SOUNDING_PSDU),
+ ENUM(REC_TYPE_CUSTOM_BLOCK),
+ ENUM(REC_TYPE_FT_SPECIFIC_EVENT),
+ ENUM(REC_TYPE_FT_SPECIFIC_REPORT),
+ ENUM(REC_TYPE_PACKET),
+ ENUM(REC_TYPE_SYSCALL),
+ ENUM(REC_TYPE_SYSTEMD_JOURNAL_EXPORT),
+ ENUM(SITA_ERROR_NO_BUFFER),
+ ENUM(SITA_ERROR_RX_ABORT),
+ ENUM(SITA_ERROR_RX_BREAK),
+ ENUM(SITA_ERROR_RX_CD_LOST),
+ ENUM(SITA_ERROR_RX_COLLISION),
+ ENUM(SITA_ERROR_RX_CRC),
+ ENUM(SITA_ERROR_RX_DPLL),
+ ENUM(SITA_ERROR_RX_FRAME_LEN_VIOL),
+ ENUM(SITA_ERROR_RX_FRAME_LONG),
+ ENUM(SITA_ERROR_RX_FRAME_SHORT),
+ ENUM(SITA_ERROR_RX_FRAMING),
+ ENUM(SITA_ERROR_RX_NONOCTET_ALIGNED),
+ ENUM(SITA_ERROR_RX_OVERRUN),
+ ENUM(SITA_ERROR_RX_PARITY),
+ ENUM(SITA_ERROR_RX_UNDEF1),
+ ENUM(SITA_ERROR_RX_UNDEF2),
+ ENUM(SITA_ERROR_RX_UNDEF3),
+ ENUM(SITA_ERROR_TX_CTS_LOST),
+ ENUM(SITA_ERROR_TX_RETX_LIMIT),
+ ENUM(SITA_ERROR_TX_UART_ERROR),
+ ENUM(SITA_ERROR_TX_UNDEF1),
+ ENUM(SITA_ERROR_TX_UNDEF2),
+ ENUM(SITA_ERROR_TX_UNDEF3),
+ ENUM(SITA_ERROR_TX_UNDEF4),
+ ENUM(SITA_ERROR_TX_UNDERRUN),
+ ENUM(SITA_FRAME_DIR),
+ ENUM(SITA_FRAME_DIR_RXED),
+ ENUM(SITA_FRAME_DIR_TXED),
+ ENUM(SITA_PROTO_ALC),
+ ENUM(SITA_PROTO_ASYNC_BLKIO),
+ ENUM(SITA_PROTO_ASYNC_INTIO),
+ ENUM(SITA_PROTO_BOP_FRL),
+ ENUM(SITA_PROTO_BOP_LAPB),
+ ENUM(SITA_PROTO_DPM_LINK),
+ ENUM(SITA_PROTO_ETHERNET),
+ ENUM(SITA_PROTO_I2C),
+ ENUM(SITA_PROTO_PPP_HDLC),
+ ENUM(SITA_PROTO_SDLC),
+ ENUM(SITA_PROTO_TOKENRING),
+ ENUM(SITA_PROTO_UNUSED),
+ ENUM(SITA_PROTO_UTS),
+ ENUM(SITA_SIG_CTS),
+ ENUM(SITA_SIG_DCD),
+ ENUM(SITA_SIG_DSR),
+ ENUM(SITA_SIG_DTR),
+ ENUM(SITA_SIG_RTS),
+ ENUM(SITA_SIG_UNDEF1),
+ ENUM(SITA_SIG_UNDEF2),
+ ENUM(SITA_SIG_UNDEF3),
+ ENUM(TRAF_FR),
+ ENUM(TRAF_GPRS_NS),
+ ENUM(TRAF_ILMI),
+ ENUM(TRAF_IPSILON),
+ ENUM(TRAF_LANE),
+ ENUM(TRAF_LLCMX),
+ ENUM(TRAF_SPANS),
+ ENUM(TRAF_SSCOP),
+ ENUM(TRAF_ST_IPSILON_FT0),
+ ENUM(TRAF_ST_IPSILON_FT1),
+ ENUM(TRAF_ST_IPSILON_FT2),
+ ENUM(TRAF_ST_LANE_802_3),
+ ENUM(TRAF_ST_LANE_802_3_MC),
+ ENUM(TRAF_ST_LANE_802_5),
+ ENUM(TRAF_ST_LANE_802_5_MC),
+ ENUM(TRAF_ST_LANE_LE_CTRL),
+ ENUM(TRAF_ST_UNKNOWN),
+ ENUM(TRAF_ST_VCMX_802_3),
+ ENUM(TRAF_ST_VCMX_802_3_FCS),
+ ENUM(TRAF_ST_VCMX_802_4),
+ ENUM(TRAF_ST_VCMX_802_4_FCS),
+ ENUM(TRAF_ST_VCMX_802_5),
+ ENUM(TRAF_ST_VCMX_802_5_FCS),
+ ENUM(TRAF_ST_VCMX_802_6),
+ ENUM(TRAF_ST_VCMX_802_6_FCS),
+ ENUM(TRAF_ST_VCMX_BPDU),
+ ENUM(TRAF_ST_VCMX_FDDI),
+ ENUM(TRAF_ST_VCMX_FDDI_FCS),
+ ENUM(TRAF_ST_VCMX_FRAGMENTS),
+ ENUM(TRAF_UMTS_FP),
+ ENUM(TRAF_UNKNOWN),
+ ENUM(TRAF_VCMX),
+ ENUM(WTAP_COMMENT_PER_INTERFACE),
+ ENUM(WTAP_COMMENT_PER_PACKET),
+ ENUM(WTAP_COMMENT_PER_SECTION),
+ ENUM(WTAP_ENCAP_3MB_ETHERNET),
+ ENUM(WTAP_ENCAP_APPLE_IP_OVER_IEEE1394),
+ ENUM(WTAP_ENCAP_ARCNET),
+ ENUM(WTAP_ENCAP_ARCNET_LINUX),
+ ENUM(WTAP_ENCAP_ASCEND),
+ ENUM(WTAP_ENCAP_ATM_PDUS),
+ ENUM(WTAP_ENCAP_ATM_PDUS_UNTRUNCATED),
+ ENUM(WTAP_ENCAP_ATM_RFC1483),
+ ENUM(WTAP_ENCAP_ATSC_ALP),
+ ENUM(WTAP_ENCAP_AUERSWALD_LOG),
+ ENUM(WTAP_ENCAP_AUTOSAR_DLT),
+ ENUM(WTAP_ENCAP_AX25),
+ ENUM(WTAP_ENCAP_AX25_KISS),
+ ENUM(WTAP_ENCAP_BACNET_MS_TP),
+ ENUM(WTAP_ENCAP_BACNET_MS_TP_WITH_PHDR),
+ ENUM(WTAP_ENCAP_BER),
+ ENUM(WTAP_ENCAP_BLUETOOTH_BREDR_BB),
+ ENUM(WTAP_ENCAP_BLUETOOTH_H4),
+ ENUM(WTAP_ENCAP_BLUETOOTH_H4_WITH_PHDR),
+ ENUM(WTAP_ENCAP_BLUETOOTH_HCI),
+ ENUM(WTAP_ENCAP_BLUETOOTH_LE_LL),
+ ENUM(WTAP_ENCAP_BLUETOOTH_LE_LL_WITH_PHDR),
+ ENUM(WTAP_ENCAP_BLUETOOTH_LINUX_MONITOR),
+ ENUM(WTAP_ENCAP_CAN20B),
+ ENUM(WTAP_ENCAP_CATAPULT_DCT2000),
+ ENUM(WTAP_ENCAP_CHDLC),
+ ENUM(WTAP_ENCAP_CHDLC_WITH_PHDR),
+ ENUM(WTAP_ENCAP_CISCO_IOS),
+ ENUM(WTAP_ENCAP_COSINE),
+ ENUM(WTAP_ENCAP_DBUS),
+ ENUM(WTAP_ENCAP_DOCSIS),
+ ENUM(WTAP_ENCAP_DOCSIS31_XRA31),
+ ENUM(WTAP_ENCAP_DPAUXMON),
+ ENUM(WTAP_ENCAP_DPNSS),
+ ENUM(WTAP_ENCAP_DVBCI),
+ ENUM(WTAP_ENCAP_EBHSCR),
+ ENUM(WTAP_ENCAP_ENC),
+ ENUM(WTAP_ENCAP_EPON),
+ ENUM(WTAP_ENCAP_ERF),
+ ENUM(WTAP_ENCAP_ERI_ENB_LOG),
+ ENUM(WTAP_ENCAP_ETHERNET),
+ ENUM(WTAP_ENCAP_ETHERNET_MPACKET),
+ ENUM(WTAP_ENCAP_ETW),
+ ENUM(WTAP_ENCAP_FDDI),
+ ENUM(WTAP_ENCAP_FDDI_BITSWAPPED),
+ ENUM(WTAP_ENCAP_FIBRE_CHANNEL_FC2),
+ ENUM(WTAP_ENCAP_FIBRE_CHANNEL_FC2_WITH_FRAME_DELIMS),
+ ENUM(WTAP_ENCAP_FIRA_UCI),
+ ENUM(WTAP_ENCAP_FLEXRAY),
+ ENUM(WTAP_ENCAP_FRELAY),
+ ENUM(WTAP_ENCAP_FRELAY_WITH_PHDR),
+ ENUM(WTAP_ENCAP_GCOM_SERIAL),
+ ENUM(WTAP_ENCAP_GCOM_TIE1),
+ ENUM(WTAP_ENCAP_GFP_F),
+ ENUM(WTAP_ENCAP_GFP_T),
+ ENUM(WTAP_ENCAP_GPRS_LLC),
+ ENUM(WTAP_ENCAP_GSM_UM),
+ ENUM(WTAP_ENCAP_HHDLC),
+ ENUM(WTAP_ENCAP_I2C_LINUX),
+ ENUM(WTAP_ENCAP_IEEE802_15_4),
+ ENUM(WTAP_ENCAP_IEEE802_15_4_NOFCS),
+ ENUM(WTAP_ENCAP_IEEE802_15_4_NONASK_PHY),
+ ENUM(WTAP_ENCAP_IEEE802_15_4_TAP),
+ ENUM(WTAP_ENCAP_IEEE802_16_MAC_CPS),
+ ENUM(WTAP_ENCAP_IEEE_802_11),
+ ENUM(WTAP_ENCAP_IEEE_802_11_AVS),
+ ENUM(WTAP_ENCAP_IEEE_802_11_NETMON),
+ ENUM(WTAP_ENCAP_IEEE_802_11_PRISM),
+ ENUM(WTAP_ENCAP_IEEE_802_11_RADIOTAP),
+ ENUM(WTAP_ENCAP_IEEE_802_11_WITH_RADIO),
+ ENUM(WTAP_ENCAP_INFINIBAND),
+ ENUM(WTAP_ENCAP_IPMB_KONTRON),
+ ENUM(WTAP_ENCAP_IPMI_TRACE),
+ ENUM(WTAP_ENCAP_IPNET),
+ ENUM(WTAP_ENCAP_IP_OVER_FC),
+ ENUM(WTAP_ENCAP_IP_OVER_IB_PCAP),
+ ENUM(WTAP_ENCAP_IP_OVER_IB_SNOOP),
+ ENUM(WTAP_ENCAP_IRDA),
+ ENUM(WTAP_ENCAP_ISDN),
+ ENUM(WTAP_ENCAP_ISO14443),
+ ENUM(WTAP_ENCAP_IXVERIWAVE),
+ ENUM(WTAP_ENCAP_JPEG_JFIF),
+ ENUM(WTAP_ENCAP_JSON),
+ ENUM(WTAP_ENCAP_JUNIPER_ATM1),
+ ENUM(WTAP_ENCAP_JUNIPER_ATM2),
+ ENUM(WTAP_ENCAP_JUNIPER_CHDLC),
+ ENUM(WTAP_ENCAP_JUNIPER_ETHER),
+ ENUM(WTAP_ENCAP_JUNIPER_FRELAY),
+ ENUM(WTAP_ENCAP_JUNIPER_GGSN),
+ ENUM(WTAP_ENCAP_JUNIPER_MLFR),
+ ENUM(WTAP_ENCAP_JUNIPER_MLPPP),
+ ENUM(WTAP_ENCAP_JUNIPER_PPP),
+ ENUM(WTAP_ENCAP_JUNIPER_PPPOE),
+ ENUM(WTAP_ENCAP_JUNIPER_ST),
+ ENUM(WTAP_ENCAP_JUNIPER_SVCS),
+ ENUM(WTAP_ENCAP_JUNIPER_VN),
+ ENUM(WTAP_ENCAP_JUNIPER_VP),
+ ENUM(WTAP_ENCAP_K12),
+ ENUM(WTAP_ENCAP_LAPB),
+ ENUM(WTAP_ENCAP_LAPD),
+ ENUM(WTAP_ENCAP_LAYER1_EVENT),
+ ENUM(WTAP_ENCAP_LIN),
+ ENUM(WTAP_ENCAP_LINUX_ATM_CLIP),
+ ENUM(WTAP_ENCAP_LINUX_LAPD),
+ ENUM(WTAP_ENCAP_LOCALTALK),
+ ENUM(WTAP_ENCAP_LOGCAT),
+ ENUM(WTAP_ENCAP_LOGCAT_BRIEF),
+ ENUM(WTAP_ENCAP_LOGCAT_LONG),
+ ENUM(WTAP_ENCAP_LOGCAT_PROCESS),
+ ENUM(WTAP_ENCAP_LOGCAT_TAG),
+ ENUM(WTAP_ENCAP_LOGCAT_THREAD),
+ ENUM(WTAP_ENCAP_LOGCAT_THREADTIME),
+ ENUM(WTAP_ENCAP_LOGCAT_TIME),
+ ENUM(WTAP_ENCAP_LOG_3GPP),
+ ENUM(WTAP_ENCAP_LOOP),
+ ENUM(WTAP_ENCAP_LORATAP),
+ ENUM(WTAP_ENCAP_MA_WFP_CAPTURE_2V4),
+ ENUM(WTAP_ENCAP_MA_WFP_CAPTURE_2V6),
+ ENUM(WTAP_ENCAP_MA_WFP_CAPTURE_AUTH_V4),
+ ENUM(WTAP_ENCAP_MA_WFP_CAPTURE_AUTH_V6),
+ ENUM(WTAP_ENCAP_MA_WFP_CAPTURE_V4),
+ ENUM(WTAP_ENCAP_MA_WFP_CAPTURE_V6),
+ ENUM(WTAP_ENCAP_MDB),
+ ENUM(WTAP_ENCAP_MIME),
+ ENUM(WTAP_ENCAP_MOST),
+ ENUM(WTAP_ENCAP_MP4),
+ ENUM(WTAP_ENCAP_MPEG),
+ ENUM(WTAP_ENCAP_MPEG_2_TS),
+ ENUM(WTAP_ENCAP_MTP2),
+ ENUM(WTAP_ENCAP_MTP2_WITH_PHDR),
+ ENUM(WTAP_ENCAP_MTP3),
+ ENUM(WTAP_ENCAP_MUX27010),
+ ENUM(WTAP_ENCAP_NETANALYZER),
+ ENUM(WTAP_ENCAP_NETANALYZER_TRANSPARENT),
+ ENUM(WTAP_ENCAP_NETLINK),
+ ENUM(WTAP_ENCAP_NETMON_HEADER),
+ ENUM(WTAP_ENCAP_NETMON_NETWORK_INFO_EX),
+ ENUM(WTAP_ENCAP_NETMON_NET_FILTER),
+ ENUM(WTAP_ENCAP_NETMON_NET_NETEVENT),
+ ENUM(WTAP_ENCAP_NETTL_ETHERNET),
+ ENUM(WTAP_ENCAP_NETTL_FDDI),
+ ENUM(WTAP_ENCAP_NETTL_RAW_ICMP),
+ ENUM(WTAP_ENCAP_NETTL_RAW_ICMPV6),
+ ENUM(WTAP_ENCAP_NETTL_RAW_IP),
+ ENUM(WTAP_ENCAP_NETTL_RAW_TELNET),
+ ENUM(WTAP_ENCAP_NETTL_TOKEN_RING),
+ ENUM(WTAP_ENCAP_NETTL_UNKNOWN),
+ ENUM(WTAP_ENCAP_NETTL_X25),
+ ENUM(WTAP_ENCAP_NFC_LLCP),
+ ENUM(WTAP_ENCAP_NFLOG),
+ ENUM(WTAP_ENCAP_NONE),
+ ENUM(WTAP_ENCAP_NORDIC_BLE),
+ ENUM(WTAP_ENCAP_NSTRACE_1_0),
+ ENUM(WTAP_ENCAP_NSTRACE_2_0),
+ ENUM(WTAP_ENCAP_NSTRACE_3_0),
+ ENUM(WTAP_ENCAP_NSTRACE_3_5),
+ ENUM(WTAP_ENCAP_NULL),
+ ENUM(WTAP_ENCAP_OLD_PFLOG),
+ ENUM(WTAP_ENCAP_PACKETLOGGER),
+ ENUM(WTAP_ENCAP_PER_PACKET),
+ ENUM(WTAP_ENCAP_PFLOG),
+ ENUM(WTAP_ENCAP_PKTAP),
+ ENUM(WTAP_ENCAP_PPI),
+ ENUM(WTAP_ENCAP_PPP),
+ ENUM(WTAP_ENCAP_PPP_ETHER),
+ ENUM(WTAP_ENCAP_PPP_WITH_PHDR),
+ ENUM(WTAP_ENCAP_RAW_IP),
+ ENUM(WTAP_ENCAP_RAW_IP4),
+ ENUM(WTAP_ENCAP_RAW_IP6),
+ ENUM(WTAP_ENCAP_RAW_IPFIX),
+ ENUM(WTAP_ENCAP_REDBACK),
+ ENUM(WTAP_ENCAP_RFC7468),
+ ENUM(WTAP_ENCAP_RTAC_SERIAL),
+ ENUM(WTAP_ENCAP_RUBY_MARSHAL),
+ ENUM(WTAP_ENCAP_SCCP),
+ ENUM(WTAP_ENCAP_SCTP),
+ ENUM(WTAP_ENCAP_SDH),
+ ENUM(WTAP_ENCAP_SDLC),
+ ENUM(WTAP_ENCAP_SILABS_DEBUG_CHANNEL),
+ ENUM(WTAP_ENCAP_SITA),
+ ENUM(WTAP_ENCAP_SLIP),
+ ENUM(WTAP_ENCAP_SLL),
+ ENUM(WTAP_ENCAP_SLL2),
+ ENUM(WTAP_ENCAP_SOCKETCAN),
+ ENUM(WTAP_ENCAP_STANAG_4607),
+ ENUM(WTAP_ENCAP_STANAG_5066_D_PDU),
+ ENUM(WTAP_ENCAP_SYMANTEC),
+ ENUM(WTAP_ENCAP_SYSTEMD_JOURNAL),
+ ENUM(WTAP_ENCAP_TNEF),
+ ENUM(WTAP_ENCAP_TOKEN_RING),
+ ENUM(WTAP_ENCAP_TZSP),
+ ENUM(WTAP_ENCAP_UNKNOWN),
+ ENUM(WTAP_ENCAP_USBPCAP),
+ ENUM(WTAP_ENCAP_USB_2_0),
+ ENUM(WTAP_ENCAP_USB_2_0_FULL_SPEED),
+ ENUM(WTAP_ENCAP_USB_2_0_HIGH_SPEED),
+ ENUM(WTAP_ENCAP_USB_2_0_LOW_SPEED),
+ ENUM(WTAP_ENCAP_USB_DARWIN),
+ ENUM(WTAP_ENCAP_USB_FREEBSD),
+ ENUM(WTAP_ENCAP_USB_LINUX),
+ ENUM(WTAP_ENCAP_USB_LINUX_MMAPPED),
+ ENUM(WTAP_ENCAP_USER0),
+ ENUM(WTAP_ENCAP_USER1),
+ ENUM(WTAP_ENCAP_USER10),
+ ENUM(WTAP_ENCAP_USER11),
+ ENUM(WTAP_ENCAP_USER12),
+ ENUM(WTAP_ENCAP_USER13),
+ ENUM(WTAP_ENCAP_USER14),
+ ENUM(WTAP_ENCAP_USER15),
+ ENUM(WTAP_ENCAP_USER2),
+ ENUM(WTAP_ENCAP_USER3),
+ ENUM(WTAP_ENCAP_USER4),
+ ENUM(WTAP_ENCAP_USER5),
+ ENUM(WTAP_ENCAP_USER6),
+ ENUM(WTAP_ENCAP_USER7),
+ ENUM(WTAP_ENCAP_USER8),
+ ENUM(WTAP_ENCAP_USER9),
+ ENUM(WTAP_ENCAP_V5_EF),
+ ENUM(WTAP_ENCAP_VPP),
+ ENUM(WTAP_ENCAP_VSOCK),
+ ENUM(WTAP_ENCAP_WFLEET_HDLC),
+ ENUM(WTAP_ENCAP_WIRESHARK_UPPER_PDU),
+ ENUM(WTAP_ENCAP_X2E_SERIAL),
+ ENUM(WTAP_ENCAP_X2E_XORAYA),
+ ENUM(WTAP_ENCAP_ZBNCP),
+ ENUM(WTAP_ENCAP_ZWAVE_SERIAL),
+ ENUM(WTAP_ERR_BAD_FILE),
+ ENUM(WTAP_ERR_CANT_CLOSE),
+ ENUM(WTAP_ERR_CANT_OPEN),
+ ENUM(WTAP_ERR_CANT_SEEK),
+ ENUM(WTAP_ERR_CANT_SEEK_COMPRESSED),
+ ENUM(WTAP_ERR_CANT_WRITE),
+ ENUM(WTAP_ERR_CANT_WRITE_TO_PIPE),
+ ENUM(WTAP_ERR_CHECK_WSLUA),
+ ENUM(WTAP_ERR_COMPRESSION_NOT_SUPPORTED),
+ ENUM(WTAP_ERR_DECOMPRESS),
+ ENUM(WTAP_ERR_DECOMPRESSION_NOT_SUPPORTED),
+ ENUM(WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED),
+ ENUM(WTAP_ERR_FILE_UNKNOWN_FORMAT),
+ ENUM(WTAP_ERR_INTERNAL),
+ ENUM(WTAP_ERR_NOT_REGULAR_FILE),
+ ENUM(WTAP_ERR_PACKET_TOO_LARGE),
+ ENUM(WTAP_ERR_RANDOM_OPEN_PIPE),
+ ENUM(WTAP_ERR_RANDOM_OPEN_STDIN),
+ ENUM(WTAP_ERR_SHORT_READ),
+ ENUM(WTAP_ERR_SHORT_WRITE),
+ ENUM(WTAP_ERR_TIME_STAMP_NOT_SUPPORTED),
+ ENUM(WTAP_ERR_UNC_OVERFLOW),
+ ENUM(WTAP_ERR_UNSUPPORTED),
+ ENUM(WTAP_ERR_UNWRITABLE_ENCAP),
+ ENUM(WTAP_ERR_UNWRITABLE_FILE_TYPE),
+ ENUM(WTAP_ERR_UNWRITABLE_REC_DATA),
+ ENUM(WTAP_ERR_UNWRITABLE_REC_TYPE),
+ ENUM(WTAP_FILE_TYPE_SUBTYPE_UNKNOWN),
+ ENUM(WTAP_GZIP_COMPRESSED),
+ ENUM(WTAP_HAS_CAP_LEN),
+ ENUM(WTAP_HAS_INTERFACE_ID),
+ ENUM(WTAP_HAS_SECTION_NUMBER),
+ ENUM(WTAP_HAS_TS),
+ ENUM(WTAP_LZ4_COMPRESSED),
+ ENUM(WTAP_MAX_PACKET_SIZE_DBUS),
+ ENUM(WTAP_MAX_PACKET_SIZE_EBHSCR),
+ ENUM(WTAP_MAX_PACKET_SIZE_STANDARD),
+ ENUM(WTAP_MAX_PACKET_SIZE_USBPCAP),
+ ENUM(WTAP_OPEN_ERROR),
+ ENUM(WTAP_OPEN_MINE),
+ ENUM(WTAP_OPEN_NOT_MINE),
+ ENUM(WTAP_TSPREC_100_MSEC),
+ ENUM(WTAP_TSPREC_100_NSEC),
+ ENUM(WTAP_TSPREC_100_USEC),
+ ENUM(WTAP_TSPREC_10_MSEC),
+ ENUM(WTAP_TSPREC_10_NSEC),
+ ENUM(WTAP_TSPREC_10_USEC),
+ ENUM(WTAP_TSPREC_CSEC),
+ ENUM(WTAP_TSPREC_DSEC),
+ ENUM(WTAP_TSPREC_MSEC),
+ ENUM(WTAP_TSPREC_NSEC),
+ ENUM(WTAP_TSPREC_PER_PACKET),
+ ENUM(WTAP_TSPREC_SEC),
+ ENUM(WTAP_TSPREC_UNKNOWN),
+ ENUM(WTAP_TSPREC_USEC),
+ ENUM(WTAP_TYPE_AUTO),
+ ENUM(WTAP_UNCOMPRESSED),
+ ENUM(WTAP_ZSTD_COMPRESSED),
+ { NULL, 0 },
+};
diff --git a/wiretap/introspection.c b/wiretap/introspection.c
new file mode 100644
index 00000000..032eee8e
--- /dev/null
+++ b/wiretap/introspection.c
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2021, João Valverde <j@v6e.pt>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#include "config.h"
+#include "introspection.h"
+#include <string.h>
+#include <stdlib.h>
+
+#include "introspection-enums.c"
+
+const ws_enum_t *wtap_inspect_enums(void)
+{
+ return all_enums;
+}
+
+size_t wtap_inspect_enums_count(void)
+{
+ /* Exclude null terminator */
+ return sizeof(all_enums)/sizeof(ws_enum_t) - 1;
+}
+
+const ws_enum_t *wtap_inspect_enums_bsearch(const char *needle)
+{
+ return ws_enums_bsearch(all_enums, wtap_inspect_enums_count(), needle);
+}
diff --git a/wiretap/introspection.h b/wiretap/introspection.h
new file mode 100644
index 00000000..c12bbcf5
--- /dev/null
+++ b/wiretap/introspection.h
@@ -0,0 +1,36 @@
+/** @file
+ * Copyright 2021, João Valverde <j@v6e.pt>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef _WTAP_INTROSPECTION_H_
+#define _WTAP_INTROSPECTION_H_
+
+#include <stddef.h>
+#include <ws_symbol_export.h>
+#include <wsutil/introspection.h>
+
+/** Returns an array of ws_enum_t elements. The array is sorted and
+ * ends with {NULL, 0}.
+ *
+ * It can be used by language bindings to the Wireshark API to obtain
+ * the value of some magic constants. The array can be binary searched,
+ * imported to a hash table, serialized, etc.
+ */
+WS_DLL_PUBLIC
+const ws_enum_t *wtap_inspect_enums(void);
+
+/** Returns size of enums array not including null terminator. */
+WS_DLL_PUBLIC
+size_t wtap_inspect_enums_count(void);
+
+/** Performs a binary search for the magic constant "needle". */
+WS_DLL_PUBLIC
+const ws_enum_t *wtap_inspect_enums_bsearch(const char *needle);
+
+#endif
diff --git a/wiretap/ipfix.c b/wiretap/ipfix.c
new file mode 100644
index 00000000..2dff3849
--- /dev/null
+++ b/wiretap/ipfix.c
@@ -0,0 +1,364 @@
+/* ipfix.c
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * File format support for ipfix file format
+ * Copyright (c) 2010 by Hadriel Kaplan <hadrielk@yahoo.com>
+ * with generous copying from other wiretaps, such as pcapng
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+/* File format reference:
+ * RFC 5655 and 5101
+ * https://tools.ietf.org/rfc/rfc5655
+ * https://tools.ietf.org/rfc/rfc5101
+ *
+ * This wiretap is for an ipfix file format reader, per RFC 5655/5101.
+ * All "records" in the file are IPFIX messages, beginning with an IPFIX
+ * message header of 16 bytes as follows from RFC 5101:
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Version Number | Length |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Export Time |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Sequence Number |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Observation Domain ID |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ Figure F: IPFIX Message Header Format
+
+ * which is then followed by one or more "Sets": Data Sets, Template Sets,
+ * and Options Template Sets. Each Set then has one or more Records in
+ * it.
+ *
+ * All IPFIX files are recorded in big-endian form (network byte order),
+ * per the RFCs. That means if we're on a little-endian system, all
+ * hell will break loose if we don't g_ntohX.
+ *
+ * Since wireshark already has an IPFIX dissector (implemented in
+ * packet-netflow.c), this reader will just set that dissector upon
+ * reading each message. Thus, an IPFIX Message is treated as a packet
+ * as far as the dissector is concerned.
+ */
+
+#include "config.h"
+
+#define WS_LOG_DOMAIN LOG_DOMAIN_WIRETAP
+
+#include <stdlib.h>
+#include <string.h>
+#include "wtap-int.h"
+#include "file_wrappers.h"
+#include "ipfix.h"
+
+#include <wsutil/strtoi.h>
+#include <wsutil/wslog.h>
+
+#define RECORDS_FOR_IPFIX_CHECK 20
+
+static gboolean
+ipfix_read(wtap *wth, wtap_rec *rec, Buffer *buf, int *err,
+ gchar **err_info, gint64 *data_offset);
+static gboolean
+ipfix_seek_read(wtap *wth, gint64 seek_off,
+ wtap_rec *rec, Buffer *buf, int *err, gchar **err_info);
+
+#define IPFIX_VERSION 10
+
+/* ipfix: message header */
+typedef struct ipfix_message_header_s {
+ guint16 version;
+ guint16 message_length;
+ guint32 export_time_secs;
+ guint32 sequence_number;
+ guint32 observation_id; /* might be 0 for none */
+ /* x bytes msg_body */
+} ipfix_message_header_t;
+#define IPFIX_MSG_HDR_SIZE 16
+
+/* ipfix: common Set header for every Set type */
+typedef struct ipfix_set_header_s {
+ guint16 set_type;
+ guint16 set_length;
+ /* x bytes set_body */
+} ipfix_set_header_t;
+#define IPFIX_SET_HDR_SIZE 4
+
+
+static int ipfix_file_type_subtype = -1;
+
+void register_ipfix(void);
+
+/* Read IPFIX message header from file. Return true on success. Set *err to
+ * 0 on EOF, any other value for "real" errors (EOF is ok, since return
+ * value is still FALSE)
+ */
+static gboolean
+ipfix_read_message_header(ipfix_message_header_t *pfx_hdr, FILE_T fh, int *err, gchar **err_info)
+{
+ if (!wtap_read_bytes_or_eof(fh, pfx_hdr, IPFIX_MSG_HDR_SIZE, err, err_info))
+ return FALSE;
+
+ /* fix endianness, because IPFIX files are always big-endian */
+ pfx_hdr->version = g_ntohs(pfx_hdr->version);
+ pfx_hdr->message_length = g_ntohs(pfx_hdr->message_length);
+ pfx_hdr->export_time_secs = g_ntohl(pfx_hdr->export_time_secs);
+ pfx_hdr->sequence_number = g_ntohl(pfx_hdr->sequence_number);
+ pfx_hdr->observation_id = g_ntohl(pfx_hdr->observation_id);
+
+ /* is the version number one we expect? */
+ if (pfx_hdr->version != IPFIX_VERSION) {
+ /* Not an ipfix file. */
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("ipfix: wrong version %d", pfx_hdr->version);
+ return FALSE;
+ }
+
+ if (pfx_hdr->message_length < 16) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("ipfix: message length %u is too short", pfx_hdr->message_length);
+ return FALSE;
+ }
+
+ /* go back to before header */
+ if (file_seek(fh, 0 - IPFIX_MSG_HDR_SIZE, SEEK_CUR, err) == -1) {
+ ws_debug("couldn't go back in file before header");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+/* Read IPFIX message header from file and fill in the struct wtap_rec
+ * for the packet, and, if that succeeds, read the packet data.
+ * Return true on success. Set *err to 0 on EOF, any other value for "real"
+ * errors (EOF is ok, since return value is still FALSE).
+ */
+static gboolean
+ipfix_read_message(FILE_T fh, wtap_rec *rec, Buffer *buf, int *err, gchar **err_info)
+{
+ ipfix_message_header_t msg_hdr;
+
+ if (!ipfix_read_message_header(&msg_hdr, fh, err, err_info))
+ return FALSE;
+ /*
+ * The maximum value of msg_hdr.message_length is 65535, which is
+ * less than WTAP_MAX_PACKET_SIZE_STANDARD will ever be, so we don't need
+ * to check it.
+ */
+
+ rec->rec_type = REC_TYPE_PACKET;
+ rec->block = wtap_block_create(WTAP_BLOCK_PACKET);
+ rec->presence_flags = WTAP_HAS_TS;
+ rec->rec_header.packet_header.len = msg_hdr.message_length;
+ rec->rec_header.packet_header.caplen = msg_hdr.message_length;
+ rec->ts.secs = msg_hdr.export_time_secs;
+ rec->ts.nsecs = 0;
+
+ return wtap_read_packet_bytes(fh, buf, msg_hdr.message_length, err, err_info);
+}
+
+
+
+/* classic wtap: open capture file. Return WTAP_OPEN_MINE on success,
+ * WTAP_OPEN_NOT_MINE on normal failure like malformed format,
+ * WTAP_OPEN_ERROR on bad error like file system
+ */
+wtap_open_return_val
+ipfix_open(wtap *wth, int *err, gchar **err_info)
+{
+ gint i, n, records_for_ipfix_check = RECORDS_FOR_IPFIX_CHECK;
+ gchar *s;
+ guint16 checked_len = 0;
+ ipfix_message_header_t msg_hdr;
+ ipfix_set_header_t set_hdr;
+
+ ws_debug("opening file");
+
+ /* number of records to scan before deciding if this really is IPFIX */
+ if ((s = getenv("IPFIX_RECORDS_TO_CHECK")) != NULL) {
+ if (ws_strtoi32(s, NULL, &n) && n > 0 && n < 101) {
+ records_for_ipfix_check = n;
+ }
+ }
+
+ /*
+ * IPFIX is a little hard because there's no magic number; we look at
+ * the first few records and see if they look enough like IPFIX
+ * records.
+ */
+ for (i = 0; i < records_for_ipfix_check; i++) {
+ /* read first message header to check version */
+ if (!ipfix_read_message_header(&msg_hdr, wth->fh, err, err_info)) {
+ ws_debug("couldn't read message header #%d with err code #%d (%s)",
+ i, *err, *err_info);
+ if (*err == WTAP_ERR_BAD_FILE) {
+ *err = 0; /* not actually an error in this case */
+ g_free(*err_info);
+ *err_info = NULL;
+ return WTAP_OPEN_NOT_MINE;
+ }
+ if (*err != 0 && *err != WTAP_ERR_SHORT_READ)
+ return WTAP_OPEN_ERROR; /* real failure */
+ /* else it's EOF */
+ if (i < 1) {
+ /* we haven't seen enough to prove this is a ipfix file */
+ return WTAP_OPEN_NOT_MINE;
+ }
+ /*
+ * If we got here, it's EOF and we haven't yet seen anything
+ * that doesn't look like an IPFIX record - i.e. everything
+ * we've seen looks like an IPFIX record - so we assume this
+ * is an IPFIX file.
+ */
+ break;
+ }
+ if (file_seek(wth->fh, IPFIX_MSG_HDR_SIZE, SEEK_CUR, err) == -1) {
+ ws_debug("failed seek to next message in file, %d bytes away",
+ msg_hdr.message_length);
+ return WTAP_OPEN_NOT_MINE;
+ }
+ checked_len = IPFIX_MSG_HDR_SIZE;
+
+ /* check each Set in IPFIX Message for sanity */
+ while (checked_len < msg_hdr.message_length) {
+ if (!wtap_read_bytes(wth->fh, &set_hdr, IPFIX_SET_HDR_SIZE,
+ err, err_info)) {
+ if (*err == WTAP_ERR_SHORT_READ) {
+ /* Not a valid IPFIX Set, so not an IPFIX file. */
+ ws_debug("error %d reading set", *err);
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ /* A real I/O error; fail. */
+ return WTAP_OPEN_ERROR;
+ }
+ set_hdr.set_length = g_ntohs(set_hdr.set_length);
+ if ((set_hdr.set_length < IPFIX_SET_HDR_SIZE) ||
+ ((set_hdr.set_length + checked_len) > msg_hdr.message_length)) {
+ ws_debug("found invalid set_length of %d",
+ set_hdr.set_length);
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ if (file_seek(wth->fh, set_hdr.set_length - IPFIX_SET_HDR_SIZE,
+ SEEK_CUR, err) == -1)
+ {
+ ws_debug("failed seek to next set in file, %d bytes away",
+ set_hdr.set_length - IPFIX_SET_HDR_SIZE);
+ return WTAP_OPEN_ERROR;
+ }
+ checked_len += set_hdr.set_length;
+ }
+ }
+
+ /* go back to beginning of file */
+ if (file_seek (wth->fh, 0, SEEK_SET, err) != 0)
+ {
+ return WTAP_OPEN_ERROR;
+ }
+
+ /* all's good, this is a IPFIX file */
+ wth->file_encap = WTAP_ENCAP_RAW_IPFIX;
+ wth->snapshot_length = 0;
+ wth->file_tsprec = WTAP_TSPREC_SEC;
+ wth->subtype_read = ipfix_read;
+ wth->subtype_seek_read = ipfix_seek_read;
+ wth->file_type_subtype = ipfix_file_type_subtype;
+
+ /*
+ * Add an IDB; we don't know how many interfaces were
+ * involved, so we just say one interface, about which
+ * we only know the link-layer type, snapshot length,
+ * and time stamp resolution.
+ */
+ wtap_add_generated_idb(wth);
+
+ return WTAP_OPEN_MINE;
+}
+
+
+/* classic wtap: read packet */
+static gboolean
+ipfix_read(wtap *wth, wtap_rec *rec, Buffer *buf, int *err,
+ gchar **err_info, gint64 *data_offset)
+{
+ *data_offset = file_tell(wth->fh);
+ ws_debug("offset is initially %" PRId64, *data_offset);
+
+ if (!ipfix_read_message(wth->fh, rec, buf, err, err_info)) {
+ ws_debug("couldn't read message header with code: %d\n, and error '%s'",
+ *err, *err_info);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+/* classic wtap: seek to file position and read packet */
+static gboolean
+ipfix_seek_read(wtap *wth, gint64 seek_off, wtap_rec *rec,
+ Buffer *buf, int *err, gchar **err_info)
+{
+ /* seek to the right file position */
+ if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1) {
+ ws_debug("couldn't read message header with code: %d\n, and error '%s'",
+ *err, *err_info);
+ return FALSE; /* Seek error */
+ }
+
+ ws_debug("reading at offset %" PRIu64, seek_off);
+
+ if (!ipfix_read_message(wth->random_fh, rec, buf, err, err_info)) {
+ ws_debug("couldn't read message header");
+ if (*err == 0)
+ *err = WTAP_ERR_SHORT_READ;
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static const struct supported_block_type ipfix_blocks_supported[] = {
+ /*
+ * We support packet blocks, with no comments or other options.
+ */
+ { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
+};
+
+static const struct file_type_subtype_info ipfix_info = {
+ "IPFIX File Format", "ipfix", "pfx", "ipfix",
+ FALSE, BLOCKS_SUPPORTED(ipfix_blocks_supported),
+ NULL, NULL, NULL
+};
+
+void register_ipfix(void)
+{
+ ipfix_file_type_subtype = wtap_register_file_type_subtype(&ipfix_info);
+
+ /*
+ * Register name for backwards compatibility with the
+ * wtap_filetypes table in Lua.
+ */
+ wtap_register_backwards_compatibility_lua_name("IPFIX",
+ ipfix_file_type_subtype);
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * vi: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */
diff --git a/wiretap/ipfix.h b/wiretap/ipfix.h
new file mode 100644
index 00000000..7f7bfe95
--- /dev/null
+++ b/wiretap/ipfix.h
@@ -0,0 +1,18 @@
+/** @file
+ *
+ * Wiretap Library
+ * Copyright (c) 2010 by Hadriel Kaplan <hadrielk@yahoo.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __W_IPFIX_H__
+#define __W_IPFIX_H__
+
+#include <glib.h>
+#include "wtap.h"
+#include "ws_symbol_export.h"
+
+wtap_open_return_val ipfix_open(wtap *wth, int *err, gchar **err_info);
+
+#endif
diff --git a/wiretap/iptrace.c b/wiretap/iptrace.c
new file mode 100644
index 00000000..2b4e9d63
--- /dev/null
+++ b/wiretap/iptrace.c
@@ -0,0 +1,909 @@
+/* iptrace.c
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ */
+#include "config.h"
+#include <stdlib.h>
+#include <string.h>
+#include "wtap-int.h"
+#include "file_wrappers.h"
+#include "atm.h"
+#include "iptrace.h"
+
+/*
+ * Private per-wtap_t data needed to read a file.
+ */
+typedef struct {
+ GHashTable *interface_ids; /* map name/description/link-layer type to interface ID */
+ guint num_interface_ids; /* Number of interface IDs assigned */
+} iptrace_t;
+
+#define IPTRACE_IFT_HF 0x3d /* Support for PERCS IP-HFI*/
+#define IPTRACE_IFT_IB 0xc7 /* IP over Infiniband. Number by IANA */
+
+static void iptrace_close(wtap *wth);
+
+static gboolean iptrace_read_1_0(wtap *wth, wtap_rec *rec,
+ Buffer *buf, int *err, gchar **err_info, gint64 *data_offset);
+static gboolean iptrace_seek_read_1_0(wtap *wth, gint64 seek_off,
+ wtap_rec *rec, Buffer *buf, int *err, gchar **err_info);
+
+static gboolean iptrace_read_2_0(wtap *wth, wtap_rec *rec,
+ Buffer *buf, int *err, gchar **err_info, gint64 *data_offset);
+static gboolean iptrace_seek_read_2_0(wtap *wth, gint64 seek_off,
+ wtap_rec *rec, Buffer *buf, int *err, gchar **err_info);
+
+static gboolean iptrace_read_rec_data(FILE_T fh, Buffer *buf,
+ wtap_rec *rec, int *err, gchar **err_info);
+static void fill_in_pseudo_header(int encap,
+ union wtap_pseudo_header *pseudo_header, const char *pkt_text);
+static int wtap_encap_ift(unsigned int ift);
+
+/*
+ * Size of the version string in the file header.
+ */
+#define VERSION_STRING_SIZE 11
+
+/*
+ * Hash table to map interface name and description, and link-layer
+ * type, to interface ID.
+ */
+#define PREFIX_SIZE 4
+
+typedef struct {
+ char prefix[PREFIX_SIZE+1];
+ guint8 unit;
+ guint8 if_type;
+} if_info;
+
+static int iptrace_1_0_file_type_subtype = -1;
+static int iptrace_2_0_file_type_subtype = -1;
+
+void register_iptrace(void);
+
+static gboolean destroy_if_info(gpointer key, gpointer value _U_,
+ gpointer user_data _U_)
+{
+ if_info *info = (if_info *)key;
+
+ g_free(info);
+
+ return TRUE;
+}
+
+static guint if_info_hash(gconstpointer info_arg)
+{
+ if_info *info = (if_info *)info_arg;
+
+ return g_str_hash(info->prefix) + info->unit + info->if_type;
+}
+
+static gboolean if_info_equal(gconstpointer info1_arg, gconstpointer info2_arg)
+{
+ if_info *info1 = (if_info *)info1_arg;
+ if_info *info2 = (if_info *)info2_arg;
+
+ return strcmp(info1->prefix, info2->prefix) == 0 &&
+ info1->unit == info2->unit &&
+ info1->if_type == info2->if_type;
+}
+
+wtap_open_return_val iptrace_open(wtap *wth, int *err, gchar **err_info)
+{
+ char version_string[VERSION_STRING_SIZE+1];
+ iptrace_t *iptrace;
+
+ if (!wtap_read_bytes(wth->fh, version_string, VERSION_STRING_SIZE,
+ err, err_info)) {
+ if (*err != WTAP_ERR_SHORT_READ)
+ return WTAP_OPEN_ERROR;
+ return WTAP_OPEN_NOT_MINE;
+ }
+ version_string[VERSION_STRING_SIZE] = '\0';
+
+ if (strcmp(version_string, "iptrace 1.0") == 0) {
+ wth->file_type_subtype = iptrace_1_0_file_type_subtype;
+ wth->subtype_read = iptrace_read_1_0;
+ wth->subtype_seek_read = iptrace_seek_read_1_0;
+ wth->file_tsprec = WTAP_TSPREC_SEC;
+ }
+ else if (strcmp(version_string, "iptrace 2.0") == 0) {
+ wth->file_type_subtype = iptrace_2_0_file_type_subtype;
+ wth->subtype_read = iptrace_read_2_0;
+ wth->subtype_seek_read = iptrace_seek_read_2_0;
+ wth->file_tsprec = WTAP_TSPREC_NSEC;
+ }
+ else {
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ /* This is an iptrace file */
+ wth->subtype_close = iptrace_close;
+ iptrace = g_new(iptrace_t, 1);
+ iptrace->interface_ids = g_hash_table_new(if_info_hash, if_info_equal);
+ iptrace->num_interface_ids = 0;
+ wth->priv = (void *)iptrace;
+
+ return WTAP_OPEN_MINE;
+}
+
+static void iptrace_close(wtap *wth)
+{
+ iptrace_t *iptrace = (iptrace_t *)wth->priv;
+
+ g_hash_table_foreach_remove(iptrace->interface_ids, destroy_if_info, NULL);
+ g_hash_table_destroy(iptrace->interface_ids);
+}
+
+static void add_new_if_info(iptrace_t *iptrace, if_info *info, gpointer *result)
+{
+ if_info *new_info = g_new(if_info, 1);
+ *new_info = *info;
+ *result = GUINT_TO_POINTER(iptrace->num_interface_ids);
+ g_hash_table_insert(iptrace->interface_ids, (gpointer)new_info, *result);
+ iptrace->num_interface_ids++;
+}
+
+/***********************************************************
+ * iptrace 1.0 *
+ ***********************************************************/
+
+/*
+ * iptrace 1.0, discovered through inspection
+ *
+ * Packet record contains:
+ *
+ * an initial header, with a length field and a time stamp, in
+ * seconds since the Epoch;
+ *
+ * data, with the specified length.
+ *
+ * The data contains:
+ *
+ * a bunch of information about the packet;
+ *
+ * padding, at least for FDDI;
+ *
+ * the raw packet data.
+ */
+
+/*
+ * Offsets of fields in the initial header.
+ */
+#define IPTRACE_1_0_REC_LENGTH_OFFSET 0 /* 0-3: size of record data */
+#define IPTRACE_1_0_TV_SEC_OFFSET 4 /* 4-7: time stamp, seconds since the Epoch */
+
+#define IPTRACE_1_0_PHDR_SIZE 8 /* initial header */
+
+/*
+ * Offsets of fields in the packet information.
+ */
+/* Bytes 0-2 unknown */
+#define IPTRACE_1_0_UNIT_OFFSET 3 /* 3: interface unit number */
+#define IPTRACE_1_0_PREFIX_OFFSET 4 /* 4-7: null-terminated name prefix */
+#define IPTRACE_1_0_PKT_TEXT_OFFSET 8 /* 8-19: text in 2.0; what is it in 1.0? */
+#define IPTRACE_1_0_IF_TYPE_OFFSET 20 /* 20: SNMP ifType value */
+#define IPTRACE_1_0_TX_FLAGS_OFFSET 21 /* 21: 0=receive, 1=transmit */
+
+#define IPTRACE_1_0_PINFO_SIZE 22 /* packet information */
+
+static gboolean
+iptrace_read_rec_1_0(wtap *wth, FILE_T fh, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info)
+{
+ iptrace_t *iptrace = (iptrace_t *)wth->priv;
+ guint8 header[IPTRACE_1_0_PHDR_SIZE];
+ guint32 record_length;
+ guint8 pkt_info[IPTRACE_1_0_PINFO_SIZE];
+ if_info info;
+ guint32 packet_size;
+ gpointer result;
+
+ if (!wtap_read_bytes_or_eof(fh, header, IPTRACE_1_0_PHDR_SIZE, err,
+ err_info)) {
+ /* Read error or EOF */
+ return FALSE;
+ }
+
+ /* Get the record length */
+ record_length = pntoh32(&header[IPTRACE_1_0_REC_LENGTH_OFFSET]);
+ if (record_length < IPTRACE_1_0_PINFO_SIZE) {
+ /*
+ * Uh-oh, the record isn't big enough to even have a
+ * packet information header.
+ */
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("iptrace: file has a %u-byte record, too small to have even a packet information header",
+ record_length);
+ return FALSE;
+ }
+
+ /*
+ * Get the packet information.
+ */
+ if (!wtap_read_bytes(fh, pkt_info, IPTRACE_1_0_PINFO_SIZE, err,
+ err_info)) {
+ /* Read error or EOF */
+ return FALSE;
+ }
+
+ /*
+ * The if_type field of the frame header appears to be an SNMP
+ * ifType value giving the type of the interface. Check out the
+ * <net/if_types.h> header file.
+ */
+ info.if_type = pkt_info[IPTRACE_1_0_IF_TYPE_OFFSET];
+ rec->rec_header.packet_header.pkt_encap = wtap_encap_ift(info.if_type);
+ if (rec->rec_header.packet_header.pkt_encap == WTAP_ENCAP_UNKNOWN) {
+ *err = WTAP_ERR_UNSUPPORTED;
+ *err_info = ws_strdup_printf("iptrace: interface type IFT=0x%02x unknown or unsupported",
+ info.if_type);
+ return FALSE;
+ }
+
+ /* Get the packet data size */
+ packet_size = record_length - IPTRACE_1_0_PINFO_SIZE;
+
+ /*
+ * AIX appears to put 3 bytes of padding in front of FDDI
+ * frames; strip that crap off.
+ */
+ if (rec->rec_header.packet_header.pkt_encap == WTAP_ENCAP_FDDI_BITSWAPPED) {
+ /*
+ * The packet size is really a record size and includes
+ * the padding.
+ */
+ if (packet_size < 3) {
+ /*
+ * Uh-oh, the record isn't big enough to even have
+ * the padding.
+ */
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("iptrace: file has a %u-byte record, too small to have even a packet meta-data header",
+ record_length);
+ return FALSE;
+ }
+ packet_size -= 3;
+
+ /*
+ * Skip the padding.
+ */
+ if (!wtap_read_bytes(fh, NULL, 3, err, err_info))
+ return FALSE;
+ }
+ if (packet_size > WTAP_MAX_PACKET_SIZE_STANDARD) {
+ /*
+ * Probably a corrupt capture file; don't blow up trying
+ * to allocate space for an immensely-large packet.
+ */
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("iptrace: File has %u-byte packet, bigger than maximum of %u",
+ packet_size, WTAP_MAX_PACKET_SIZE_STANDARD);
+ return FALSE;
+ }
+
+ rec->rec_type = REC_TYPE_PACKET;
+ rec->block = wtap_block_create(WTAP_BLOCK_PACKET);
+ rec->presence_flags = WTAP_HAS_TS | WTAP_HAS_INTERFACE_ID;
+ rec->rec_header.packet_header.len = packet_size;
+ rec->rec_header.packet_header.caplen = packet_size;
+ rec->ts.secs = pntoh32(&header[IPTRACE_1_0_TV_SEC_OFFSET]);
+ rec->ts.nsecs = 0;
+ wtap_block_add_uint32_option(rec->block, OPT_PKT_FLAGS,
+ pkt_info[IPTRACE_1_0_TX_FLAGS_OFFSET] ?
+ (PACK_FLAGS_DIRECTION_OUTBOUND << PACK_FLAGS_DIRECTION_SHIFT) :
+ (PACK_FLAGS_DIRECTION_INBOUND << PACK_FLAGS_DIRECTION_SHIFT));
+
+ /* Fill in the pseudo-header. */
+ fill_in_pseudo_header(rec->rec_header.packet_header.pkt_encap,
+ &rec->rec_header.packet_header.pseudo_header,
+ (const char *)&pkt_info[IPTRACE_1_0_PKT_TEXT_OFFSET]);
+
+ /* Get the packet data */
+ if (!iptrace_read_rec_data(fh, buf, rec, err, err_info))
+ return FALSE;
+
+ /*
+ * No errors - get the interface ID.
+ *
+ * We do *not* trust the name to be null-terminated.
+ */
+ memcpy(info.prefix, &pkt_info[IPTRACE_1_0_PREFIX_OFFSET],
+ sizeof info.prefix);
+ info.prefix[PREFIX_SIZE] = '\0';
+ info.unit = pkt_info[IPTRACE_1_0_UNIT_OFFSET];
+
+ /*
+ * Try to find the entry with that name, description, and
+ * interface type.
+ */
+ if (!g_hash_table_lookup_extended(iptrace->interface_ids,
+ (gconstpointer)&info, NULL, &result)) {
+ wtap_block_t int_data;
+ wtapng_if_descr_mandatory_t *int_data_mand;
+
+ /*
+ * Not found; make a new entry.
+ */
+ add_new_if_info(iptrace, &info, &result);
+
+ /*
+ * Now make a new IDB and add it.
+ */
+ int_data = wtap_block_create(WTAP_BLOCK_IF_ID_AND_INFO);
+ int_data_mand = (wtapng_if_descr_mandatory_t *)wtap_block_get_mandatory_data(int_data);
+
+ int_data_mand->wtap_encap = rec->rec_header.packet_header.pkt_encap;
+ int_data_mand->tsprecision = WTAP_TSPREC_SEC;
+ int_data_mand->time_units_per_second = 1; /* No fractional time stamp */
+ int_data_mand->snap_len = WTAP_MAX_PACKET_SIZE_STANDARD; /* XXX - not known */
+
+ wtap_block_add_uint8_option(int_data, OPT_IDB_TSRESOL, 0); /* 1-second resolution */
+ /* Interface statistics */
+ int_data_mand->num_stat_entries = 0;
+ int_data_mand->interface_statistics = NULL;
+
+ wtap_block_set_string_option_value_format(int_data,
+ OPT_IDB_NAME, "%s%u", info.prefix, info.unit);
+ wtap_add_idb(wth, int_data);
+ }
+ rec->rec_header.packet_header.interface_id = GPOINTER_TO_UINT(result);
+ return TRUE;
+}
+
+/* Read the next packet */
+static gboolean iptrace_read_1_0(wtap *wth, wtap_rec *rec,
+ Buffer *buf, int *err, gchar **err_info, gint64 *data_offset)
+{
+ *data_offset = file_tell(wth->fh);
+
+ /* Read the packet */
+ if (!iptrace_read_rec_1_0(wth, wth->fh, rec, buf, err, err_info)) {
+ /* Read error or EOF */
+ return FALSE;
+ }
+
+ /* If the per-file encapsulation isn't known, set it to this
+ packet's encapsulation.
+
+ If it *is* known, and it isn't this packet's encapsulation,
+ set it to WTAP_ENCAP_PER_PACKET, as this file doesn't
+ have a single encapsulation for all packets in the file. */
+ if (wth->file_encap == WTAP_ENCAP_UNKNOWN)
+ wth->file_encap = rec->rec_header.packet_header.pkt_encap;
+ else {
+ if (wth->file_encap != rec->rec_header.packet_header.pkt_encap)
+ wth->file_encap = WTAP_ENCAP_PER_PACKET;
+ }
+
+ return TRUE;
+}
+
+static gboolean iptrace_seek_read_1_0(wtap *wth, gint64 seek_off,
+ wtap_rec *rec, Buffer *buf, int *err, gchar **err_info)
+{
+ if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
+ return FALSE;
+
+ /* Read the packet */
+ if (!iptrace_read_rec_1_0(wth, wth->random_fh, rec, buf, err,
+ err_info)) {
+ if (*err == 0)
+ *err = WTAP_ERR_SHORT_READ;
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/***********************************************************
+ * iptrace 2.0 *
+ ***********************************************************/
+
+/*
+ * iptrace 2.0, discovered through inspection
+ *
+ * Packet record contains:
+ *
+ * an initial header, with a length field and a time stamp, in
+ * seconds since the Epoch;
+ *
+ * data, with the specified length.
+ *
+ * The data contains:
+ *
+ * a bunch of information about the packet;
+ *
+ * padding, at least for FDDI;
+ *
+ * the raw packet data.
+ */
+
+/*
+ * Offsets of fields in the initial header.
+ */
+#define IPTRACE_2_0_REC_LENGTH_OFFSET 0 /* 0-3: size of record data */
+#define IPTRACE_2_0_TV_SEC0_OFFSET 4 /* 4-7: time stamp, seconds since the Epoch */
+
+#define IPTRACE_2_0_PHDR_SIZE 8 /* initial header */
+
+/*
+ * Offsets of fields in the packet information.
+ */
+/* Bytes 0-2 unknown */
+#define IPTRACE_2_0_UNIT_OFFSET 3 /* 3: interface unit number */
+#define IPTRACE_2_0_PREFIX_OFFSET 4 /* 4-7: null-terminated name prefix */
+#define IPTRACE_2_0_PKT_TEXT_OFFSET 8 /* 8-19: text stuff */
+#define IPTRACE_2_0_IF_TYPE_OFFSET 20 /* 20: SNMP ifType value */
+#define IPTRACE_2_0_TX_FLAGS_OFFSET 21 /* 21: 0=receive, 1=transmit */
+/* Bytes 22-23 unknown */
+#define IPTRACE_2_0_TV_SEC_OFFSET 24 /* 24-27: time stamp, seconds since the Epoch */
+#define IPTRACE_2_0_TV_NSEC_OFFSET 28 /* 28-31: nanoseconds since that second */
+
+#define IPTRACE_2_0_PINFO_SIZE 32 /* packet information */
+
+static gboolean
+iptrace_read_rec_2_0(wtap *wth, FILE_T fh, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info)
+{
+ iptrace_t *iptrace = (iptrace_t *)wth->priv;
+ guint8 header[IPTRACE_2_0_PHDR_SIZE];
+ guint32 record_length;
+ guint8 pkt_info[IPTRACE_2_0_PINFO_SIZE];
+ if_info info;
+ guint32 packet_size;
+ gpointer result;
+
+ if (!wtap_read_bytes_or_eof(fh, header, IPTRACE_2_0_PHDR_SIZE, err,
+ err_info)) {
+ /* Read error or EOF */
+ return FALSE;
+ }
+
+ /* Get the record length */
+ record_length = pntoh32(&header[IPTRACE_2_0_REC_LENGTH_OFFSET]);
+ if (record_length < IPTRACE_2_0_PINFO_SIZE) {
+ /*
+ * Uh-oh, the record isn't big enough to even have a
+ * packet information header.
+ */
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("iptrace: file has a %u-byte record, too small to have even a packet information header",
+ record_length);
+ return FALSE;
+ }
+
+ /*
+ * Get the packet information.
+ */
+ if (!wtap_read_bytes(fh, pkt_info, IPTRACE_2_0_PINFO_SIZE, err,
+ err_info)) {
+ /* Read error or EOF */
+ return FALSE;
+ }
+
+ /*
+ * The if_type field of the frame header appears to be an SNMP
+ * ifType value giving the type of the interface. Check out the
+ * <net/if_types.h> header file.
+ */
+ info.if_type = pkt_info[IPTRACE_2_0_IF_TYPE_OFFSET];
+ rec->rec_header.packet_header.pkt_encap = wtap_encap_ift(info.if_type);
+#if 0
+ /*
+ * We used to error out if the interface type in iptrace was
+ * unknown/unhandled, but an iptrace may contain packets
+ * from a variety of interfaces, some known, and others
+ * unknown.
+ *
+ * It is better to display the data even for unknown interface
+ * types, isntead of erroring out. In the future, it would be
+ * nice to be able to flag which frames are shown as data
+ * because their interface type is unknown, and also present
+ * the interface type number to the user so that it can be
+ * reported easily back to the Wireshark developer.
+ *
+ * XXX - what types are there that are used in files but
+ * that we don't handle?
+ */
+ if (rec->rec_header.packet_header.pkt_encap == WTAP_ENCAP_UNKNOWN) {
+ *err = WTAP_ERR_UNSUPPORTED;
+ *err_info = ws_strdup_printf("iptrace: interface type IFT=0x%02x unknown or unsupported",
+ info.if_type);
+ return FALSE;
+ }
+#endif
+
+ /* Get the packet data size */
+ packet_size = record_length - IPTRACE_2_0_PINFO_SIZE;
+
+ /*
+ * AIX appears to put 3 bytes of padding in front of FDDI
+ * frames; strip that crap off.
+ */
+ if (rec->rec_header.packet_header.pkt_encap == WTAP_ENCAP_FDDI_BITSWAPPED) {
+ /*
+ * The packet size is really a record size and includes
+ * the padding.
+ */
+ if (packet_size < 3) {
+ /*
+ * Uh-oh, the record isn't big enough to even have
+ * the padding.
+ */
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("iptrace: file has a %u-byte record, too small to have even a packet meta-data header",
+ record_length);
+ return FALSE;
+ }
+ packet_size -= 3;
+
+ /*
+ * Skip the padding.
+ */
+ if (!wtap_read_bytes(fh, NULL, 3, err, err_info))
+ return FALSE;
+ }
+ if (packet_size > WTAP_MAX_PACKET_SIZE_STANDARD) {
+ /*
+ * Probably a corrupt capture file; don't blow up trying
+ * to allocate space for an immensely-large packet.
+ */
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("iptrace: File has %u-byte packet, bigger than maximum of %u",
+ packet_size, WTAP_MAX_PACKET_SIZE_STANDARD);
+ return FALSE;
+ }
+
+ rec->rec_type = REC_TYPE_PACKET;
+ rec->block = wtap_block_create(WTAP_BLOCK_PACKET);
+ rec->presence_flags = WTAP_HAS_TS | WTAP_HAS_INTERFACE_ID;
+ rec->rec_header.packet_header.len = packet_size;
+ rec->rec_header.packet_header.caplen = packet_size;
+ rec->ts.secs = pntoh32(&pkt_info[IPTRACE_2_0_TV_SEC_OFFSET]);
+ rec->ts.nsecs = pntoh32(&pkt_info[IPTRACE_2_0_TV_NSEC_OFFSET]);
+ wtap_block_add_uint32_option(rec->block, OPT_PKT_FLAGS,
+ pkt_info[IPTRACE_2_0_TX_FLAGS_OFFSET] ?
+ (PACK_FLAGS_DIRECTION_OUTBOUND << PACK_FLAGS_DIRECTION_SHIFT) :
+ (PACK_FLAGS_DIRECTION_INBOUND << PACK_FLAGS_DIRECTION_SHIFT));
+
+ /* Fill in the pseudo-header. */
+ fill_in_pseudo_header(rec->rec_header.packet_header.pkt_encap,
+ &rec->rec_header.packet_header.pseudo_header,
+ (const char *)&pkt_info[IPTRACE_1_0_PKT_TEXT_OFFSET]);
+
+ /* Get the packet data */
+ if (!iptrace_read_rec_data(fh, buf, rec, err, err_info))
+ return FALSE;
+
+ /*
+ * No errors - get the interface ID.
+ *
+ * We do *not* trust the name to be null-terminated.
+ */
+ memcpy(info.prefix, &pkt_info[IPTRACE_2_0_PREFIX_OFFSET],
+ sizeof info.prefix);
+ info.prefix[PREFIX_SIZE] = '\0';
+ info.unit = pkt_info[IPTRACE_2_0_UNIT_OFFSET];
+
+ /*
+ * Try to find the entry with that name, description, and
+ * interface type.
+ */
+ if (!g_hash_table_lookup_extended(iptrace->interface_ids,
+ (gconstpointer)&info, NULL, &result)) {
+ wtap_block_t int_data;
+ wtapng_if_descr_mandatory_t *int_data_mand;
+
+ /*
+ * Not found; make a new entry.
+ */
+ add_new_if_info(iptrace, &info, &result);
+
+ /*
+ * Now make a new IDB and add it.
+ */
+ int_data = wtap_block_create(WTAP_BLOCK_IF_ID_AND_INFO);
+ int_data_mand = (wtapng_if_descr_mandatory_t *)wtap_block_get_mandatory_data(int_data);
+
+ int_data_mand->wtap_encap = rec->rec_header.packet_header.pkt_encap;
+ int_data_mand->tsprecision = WTAP_TSPREC_NSEC;
+ int_data_mand->time_units_per_second = 1000000000; /* Nanosecond resolution */
+ int_data_mand->snap_len = WTAP_MAX_PACKET_SIZE_STANDARD; /* XXX - not known */
+
+ wtap_block_add_uint8_option(int_data, OPT_IDB_TSRESOL, 0x09); /* nanosecond resolution */
+ /* Interface statistics */
+ int_data_mand->num_stat_entries = 0;
+ int_data_mand->interface_statistics = NULL;
+
+ wtap_block_set_string_option_value_format(int_data,
+ OPT_IDB_NAME, "%s%u", info.prefix, info.unit);
+ wtap_add_idb(wth, int_data);
+ }
+ rec->rec_header.packet_header.interface_id = GPOINTER_TO_UINT(result);
+ return TRUE;
+}
+
+/* Read the next packet */
+static gboolean iptrace_read_2_0(wtap *wth, wtap_rec *rec,
+ Buffer *buf, int *err, gchar **err_info, gint64 *data_offset)
+{
+ *data_offset = file_tell(wth->fh);
+
+ /* Read the packet */
+ if (!iptrace_read_rec_2_0(wth, wth->fh, rec, buf, err, err_info)) {
+ /* Read error or EOF */
+ return FALSE;
+ }
+
+ /* If the per-file encapsulation isn't known, set it to this
+ packet's encapsulation.
+
+ If it *is* known, and it isn't this packet's encapsulation,
+ set it to WTAP_ENCAP_PER_PACKET, as this file doesn't
+ have a single encapsulation for all packets in the file. */
+ if (wth->file_encap == WTAP_ENCAP_UNKNOWN)
+ wth->file_encap = rec->rec_header.packet_header.pkt_encap;
+ else {
+ if (wth->file_encap != rec->rec_header.packet_header.pkt_encap)
+ wth->file_encap = WTAP_ENCAP_PER_PACKET;
+ }
+
+ return TRUE;
+}
+
+static gboolean iptrace_seek_read_2_0(wtap *wth, gint64 seek_off,
+ wtap_rec *rec, Buffer *buf, int *err, gchar **err_info)
+{
+ if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
+ return FALSE;
+
+ /* Read the packet */
+ if (!iptrace_read_rec_2_0(wth, wth->random_fh, rec, buf, err,
+ err_info)) {
+ if (*err == 0)
+ *err = WTAP_ERR_SHORT_READ;
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static gboolean
+iptrace_read_rec_data(FILE_T fh, Buffer *buf, wtap_rec *rec,
+ int *err, gchar **err_info)
+{
+ if (!wtap_read_packet_bytes(fh, buf, rec->rec_header.packet_header.caplen, err, err_info))
+ return FALSE;
+
+ if (rec->rec_header.packet_header.pkt_encap == WTAP_ENCAP_ATM_PDUS) {
+ /*
+ * Attempt to guess from the packet data, the VPI,
+ * and the VCI information about the type of traffic.
+ */
+ atm_guess_traffic_type(rec, ws_buffer_start_ptr(buf));
+ }
+
+ return TRUE;
+}
+
+/*
+ * Fill in the pseudo-header information we can.
+ *
+ * For ATM traffic, "iptrace", alas, doesn't tell us what type of traffic
+ * is in the packet - it was presumably run on a machine that was one of
+ * the endpoints of the connection, so in theory it could presumably have
+ * told us, but, for whatever reason, it failed to do so - perhaps the
+ * low-level mechanism that feeds the presumably-AAL5 frames to us doesn't
+ * have access to that information (e.g., because it's in the ATM driver,
+ * and the ATM driver merely knows that stuff on VPI/VCI X.Y should be
+ * handed up to some particular client, it doesn't know what that client is).
+ *
+ * We let our caller try to figure out what kind of traffic it is, either
+ * by guessing based on the VPI/VCI, guessing based on the header of the
+ * packet, seeing earlier traffic that set up the circuit and specified
+ * in some fashion what sort of traffic it is, or being told by the user.
+ */
+static void
+fill_in_pseudo_header(int encap, union wtap_pseudo_header *pseudo_header,
+ const char *pkt_text)
+{
+ char if_text[9];
+ char *decimal;
+ int Vpi = 0;
+ int Vci = 0;
+
+ switch (encap) {
+
+ case WTAP_ENCAP_ATM_PDUS:
+ /* Rip apart the "x.y" text into Vpi/Vci numbers */
+ memcpy(if_text, &pkt_text[4], 8);
+ if_text[8] = '\0';
+ decimal = strchr(if_text, '.');
+ if (decimal) {
+ *decimal = '\0';
+ Vpi = (int)strtoul(if_text, NULL, 10);
+ decimal++;
+ Vci = (int)strtoul(decimal, NULL, 10);
+ }
+
+ /*
+ * OK, which value means "DTE->DCE" and which value means
+ * "DCE->DTE"?
+ */
+ pseudo_header->atm.channel = pkt_text[13];
+
+ pseudo_header->atm.vpi = Vpi;
+ pseudo_header->atm.vci = Vci;
+
+ /* We don't have this information */
+ pseudo_header->atm.flags = 0;
+ pseudo_header->atm.cells = 0;
+ pseudo_header->atm.aal5t_u2u = 0;
+ pseudo_header->atm.aal5t_len = 0;
+ pseudo_header->atm.aal5t_chksum = 0;
+ break;
+
+ case WTAP_ENCAP_ETHERNET:
+ /* We assume there's no FCS in this frame. */
+ pseudo_header->eth.fcs_len = 0;
+ break;
+ }
+}
+
+/* Given an RFC1573 (SNMP ifType) interface type,
+ * return the appropriate Wiretap Encapsulation Type.
+ */
+static int
+wtap_encap_ift(unsigned int ift)
+{
+
+ static const int ift_encap[] = {
+/* 0x0 */ WTAP_ENCAP_UNKNOWN, /* nothing */
+/* 0x1 */ WTAP_ENCAP_UNKNOWN, /* IFT_OTHER */
+/* 0x2 */ WTAP_ENCAP_UNKNOWN, /* IFT_1822 */
+/* 0x3 */ WTAP_ENCAP_UNKNOWN, /* IFT_HDH1822 */
+/* 0x4 */ WTAP_ENCAP_RAW_IP, /* IFT_X25DDN */
+/* 0x5 */ WTAP_ENCAP_UNKNOWN, /* IFT_X25 */
+/* 0x6 */ WTAP_ENCAP_ETHERNET, /* IFT_ETHER */
+/* 0x7 */ WTAP_ENCAP_ETHERNET, /* IFT_ISO88023 */
+/* 0x8 */ WTAP_ENCAP_UNKNOWN, /* IFT_ISO88024 */
+/* 0x9 */ WTAP_ENCAP_TOKEN_RING, /* IFT_ISO88025 */
+/* 0xa */ WTAP_ENCAP_UNKNOWN, /* IFT_ISO88026 */
+/* 0xb */ WTAP_ENCAP_UNKNOWN, /* IFT_STARLAN */
+/* 0xc */ WTAP_ENCAP_RAW_IP, /* IFT_P10, IBM SP switch */
+/* 0xd */ WTAP_ENCAP_UNKNOWN, /* IFT_P80 */
+/* 0xe */ WTAP_ENCAP_UNKNOWN, /* IFT_HY */
+/* 0xf */ WTAP_ENCAP_FDDI_BITSWAPPED, /* IFT_FDDI */
+/* 0x10 */ WTAP_ENCAP_LAPB, /* IFT_LAPB */ /* no data to back this up */
+/* 0x11 */ WTAP_ENCAP_UNKNOWN, /* IFT_SDLC */
+/* 0x12 */ WTAP_ENCAP_UNKNOWN, /* IFT_T1 */
+/* 0x13 */ WTAP_ENCAP_UNKNOWN, /* IFT_CEPT */
+/* 0x14 */ WTAP_ENCAP_UNKNOWN, /* IFT_ISDNBASIC */
+/* 0x15 */ WTAP_ENCAP_UNKNOWN, /* IFT_ISDNPRIMARY */
+/* 0x16 */ WTAP_ENCAP_UNKNOWN, /* IFT_PTPSERIAL */
+/* 0x17 */ WTAP_ENCAP_UNKNOWN, /* IFT_PPP */
+/* 0x18 */ WTAP_ENCAP_RAW_IP, /* IFT_LOOP */
+/* 0x19 */ WTAP_ENCAP_UNKNOWN, /* IFT_EON */
+/* 0x1a */ WTAP_ENCAP_UNKNOWN, /* IFT_XETHER */
+/* 0x1b */ WTAP_ENCAP_UNKNOWN, /* IFT_NSIP */
+/* 0x1c */ WTAP_ENCAP_UNKNOWN, /* IFT_SLIP */
+/* 0x1d */ WTAP_ENCAP_UNKNOWN, /* IFT_ULTRA */
+/* 0x1e */ WTAP_ENCAP_UNKNOWN, /* IFT_DS3 */
+/* 0x1f */ WTAP_ENCAP_UNKNOWN, /* IFT_SIP */
+/* 0x20 */ WTAP_ENCAP_UNKNOWN, /* IFT_FRELAY */
+/* 0x21 */ WTAP_ENCAP_UNKNOWN, /* IFT_RS232 */
+/* 0x22 */ WTAP_ENCAP_UNKNOWN, /* IFT_PARA */
+/* 0x23 */ WTAP_ENCAP_UNKNOWN, /* IFT_ARCNET */
+/* 0x24 */ WTAP_ENCAP_UNKNOWN, /* IFT_ARCNETPLUS */
+/* 0x25 */ WTAP_ENCAP_ATM_PDUS, /* IFT_ATM */
+ };
+ #define NUM_IFT_ENCAPS (sizeof ift_encap / sizeof ift_encap[0])
+
+ if (ift < NUM_IFT_ENCAPS) {
+ return ift_encap[ift];
+ }
+ else {
+ switch(ift) {
+ /* Infiniband*/
+ case IPTRACE_IFT_IB:
+ return WTAP_ENCAP_INFINIBAND;
+ break;
+
+ /* Host Fabric Interface */
+ case IPTRACE_IFT_HF:
+ /* The HFI interface on AIX provides raw IP
+ in the packet trace. It's unclear if the HFI
+ can be configured for any other protocol, and if
+ any field in the iptrace header indicates what
+ that protocol is. For now, we are hard-coding
+ this as RAW_IP, but if we find another iptrace file
+ using HFI that provides another protocol, we will
+ have to figure out which field in the iptrace file
+ encodes it. */
+ return WTAP_ENCAP_RAW_IP;
+ break;
+
+ default:
+ return WTAP_ENCAP_UNKNOWN;
+ }
+ }
+}
+
+/* Options for interface blocks. */
+static const struct supported_option_type interface_block_options_supported[] = {
+ /* No comments, just an interface name. */
+ { OPT_IDB_NAME, ONE_OPTION_SUPPORTED }
+};
+
+static const struct supported_block_type iptrace_1_0_blocks_supported[] = {
+ /*
+ * iptrace supports multiple interfaces, with descriptions, and
+ * supports associating packets with interfaces. Interface
+ * description blocks are used for that.
+ */
+ { WTAP_BLOCK_IF_ID_AND_INFO, MULTIPLE_BLOCKS_SUPPORTED, OPTION_TYPES_SUPPORTED(interface_block_options_supported) },
+
+ /*
+ * iptrace is a capture format, so it obviously supports packets.
+ * It supports no packet options, however.
+ */
+ { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
+};
+
+static const struct file_type_subtype_info iptrace_1_0_info = {
+ "AIX iptrace 1.0", "iptrace_1", NULL, NULL,
+ FALSE, BLOCKS_SUPPORTED(iptrace_1_0_blocks_supported),
+ NULL, NULL, NULL
+};
+
+static const struct supported_block_type iptrace_2_0_blocks_supported[] = {
+ /*
+ * iptrace supports multiple interfaces, with descriptions, and
+ * supports associating packets with interfaces. Interface
+ * description blocks are used for that.
+ */
+ { WTAP_BLOCK_IF_ID_AND_INFO, MULTIPLE_BLOCKS_SUPPORTED, OPTION_TYPES_SUPPORTED(interface_block_options_supported) },
+
+ /*
+ * iptrace is a capture format, so it obviously supports packets.
+ * It supports no packet options, however.
+ */
+ { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
+};
+
+static const struct file_type_subtype_info iptrace_2_0_info = {
+ "AIX iptrace 2.0", "iptrace_2", NULL, NULL,
+ FALSE, BLOCKS_SUPPORTED(iptrace_2_0_blocks_supported),
+ NULL, NULL, NULL
+};
+
+void register_iptrace(void)
+{
+ iptrace_1_0_file_type_subtype = wtap_register_file_type_subtype(&iptrace_1_0_info);
+ iptrace_2_0_file_type_subtype = wtap_register_file_type_subtype(&iptrace_2_0_info);
+
+ /*
+ * Register names for backwards compatibility with the
+ * wtap_filetypes table in Lua.
+ */
+ wtap_register_backwards_compatibility_lua_name("IPTRACE_1_0",
+ iptrace_1_0_file_type_subtype);
+ wtap_register_backwards_compatibility_lua_name("IPTRACE_2_0",
+ iptrace_2_0_file_type_subtype);
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/wiretap/iptrace.h b/wiretap/iptrace.h
new file mode 100644
index 00000000..1328ea33
--- /dev/null
+++ b/wiretap/iptrace.h
@@ -0,0 +1,18 @@
+/** @file
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ */
+
+#ifndef __IPTRACE_H__
+#define __IPTRACE_H__
+
+#include <glib.h>
+#include "wtap.h"
+
+wtap_open_return_val iptrace_open(wtap *wth, int *err, gchar **err_info);
+
+#endif
diff --git a/wiretap/iseries.c b/wiretap/iseries.c
new file mode 100644
index 00000000..180ed01a
--- /dev/null
+++ b/wiretap/iseries.c
@@ -0,0 +1,1108 @@
+/* iseries.c
+ *
+ * Wiretap Library
+ * Copyright (c) 2011 by Martin Warnes <Martin_Warnes@uk.ibm.com>
+ *
+ * Based on toshiba.c and vms.c
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+/*
+ * This module will read the contents of the iSeries (OS/400) Communication trace
+ * Both ASCII & Unicode (little-endian UCS-2) formatted traces are supported.
+ *
+ * iSeries Comms traces consist of a header page and a subsequent number of packet records
+ *
+ * The header page contains details on the options set during running of the trace,
+ * currently the following options are a requirement for this module:
+ *
+ * 1. Object protocol = ETHERNET (Default)
+ * 2. ASCII or Unicode file formats.
+ *
+ * The above can be achieved by passing option ASCII(*YES) with the trace command
+ *
+ */
+
+/* iSeries header page
+
+ COMMUNICATIONS TRACE Title: OS400 - OS400 trace 10/28/05 11:44:50 Page: 1
+ Trace Description . . . . . : OS400 - OS400 trace
+ Configuration object . . . . : ETH0
+ Type . . . . . . . . . . . . : 1 1=Line, 2=Network Interface
+ 3=Network server
+ Object protocol . . . . . . : ETHERNET
+ Start date/Time . . . . . . : 10/28/05 11:43:00.341
+ End date/Time . . . . . . . : 10/28/05 11:44:22.148
+ Bytes collected . . . . . . : 11999
+ Buffer size . . . . . . . . : 2048 kilobytes
+ Data direction . . . . . . . : 3 1=Sent, 2=Received, 3=Both
+ Stop on buffer full . . . . : Y Y=Yes, N=No
+ Number of bytes to trace
+ Beginning bytes . . . . . : *MAX Value, *CALC, *MAX
+ Ending bytes . . . . . . : *CALC Value, *CALC
+ Controller name . . . . . . : *ALL *ALL, name
+ Data representation . . . . : 1 1=ASCII, 2=EBCDIC, 3=*CALC
+ Format SNA data only . . . . : N Y=Yes, N=No
+ Format RR, RNR commands . . : N Y=Yes, N=No
+ Format TCP/IP data only . . : Y Y=Yes, N=No
+ IP address . . . . . . . . : *ALL *ALL, address
+ IP address . . . . . . . . : *ALL *ALL, address
+ IP port . . . . . . . . . : *ALL *ALL, IP port
+ Format UI data only . . . . : N Y=Yes, N=No
+ Select Ethernet data . . . . : 3 1=802.3, 2=ETHV2, 3=Both
+ Format Broadcast data . . . : Y Y=Yes, N=No
+*/
+
+/* iSeries IPv4 formatted packet records consist of a packet header line
+ * identifying the packet number, direction, size, timestamp,
+ * source/destination MAC addresses and packet type.
+ *
+ * Thereafter there will be a formatted display of the headers above
+ * the link layer, such as ARP, IP, TCP, UDP, and ICMP (all but
+ * ICMP have either been seen in captures or on pages such as the ones
+ * at
+ *
+ * http://www-912.ibm.com/s_dir/SLKBase.nsf/1ac66549a21402188625680b0002037e/e05fb0515bc3449686256ce600512c37?OpenDocument
+ *
+ * and
+ *
+ * http://publib.boulder.ibm.com/infocenter/javasdk/v5r0/index.jsp?topic=%2Fcom.ibm.java.doc.diagnostics.50%2Fdiag%2Fproblem_determination%2Fi5os_perf_io_commstrace.html
+ *
+ * so we cannot assume that "IP Header" or "TCP Header" will appear). The
+ * formatted display includes lines that show the contents of some of the
+ * fields in the header, as well as hex strings dumps of the headers
+ * themselves, with tags such as "IP Header :", "ARP Header :",
+ * "TCP Header :", "UDP Header :", and (presumably) "ICMP Header:".
+ *
+ * If the packet contains data this is displayed as 4 groups of 16 hex digits
+ * followed by an ASCII representation of the data line.
+ *
+ * Information from the packet header line, higher-level headers and, if
+ * available, data lines are extracted by the module for displaying.
+ *
+ *
+ Record Data Record Controller Destination Source Frame
+ Number S/R Length Timer Name MAC Address MAC Address Format
+ ------ --- ------ --------------- ---------- ------------ ------------ ------
+ 8 S 145 11:43:59.82956 0006299C14AE 0006299C14FE ETHV2 Type: 0800
+ Frame Type : IP DSCP: 0 ECN: 00-NECT Length: 145 Protocol: TCP Datagram ID: 388B
+ Src Addr: 10.20.144.150 Dest Addr: 10.20.144.151 Fragment Flags: DON'T,LAST
+ IP Header : 45000091388B40004006CC860A1490960A149097
+ IP Options : NONE
+ TCP . . . : Src Port: 6006,Unassigned Dest Port: 35366,Unassigned
+ SEQ Number: 2666470699 ('9EEF1D2B'X) ACK Number: 2142147535 ('7FAE93CF'X)
+ Code Bits: ACK PSH Window: 32648 TCP Option: NO OP
+ TCP Header : 17768A269EEF1D2B7FAE93CF80187F885B5600000101080A0517E0F805166DE0
+ Data . . . . . : 5443503200020010 0000004980000000 B800000080470103 01001E0000002000 *TCP2.......I*...*...*G........ .*
+ 002F010080000004 0300800700C00600 4002008000000304 00800000060FB067 *./..*.....*..*..@..*.....*....*G*
+ FC276228786B3EB0 EF34F5F1D27EF8DF 20926820E7B322AA 739F1FB20D **'B(XK>**4***.** *H **"*S*.*. *
+*/
+
+/* iSeries IPv6 formatted traces are similar to the IPv4 version above,
+ * except that the higher-level headers have "IPv6 Header:" and
+ * "ICMPv6 Hdr:", and data is no longer output in groups of 16 hex
+ * digits.
+ *
+
+Record Data Record Destination Source Frame
+Number S/R Length Timer MAC Address MAC Address Format
+------ --- ------ ------------ ------------ ------------ ------
+ 218 S 1488 15:01:14.389 0011BC358680 00096B6BD918 ETHV2 Type: 86DD
+ IPv6 Data: Ver: 06 Traffic Class: 00 Flow Label: 000000
+ Payload Length: 1448 Next Header: 06,TCP Hop Limit: 64
+ Src Addr: fd00:0:0:20f2::122
+ Dest Addr: fd00:0:0:20a0::155
+ IPv6 Header: 6000000005A80640FD000000000020F20000000000000122FD000000000020A0
+ 0000000000000155
+ TCP . . . : Src Port: 21246,Unassigned Dest Port: 13601,Unassigned
+ SEQ Number: 2282300877 ('880925CD'X) ACK Number: 3259003715 ('C2407343'X)
+ Code Bits: ACK Window: 65535 TCP Option: NO OP
+ TCP Header : 52FE3521880925CDC24073438010FFFFCFBB00000101080A0E15127000237A08
+ Data . . . . . : 54435032000200140000061880000000ECBEB867F0000000004CE640E6C1D9D5 *TCP2........*...***g*....L*@*****
+ C9D5C740E3C8C9E240C9E240E3C8C540E6C1D9D5C9D5C740C6C9C5D3C4404040 ****@****@**@***@*******@*****@@@*
+ 4040404040404040404040404040404040404040404040404040404040404040 *@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*
+*/
+
+/* iSeries unformatted packet record consist of the same header record as
+ * the formatted trace but all other records are simply unformatted data
+ * containing higher-level headers and packet data combined.
+ *
+ Record Data Record Controller Destination Source Frame Number Number Poll/
+ Number S/R Length Timer Name MAC Address MAC Address Format Command Sent Received Final DSAP SSAP
+ ------ --- ------ --------------- ---------- ------------ ------------ ------ ------- ------ -------- ----- ---- ----
+ 1 R 64 12:19:29.97108 000629ECF48E 0006D78E23C2 ETHV2 Type: 0800
+ Data . . . . . : 4500003C27954000 3A06CE3D9797440F 0A5964EAC4F50554 58C9915500000000 *E..<'*@.:.*=**D..YD***.TX**U....*
+ A00216D06A200000 020405B40402080A 1104B6C000000000 010303000B443BF1 **..*J .....*......**.........D;**
+*/
+
+#include "config.h"
+#include "wtap-int.h"
+#include "iseries.h"
+#include "file_wrappers.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <wsutil/str_util.h>
+#include <wsutil/strtoi.h>
+#include <wsutil/ws_assert.h>
+
+#define ISERIES_LINE_LENGTH 270
+#define ISERIES_HDR_LINES_TO_CHECK 100
+#define ISERIES_PKT_LINES_TO_CHECK 4
+#define ISERIES_MAX_TRACE_LEN 99999999
+#define ISERIES_FORMAT_ASCII 1
+#define ISERIES_FORMAT_UNICODE 2
+
+/*
+ * Magic strings - "COMMUNICATIONS TRACE", in ASCII and little-endian UCS-2.
+ */
+static const char iseries_hdr_magic_ascii[] = {
+ 'C', 'O', 'M', 'M',
+ 'U', 'N', 'I', 'C',
+ 'A', 'T', 'I', 'O',
+ 'N', 'S', ' ', 'T',
+ 'R', 'A', 'C', 'E'
+};
+static const char iseries_hdr_magic_le_ucs_2[] = {
+ 'C', 0x0, 'O', 0x0, 'M', 0x0, 'M', 0x0,
+ 'U', 0x0, 'N', 0x0, 'I', 0x0, 'C', 0x0,
+ 'A', 0x0, 'T', 0x0, 'I', 0x0, 'O', 0x0,
+ 'N', 0x0, 'S', 0x0, ' ', 0x0, 'T', 0x0,
+ 'R', 0x0, 'A', 0x0, 'C', 0x0, 'E', 0x0
+};
+
+typedef struct {
+ gboolean have_date; /* TRUE if we found a capture start date */
+ int year, month, day; /* The start date */
+ int format; /* Trace format type */
+} iseries_t;
+
+static gboolean iseries_read (wtap * wth, wtap_rec *rec, Buffer *buf,
+ int *err, gchar ** err_info, gint64 *data_offset);
+static gboolean iseries_seek_read (wtap * wth, gint64 seek_off,
+ wtap_rec *rec,
+ Buffer * buf, int *err, gchar ** err_info);
+static gboolean iseries_check_file_type (wtap * wth, int *err, gchar **err_info,
+ int format);
+static gint64 iseries_seek_next_packet (wtap * wth, int *err, gchar **err_info);
+static gboolean iseries_parse_packet (wtap * wth, FILE_T fh,
+ wtap_rec *rec,
+ Buffer * buf, int *err, gchar ** err_info);
+static int iseries_UNICODE_to_ASCII (guint8 * buf, guint bytes);
+static gboolean iseries_parse_hex_string (const char * ascii, guint8 * buf,
+ size_t len);
+
+static int iseries_file_type_subtype = -1;
+static int iseries_unicode_file_type_subtype = -1;
+
+void register_iseries(void);
+
+/*
+ * XXX - it would probably be cleaner to use a UCS-2 flavor of file_gets(),
+ * rather than file_gets(), if we're reading a UCS-2 file.
+ */
+wtap_open_return_val
+iseries_open (wtap * wth, int *err, gchar ** err_info)
+{
+ gint offset;
+ char magic[ISERIES_LINE_LENGTH];
+
+ /*
+ * Check that file starts with a valid iSeries COMMS TRACE header
+ * by scanning for it in the first line
+ */
+ if (!wtap_read_bytes (wth->fh, &magic, sizeof magic, err, err_info))
+ {
+ if (*err != WTAP_ERR_SHORT_READ)
+ return WTAP_OPEN_ERROR;
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ /*
+ * Check if this is a little-endian UCS-2 Unicode formatted file by scanning
+ * for the magic string
+ */
+ offset=0;
+ while ((unsigned int)offset < (ISERIES_LINE_LENGTH - (sizeof iseries_hdr_magic_le_ucs_2)))
+ {
+ if (memcmp (magic + offset, iseries_hdr_magic_le_ucs_2, sizeof iseries_hdr_magic_le_ucs_2) == 0) {
+ if (file_seek (wth->fh, 0, SEEK_SET, err) == -1)
+ {
+ return WTAP_OPEN_ERROR;
+ }
+ /*
+ * Do some basic sanity checking to ensure we can handle the
+ * contents of this trace
+ */
+ if (!iseries_check_file_type (wth, err, err_info, ISERIES_FORMAT_UNICODE))
+ {
+ if (*err == 0)
+ return WTAP_OPEN_NOT_MINE;
+ else
+ return WTAP_OPEN_ERROR;
+ }
+
+ wth->file_encap = WTAP_ENCAP_ETHERNET;
+ wth->file_type_subtype = iseries_unicode_file_type_subtype;
+ wth->snapshot_length = 0;
+ wth->subtype_read = iseries_read;
+ wth->subtype_seek_read = iseries_seek_read;
+ wth->file_tsprec = WTAP_TSPREC_USEC;
+
+ if (file_seek (wth->fh, 0, SEEK_SET, err) == -1)
+ {
+ return WTAP_OPEN_ERROR;
+ }
+
+ /*
+ * Add an IDB; we don't know how many interfaces were
+ * involved, so we just say one interface, about which
+ * we only know the link-layer type, snapshot length,
+ * and time stamp resolution.
+ */
+ wtap_add_generated_idb(wth);
+
+ return WTAP_OPEN_MINE;
+ }
+ offset += 1;
+ }
+
+ /*
+ * Check if this is a ASCII formatted file by scanning for the magic string
+ */
+ offset=0;
+ while ((unsigned int)offset < (ISERIES_LINE_LENGTH - sizeof iseries_hdr_magic_ascii))
+ {
+ if (memcmp (magic + offset, iseries_hdr_magic_ascii, sizeof iseries_hdr_magic_ascii) == 0)
+ {
+ if (file_seek (wth->fh, 0, SEEK_SET, err) == -1)
+ {
+ return WTAP_OPEN_ERROR;
+ }
+ /*
+ * Do some basic sanity checking to ensure we can handle the
+ * contents of this trace
+ */
+ if (!iseries_check_file_type (wth, err, err_info, ISERIES_FORMAT_ASCII))
+ {
+ if (*err == 0)
+ return WTAP_OPEN_NOT_MINE;
+ else
+ return WTAP_OPEN_ERROR;
+ }
+
+ wth->file_encap = WTAP_ENCAP_ETHERNET;
+ wth->file_type_subtype = iseries_file_type_subtype;
+ wth->snapshot_length = 0;
+ wth->subtype_read = iseries_read;
+ wth->subtype_seek_read = iseries_seek_read;
+ wth->file_tsprec = WTAP_TSPREC_USEC;
+
+ if (file_seek (wth->fh, 0, SEEK_SET, err) == -1)
+ {
+ return WTAP_OPEN_ERROR;
+ }
+
+ /*
+ * Add an IDB; we don't know how many interfaces were
+ * involved, so we just say one interface, about which
+ * we only know the link-layer type, snapshot length,
+ * and time stamp resolution.
+ */
+ wtap_add_generated_idb(wth);
+
+ return WTAP_OPEN_MINE;
+ }
+ offset += 1;
+ }
+
+ /* Neither ASCII or UNICODE so not supported */
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+/*
+ * Do some basic sanity checking to ensure we can handle the
+ * contents of this trace by checking the header page for
+ * requisite requirements and additional information.
+ */
+static gboolean
+iseries_check_file_type (wtap * wth, int *err, gchar **err_info, int format)
+{
+ gboolean is_iseries = FALSE;
+ guint line;
+ int num_items_scanned;
+ char buf[ISERIES_LINE_LENGTH], protocol[9];
+ iseries_t *iseries;
+
+ /* Save trace format for passing between packets */
+ iseries = g_new(iseries_t, 1);
+ iseries->have_date = FALSE;
+ iseries->format = format;
+
+ for (line = 0; line < ISERIES_HDR_LINES_TO_CHECK; line++)
+ {
+ memset(buf, 0x0, sizeof(buf));
+ if (file_gets (buf, ISERIES_LINE_LENGTH, wth->fh) == NULL)
+ {
+ /* EOF or error. */
+ *err = file_error (wth->fh, err_info);
+ if (*err == WTAP_ERR_SHORT_READ)
+ *err = 0;
+ break;
+ }
+
+ /*
+ * Check that we are dealing with an ETHERNET trace
+ */
+ if (iseries->format == ISERIES_FORMAT_UNICODE)
+ {
+ iseries_UNICODE_to_ASCII ((guint8 *)buf, ISERIES_LINE_LENGTH);
+ }
+ ascii_strup_inplace (buf);
+ num_items_scanned = sscanf (buf,
+ "%*[ \n\t]OBJECT PROTOCOL%*[ .:\n\t]%8s",
+ protocol);
+ if (num_items_scanned == 1)
+ {
+ if (memcmp (protocol, "ETHERNET", 8) == 0)
+ {
+ *err = 0;
+ is_iseries = TRUE;
+ }
+ }
+
+ /*
+ * The header is the only place where the date part of the timestamp is held, so
+ * extract it here and store for all packets to access
+ */
+ num_items_scanned = sscanf (buf,
+ "%*[ \n\t]START DATE/TIME%*[ .:\n\t]%2d/%2d/%2d",
+ &iseries->month, &iseries->day,
+ &iseries->year);
+ if (num_items_scanned == 3)
+ {
+ iseries->have_date = TRUE;
+ }
+ }
+
+ if (is_iseries)
+ wth->priv = (void *) iseries;
+ else
+ g_free(iseries);
+
+ return is_iseries;
+}
+
+/*
+ * Find the next packet and parse it; called from wtap_read().
+ */
+static gboolean
+iseries_read (wtap * wth, wtap_rec *rec, Buffer *buf, int *err,
+ gchar ** err_info, gint64 *data_offset)
+{
+ gint64 offset;
+
+ /*
+ * Locate the next packet
+ */
+ offset = iseries_seek_next_packet (wth, err, err_info);
+ if (offset < 0)
+ return FALSE;
+ *data_offset = offset;
+
+ /*
+ * Parse the packet and extract the various fields
+ */
+ return iseries_parse_packet (wth, wth->fh, rec, buf, err, err_info);
+}
+
+/*
+ * Seeks to the beginning of the next packet, and returns the
+ * byte offset. Returns -1 on failure or EOF; on EOF, sets
+ * *err to 0, and, on failure, sets *err to the error and *err_info
+ * to null or an additional error string.
+ */
+static gint64
+iseries_seek_next_packet (wtap * wth, int *err, gchar **err_info)
+{
+ iseries_t *iseries = (iseries_t *)wth->priv;
+ char buf[ISERIES_LINE_LENGTH],type[5];
+ int line, num_items_scanned;
+ gint64 cur_off;
+ long buflen;
+
+ for (line = 0; line < ISERIES_MAX_TRACE_LEN; line++)
+ {
+ if (file_gets (buf, ISERIES_LINE_LENGTH, wth->fh) == NULL)
+ {
+ /* EOF or error. */
+ *err = file_error (wth->fh, err_info);
+ return -1;
+ }
+ /* Convert UNICODE to ASCII if required and determine */
+ /* the number of bytes to rewind to beginning of record. */
+ if (iseries->format == ISERIES_FORMAT_UNICODE)
+ {
+ /* buflen is #bytes to 1st 0x0A */
+ buflen = iseries_UNICODE_to_ASCII ((guint8 *) buf, ISERIES_LINE_LENGTH);
+ }
+ else
+ {
+ /* Else buflen is just length of the ASCII string */
+ buflen = (long) strlen (buf);
+ }
+ ascii_strup_inplace (buf);
+ /* Check we have enough data in the line */
+ if (buflen < 78)
+ {
+ continue;
+ }
+ /* If packet header found return the offset */
+ num_items_scanned =
+ sscanf (buf+78,
+ "%*[ \n\t]ETHV2%*[ .:\n\t]TYPE%*[ .:\n\t]%4s",type);
+ if (num_items_scanned == 1)
+ {
+ /* Rewind to beginning of line */
+ cur_off = file_tell (wth->fh);
+ if (cur_off == -1)
+ {
+ *err = file_error (wth->fh, err_info);
+ return -1;
+ }
+ if (file_seek (wth->fh, cur_off - buflen, SEEK_SET, err) == -1)
+ {
+ return -1;
+ }
+ return cur_off - buflen;
+ }
+ }
+
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info =
+ ws_strdup_printf ("iseries: next packet header not found within %d lines",
+ ISERIES_MAX_TRACE_LEN);
+ return -1;
+}
+
+/*
+ * Read packets in random-access fashion
+ */
+static gboolean
+iseries_seek_read (wtap * wth, gint64 seek_off, wtap_rec *rec,
+ Buffer * buf, int *err, gchar ** err_info)
+{
+
+ /* seek to packet location */
+ if (file_seek (wth->random_fh, seek_off - 1, SEEK_SET, err) == -1)
+ return FALSE;
+
+ /*
+ * Parse the packet and extract the various fields
+ */
+ return iseries_parse_packet (wth, wth->random_fh, rec, buf,
+ err, err_info);
+}
+
+static int
+append_hex_digits(char *ascii_buf, int ascii_offset, int max_offset,
+ char *data, int *err, gchar **err_info)
+{
+ int in_offset, out_offset;
+ int c;
+ unsigned int i;
+ gboolean overflow = FALSE;
+
+ in_offset = 0;
+ out_offset = ascii_offset;
+ for (;;)
+ {
+ /*
+ * Process a block of up to 16 hex digits.
+ * The block is terminated early by an end-of-line indication (NUL,
+ * CR, or LF), by a space (which terminates the last block of the
+ * data we're processing), or by a "*", which introduces the ASCII representation
+ * of the data.
+ * All characters in the block must be upper-case hex digits;
+ * there might or might not be a space *after* a block, but, if so,
+ * that will be skipped over after the block is processed.
+ */
+ for (i = 0; i < 16; i++, in_offset++)
+ {
+ /*
+ * If we see an end-of-line indication, or an early-end-of-block
+ * indication (space), we're done. (Only the last block ends
+ * early.)
+ */
+ c = data[in_offset] & 0xFF;
+ if (c == '\0' || c == ' ' || c == '*' || c == '\r' || c == '\n')
+ {
+ goto done;
+ }
+ if (!g_ascii_isxdigit(c) || g_ascii_islower(c))
+ {
+ /*
+ * Not a hex digit, or a lower-case hex digit.
+ * Treat this as an indication that the line isn't a data
+ * line, so we just ignore it.
+ *
+ * XXX - do so only for continuation lines; treat non-hex-digit
+ * characters as errors for other lines?
+ */
+ return ascii_offset; /* pretend we appended nothing */
+ }
+ if (out_offset >= max_offset)
+ overflow = TRUE;
+ else
+ {
+ ascii_buf[out_offset] = c;
+ out_offset++;
+ }
+ }
+ /*
+ * Skip blanks, if any.
+ */
+ for (; (data[in_offset] & 0xFF) == ' '; in_offset++)
+ ;
+ }
+done:
+ /*
+ * If we processed an *odd* number of hex digits, report an error.
+ */
+ if ((i % 2) != 0)
+ {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("iseries: odd number of hex digits in a line");
+ return -1;
+ }
+ if (overflow)
+ {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("iseries: more packet data than the packet length indicated");
+ return -1;
+ }
+ return out_offset;
+}
+
+/* return the multiplier for nanoseconds */
+static guint32
+csec_multiplier(guint32 csec)
+{
+ if (csec < 10) return 100000000;
+ if (csec < 100) return 10000000;
+ if (csec < 1000) return 1000000;
+ if (csec < 10000) return 100000;
+ if (csec < 100000) return 10000;
+ if (csec < 1000000) return 1000;
+ if (csec < 10000000) return 100;
+ if (csec < 100000000) return 10;
+ return 1;
+}
+
+/* Parses a packet. */
+static gboolean
+iseries_parse_packet (wtap * wth, FILE_T fh, wtap_rec *rec,
+ Buffer *buf, int *err, gchar **err_info)
+{
+ iseries_t *iseries = (iseries_t *)wth->priv;
+ gint64 cur_off;
+ gboolean isValid, isCurrentPacket;
+ int num_items_scanned, line, pktline, buflen;
+ int pkt_len, pktnum, hr, min, sec;
+ char direction[2], destmac[13], srcmac[13], type[5];
+ guint32 csec;
+ char data[ISERIES_LINE_LENGTH * 2];
+ int offset;
+ char *ascii_buf;
+ int ascii_offset;
+ struct tm tm;
+
+ /*
+ * Check for packet headers in first 3 lines this should handle page breaks
+ * situations and the header lines output at each page throw and ensure we
+ * read both the captured and packet lengths.
+ */
+ isValid = FALSE;
+ for (line = 1; line < ISERIES_PKT_LINES_TO_CHECK; line++)
+ {
+ if (file_gets (data, ISERIES_LINE_LENGTH, fh) == NULL)
+ {
+ *err = file_error (fh, err_info);
+ return FALSE;
+ }
+ /* Convert UNICODE data to ASCII */
+ if (iseries->format == ISERIES_FORMAT_UNICODE)
+ {
+ iseries_UNICODE_to_ASCII ((guint8 *)data, ISERIES_LINE_LENGTH);
+ }
+ ascii_strup_inplace (data);
+ num_items_scanned =
+ sscanf (data,
+ "%*[ \n\t]%6d%*[ *\n\t]%1s%*[ \n\t]%6d%*[ \n\t]%2d:%2d:%2d.%9u%*[ \n\t]"
+ "%12s%*[ \n\t]%12s%*[ \n\t]ETHV2%*[ \n\t]TYPE:%*[ \n\t]%4s",
+ &pktnum, direction, &pkt_len, &hr, &min, &sec, &csec, destmac,
+ srcmac, type);
+ if (num_items_scanned == 10)
+ {
+ if (pktnum < 0)
+ {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup ("iseries: packet header has a negative packet number");
+ return FALSE;
+ }
+
+ if (pkt_len < 0)
+ {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup ("iseries: packet header has a negative packet length");
+ return FALSE;
+ }
+
+ if (hr < 0)
+ {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup ("iseries: packet header has a negative hour in the time stamp");
+ return FALSE;
+ }
+
+ if (hr > 23)
+ {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup ("iseries: packet header has a hour in the time stamp greater than 23");
+ return FALSE;
+ }
+
+ if (min < 0)
+ {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup ("iseries: packet header has a negative minute in the time stamp");
+ return FALSE;
+ }
+
+ if (min > 59)
+ {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup ("iseries: packet header has a minute in the time stamp greater than 59");
+ return FALSE;
+ }
+
+ if (sec < 0)
+ {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup ("iseries: packet header has a negative second in the time stamp");
+ return FALSE;
+ }
+
+ /*
+ * Yes, 60, even though the time-conversion routines on most OSes
+ * might not handle leap seconds.
+ */
+ if (sec > 60)
+ {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup ("iseries: packet header has a second in the time stamp greater than 60");
+ return FALSE;
+ }
+
+ if (strlen(destmac) != 12)
+ {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup ("iseries: packet header has a destination MAC address shorter than 6 bytes");
+ return FALSE;
+ }
+
+ if (strlen(srcmac) != 12)
+ {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup ("iseries: packet header has a source MAC address shorter than 6 bytes");
+ return FALSE;
+ }
+
+ if (strlen(type) != 4)
+ {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup ("iseries: packet header has an Ethernet type/length field than 2 bytes");
+ return FALSE;
+ }
+
+ /* OK! We found the packet header line */
+ isValid = TRUE;
+ /*
+ * XXX - The Capture length returned by the iSeries trace doesn't
+ * seem to include the Ethernet header, so we add its length here.
+ *
+ * Check the length first, just in case it's *so* big that, after
+ * adding the Ethernet header length, it overflows.
+ */
+ if ((guint)pkt_len > WTAP_MAX_PACKET_SIZE_STANDARD - 14)
+ {
+ /*
+ * Probably a corrupt capture file; don't blow up trying
+ * to allocate space for an immensely-large packet, and
+ * don't think it's a really *small* packet because it
+ * overflowed. (Calculate the size as a 64-bit value in
+ * the error message, to avoid an overflow.)
+ */
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("iseries: File has %" PRIu64 "-byte packet, bigger than maximum of %u",
+ (guint64)pkt_len + 14,
+ WTAP_MAX_PACKET_SIZE_STANDARD);
+ return FALSE;
+ }
+ pkt_len += 14;
+ break;
+ }
+ }
+
+ /*
+ * If no packet header found we exit at this point and inform the user.
+ */
+ if (!isValid)
+ {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup ("iseries: packet header isn't valid");
+ return FALSE;
+ }
+
+ rec->rec_type = REC_TYPE_PACKET;
+ rec->block = wtap_block_create(WTAP_BLOCK_PACKET);
+ rec->presence_flags = WTAP_HAS_CAP_LEN;
+
+ /*
+ * If we have Wiretap Header then populate it here
+ *
+ * Timer resolution on the iSeries is hardware dependent. We determine
+ * the resolution based on how many digits we see.
+ */
+ if (iseries->have_date)
+ {
+ rec->presence_flags |= WTAP_HAS_TS;
+ tm.tm_year = 100 + iseries->year;
+ tm.tm_mon = iseries->month - 1;
+ tm.tm_mday = iseries->day;
+ tm.tm_hour = hr;
+ tm.tm_min = min;
+ tm.tm_sec = sec;
+ tm.tm_isdst = -1;
+ rec->ts.secs = mktime (&tm);
+ rec->ts.nsecs = csec * csec_multiplier(csec);
+ }
+
+ rec->rec_header.packet_header.len = pkt_len;
+ rec->rec_header.packet_header.pkt_encap = WTAP_ENCAP_ETHERNET;
+ rec->rec_header.packet_header.pseudo_header.eth.fcs_len = -1;
+
+ /*
+ * Allocate a buffer big enough to hold the claimed packet length
+ * worth of byte values; each byte will be two hex digits, so the
+ * buffer's size should be twice the packet length.
+ *
+ * (There is no need to null-terminate the buffer.)
+ */
+ ascii_buf = (char *)g_malloc (pkt_len*2);
+ ascii_offset = 0;
+
+ /*
+ * Copy in the Ethernet header.
+ *
+ * The three fields have already been checked to have the right length
+ * (6 bytes, hence 12 characters, of hex-dump destination and source
+ * addresses, and 2 bytes, hence 4 characters, of hex-dump type/length).
+ *
+ * pkt_len is guaranteed to be >= 14, so 2*pkt_len is guaranteed to be
+ * >= 28, so we don't need to do any bounds checking.
+ */
+ memcpy(&ascii_buf[0], destmac, 12);
+ ascii_offset += 12;
+ memcpy(&ascii_buf[12], srcmac, 12);
+ ascii_offset += 12;
+ memcpy(&ascii_buf[24], type, 4);
+ ascii_offset += 4;
+
+ /*
+ * Start reading packet contents
+ */
+ isCurrentPacket = TRUE;
+
+ /* loop through packet lines and breakout when the next packet header is read */
+ pktline = 0;
+ while (isCurrentPacket)
+ {
+ pktline++;
+ /* Read the next line */
+ if (file_gets (data, ISERIES_LINE_LENGTH, fh) == NULL)
+ {
+ *err = file_error (fh, err_info);
+ if (*err == 0)
+ {
+ /* Hit the EOF without an error */
+ break;
+ }
+ goto errxit;
+ }
+
+ /* Convert UNICODE data to ASCII and determine line length */
+ if (iseries->format == ISERIES_FORMAT_UNICODE)
+ {
+ buflen = iseries_UNICODE_to_ASCII ((guint8 *)data, ISERIES_LINE_LENGTH);
+ }
+ else
+ {
+ /* Else bytes to rewind is just length of ASCII string */
+ buflen = (int) strlen (data);
+ }
+
+ /*
+ * Skip leading white space.
+ */
+ for (offset = 0; g_ascii_isspace(data[offset]); offset++)
+ ;
+
+ /*
+ * The higher-level header information starts at an offset of
+ * 22 characters. The header tags are 14 characters long.
+ *
+ * XXX - for IPv6, if the next header isn't the last header,
+ * the intermediate headers do *NOT* appear to be shown in
+ * the dump file *at all*, so the packet *cannot* be
+ * reconstructed!
+ */
+ if (offset == 22)
+ {
+ if (strncmp(data + 22, "IP Header : ", 14) == 0 ||
+ strncmp(data + 22, "IPv6 Header: ", 14) == 0 ||
+ strncmp(data + 22, "ARP Header : ", 14) == 0 ||
+ strncmp(data + 22, "TCP Header : ", 14) == 0 ||
+ strncmp(data + 22, "UDP Header : ", 14) == 0 ||
+ strncmp(data + 22, "ICMP Header: ", 14) == 0 ||
+ strncmp(data + 22, "ICMPv6 Hdr: ", 14) == 0 ||
+ strncmp(data + 22, "Option Hdr: ", 14) == 0)
+ {
+ ascii_offset = append_hex_digits(ascii_buf, ascii_offset,
+ pkt_len*2,
+ data + 22 + 14, err,
+ err_info);
+ if (ascii_offset == -1)
+ {
+ /* Bad line. */
+ return FALSE;
+ }
+ continue;
+ }
+ }
+
+ /*
+ * Is this a data line?
+ *
+ * The "Data" starts at an offset of 8.
+ */
+ if (offset == 9)
+ {
+ if (strncmp(data + 9, "Data . . . . . : ", 18) == 0)
+ {
+ ascii_offset = append_hex_digits(ascii_buf, ascii_offset,
+ pkt_len*2,
+ data + 9 + 18, err,
+ err_info);
+ if (ascii_offset == -1)
+ {
+ /* Bad line. */
+ return FALSE;
+ }
+ continue;
+ }
+ }
+
+ /*
+ * Is this a continuation of a previous header or data line?
+ * That's blanks followed by hex digits; first try the
+ * "no column separators" form.
+ *
+ * Continuations of header lines begin at an offset of 36;
+ * continuations of data lines begin at an offset of 27.
+ */
+ if (offset == 36 || offset == 27)
+ {
+ ascii_offset = append_hex_digits(ascii_buf, ascii_offset,
+ pkt_len*2,
+ data + offset, err,
+ err_info);
+ if (ascii_offset == -1)
+ {
+ /* Bad line. */
+ return FALSE;
+ }
+ continue;
+ }
+
+ /*
+ * If we see the identifier for the next packet then rewind and set
+ * isCurrentPacket FALSE
+ */
+ ascii_strup_inplace (data);
+ /* If packet header found return the offset */
+ num_items_scanned =
+ sscanf (data+78,
+ "%*[ \n\t]ETHV2%*[ .:\n\t]TYPE%*[ .:\n\t]%4s",type);
+ if ((num_items_scanned == 1) && pktline > 1)
+ {
+ isCurrentPacket = FALSE;
+ cur_off = file_tell( fh);
+ if (cur_off == -1)
+ {
+ /* Error. */
+ *err = file_error (fh, err_info);
+ goto errxit;
+ }
+ if (file_seek (fh, cur_off - buflen, SEEK_SET, err) == -1)
+ {
+ /* XXX: need to set err_info ?? */
+ goto errxit;
+ }
+ }
+ }
+
+ /*
+ * Make the captured length be the amount of bytes we've read (which
+ * is half the number of characters of hex dump we have).
+ *
+ * XXX - this can happen for IPv6 packets if the next header isn't the
+ * last header.
+ */
+ rec->rec_header.packet_header.caplen = ((guint32) ascii_offset)/2;
+
+ /* Make sure we have enough room for the packet. */
+ ws_buffer_assure_space (buf, rec->rec_header.packet_header.caplen);
+ /* Convert ascii data to binary and return in the frame buffer */
+ iseries_parse_hex_string (ascii_buf, ws_buffer_start_ptr (buf), ascii_offset);
+
+ /* free buffer allocs and return */
+ *err = 0;
+ g_free (ascii_buf);
+ return TRUE;
+
+errxit:
+ g_free (ascii_buf);
+ return FALSE;
+}
+
+/*
+ * Simple routine to convert an UNICODE buffer to ASCII
+ *
+ * XXX - This may be possible with iconv or similar
+ */
+static int
+iseries_UNICODE_to_ASCII (guint8 * buf, guint bytes)
+{
+ guint i;
+ guint8 *bufptr;
+
+ bufptr = buf;
+
+ for (i = 0; i < bytes; i++)
+ {
+ switch (buf[i])
+ {
+ case 0xFE:
+ case 0xFF:
+ case 0x00:
+ break;
+ default:
+ *bufptr = buf[i];
+ bufptr++;
+ }
+ if (buf[i] == 0x0A)
+ break;
+ }
+ ws_assert(bufptr < buf + bytes);
+ *bufptr = '\0';
+ return i;
+}
+
+/*
+ * Simple routine to convert an ASCII hex string to binary data
+ * Requires ASCII hex data and buffer to populate with binary data
+ */
+static gboolean
+iseries_parse_hex_string (const char * ascii, guint8 * buf, size_t len)
+{
+ size_t i;
+ int byte;
+ gint hexvalue;
+ guint8 bytevalue;
+
+ byte = 0;
+ for (i = 0; i < len; i++)
+ {
+ hexvalue = g_ascii_xdigit_value(ascii[i]);
+ i++;
+ if (hexvalue == -1)
+ return FALSE; /* not a valid hex digit */
+ bytevalue = (guint8)(hexvalue << 4);
+ if (i >= len)
+ return FALSE; /* only one hex digit of the byte is present */
+ hexvalue = g_ascii_xdigit_value(ascii[i]);
+ if (hexvalue == -1)
+ return FALSE; /* not a valid hex digit */
+ bytevalue |= (guint8) hexvalue;
+ buf[byte] = bytevalue;
+ byte++;
+ }
+ return TRUE;
+}
+
+static const struct supported_block_type iseries_blocks_supported[] = {
+ /*
+ * We support packet blocks, with no comments or other options.
+ */
+ { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
+};
+
+static const struct file_type_subtype_info iseries_info = {
+ "IBM iSeries comm. trace (ASCII)", "iseries_ascii", "txt", NULL,
+ FALSE, BLOCKS_SUPPORTED(iseries_blocks_supported),
+ NULL, NULL, NULL
+};
+
+static const struct supported_block_type iseries_unicode_blocks_supported[] = {
+ /*
+ * We support packet blocks, with no comments or other options.
+ */
+ { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
+};
+
+static const struct file_type_subtype_info iseries_unicode_info = {
+ "IBM iSeries comm. trace (Unicode)", "iseries_unicode", "txt", NULL,
+ FALSE, BLOCKS_SUPPORTED(iseries_unicode_blocks_supported),
+ NULL, NULL, NULL
+};
+
+void register_iseries(void)
+{
+ iseries_file_type_subtype = wtap_register_file_type_subtype(&iseries_info);
+ iseries_unicode_file_type_subtype = wtap_register_file_type_subtype(&iseries_unicode_info);
+
+ /*
+ * Register names for backwards compatibility with the
+ * wtap_filetypes table in Lua.
+ */
+ wtap_register_backwards_compatibility_lua_name("ISERIES",
+ iseries_file_type_subtype);
+ wtap_register_backwards_compatibility_lua_name("ISERIES_UNICODE",
+ iseries_unicode_file_type_subtype);
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 2
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * vi: set shiftwidth=2 tabstop=8 expandtab:
+ * :indentSize=2:tabSize=8:noTabs=true:
+ */
diff --git a/wiretap/iseries.h b/wiretap/iseries.h
new file mode 100644
index 00000000..a75e4905
--- /dev/null
+++ b/wiretap/iseries.h
@@ -0,0 +1,17 @@
+/** @file
+ *
+ * Wiretap Library
+ * Copyright (c) 2005 by Martin Warnes <martin@warnes.homeip.net>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __W_ISERIES_H__
+#define __W_ISERIES_H__
+
+#include <glib.h>
+#include "wtap.h"
+
+wtap_open_return_val iseries_open(wtap *wth, int *err, gchar **err_info);
+
+#endif
diff --git a/wiretap/json.c b/wiretap/json.c
new file mode 100644
index 00000000..e2213e95
--- /dev/null
+++ b/wiretap/json.c
@@ -0,0 +1,108 @@
+/* json.c
+ *
+ * Copyright 2015, Dario Lombardo <lomato@gmail.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include "wtap-int.h"
+#include "file_wrappers.h"
+
+#include "json.h"
+#include <wsutil/wsjson.h>
+
+/* Maximum size of json file. */
+#define MAX_FILE_SIZE (50*1024*1024)
+
+static int json_file_type_subtype = -1;
+
+void register_json(void);
+
+wtap_open_return_val json_open(wtap *wth, int *err, gchar **err_info)
+{
+ guint8* filebuf;
+ int bytes_read;
+
+ /* XXX checking the full file contents might be a bit expensive, maybe
+ * resort to simpler heuristics like '{' or '[' (with some other chars)? */
+ filebuf = (guint8*)g_malloc0(MAX_FILE_SIZE);
+ if (!filebuf)
+ return WTAP_OPEN_ERROR;
+
+ bytes_read = file_read(filebuf, MAX_FILE_SIZE, wth->fh);
+ if (bytes_read < 0) {
+ /* Read error. */
+ *err = file_error(wth->fh, err_info);
+ g_free(filebuf);
+ return WTAP_OPEN_ERROR;
+ }
+ if (bytes_read == 0) {
+ /* empty file, not *anybody's* */
+ g_free(filebuf);
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ if (json_validate(filebuf, bytes_read) == FALSE) {
+ g_free(filebuf);
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ if (file_seek(wth->fh, 0, SEEK_SET, err) == -1) {
+ g_free(filebuf);
+ return WTAP_OPEN_ERROR;
+ }
+
+ wth->file_type_subtype = json_file_type_subtype;
+ wth->file_encap = WTAP_ENCAP_JSON;
+ wth->file_tsprec = WTAP_TSPREC_SEC;
+ wth->subtype_read = wtap_full_file_read;
+ wth->subtype_seek_read = wtap_full_file_seek_read;
+ wth->snapshot_length = 0;
+
+ g_free(filebuf);
+ return WTAP_OPEN_MINE;
+}
+
+static const struct supported_block_type json_blocks_supported[] = {
+ /*
+ * This is a file format that we dissect, so we provide only one
+ * "packet" with the file's contents, and don't support any
+ * options.
+ */
+ { WTAP_BLOCK_PACKET, ONE_BLOCK_SUPPORTED, NO_OPTIONS_SUPPORTED }
+};
+
+static const struct file_type_subtype_info json_info = {
+ "JavaScript Object Notation", "json", "json", NULL,
+ FALSE, BLOCKS_SUPPORTED(json_blocks_supported),
+ NULL, NULL, NULL
+};
+
+void register_json(void)
+{
+ json_file_type_subtype = wtap_register_file_type_subtype(&json_info);
+
+ /*
+ * Register name for backwards compatibility with the
+ * wtap_filetypes table in Lua.
+ */
+ wtap_register_backwards_compatibility_lua_name("JSON",
+ json_file_type_subtype);
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * vi: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */
diff --git a/wiretap/json.h b/wiretap/json.h
new file mode 100644
index 00000000..cbca4fcf
--- /dev/null
+++ b/wiretap/json.h
@@ -0,0 +1,31 @@
+/** @file
+ *
+ * Copyright 2015, Dario Lombardo <lomato@gmail.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ */
+
+#ifndef __JSON_H__
+#define __JSON_H__
+
+#include <glib.h>
+
+#include "wtap.h"
+
+wtap_open_return_val json_open(wtap *wth, int *err, gchar **err_info);
+
+#endif
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * vi: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */
diff --git a/wiretap/k12.c b/wiretap/k12.c
new file mode 100644
index 00000000..f03e859d
--- /dev/null
+++ b/wiretap/k12.c
@@ -0,0 +1,1440 @@
+/*
+ * k12.c
+ *
+ * routines for importing tektronix k12xx *.rf5 files
+ *
+ * Copyright (c) 2005, Luis E. Garia Ontanon <luis@ontanon.org>
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "config.h"
+#include <stdlib.h>
+#include <string.h>
+
+#include "wtap-int.h"
+#include "file_wrappers.h"
+#include "k12.h"
+
+#include <wsutil/str_util.h>
+#include <wsutil/glib-compat.h>
+
+/*
+ * See
+ *
+ * https://www.tek.com/manual/record-file-api-programmer-manual
+ *
+ * for some information about the file format. You may have to fill in
+ * a form to download the document ("Record File API Programmer Manual").
+ *
+ * Unfortunately, it describes an API that delivers records from an rf5
+ * file, not the raw format of an rf5 file, so, while it gives the formats
+ * of the records with various types, it does not indicate how those records
+ * are stored in the file.
+ */
+
+static int k12_file_type_subtype = -1;
+
+void register_k12(void);
+
+/* #define DEBUG_K12 */
+#ifdef DEBUG_K12
+#include <stdio.h>
+#include <stdarg.h>
+#include <wsutil/file_util.h>
+
+FILE* dbg_out = NULL;
+char* env_file = NULL;
+
+static unsigned int debug_level = 0;
+
+void k12_fprintf(const char* fmt, ...) {
+ va_list ap;
+
+ va_start(ap,fmt);
+ vfprintf(dbg_out, fmt, ap);
+ va_end(ap);
+}
+
+#define CAT(a,b) a##b
+#define K12_DBG(level,args) do { if (level <= debug_level) { \
+ fprintf(dbg_out,"%s:%d: ",CAT(__FI,LE__),CAT(__LI,NE__)); \
+ k12_fprintf args ; \
+ fprintf(dbg_out,"\n"); \
+} } while(0)
+
+void k12_hex_ascii_dump(guint level, gint64 offset, const char* label, const unsigned char* b, unsigned int len) {
+ static const char* c2t[] = {
+ "00","01","02","03","04","05","06","07","08","09","0a","0b","0c","0d","0e","0f",
+ "10","11","12","13","14","15","16","17","18","19","1a","1b","1c","1d","1e","1f",
+ "20","21","22","23","24","25","26","27","28","29","2a","2b","2c","2d","2e","2f",
+ "30","31","32","33","34","35","36","37","38","39","3a","3b","3c","3d","3e","3f",
+ "40","41","42","43","44","45","46","47","48","49","4a","4b","4c","4d","4e","4f",
+ "50","51","52","53","54","55","56","57","58","59","5a","5b","5c","5d","5e","5f",
+ "60","61","62","63","64","65","66","67","68","69","6a","6b","6c","6d","6e","6f",
+ "70","71","72","73","74","75","76","77","78","79","7a","7b","7c","7d","7e","7f",
+ "80","81","82","83","84","85","86","87","88","89","8a","8b","8c","8d","8e","8f",
+ "90","91","92","93","94","95","96","97","98","99","9a","9b","9c","9d","9e","9f",
+ "a0","a1","a2","a3","a4","a5","a6","a7","a8","a9","aa","ab","ac","ad","ae","af",
+ "b0","b1","b2","b3","b4","b5","b6","b7","b8","b9","ba","bb","bc","bd","be","bf",
+ "c0","c1","c2","c3","c4","c5","c6","c7","c8","c9","ca","cb","cc","cd","ce","cf",
+ "d0","d1","d2","d3","d4","d5","d6","d7","d8","d9","da","db","dc","dd","de","df",
+ "e0","e1","e2","e3","e4","e5","e6","e7","e8","e9","ea","eb","ec","ed","ee","ef",
+ "f0","f1","f2","f3","f4","f5","f6","f7","f8","f9","fa","fb","fc","fd","fe","ff"
+ };
+ unsigned int i, j;
+
+ if (debug_level < level) return;
+
+ fprintf(dbg_out,"%s(%.8" PRIx64 ",%.4x):\n",label,offset,len);
+
+ for (i=0 ; i<len ; i += 16) {
+ for (j=0; j<16; j++) {
+ if ((j%4)==0)
+ fprintf(dbg_out," ");
+ if ((i+j)<len)
+ fprintf(dbg_out, "%s", c2t[b[i+j]]);
+ else
+ fprintf(dbg_out, " ");
+ }
+ fprintf(dbg_out, " ");
+ for (j=0; j<16; j++) {
+ if ((i+j)<len)
+ fprintf(dbg_out, "%c", g_ascii_isprint(b[i+j]) ? b[i+j] : '.');
+ }
+ fprintf(dbg_out,"\n");
+ }
+}
+
+#define K12_HEX_ASCII_DUMP(x,a,b,c,d) k12_hex_ascii_dump(x,a,b,c,d)
+
+void k12_ascii_dump(guint level, guint8 *buf, guint32 len, guint32 buf_offset) {
+ guint32 i;
+
+ if (debug_level < level) return;
+
+ for (i = buf_offset; i < len; i++) {
+ if (g_ascii_isprint(buf[i]) || buf[i] == '\n' || buf[i] == '\t')
+ putc(buf[i], dbg_out);
+ else if (buf[i] == '\0')
+ fprintf(dbg_out, "(NUL)\n");
+ }
+}
+
+#define K12_ASCII_DUMP(x,a,b,c) k12_ascii_dump(x,a,b,c)
+
+#else
+#define K12_DBG(level,args) (void)0
+#define K12_HEX_ASCII_DUMP(x,a,b,c,d)
+#define K12_ASCII_DUMP(x,a,b,c)
+#endif
+
+
+
+/*
+ * A 32-bit .rf5 file begins with a 512-byte file header, containing:
+ *
+ * a 32-bit big-endian file header length, in bytes - always 512 in
+ * the files we've seen;
+ *
+ * 4 unknown bytes, always 0x12 0x05 0x00 0x10;
+ *
+ * a 32-bit big-endian file length, giving the total length of the file,
+ * in bytes;
+ *
+ * a 32-bit big-endian number giving the "page size" of the file, in
+ * bytes, which is normally 8192;
+ *
+ * 20 unknown bytes;
+ *
+ * a 32-bit count of the number of records in the file;
+ *
+ * 4 unknown bytes;
+ *
+ * a 32-bit count of the number of records in the file;
+ *
+ * 464 unknown bytes;
+ *
+ * followed by a sequence of records containing:
+ *
+ * a 32-bit big-endian record length;
+ *
+ * a 32-bit big-endian record type;
+ *
+ * a 32-bit big-endian frame length;
+ *
+ * a 32-bit big-endian source ID.
+ *
+ * Every 8192 bytes, starting immediately after the 512-byte header,
+ * there's a 16-byte blob; it's not part of the record data.
+ * There's no obvious pattern to the data; it might be junk left
+ * in memory as the file was being written.
+ *
+ * There's a 16-bit terminator FFFF at the end.
+ *
+ * Older versions of the Wireshark .rf5 writing code incorrectly wrote
+ * the header - they put 512 in the file length field (counting only the
+ * header), put a count of records into the "page size" field, and wrote
+ * out zeroes in the rest of the header. We detect those files by
+ * checking whether the rest of the header is zero.
+ */
+
+/*
+ * We use the first 8 bytes of the file header as a magic number.
+ */
+static const guint8 k12_file_magic[] = { 0x00, 0x00, 0x02, 0x00 ,0x12, 0x05, 0x00, 0x10 };
+
+#define K12_FILE_HDR_LEN 512
+
+/*
+ * Offsets in the file header.
+ */
+#define K12_FILE_HDR_MAGIC_NUMBER 0x00
+#define K12_FILE_HDR_FILE_SIZE 0x08
+#define K12_FILE_HDR_PAGE_SIZE 0x0C
+#define K12_FILE_HDR_RECORD_COUNT_1 0x24
+#define K12_FILE_HDR_RECORD_COUNT_2 0x2C
+
+#define K12_FILE_BLOB_LEN 16
+
+typedef struct {
+ guint32 file_len;
+ guint32 num_of_records; /* XXX: not sure about this */
+
+ GHashTable* src_by_id; /* k12_srcdsc_recs by input */
+ GHashTable* src_by_name; /* k12_srcdsc_recs by stack_name */
+
+ guint8 *seq_read_buff; /* read buffer for sequential reading */
+ guint seq_read_buff_len; /* length of that buffer */
+ guint8 *rand_read_buff; /* read buffer for random reading */
+ guint rand_read_buff_len; /* length of that buffer */
+
+ Buffer extra_info; /* Buffer to hold per packet extra information */
+} k12_t;
+
+typedef struct _k12_src_desc_t {
+ guint32 input;
+ guint32 input_type;
+ gchar* input_name;
+ gchar* stack_file;
+ k12_input_info_t input_info;
+} k12_src_desc_t;
+
+
+/*
+ * According to the Tektronix documentation, this value is a combination of
+ * a "group" code and a "type" code, with both being 2-byte values and
+ * with the "group" code followe by the "type" code. The "group" values
+ * are:
+ *
+ * 0x0001 - "data event"
+ * 0x0002 - "text or L1 event"
+ * 0x0007 - "configuration event"
+ *
+ * and the "type" values are:
+ *
+ * data events:
+ * 0x0020 - "frame" (i.e., "an actual packet")
+ * 0x0021 - "transparent frame"
+ * 0x0022 - "bit data (TRAU frame)"
+ * 0x0024 - "used to mark the frame which is a fragment"
+ * 0x0026 - "used to mark the frame which is a fragment"
+ * 0x0028 - "used to mark the frame which is generated by the LSA"
+ * 0x002A - "used to mark the frame which is generated by the LSA"
+ *
+ * text or L1 events:
+ * 0x0030 - "text event"
+ * 0x0031 - "L1 event"
+ * 0x0032 - "L1 event (BAI)"
+ * 0x0033 - "L1 event (VX)"
+ *
+ * configuration events:
+ * 0x0040 - Logical Data Source configuration event
+ * 0x0041 - Logical Link configuration event
+ */
+/* so far we've seen these types of records */
+#define K12_REC_PACKET 0x00010020 /* an actual packet */
+#define K12_REC_D0020 0x000d0020 /* an actual packet, seen in a k18 file */
+#define K12_REC_SCENARIO 0x00070040 /* what appears as the window's title */
+#define K12_REC_SRCDSC 0x00070041 /* port-stack mapping + more, the key of the whole thing */
+#define K12_REC_STK_FILE 0x00070042 /* a dump of an stk file */
+#define K12_REC_SRCDSC2 0x00070043 /* another port-stack mapping */
+#define K12_REC_TEXT 0x00070044 /* a string containing something with a grammar (conditions/responses?) */
+#define K12_REC_START 0x00020030 /* a string containing human readable start time */
+#define K12_REC_STOP 0x00020031 /* a string containing human readable stop time */
+
+/*
+ * According to the Tektronix documentation, packets, i.e. "data events",
+ * have several different group/type values, which differ in the last
+ * nibble of the type code. For now, we just mask that nibble off; the
+ * format of the items are different, so we might have to treat different
+ * data event types differently.
+ */
+#define K12_MASK_PACKET 0xfffffff0
+
+/* offsets of elements in the records */
+#define K12_RECORD_LEN 0x0 /* uint32, in bytes */
+#define K12_RECORD_TYPE 0x4 /* uint32, see above */
+#define K12_RECORD_FRAME_LEN 0x8 /* uint32, in bytes */
+#define K12_RECORD_SRC_ID 0xc /* uint32 */
+
+/*
+ * Some records from K15 files have a port ID of an undeclared
+ * interface which happens to be the only one with the first byte changed.
+ * It is still unknown how to recognize when this happens.
+ * If the lookup of the interface record fails we'll mask it
+ * and retry.
+ */
+#define K12_RECORD_SRC_ID_MASK 0x00ffffff
+
+/* elements of packet records */
+#define K12_PACKET_TIMESTAMP 0x18 /* int64 (8b) representing 1/2us since 01-01-1990 Z00:00:00 */
+
+#define K12_PACKET_FRAME 0x20 /* start of the actual frame in the record */
+#define K12_PACKET_FRAME_D0020 0x34 /* start of the actual frame in the record */
+
+#define K12_PACKET_OFFSET_VP 0x08 /* 2 bytes, big endian */
+#define K12_PACKET_OFFSET_VC 0x0a /* 2 bytes, big endian */
+#define K12_PACKET_OFFSET_CID 0x0c /* 1 byte */
+
+/* elements of the source description records */
+#define K12_SRCDESC_COLOR_FOREGROUND 0x12 /* 1 byte */
+#define K12_SRCDESC_COLOR_BACKGROUND 0x13 /* 1 byte */
+
+#define K12_SRCDESC_PORT_TYPE 0x1a /* 1 byte */
+#define K12_SRCDESC_HWPARTLEN 0x1e /* uint16, big endian */
+#define K12_SRCDESC_NAMELEN 0x20 /* uint16, big endian */
+#define K12_SRCDESC_STACKLEN 0x22 /* uint16, big endian */
+
+/* Hardware part of the record */
+#define K12_SRCDESC_HWPART 0x24 /* offset of the hardware part */
+
+/* Offsets relative to the beginning of the hardware part */
+#define K12_SRCDESC_HWPARTTYPE 0 /* uint32, big endian */
+
+#define K12_SRCDESC_DS0_MASK 24 /* variable-length */
+
+#define K12_SRCDESC_ATM_VPI 20 /* uint16, big endian */
+#define K12_SRCDESC_ATM_VCI 22 /* uint16, big endian */
+#define K12_SRCDESC_ATM_AAL 24 /* 1 byte */
+
+/*
+ * A "stack file", as appears in a K12_REC_STK_FILE record, is a text
+ * file (with CR-LF line endings) with a sequence of lines, each of
+ * which begins with a keyword, and has white-space-separated tokens
+ * after that.
+ *
+ * They appear to be:
+ *
+ * STKVER, which is followed by a number (presumably a version number
+ * for the stack file format)
+ *
+ * STACK, which is followed by a quoted string ("ProtocolStack" in one
+ * file) and two numbers
+ *
+ * PATH, which is followed by a non-quoted string giving the pathname
+ * of the directory containing the stack file
+ *
+ * HLAYER, which is followed by a quoted string, a path for something
+ * (protocol module?), a keyword ("LOADED", in one file), and a
+ * quoted string giving a description - this is probably a protocol
+ * layer of some sort
+ *
+ * LAYER, which has a similar syntax to HLAYER - the first quoted
+ * string is a protocol name
+ *
+ * RELATION, which has a quoted string giving a protocol name,
+ * another quoted string giving a protocol name, and a condition
+ * specifier of some sort, which probably says the second protocol
+ * is layered atop the first protocol if the condition is true.
+ * The first protocol can also be "BASE", which means that the
+ * second protocol is the lowest-level protocol.
+ * The conditions are:
+ *
+ * CPLX, which may mean "complex" - it has parenthesized expressions
+ * including "&", presumably a boolean AND, with the individual
+ * tests being L:expr, where L is a letter such as "L", "D", or "P",
+ * and expr is:
+ *
+ * 0x........ for L, where each . is a hex digit or a ?, presumably
+ * meaning "don't care"
+ *
+ * 0;0{=,!=}0b........ for D, where . is presumably a bit or a ?
+ *
+ * param=value for P, where param is something such as "src_port"
+ * and value is a value, presumably to test, for example, TCP or
+ * UDP ports
+ *
+ * UNCOND, presumably meaning "always"
+ *
+ * PARAM, followed by a parameter name (as with P:) and a value,
+ * possibly followed by LAYPARAM and a hex value
+ *
+ * DECKRNL, followed by a quoted string protocol name, un-quoted
+ * "LSBF" or "MSBF" (Least/Most Significant Byte First?), and
+ * an un-quoted string ending with _DK
+ *
+ * LAYPARAM, followed by a quoted protocol name and a number (-2147221504
+ * in one file, which is 0x80040000)
+ *
+ * SPC_CONF, folloed by a number, a quoted string with numbers separated
+ * by hyphens, and another number
+ *
+ * CIC_CONF, with a similar syntax to SPC_CONF
+ *
+ * LAYPOS, followed by a protocol name or "BASE" and 3 numbers.
+ *
+ * Most of this is probably not useful, but the RELATION lines with
+ * "BASE" could be used to figure out how to start the dissection
+ * (if we knew what "L" and "D" did), and *some* of the others might
+ * be useful if they don't match what's already in various dissector
+ * tables (the ones for IP and a higher-level protocol, for example,
+ * aren't very useful, as those are standardized, but the ones for
+ * TCP, UDP, and SCTP ports, and SCTP PPIs, might be useful).
+ */
+
+/*
+ * get_record: Get the next record into a buffer
+ * Every 8192 bytes 16 bytes are inserted in the file,
+ * even in the middle of a record.
+ * This reads the next record without the eventual 16 bytes.
+ * returns the length of the record + the stuffing (if any)
+ *
+ * Returns number of bytes read on success, 0 on EOF, -1 on error;
+ * if -1 is returned, *err is set to the error indication and, for
+ * errors where that's appropriate, *err_info is set to an additional
+ * error string.
+ *
+ * XXX: works at most with 8191 bytes per record
+ */
+static gint get_record(k12_t *file_data, FILE_T fh, gint64 file_offset,
+ gboolean is_random, int *err, gchar **err_info) {
+ guint8 *buffer = is_random ? file_data->rand_read_buff : file_data->seq_read_buff;
+ guint buffer_len = is_random ? file_data->rand_read_buff_len : file_data->seq_read_buff_len;
+ guint total_read = 0;
+ guint left;
+ guint8* writep;
+#ifdef DEBUG_K12
+ guint actual_len;
+#endif
+
+ /*
+ * Where the next unknown 16 bytes are stuffed to the file.
+ * Following the file header, they appear every 8192 bytes,
+ * starting right after the file header, so if the file offset
+ * relative to the file header is a multiple of 8192, the
+ * 16-byte blob is there.
+ */
+ guint junky_offset = 8192 - (gint) ( (file_offset - K12_FILE_HDR_LEN) % 8192 );
+
+ K12_DBG(6,("get_record: ENTER: junky_offset=%" PRId64 ", file_offset=%" PRId64,junky_offset,file_offset));
+
+ /* no buffer is given, lets create it */
+ if (buffer == NULL) {
+ buffer = (guint8*)g_malloc(8192);
+ buffer_len = 8192;
+ if (is_random) {
+ file_data->rand_read_buff = buffer;
+ file_data->rand_read_buff_len = buffer_len;
+ } else {
+ file_data->seq_read_buff = buffer;
+ file_data->seq_read_buff_len = buffer_len;
+ }
+ }
+
+ if ( junky_offset == 8192 ) {
+ /*
+ * We're at the beginning of one of the 16-byte blobs,
+ * so we first need to skip the blob.
+ *
+ * XXX - what if the blob is in the middle of the record
+ * length? If the record length is always a multiple of
+ * 4 bytes, that won't happen.
+ */
+ if ( ! wtap_read_bytes( fh, NULL, K12_FILE_BLOB_LEN, err, err_info ) )
+ return -1;
+ total_read += K12_FILE_BLOB_LEN;
+ }
+
+ /*
+ * Read the record length.
+ */
+ if ( !wtap_read_bytes( fh, buffer, 4, err, err_info ) )
+ return -1;
+ total_read += 4;
+
+ left = pntoh32(buffer + K12_RECORD_LEN);
+#ifdef DEBUG_K12
+ actual_len = left;
+#endif
+ junky_offset -= 4;
+
+ K12_DBG(5,("get_record: GET length=%u",left));
+
+ /*
+ * Record length must be at least large enough for the length
+ * and type, hence 8 bytes.
+ *
+ * XXX - is WTAP_MAX_PACKET_SIZE_STANDARD the right check for a maximum
+ * record size? Should we report this error differently?
+ */
+ if (left < 8) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("k12: Record length %u is less than 8 bytes long",left);
+ return -1;
+ }
+ if (left > WTAP_MAX_PACKET_SIZE_STANDARD) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("k12: Record length %u is greater than the maximum %u",left,WTAP_MAX_PACKET_SIZE_STANDARD);
+ return -1;
+ }
+
+ /*
+ * XXX - calculate the lowest power of 2 >= left, rather than just
+ * looping.
+ */
+ while (left > buffer_len) {
+ buffer = (guint8*)g_realloc(buffer,buffer_len*=2);
+ if (is_random) {
+ file_data->rand_read_buff = buffer;
+ file_data->rand_read_buff_len = buffer_len;
+ } else {
+ file_data->seq_read_buff = buffer;
+ file_data->seq_read_buff_len = buffer_len;
+ }
+ }
+
+ writep = buffer + 4;
+ left -= 4;
+
+ /* Read the rest of the record. */
+ do {
+ K12_DBG(6,("get_record: looping left=%d junky_offset=%" PRId64,left,junky_offset));
+
+ if (junky_offset > left) {
+ /*
+ * The next 16-byte blob is past the end of this record.
+ * Just read the rest of the record.
+ */
+ if ( !wtap_read_bytes( fh, writep, left, err, err_info ) )
+ return -1;
+ total_read += left;
+ break;
+ } else {
+ /*
+ * The next 16-byte blob is part of this record.
+ * Read up to the blob.
+ */
+ if ( !wtap_read_bytes( fh, writep, junky_offset, err, err_info ) )
+ return -1;
+
+ total_read += junky_offset;
+ writep += junky_offset;
+
+ /*
+ * Skip the blob.
+ */
+ if ( !wtap_read_bytes( fh, NULL, K12_FILE_BLOB_LEN, err, err_info ) )
+ return -1;
+ total_read += K12_FILE_BLOB_LEN;
+
+ left -= junky_offset;
+ junky_offset = 8192;
+ }
+
+ } while(left);
+
+ K12_HEX_ASCII_DUMP(5,file_offset, "GOT record", buffer, actual_len);
+ return total_read;
+}
+
+static gboolean
+memiszero(const void *ptr, size_t count)
+{
+ const guint8 *p = (const guint8 *)ptr;
+
+ while (count != 0) {
+ if (*p != 0)
+ return FALSE;
+ p++;
+ count--;
+ }
+ return TRUE;
+}
+
+static gboolean
+process_packet_data(wtap_rec *rec, Buffer *target, guint8 *buffer,
+ guint record_len, k12_t *k12, int *err, gchar **err_info)
+{
+ guint32 type;
+ guint buffer_offset;
+ guint64 ts;
+ guint32 length;
+ guint32 extra_len;
+ guint32 src_id;
+ k12_src_desc_t* src_desc;
+
+ type = pntoh32(buffer + K12_RECORD_TYPE);
+ buffer_offset = (type == K12_REC_D0020) ? K12_PACKET_FRAME_D0020 : K12_PACKET_FRAME;
+ if (buffer_offset > record_len) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("k12: Frame data offset %u > record length %u",
+ buffer_offset, record_len);
+ return FALSE;
+ }
+
+ length = pntoh32(buffer + K12_RECORD_FRAME_LEN) & 0x00001FFF;
+ if (length > record_len - buffer_offset) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("k12: Frame length %u > record frame data %u",
+ length, record_len - buffer_offset);
+ return FALSE;
+ }
+
+ rec->rec_type = REC_TYPE_PACKET;
+ rec->block = wtap_block_create(WTAP_BLOCK_PACKET);
+ rec->presence_flags = WTAP_HAS_TS;
+
+ ts = pntoh64(buffer + K12_PACKET_TIMESTAMP);
+
+ rec->ts.secs = (time_t) ((ts / 2000000) + 631152000);
+ rec->ts.nsecs = (guint32) ( (ts % 2000000) * 500 );
+
+ rec->rec_header.packet_header.len = rec->rec_header.packet_header.caplen = length;
+
+ ws_buffer_assure_space(target, length);
+ memcpy(ws_buffer_start_ptr(target), buffer + buffer_offset, length);
+
+ /* extra information need by some protocols */
+ extra_len = record_len - buffer_offset - length;
+ ws_buffer_assure_space(&(k12->extra_info), extra_len);
+ memcpy(ws_buffer_start_ptr(&(k12->extra_info)),
+ buffer + buffer_offset + length, extra_len);
+ rec->rec_header.packet_header.pseudo_header.k12.extra_info = (guint8*)ws_buffer_start_ptr(&(k12->extra_info));
+ rec->rec_header.packet_header.pseudo_header.k12.extra_length = extra_len;
+
+ src_id = pntoh32(buffer + K12_RECORD_SRC_ID);
+ K12_DBG(5,("process_packet_data: src_id=%.8x",src_id));
+ rec->rec_header.packet_header.pseudo_header.k12.input = src_id;
+
+ if ( ! (src_desc = (k12_src_desc_t*)g_hash_table_lookup(k12->src_by_id,GUINT_TO_POINTER(src_id))) ) {
+ /*
+ * Some records from K15 files have a port ID of an undeclared
+ * interface which happens to be the only one with the first byte changed.
+ * It is still unknown how to recognize when this happens.
+ * If the lookup of the interface record fails we'll mask it
+ * and retry.
+ */
+ src_desc = (k12_src_desc_t*)g_hash_table_lookup(k12->src_by_id,GUINT_TO_POINTER(src_id&K12_RECORD_SRC_ID_MASK));
+ }
+
+ if (src_desc) {
+ K12_DBG(5,("process_packet_data: input_name='%s' stack_file='%s' type=%x",src_desc->input_name,src_desc->stack_file,src_desc->input_type));
+ rec->rec_header.packet_header.pseudo_header.k12.input_name = src_desc->input_name;
+ rec->rec_header.packet_header.pseudo_header.k12.stack_file = src_desc->stack_file;
+ rec->rec_header.packet_header.pseudo_header.k12.input_type = src_desc->input_type;
+
+ switch(src_desc->input_type) {
+ case K12_PORT_ATMPVC:
+ if (buffer_offset + length + K12_PACKET_OFFSET_CID < record_len) {
+ rec->rec_header.packet_header.pseudo_header.k12.input_info.atm.vp = pntoh16(buffer + buffer_offset + length + K12_PACKET_OFFSET_VP);
+ rec->rec_header.packet_header.pseudo_header.k12.input_info.atm.vc = pntoh16(buffer + buffer_offset + length + K12_PACKET_OFFSET_VC);
+ rec->rec_header.packet_header.pseudo_header.k12.input_info.atm.cid = *((unsigned char*)(buffer + buffer_offset + length + K12_PACKET_OFFSET_CID));
+ break;
+ }
+ /* Fall through */
+ default:
+ memcpy(&(rec->rec_header.packet_header.pseudo_header.k12.input_info),&(src_desc->input_info),sizeof(src_desc->input_info));
+ break;
+ }
+ } else {
+ K12_DBG(5,("process_packet_data: NO SRC_RECORD FOUND"));
+
+ memset(&(rec->rec_header.packet_header.pseudo_header.k12),0,sizeof(rec->rec_header.packet_header.pseudo_header.k12));
+ rec->rec_header.packet_header.pseudo_header.k12.input_name = "unknown port";
+ rec->rec_header.packet_header.pseudo_header.k12.stack_file = "unknown stack file";
+ }
+
+ rec->rec_header.packet_header.pseudo_header.k12.input = src_id;
+ rec->rec_header.packet_header.pseudo_header.k12.stuff = k12;
+ return TRUE;
+}
+
+static gboolean k12_read(wtap *wth, wtap_rec *rec, Buffer *buf, int *err, gchar **err_info, gint64 *data_offset) {
+ k12_t *k12 = (k12_t *)wth->priv;
+ k12_src_desc_t* src_desc;
+ guint8* buffer;
+ gint64 offset;
+ gint len;
+ guint32 type;
+ guint32 src_id;
+
+ offset = file_tell(wth->fh);
+
+ /* ignore the record if it isn't a packet */
+ do {
+ if ( k12->num_of_records == 0 ) {
+ /* No more records */
+ *err = 0;
+ return FALSE;
+ }
+
+ K12_DBG(5,("k12_read: offset=%i",offset));
+
+ *data_offset = offset;
+
+ len = get_record(k12, wth->fh, offset, FALSE, err, err_info);
+
+ if (len < 0) {
+ /* read error */
+ return FALSE;
+ } else if (len == 0) {
+ /* EOF */
+ *err = WTAP_ERR_SHORT_READ;
+ return FALSE;
+ } else if (len < K12_RECORD_SRC_ID + 4) {
+ /* Record not large enough to contain a src ID */
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("k12: Data record length %d too short", len);
+ return FALSE;
+ }
+ k12->num_of_records--;
+
+ buffer = k12->seq_read_buff;
+
+ type = pntoh32(buffer + K12_RECORD_TYPE);
+ src_id = pntoh32(buffer + K12_RECORD_SRC_ID);
+
+
+ if ( ! (src_desc = (k12_src_desc_t*)g_hash_table_lookup(k12->src_by_id,GUINT_TO_POINTER(src_id))) ) {
+ /*
+ * Some records from K15 files have a port ID of an undeclared
+ * interface which happens to be the only one with the first byte changed.
+ * It is still unknown how to recognize when this happens.
+ * If the lookup of the interface record fails we'll mask it
+ * and retry.
+ */
+ src_desc = (k12_src_desc_t*)g_hash_table_lookup(k12->src_by_id,GUINT_TO_POINTER(src_id&K12_RECORD_SRC_ID_MASK));
+ }
+
+ K12_DBG(5,("k12_read: record type=%x src_id=%x",type,src_id));
+
+ offset += len;
+
+ } while ( ((type & K12_MASK_PACKET) != K12_REC_PACKET && (type & K12_MASK_PACKET) != K12_REC_D0020) || !src_id || !src_desc );
+
+ return process_packet_data(rec, buf, buffer, (guint)len, k12, err, err_info);
+}
+
+
+static gboolean k12_seek_read(wtap *wth, gint64 seek_off, wtap_rec *rec, Buffer *buf, int *err, gchar **err_info) {
+ k12_t *k12 = (k12_t *)wth->priv;
+ guint8* buffer;
+ gint len;
+ gboolean status;
+
+ K12_DBG(5,("k12_seek_read: ENTER"));
+
+ if ( file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1) {
+ K12_DBG(5,("k12_seek_read: SEEK ERROR"));
+ return FALSE;
+ }
+
+ len = get_record(k12, wth->random_fh, seek_off, TRUE, err, err_info);
+ if (len < 0) {
+ K12_DBG(5,("k12_seek_read: READ ERROR"));
+ return FALSE;
+ } else if (len < K12_RECORD_SRC_ID + 4) {
+ /* Record not large enough to contain a src ID */
+ K12_DBG(5,("k12_seek_read: SHORT READ"));
+ *err = WTAP_ERR_SHORT_READ;
+ return FALSE;
+ }
+
+ buffer = k12->rand_read_buff;
+
+ status = process_packet_data(rec, buf, buffer, (guint)len, k12, err, err_info);
+
+ K12_DBG(5,("k12_seek_read: DONE OK"));
+
+ return status;
+}
+
+
+static k12_t* new_k12_file_data(void) {
+ k12_t* fd = g_new(k12_t,1);
+
+ fd->file_len = 0;
+ fd->num_of_records = 0;
+ fd->src_by_name = g_hash_table_new(g_str_hash,g_str_equal);
+ fd->src_by_id = g_hash_table_new(g_direct_hash,g_direct_equal);
+ fd->seq_read_buff = NULL;
+ fd->seq_read_buff_len = 0;
+ fd->rand_read_buff = NULL;
+ fd->rand_read_buff_len = 0;
+
+ ws_buffer_init(&(fd->extra_info), 100);
+
+ return fd;
+}
+
+static gboolean destroy_srcdsc(gpointer k _U_, gpointer v, gpointer p _U_) {
+ k12_src_desc_t* rec = (k12_src_desc_t*)v;
+
+ g_free(rec->input_name);
+ g_free(rec->stack_file);
+ g_free(rec);
+
+ return TRUE;
+}
+
+static void destroy_k12_file_data(k12_t* fd) {
+ g_hash_table_destroy(fd->src_by_id);
+ g_hash_table_foreach_remove(fd->src_by_name,destroy_srcdsc,NULL);
+ g_hash_table_destroy(fd->src_by_name);
+ ws_buffer_free(&(fd->extra_info));
+ g_free(fd->seq_read_buff);
+ g_free(fd->rand_read_buff);
+ g_free(fd);
+}
+
+static void k12_close(wtap *wth) {
+ k12_t *k12 = (k12_t *)wth->priv;
+
+ destroy_k12_file_data(k12);
+ wth->priv = NULL; /* destroy_k12_file_data freed it */
+#ifdef DEBUG_K12
+ K12_DBG(5,("k12_close: CLOSED"));
+ if (env_file) fclose(dbg_out);
+#endif
+}
+
+
+wtap_open_return_val k12_open(wtap *wth, int *err, gchar **err_info) {
+ k12_src_desc_t* rec;
+ guint8 header_buffer[K12_FILE_HDR_LEN];
+ guint8* read_buffer;
+ guint32 type;
+ long offset;
+ long len;
+ guint port_type;
+ guint32 rec_len;
+ guint32 hwpart_len;
+ guint32 name_len;
+ guint32 stack_len;
+ guint i;
+ k12_t* file_data;
+
+#ifdef DEBUG_K12
+ gchar* env_level = getenv("K12_DEBUG_LEVEL");
+ env_file = getenv("K12_DEBUG_FILENAME");
+ if ( env_file ) {
+ dbg_out = ws_fopen(env_file,"w");
+ if (dbg_out == NULL) {
+ dbg_out = stderr;
+ K12_DBG(1,("unable to open K12 DEBUG FILENAME for writing! Logging to standard error"));
+ }
+ }
+ else
+ dbg_out = stderr;
+ if ( env_level ) debug_level = (unsigned int)strtoul(env_level,NULL,10);
+ K12_DBG(1,("k12_open: ENTER debug_level=%u",debug_level));
+#endif
+
+ if ( !wtap_read_bytes(wth->fh,header_buffer,K12_FILE_HDR_LEN,err,err_info) ) {
+ K12_DBG(1,("k12_open: FILE HEADER TOO SHORT OR READ ERROR"));
+ if (*err != WTAP_ERR_SHORT_READ) {
+ return WTAP_OPEN_ERROR;
+ }
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ if ( memcmp(header_buffer,k12_file_magic,8) != 0 ) {
+ K12_DBG(1,("k12_open: BAD MAGIC"));
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ offset = K12_FILE_HDR_LEN;
+
+ file_data = new_k12_file_data();
+
+ file_data->file_len = pntoh32( header_buffer + 0x8);
+ if (memiszero(header_buffer + 0x10, K12_FILE_HDR_LEN - 0x10)) {
+ /*
+ * The rest of the file header is all zeroes. That means
+ * this is a file written by the old Wireshark code, and
+ * a count of records in the file is at an offset of 0x0C.
+ */
+ file_data->num_of_records = pntoh32( header_buffer + 0x0C );
+ } else {
+ /*
+ * There's at least one non-zero byte in the rest of the
+ * header. The value 8192 is at 0xC (page size?), and
+ * what appears to be the number of records in the file
+ * is at an offset of 0x24 and at an offset of 0x2c.
+ *
+ * If the two values are not the same, we fail; if that's
+ * the case, we need to see the file to figure out which
+ * of those two values, if any, is the count.
+ */
+ file_data->num_of_records = pntoh32( header_buffer + K12_FILE_HDR_RECORD_COUNT_1 );
+ if ( file_data->num_of_records != pntoh32( header_buffer + K12_FILE_HDR_RECORD_COUNT_2 ) ) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("k12: two different record counts, %u at 0x%02x and %u at 0x%02x",
+ file_data->num_of_records,
+ K12_FILE_HDR_RECORD_COUNT_1,
+ pntoh32( header_buffer + K12_FILE_HDR_RECORD_COUNT_2 ),
+ K12_FILE_HDR_RECORD_COUNT_2 );
+ destroy_k12_file_data(file_data);
+ return WTAP_OPEN_ERROR;
+ }
+ }
+
+ K12_DBG(5,("k12_open: FILE_HEADER OK: offset=%x file_len=%i records=%i",
+ offset,
+ file_data->file_len,
+ file_data->num_of_records ));
+
+ do {
+ if ( file_data->num_of_records == 0 ) {
+ *err = WTAP_ERR_SHORT_READ;
+ destroy_k12_file_data(file_data);
+ return WTAP_OPEN_ERROR;
+ }
+
+ len = get_record(file_data, wth->fh, offset, FALSE, err, err_info);
+
+ if ( len < 0 ) {
+ K12_DBG(1,("k12_open: BAD HEADER RECORD",len));
+ destroy_k12_file_data(file_data);
+ return WTAP_OPEN_ERROR;
+ }
+ if ( len == 0 ) {
+ K12_DBG(1,("k12_open: BAD HEADER RECORD",len));
+ *err = WTAP_ERR_SHORT_READ;
+ destroy_k12_file_data(file_data);
+ return WTAP_OPEN_ERROR;
+ }
+
+ read_buffer = file_data->seq_read_buff;
+
+ rec_len = pntoh32( read_buffer + K12_RECORD_LEN );
+ if (rec_len < K12_RECORD_TYPE + 4) {
+ /* Record isn't long enough to have a type field */
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("k12: record length %u < %u",
+ rec_len, K12_RECORD_TYPE + 4);
+ destroy_k12_file_data(file_data);
+ return WTAP_OPEN_ERROR;
+ }
+ type = pntoh32( read_buffer + K12_RECORD_TYPE );
+
+ if ( (type & K12_MASK_PACKET) == K12_REC_PACKET ||
+ (type & K12_MASK_PACKET) == K12_REC_D0020) {
+ /*
+ * we are at the first packet record, rewind and leave.
+ */
+ if (file_seek(wth->fh, offset, SEEK_SET, err) == -1) {
+ destroy_k12_file_data(file_data);
+ return WTAP_OPEN_ERROR;
+ }
+ K12_DBG(5,("k12_open: FIRST PACKET offset=%x",offset));
+ break;
+ }
+
+ switch (type) {
+
+ case K12_REC_SRCDSC:
+ case K12_REC_SRCDSC2:
+ rec = g_new0(k12_src_desc_t,1);
+
+ if (rec_len < K12_SRCDESC_HWPART) {
+ /*
+ * Record isn't long enough to have the fixed-length portion
+ * of the source descriptor field.
+ */
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("k12: source descriptor record length %u < %u",
+ rec_len, K12_SRCDESC_HWPART);
+ destroy_k12_file_data(file_data);
+ g_free(rec);
+ return WTAP_OPEN_ERROR;
+ }
+ port_type = read_buffer[K12_SRCDESC_PORT_TYPE];
+ hwpart_len = pntoh16( read_buffer + K12_SRCDESC_HWPARTLEN );
+ name_len = pntoh16( read_buffer + K12_SRCDESC_NAMELEN );
+ stack_len = pntoh16( read_buffer + K12_SRCDESC_STACKLEN );
+
+ rec->input = pntoh32( read_buffer + K12_RECORD_SRC_ID );
+
+ K12_DBG(5,("k12_open: INTERFACE RECORD offset=%x interface=%x",offset,rec->input));
+
+ if (name_len == 0) {
+ K12_DBG(5,("k12_open: failed (name_len == 0 in source description"));
+ destroy_k12_file_data(file_data);
+ g_free(rec);
+ return WTAP_OPEN_NOT_MINE;
+ }
+ if (stack_len == 0) {
+ K12_DBG(5,("k12_open: failed (stack_len == 0 in source description"));
+ destroy_k12_file_data(file_data);
+ g_free(rec);
+ return WTAP_OPEN_NOT_MINE;
+ }
+ if (rec_len < K12_SRCDESC_HWPART + hwpart_len + name_len + stack_len) {
+ /*
+ * Record isn't long enough to have the full source descriptor
+ * field, including the variable-length parts.
+ */
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("k12: source descriptor record length %u < %u (%u + %u + %u + %u)",
+ rec_len,
+ K12_SRCDESC_HWPART + hwpart_len + name_len + stack_len,
+ K12_SRCDESC_HWPART, hwpart_len, name_len, stack_len);
+ destroy_k12_file_data(file_data);
+ g_free(rec);
+ return WTAP_OPEN_ERROR;
+ }
+
+ if (hwpart_len) {
+ if (hwpart_len < 4) {
+ /* Hardware part isn't long enough to have a type field */
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("k12: source descriptor hardware part length %u < 4",
+ hwpart_len);
+ destroy_k12_file_data(file_data);
+ g_free(rec);
+ return WTAP_OPEN_ERROR;
+ }
+ switch(( rec->input_type = pntoh32( read_buffer + K12_SRCDESC_HWPART + K12_SRCDESC_HWPARTTYPE ) )) {
+ case K12_PORT_DS0S:
+ /* This appears to be variable-length */
+ rec->input_info.ds0mask = 0x00000000;
+ if (hwpart_len > K12_SRCDESC_DS0_MASK) {
+ for (i = 0; i < hwpart_len - K12_SRCDESC_DS0_MASK; i++) {
+ rec->input_info.ds0mask |= ( *(read_buffer + K12_SRCDESC_HWPART + K12_SRCDESC_DS0_MASK + i) == 0xff ) ? 1U<<(31-i) : 0x0;
+ }
+ }
+ break;
+ case K12_PORT_ATMPVC:
+ if (hwpart_len < K12_SRCDESC_ATM_VCI + 2) {
+ /* Hardware part isn't long enough to have ATM information */
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("k12: source descriptor hardware part length %u < %u",
+ hwpart_len,
+ K12_SRCDESC_ATM_VCI + 2);
+ destroy_k12_file_data(file_data);
+ g_free(rec);
+ return WTAP_OPEN_ERROR;
+ }
+
+ rec->input_info.atm.vp = pntoh16( read_buffer + K12_SRCDESC_HWPART + K12_SRCDESC_ATM_VPI );
+ rec->input_info.atm.vc = pntoh16( read_buffer + K12_SRCDESC_HWPART + K12_SRCDESC_ATM_VCI );
+ break;
+ default:
+ break;
+ }
+ } else {
+ /* Record viewer generated files don't have this information */
+ if (port_type >= 0x14
+ && port_type <= 0x17) {
+ /* For ATM2_E1DS1, ATM2_E3DS3,
+ ATM2_STM1EL and ATM2_STM1OP */
+ rec->input_type = K12_PORT_ATMPVC;
+ rec->input_info.atm.vp = 0;
+ rec->input_info.atm.vc = 0;
+ }
+ }
+
+ if (read_buffer[K12_SRCDESC_HWPART + hwpart_len + name_len - 1] != '\0') {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("k12_open: source descriptor record contains non-null-terminated link-layer name");
+ destroy_k12_file_data(file_data);
+ g_free(rec);
+ return WTAP_OPEN_ERROR;
+ }
+ if (read_buffer[K12_SRCDESC_HWPART + hwpart_len + name_len + stack_len - 1] != '\0') {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("k12_open: source descriptor record contains non-null-terminated stack path");
+ destroy_k12_file_data(file_data);
+ g_free(rec);
+ return WTAP_OPEN_ERROR;
+ }
+ rec->input_name = (gchar *)g_memdup2(read_buffer + K12_SRCDESC_HWPART + hwpart_len, name_len);
+ rec->stack_file = (gchar *)g_memdup2(read_buffer + K12_SRCDESC_HWPART + hwpart_len + name_len, stack_len);
+
+ ascii_strdown_inplace (rec->stack_file);
+
+ g_hash_table_insert(file_data->src_by_id,GUINT_TO_POINTER(rec->input),rec);
+ g_hash_table_insert(file_data->src_by_name,rec->stack_file,rec);
+ break;
+
+ case K12_REC_STK_FILE:
+ K12_DBG(1,("k12_open: K12_REC_STK_FILE"));
+ K12_DBG(1,("Field 1: 0x%08x",pntoh32( read_buffer + 0x08 )));
+ K12_DBG(1,("Field 2: 0x%08x",pntoh32( read_buffer + 0x0c )));
+ K12_ASCII_DUMP(1, read_buffer, rec_len, 16);
+ break;
+
+ default:
+ K12_DBG(1,("k12_open: RECORD TYPE 0x%08x",type));
+ break;
+ }
+ offset += len;
+ file_data->num_of_records--;
+ } while(1);
+
+ wth->file_type_subtype = k12_file_type_subtype;
+ wth->file_encap = WTAP_ENCAP_K12;
+ wth->snapshot_length = 0;
+ wth->subtype_read = k12_read;
+ wth->subtype_seek_read = k12_seek_read;
+ wth->subtype_close = k12_close;
+ wth->priv = (void *)file_data;
+ wth->file_tsprec = WTAP_TSPREC_NSEC;
+
+ /*
+ * Add an IDB; we don't know how many interfaces were
+ * involved, so we just say one interface, about which
+ * we only know the link-layer type, snapshot length,
+ * and time stamp resolution.
+ */
+ wtap_add_generated_idb(wth);
+
+ return WTAP_OPEN_MINE;
+}
+
+typedef struct {
+ guint32 file_len;
+ guint32 num_of_records;
+ guint32 file_offset;
+} k12_dump_t;
+
+static int k12_dump_can_write_encap(int encap) {
+
+ if (encap == WTAP_ENCAP_PER_PACKET)
+ return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
+
+ if (encap != WTAP_ENCAP_K12)
+ return WTAP_ERR_UNWRITABLE_ENCAP;
+
+ return 0;
+}
+
+static const gchar dumpy_junk[] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };
+
+static gboolean k12_dump_record(wtap_dumper *wdh, guint32 len, guint8* buffer, int *err_p) {
+ k12_dump_t *k12 = (k12_dump_t *)wdh->priv;
+ guint32 junky_offset = (8192 - ( (k12->file_offset - K12_FILE_HDR_LEN) % 8192 )) % 8192;
+
+ if (len > junky_offset) {
+ if (junky_offset) {
+ if (! wtap_dump_file_write(wdh, buffer, junky_offset, err_p))
+ return FALSE;
+ }
+ if (! wtap_dump_file_write(wdh, dumpy_junk, K12_FILE_BLOB_LEN, err_p))
+ return FALSE;
+
+ if (! wtap_dump_file_write(wdh, buffer+junky_offset, len - junky_offset, err_p))
+ return FALSE;
+
+ k12->file_offset += len + K12_FILE_BLOB_LEN;
+ k12->file_len += len + K12_FILE_BLOB_LEN;
+ } else {
+ if (! wtap_dump_file_write(wdh, buffer, len, err_p))
+ return FALSE;
+ k12->file_offset += len;
+ k12->file_len += len;
+ }
+
+ k12->num_of_records++;
+ return TRUE;
+}
+
+static void k12_dump_src_setting(gpointer k _U_, gpointer v, gpointer p) {
+ k12_src_desc_t* src_desc = (k12_src_desc_t*)v;
+ wtap_dumper *wdh = (wtap_dumper *)p;
+ guint32 len;
+ guint offset;
+ guint i;
+ int errxxx; /* dummy */
+
+ union {
+ guint8 buffer[8192];
+
+ struct {
+ guint32 len;
+ guint32 type;
+ guint32 unk32_1;
+ guint32 input;
+
+ guint16 unk32_2;
+ guint16 color;
+ guint32 unk32_3;
+ guint32 unk32_4;
+ guint16 unk16_1;
+ guint16 hwpart_len;
+
+ guint16 name_len;
+ guint16 stack_len;
+
+ struct {
+ guint32 type;
+
+ union {
+ struct {
+ guint32 unk32;
+ guint8 mask[32];
+ } ds0mask;
+
+ struct {
+ guint8 unk_data[16];
+ guint16 vp;
+ guint16 vc;
+ } atm;
+
+ guint32 unk;
+ } desc;
+ } extra;
+ } record;
+ } obj;
+
+ obj.record.type = g_htonl(K12_REC_SRCDSC);
+ obj.record.unk32_1 = g_htonl(0x00000001);
+ obj.record.input = g_htonl(src_desc->input);
+
+ obj.record.unk32_2 = g_htons(0x0000);
+ obj.record.color = g_htons(0x060f);
+ obj.record.unk32_3 = g_htonl(0x00000003);
+ switch (src_desc->input_type) {
+ case K12_PORT_ATMPVC:
+ obj.record.unk32_4 = g_htonl(0x01001400);
+ break;
+ default:
+ obj.record.unk32_4 = g_htonl(0x01000100);
+ }
+
+ obj.record.unk16_1 = g_htons(0x0000);
+ obj.record.name_len = (guint16) strlen(src_desc->input_name) + 1;
+ obj.record.stack_len = (guint16) strlen(src_desc->stack_file) + 1;
+
+ obj.record.extra.type = g_htonl(src_desc->input_type);
+
+ switch (src_desc->input_type) {
+ case K12_PORT_ATMPVC:
+ obj.record.hwpart_len = g_htons(0x18);
+ obj.record.extra.desc.atm.vp = g_htons(src_desc->input_info.atm.vp);
+ obj.record.extra.desc.atm.vc = g_htons(src_desc->input_info.atm.vc);
+ offset = 0x3c;
+ break;
+ case K12_PORT_DS0S:
+ obj.record.hwpart_len = g_htons(0x18);
+ for( i=0; i<32; i++ ) {
+ obj.record.extra.desc.ds0mask.mask[i] =
+ (src_desc->input_info.ds0mask & (1UL << i)) ? 0xff : 0x00;
+ }
+ offset = 0x3c;
+ break;
+ default:
+ obj.record.hwpart_len = g_htons(0x08);
+ offset = 0x2c;
+ break;
+ }
+
+ memcpy(obj.buffer + offset,
+ src_desc->input_name,
+ obj.record.name_len);
+
+ memcpy(obj.buffer + offset + obj.record.name_len,
+ src_desc->stack_file,
+ obj.record.stack_len);
+
+ len = offset + obj.record.name_len + obj.record.stack_len;
+ len += (len % 4) ? 4 - (len % 4) : 0;
+
+ obj.record.len = g_htonl(len);
+ obj.record.name_len = g_htons(obj.record.name_len);
+ obj.record.stack_len = g_htons(obj.record.stack_len);
+
+ k12_dump_record(wdh,len,obj.buffer, &errxxx); /* fwrite errs ignored: see k12_dump below */
+}
+
+static gboolean k12_dump(wtap_dumper *wdh, const wtap_rec *rec,
+ const guint8 *pd, int *err, gchar **err_info _U_) {
+ const union wtap_pseudo_header *pseudo_header = &rec->rec_header.packet_header.pseudo_header;
+ k12_dump_t *k12 = (k12_dump_t *)wdh->priv;
+ guint32 len;
+ union {
+ guint8 buffer[8192];
+ struct {
+ guint32 len;
+ guint32 type;
+ guint32 frame_len;
+ guint32 input;
+
+ guint32 datum_1;
+ guint32 datum_2;
+ guint64 ts;
+
+ guint8 frame[0x1fc0];
+ } record;
+ } obj;
+
+ /* We can only write packet records. */
+ if (rec->rec_type != REC_TYPE_PACKET) {
+ *err = WTAP_ERR_UNWRITABLE_REC_TYPE;
+ return FALSE;
+ }
+
+ /*
+ * Make sure this packet doesn't have a link-layer type that
+ * differs from the one for the file.
+ */
+ if (wdh->file_encap != rec->rec_header.packet_header.pkt_encap) {
+ *err = WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
+ return FALSE;
+ }
+
+ if (k12->num_of_records == 0) {
+ k12_t* file_data = (k12_t*)pseudo_header->k12.stuff;
+ /* XXX: We'll assume that any fwrite errors in k12_dump_src_setting will */
+ /* repeat during the final k12_dump_record at the end of k12_dump */
+ /* (and thus cause an error return from k12_dump). */
+ /* (I don't see a reasonably clean way to handle any fwrite errors */
+ /* encountered in k12_dump_src_setting). */
+ g_hash_table_foreach(file_data->src_by_id,k12_dump_src_setting,wdh);
+ }
+ obj.record.len = 0x20 + rec->rec_header.packet_header.caplen;
+ obj.record.len += (obj.record.len % 4) ? 4 - obj.record.len % 4 : 0;
+
+ len = obj.record.len;
+
+ obj.record.len = g_htonl(obj.record.len);
+
+ obj.record.type = g_htonl(K12_REC_PACKET);
+ obj.record.frame_len = g_htonl(rec->rec_header.packet_header.caplen);
+ obj.record.input = g_htonl(pseudo_header->k12.input);
+
+ obj.record.ts = GUINT64_TO_BE((((guint64)rec->ts.secs - 631152000) * 2000000) + (rec->ts.nsecs / 1000 * 2));
+
+ memcpy(obj.record.frame,pd,rec->rec_header.packet_header.caplen);
+
+ return k12_dump_record(wdh,len,obj.buffer, err);
+}
+
+static const guint8 k12_eof[] = {0xff,0xff};
+
+static gboolean k12_dump_finish(wtap_dumper *wdh, int *err, gchar **err_info _U_) {
+ k12_dump_t *k12 = (k12_dump_t *)wdh->priv;
+ union {
+ guint8 b[sizeof(guint32)];
+ guint32 u;
+ } d;
+
+ if (! wtap_dump_file_write(wdh, k12_eof, 2, err))
+ return FALSE;
+ k12->file_len += 2;
+
+ if (wtap_dump_file_seek(wdh, K12_FILE_HDR_FILE_SIZE, SEEK_SET, err) == -1)
+ return FALSE;
+
+ d.u = g_htonl(k12->file_len);
+
+ if (! wtap_dump_file_write(wdh, d.b, 4, err))
+ return FALSE;
+
+ if (wtap_dump_file_seek(wdh, K12_FILE_HDR_PAGE_SIZE, SEEK_SET, err) == -1)
+ return FALSE;
+
+ d.u = g_htonl(8192);
+
+ if (! wtap_dump_file_write(wdh, d.b, 4, err))
+ return FALSE;
+
+ if (wtap_dump_file_seek(wdh, K12_FILE_HDR_RECORD_COUNT_1, SEEK_SET, err) == -1)
+ return FALSE;
+
+ d.u = g_htonl(k12->num_of_records);
+
+ if (! wtap_dump_file_write(wdh, d.b, 4, err))
+ return FALSE;
+
+ if (wtap_dump_file_seek(wdh, K12_FILE_HDR_RECORD_COUNT_2, SEEK_SET, err) == -1)
+ return FALSE;
+
+ d.u = g_htonl(k12->num_of_records);
+
+ if (! wtap_dump_file_write(wdh, d.b, 4, err))
+ return FALSE;
+
+ /* Prevent the above calls to wtap_dump_file_write() from
+ * double-counting the header length
+ */
+ wdh->bytes_dumped = k12->file_len;
+ return TRUE;
+}
+
+
+static gboolean k12_dump_open(wtap_dumper *wdh, int *err, gchar **err_info _U_) {
+ k12_dump_t *k12;
+
+ if ( ! wtap_dump_file_write(wdh, k12_file_magic, 8, err)) {
+ return FALSE;
+ }
+
+ if (wtap_dump_file_seek(wdh, K12_FILE_HDR_LEN, SEEK_SET, err) == -1)
+ return FALSE;
+
+ wdh->bytes_dumped = K12_FILE_HDR_LEN;
+ wdh->subtype_write = k12_dump;
+ wdh->subtype_finish = k12_dump_finish;
+
+ k12 = g_new(k12_dump_t, 1);
+ wdh->priv = (void *)k12;
+ k12->file_len = K12_FILE_HDR_LEN;
+ k12->num_of_records = 0;
+ k12->file_offset = K12_FILE_HDR_LEN;
+
+ return TRUE;
+}
+
+static const struct supported_block_type k12_blocks_supported[] = {
+ /*
+ * We support packet blocks, with no comments or other options.
+ */
+ { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
+};
+
+static const struct file_type_subtype_info k12_info = {
+ "Tektronix K12xx 32-bit .rf5 format", "rf5", "rf5", NULL,
+ TRUE, BLOCKS_SUPPORTED(k12_blocks_supported),
+ k12_dump_can_write_encap, k12_dump_open, NULL
+};
+
+void register_k12(void)
+{
+ k12_file_type_subtype = wtap_register_file_type_subtype(&k12_info);
+
+ /*
+ * Register name for backwards compatibility with the
+ * wtap_filetypes table in Lua.
+ */
+ wtap_register_backwards_compatibility_lua_name("K12",
+ k12_file_type_subtype);
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * vi: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */
diff --git a/wiretap/k12.h b/wiretap/k12.h
new file mode 100644
index 00000000..b0d7fa2f
--- /dev/null
+++ b/wiretap/k12.h
@@ -0,0 +1,19 @@
+/** @file
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __W_K12_H__
+#define __W_K12_H__
+
+#include <glib.h>
+#include "wtap.h"
+
+wtap_open_return_val k12_open(wtap *wth, int *err, gchar **err_info);
+wtap_open_return_val k12text_open(wtap *wth, int *err, gchar **err_info);
+
+#endif
+
diff --git a/wiretap/k12text.l b/wiretap/k12text.l
new file mode 100644
index 00000000..7ba4587c
--- /dev/null
+++ b/wiretap/k12text.l
@@ -0,0 +1,604 @@
+%top {
+/* Include this before everything else, for various large-file definitions */
+#include "config.h"
+#include <wireshark.h>
+}
+
+/*
+ * We want a reentrant scanner.
+ */
+%option reentrant
+
+/*
+ * We don't use input, so don't generate code for it.
+ */
+%option noinput
+
+/*
+ * We don't use unput, so don't generate code for it.
+ */
+%option nounput
+
+/*
+ * We don't read interactively from the terminal.
+ */
+%option never-interactive
+
+/*
+ * We want to stop processing when we get to the end of the input.
+ */
+%option noyywrap
+
+/*
+ * The type for the state we keep for a scanner.
+ */
+%option extra-type="k12text_state_t *"
+
+/*
+ * Prefix scanner routines with "k12text_" rather than "yy", so this scanner
+ * can coexist with other scanners.
+ */
+%option prefix="k12text_"
+
+%option outfile="k12text.c"
+
+/* Options useful for debugging */
+/* noline: Prevent generation of #line directives */
+/* Seems to be required when using the */
+/* Windows VS debugger so as to be able */
+/* to properly step through the code and */
+/* set breakpoints & etc using the */
+/* k12text.c file rather than the */
+/* k12text.l file */
+/* XXX: %option noline gives an error message: */
+/* "unrecognized %option: line" */
+/* with flex 2.5.35; the --noline */
+/* command-line option works OK. */
+/* */
+/* debug: Do output of "rule acceptance" info */
+/* during parse */
+/* */
+/* %option noline */
+/* %option debug */
+
+/*
+ * We have to override the memory allocators so that we don't get
+ * "unused argument" warnings from the yyscanner argument (which
+ * we don't use, as we have a global memory allocator).
+ *
+ * We provide, as macros, our own versions of the routines generated by Flex,
+ * which just call malloc()/realloc()/free() (as the Flex versions do),
+ * discarding the extra argument.
+ */
+%option noyyalloc
+%option noyyrealloc
+%option noyyfree
+
+%{
+/* k12text.l
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+ /*
+ * TODO:
+ * - fix timestamps after midnight
+ * - verify encapsulations
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+#include "wtap-int.h"
+#include "wtap.h"
+#include "file_wrappers.h"
+#include <wsutil/buffer.h>
+#include "k12.h"
+
+#ifndef HAVE_UNISTD_H
+#define YY_NO_UNISTD_H
+#endif
+
+/*
+ * Disable diagnostics in the code generated by Flex.
+ */
+DIAG_OFF_FLEX()
+
+/*
+ * State kept by the scanner.
+ */
+typedef struct {
+ FILE_T fh;
+ int err;
+ gchar *err_info;
+ int start_state;
+
+ guint g_h;
+ guint g_m;
+ guint g_s;
+ guint g_ms;
+ guint g_ns;
+ gint g_encap;
+ guint8 *bb;
+ guint ii;
+ gboolean is_k12text;
+ gboolean at_eof;
+ guint junk_chars;
+ gchar* error_str;
+ guint64 file_bytes_read;
+ gboolean ok_frame;
+} k12text_state_t;
+
+#define KERROR(text) do { yyextra->error_str = g_strdup(text); yyterminate(); } while(0)
+#define SET_HOURS(text) yyextra->g_h = (guint) strtoul(text,NULL,10)
+#define SET_MINUTES(text) yyextra->g_m = (guint) strtoul(text,NULL,10)
+#define SET_SECONDS(text) yyextra->g_s = (guint) strtoul(text,NULL,10)
+#define SET_MS(text) yyextra->g_ms = (guint) strtoul(text,NULL,10)
+#define SET_NS(text) yyextra->g_ns = (guint) strtoul(text,NULL,10)
+#define ADD_BYTE(text) do {if (yyextra->ii >= WTAP_MAX_PACKET_SIZE_STANDARD) {KERROR("frame too large");} yyextra->bb[yyextra->ii++] = (guint8)strtoul(text,NULL,16); } while(0)
+#define FINALIZE_FRAME() do { yyextra->ok_frame = TRUE; } while (0)
+/*~ #define ECHO*/
+#define YY_USER_ACTION yyextra->file_bytes_read += yyleng;
+#define YY_USER_INIT { \
+ k12text_state_t *scanner_state = k12text_get_extra(yyscanner); \
+ BEGIN(scanner_state->start_state); \
+}
+#define YY_INPUT(buf,result,max_size) { \
+ k12text_state_t *scanner_state = k12text_get_extra(yyscanner); \
+ int c = file_getc(scanner_state->fh); \
+ if (c == EOF) { \
+ scanner_state->err = file_error(scanner_state->fh, \
+ &scanner_state->err_info); \
+ if (scanner_state->err == 0) \
+ scanner_state->err = WTAP_ERR_SHORT_READ; \
+ result = YY_NULL; \
+ } else { \
+ buf[0] = c; \
+ result = 1; \
+ } \
+}
+#define MAX_JUNK 400000
+#define ECHO
+
+/*
+ * Private per-file data.
+ */
+typedef struct {
+ /*
+ * The file position after the end of the previous frame processed by
+ * k12text_read.
+ *
+ * We need to keep this around, and seek to it at the beginning of
+ * each call to k12text_read(), since the lexer undoubtedly did some
+ * amount of look-ahead when processing the previous frame.
+ */
+ gint64 next_frame_offset;
+} k12text_t;
+
+/*
+ * Sleazy hack to suppress compiler warnings in yy_fatal_error().
+ */
+#define YY_EXIT_FAILURE ((void)yyscanner, 2)
+
+/*
+ * Macros for the allocators, to discard the extra argument.
+ */
+#define k12text_alloc(size, yyscanner) (void *)malloc(size)
+#define k12text_realloc(ptr, size, yyscanner) (void *)realloc((char *)(ptr), (size))
+#define k12text_free(ptr, yyscanner) free((char *)ptr)
+
+static int k12text_file_type_subtype = -1;
+
+void register_k12text(void);
+
+%}
+start_timestamp \053[\055]{9}\053[\055]{15,100}\053[\055]{10,100}\053
+oneormoredigits [0-9]+:
+twodigits [0-9][0-9]
+colon :
+comma ,
+threedigits [0-9][0-9][0-9]
+start_bytes \174\060\040\040\040\174
+bytes_junk \174[A-F0-9][A-F0-9\040][A-F0-9\040][A-F0-9\040]\174
+byte [a-f0-9][a-f0-9]\174
+end_bytes \015?\012\015?\012
+eth ETHER
+mtp2 MTP-L2
+sscop SSCOP
+sscfnni SSCF
+hdlc HDLC
+
+%START MAGIC NEXT_FRAME HOURS MINUTES M2S SECONDS S2M MS M2N NS ENCAP STARTBYTES BYTE
+%%
+<MAGIC>{start_timestamp} { yyextra->is_k12text = TRUE; yyterminate(); }
+
+<MAGIC>. { if (++ yyextra->junk_chars > MAX_JUNK) { yyextra->is_k12text = FALSE; yyterminate(); } }
+
+<NEXT_FRAME>{start_timestamp} {BEGIN(HOURS); }
+<HOURS>{oneormoredigits} { SET_HOURS(yytext); BEGIN(MINUTES); }
+<MINUTES>{twodigits} { SET_MINUTES(yytext); BEGIN(M2S);}
+<M2S>{colon} { BEGIN(SECONDS);}
+<SECONDS>{twodigits} { SET_SECONDS(yytext); BEGIN(S2M); }
+<S2M>{comma} { BEGIN(MS); }
+<MS>{threedigits} { SET_MS(yytext); BEGIN(M2N); }
+<M2N>{comma} { BEGIN(NS); }
+<NS>{threedigits} { SET_NS(yytext); BEGIN(ENCAP);}
+<ENCAP>{eth} {yyextra->g_encap = WTAP_ENCAP_ETHERNET; BEGIN(STARTBYTES); }
+<ENCAP>{mtp2} {yyextra->g_encap = WTAP_ENCAP_MTP2; BEGIN(STARTBYTES); }
+<ENCAP>{sscop} {yyextra->g_encap = WTAP_ENCAP_ATM_PDUS; BEGIN(STARTBYTES); }
+<ENCAP>{sscfnni} {yyextra->g_encap = WTAP_ENCAP_MTP3; BEGIN(STARTBYTES); }
+<ENCAP>{hdlc} {yyextra->g_encap = WTAP_ENCAP_CHDLC; BEGIN(STARTBYTES); }
+<ENCAP,STARTBYTES>{start_bytes} { BEGIN(BYTE); }
+<BYTE>{byte} { ADD_BYTE(yytext); }
+<BYTE>{bytes_junk} ;
+<BYTE>{end_bytes} { FINALIZE_FRAME(); yyterminate(); }
+
+. { if (++yyextra->junk_chars > MAX_JUNK) { KERROR("too much junk"); } }
+<<EOF>> { yyextra->at_eof = TRUE; yyterminate(); }
+
+%%
+
+/*
+ * Turn diagnostics back on, so we check the code that we've written.
+ */
+DIAG_ON_FLEX()
+
+/* Fill in pkthdr */
+
+static gboolean
+k12text_set_headers(wtap_rec *rec, k12text_state_t *state,
+ int *err, gchar **err_info)
+{
+ rec->rec_type = REC_TYPE_PACKET;
+ rec->block = wtap_block_create(WTAP_BLOCK_PACKET);
+ rec->presence_flags = WTAP_HAS_TS|WTAP_HAS_CAP_LEN;
+
+ rec->ts.secs = 946681200 + (3600*state->g_h) + (60*state->g_m) + state->g_s;
+ rec->ts.nsecs = 1000000*state->g_ms + 1000*state->g_ns;
+
+ rec->rec_header.packet_header.caplen = rec->rec_header.packet_header.len = state->ii;
+
+ rec->rec_header.packet_header.pkt_encap = state->g_encap;
+
+ /* The file-encap is WTAP_ENCAP_PER_PACKET */
+ switch(state->g_encap) {
+ case WTAP_ENCAP_ETHERNET:
+ rec->rec_header.packet_header.pseudo_header.eth.fcs_len = 0;
+ break;
+ case WTAP_ENCAP_MTP3:
+ case WTAP_ENCAP_CHDLC:
+ /* no pseudo_header to fill in for these types */
+ break;
+ case WTAP_ENCAP_MTP2: /* not (yet) supported */
+ /* XXX: I don't know how to fill in the */
+ /* pseudo_header for these types. */
+ *err = WTAP_ERR_UNSUPPORTED;
+ *err_info = g_strdup("k12text: MTP2 packets not yet supported");
+ return FALSE;
+ case WTAP_ENCAP_ATM_PDUS: /* not (yet) supported */
+ /* XXX: I don't know how to fill in the */
+ /* pseudo_header for these types. */
+ *err = WTAP_ERR_UNSUPPORTED;
+ *err_info = g_strdup("k12text: SSCOP packets not yet supported");
+ return FALSE;
+ default:
+ *err = WTAP_ERR_UNSUPPORTED;
+ *err_info = g_strdup("k12text: unknown encapsulation type");
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/* Note: k12text_reset is called each time data is to be processed from */
+/* a file. This ensures that no "state" from a previous read is */
+/* used (such as the lexer look-ahead buffer, file_handle, file */
+/* position and so on. This allows a single lexer buffer to be */
+/* used even when multiple files are open simultaneously (as for */
+/* a file merge). */
+
+static gboolean
+k12text_run_scanner(k12text_state_t *state, FILE_T fh, int start_state,
+ int *err, gchar **err_info)
+{
+ yyscan_t scanner = NULL;
+
+ if (yylex_init(&scanner) != 0) {
+ /* errno is set if this fails */
+ *err = errno;
+ *err_info = NULL;
+ return FALSE;
+ }
+ state->fh = fh;
+ state->err = 0;
+ state->err_info = NULL;
+ state->start_state = start_state;
+
+ state->g_encap = WTAP_ENCAP_UNKNOWN;
+ state->ok_frame = FALSE;
+ state->is_k12text = FALSE;
+ state->at_eof = FALSE;
+ state->junk_chars = 0;
+ state->error_str = NULL;
+ state->file_bytes_read=0;
+ state->g_h=0;
+ state->g_m=0;
+ state->g_s=0;
+ state->g_ns=0;
+ state->g_ms=0;
+ state->ii=0;
+
+ /* Associate the state with the scanner */
+ k12text_set_extra(state, scanner);
+
+ yylex(scanner);
+ yylex_destroy(scanner);
+ if (state->err != 0 && state->err != WTAP_ERR_SHORT_READ) {
+ /* I/O error. */
+ *err = state->err;
+ *err_info = state->err_info;
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static gboolean
+k12text_read(wtap *wth, wtap_rec *rec, Buffer *buf, int *err, char ** err_info, gint64 *data_offset)
+{
+ k12text_t *k12text = (k12text_t *)wth->priv;
+ k12text_state_t state;
+
+ /*
+ * We seek to the file position after the end of the previous frame
+ * processed by k12text_read(), since the lexer undoubtedly did some
+ * amount of look-ahead when processing the previous frame.
+ *
+ * We also clear out any lexer state (eg: look-ahead buffer) and
+ * init vars set by lexer.
+ */
+
+ if ( file_seek(wth->fh, k12text->next_frame_offset, SEEK_SET, err) == -1) {
+ return FALSE;
+ }
+ state.bb = (guint8*)g_malloc(WTAP_MAX_PACKET_SIZE_STANDARD);
+
+ if (!k12text_run_scanner(&state, wth->fh, NEXT_FRAME, err, err_info)) {
+ g_free(state.bb);
+ return FALSE;
+ }
+
+ if (state.ok_frame == FALSE) {
+ if (state.at_eof) {
+ *err = 0;
+ *err_info = NULL;
+ } else {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = state.error_str;
+ }
+ g_free(state.bb);
+ return FALSE;
+ }
+
+ *data_offset = k12text->next_frame_offset; /* file position for beginning of this frame */
+ k12text->next_frame_offset += state.file_bytes_read; /* file position after end of this frame */
+
+ if (!k12text_set_headers(rec, &state, err, err_info)) {
+ g_free(state.bb);
+ return FALSE;
+ }
+ ws_buffer_assure_space(buf, rec->rec_header.packet_header.caplen);
+ memcpy(ws_buffer_start_ptr(buf), state.bb, rec->rec_header.packet_header.caplen);
+
+ g_free(state.bb);
+ return TRUE;
+}
+
+static gboolean
+k12text_seek_read(wtap *wth, gint64 seek_off, wtap_rec *rec, Buffer *buf, int *err, char **err_info)
+{
+ k12text_state_t state;
+
+ if ( file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1) {
+ return FALSE;
+ }
+ state.bb = (guint8*)g_malloc(WTAP_MAX_PACKET_SIZE_STANDARD);
+
+ if (!k12text_run_scanner(&state, wth->random_fh, NEXT_FRAME, err, err_info)) {
+ g_free(state.bb);
+ return FALSE;
+ }
+
+ if (state.ok_frame == FALSE) {
+ *err = WTAP_ERR_BAD_FILE;
+ if (state.at_eof) {
+ /* What happened ? The desired frame was previously read without a problem */
+ *err_info = g_strdup("Unexpected EOF (program error ?)");
+ } else {
+ *err_info = state.error_str;
+ }
+ g_free(state.bb);
+ return FALSE;
+ }
+
+ if (!k12text_set_headers(rec, &state, err, err_info)) {
+ g_free(state.bb);
+ return FALSE;
+ }
+ ws_buffer_assure_space(buf, rec->rec_header.packet_header.caplen);
+ memcpy(ws_buffer_start_ptr(buf), state.bb, rec->rec_header.packet_header.caplen);
+
+ g_free(state.bb);
+ return TRUE;
+}
+
+wtap_open_return_val
+k12text_open(wtap *wth, int *err, gchar **err_info)
+{
+ k12text_t *k12text;
+ k12text_state_t state;
+
+ state.bb = (guint8*)g_malloc(WTAP_MAX_PACKET_SIZE_STANDARD);
+ if (!k12text_run_scanner(&state, wth->fh, MAGIC, err, err_info)) {
+ g_free(state.bb);
+ return WTAP_OPEN_ERROR;
+ }
+
+ if (!state.is_k12text) {
+ /* *err might have been set to WTAP_ERR_SHORT_READ */
+ *err = 0;
+ g_free(state.bb);
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ if ( file_seek(wth->fh, 0, SEEK_SET, err) == -1) {
+ g_free(state.bb);
+ return WTAP_OPEN_ERROR;
+ }
+
+ k12text = g_new(k12text_t, 1);
+ wth->priv = (void *)k12text;
+ k12text->next_frame_offset = 0;
+ wth->file_type_subtype = k12text_file_type_subtype;
+ wth->file_encap = WTAP_ENCAP_PER_PACKET;
+ wth->snapshot_length = 0;
+ wth->subtype_read = k12text_read;
+ wth->subtype_seek_read = k12text_seek_read;
+ wth->file_tsprec = WTAP_TSPREC_NSEC;
+
+ g_free(state.bb);
+ return WTAP_OPEN_MINE;
+}
+
+
+static const struct { int e; const char* s; } encaps[] = {
+ { WTAP_ENCAP_ETHERNET, "ETHER" },
+ { WTAP_ENCAP_MTP2, "MTP-L2" },
+ { WTAP_ENCAP_ATM_PDUS, "SSCOP" },
+ { WTAP_ENCAP_MTP3, "SSCF" },
+ { WTAP_ENCAP_CHDLC, "HDLC" },
+ /* ... */
+ { 0, NULL }
+};
+
+static gboolean
+k12text_dump(wtap_dumper *wdh, const wtap_rec *rec,
+ const guint8 *pd, int *err, gchar **err_info _U_) {
+#define K12BUF_SIZE 196808
+ char *buf;
+ size_t left = K12BUF_SIZE;
+ size_t wl;
+ char *p;
+ const char* str_enc;
+ guint i;
+ guint ns;
+ guint ms;
+ gboolean ret;
+ struct tm *tmp;
+
+ /* Don't write anything bigger than we're willing to read. */
+ if (rec->rec_header.packet_header.caplen > WTAP_MAX_PACKET_SIZE_STANDARD) {
+ *err = WTAP_ERR_PACKET_TOO_LARGE;
+ return FALSE;
+ }
+
+ str_enc = NULL;
+ for(i=0; encaps[i].s; i++) {
+ if (rec->rec_header.packet_header.pkt_encap == encaps[i].e) {
+ str_enc = encaps[i].s;
+ break;
+ }
+ }
+ if (str_enc == NULL) {
+ /*
+ * That encapsulation type is not supported. Fail.
+ */
+ *err = WTAP_ERR_UNWRITABLE_ENCAP;
+ return FALSE;
+ }
+
+ buf = (char *)g_malloc(K12BUF_SIZE);
+ p = buf;
+
+ ms = rec->ts.nsecs / 1000000;
+ ns = (rec->ts.nsecs - (1000000*ms))/1000;
+
+ tmp = gmtime(&rec->ts.secs);
+ if (tmp == NULL)
+ snprintf(p, 90, "+---------+---------------+----------+\r\nXX:XX:XX,");
+ else
+ strftime(p, 90, "+---------+---------------+----------+\r\n%H:%M:%S,", tmp);
+ wl = strlen(p);
+ p += wl;
+ left -= wl;
+
+ wl = snprintf(p, left, "%.3d,%.3d %s\r\n|0 |", ms, ns, str_enc);
+ p += wl;
+ left -= wl;
+
+ for(i = 0; i < rec->rec_header.packet_header.caplen && left > 2; i++) {
+ wl = snprintf(p, left, "%.2x|", pd[i]);
+ p += wl;
+ left -= wl;
+ }
+
+ wl = snprintf(p, left, "\r\n\r\n");
+ left -= wl;
+
+ ret = wtap_dump_file_write(wdh, buf, K12BUF_SIZE - left, err);
+
+ g_free(buf);
+ return ret;
+}
+
+
+static gboolean
+k12text_dump_open(wtap_dumper *wdh, int *err _U_, gchar **err_info _U_)
+{
+ wdh->subtype_write = k12text_dump;
+
+ return TRUE;
+}
+
+static int
+k12text_dump_can_write_encap(int encap)
+{
+ switch (encap) {
+ case WTAP_ENCAP_PER_PACKET:
+ case WTAP_ENCAP_ETHERNET:
+ case WTAP_ENCAP_MTP3:
+ case WTAP_ENCAP_CHDLC:
+ return 0;
+ case WTAP_ENCAP_MTP2:
+ case WTAP_ENCAP_ATM_PDUS:
+ default:
+ return WTAP_ERR_UNWRITABLE_ENCAP;
+ }
+}
+
+static const struct supported_block_type k12text_blocks_supported[] = {
+ /*
+ * We support packet blocks, with no comments or other options.
+ */
+ { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
+};
+
+static const struct file_type_subtype_info k12text_info = {
+ "K12 text file", "k12text", "txt", NULL,
+ FALSE, BLOCKS_SUPPORTED(k12text_blocks_supported),
+ k12text_dump_can_write_encap, k12text_dump_open, NULL
+};
+
+void register_k12text(void)
+{
+ k12text_file_type_subtype = wtap_register_file_type_subtype(&k12text_info);
+
+ /*
+ * Register name for backwards compatibility with the
+ * wtap_filetypes table in Lua.
+ */
+ wtap_register_backwards_compatibility_lua_name("K12TEXT",
+ k12text_file_type_subtype);
+}
diff --git a/wiretap/lanalyzer.c b/wiretap/lanalyzer.c
new file mode 100644
index 00000000..642db819
--- /dev/null
+++ b/wiretap/lanalyzer.c
@@ -0,0 +1,1010 @@
+/* lanalyzer.c
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "config.h"
+
+#define WS_LOG_DOMAIN LOG_DOMAIN_WIRETAP
+
+#include <stdlib.h>
+#include <errno.h>
+#include "wtap-int.h"
+#include "file_wrappers.h"
+#include "lanalyzer.h"
+
+/* The LANalyzer format is documented (at least in part) in Novell document
+ TID022037, which can be found at, among other places:
+
+ http://www.blacksheepnetworks.com/security/info/nw/lan/trace.txt
+ */
+
+/* Record header format */
+
+typedef struct {
+ guint8 record_type[2];
+ guint8 record_length[2];
+} LA_RecordHeader;
+
+#define LA_RecordHeaderSize 4
+
+/* Record type codes: */
+
+#define RT_HeaderRegular 0x1001
+#define RT_HeaderCyclic 0x1007
+#define RT_RxChannelName 0x1006
+#define RT_TxChannelName 0x100b
+#define RT_FilterName 0x1032
+#define RT_RxTemplateName 0x1035
+#define RT_TxTemplateName 0x1036
+#define RT_DisplayOptions 0x100a
+#define RT_Summary 0x1002
+#define RT_SubfileSummary 0x1003
+#define RT_CyclicInformation 0x1009
+#define RT_Index 0x1004
+#define RT_PacketData 0x1005
+
+#define LA_ProFileLimit (1024 * 1024 * 32)
+
+typedef guint8 Eadr[6];
+typedef guint16 TimeStamp[3]; /* 0.5 microseconds since start of trace */
+
+/*
+ * These records have only 2-byte alignment for 4-byte quantities,
+ * so the structures aren't necessarily valid; they're kept as comments
+ * for reference purposes.
+ */
+
+/*
+ * typedef struct {
+ * guint8 day;
+ * guint8 mon;
+ * gint16 year;
+ * } Date;
+ */
+
+/*
+ * typedef struct {
+ * guint8 second;
+ * guint8 minute;
+ * guint8 hour;
+ * guint8 day;
+ * gint16 reserved;
+ * } Time;
+ */
+
+/*
+ * RT_Summary:
+ *
+ * typedef struct {
+ * Date datcre;
+ * Date datclo;
+ * Time timeopn;
+ * Time timeclo;
+ * Eadr statadr;
+ * gint16 mxseqno;
+ * gint16 slcoff;
+ * gint16 mxslc;
+ * gint32 totpktt;
+ * gint32 statrg;
+ * gint32 stptrg;
+ * gint32 mxpkta[36];
+ * gint16 board_type;
+ * gint16 board_version;
+ * gint8 reserved[18];
+ * } Summary;
+ */
+
+#define SummarySize (18+22+(4*36)+6+6+6+4+4)
+
+/*
+ * typedef struct {
+ * gint16 rid;
+ * gint16 rlen;
+ * Summary s;
+ * } LA_SummaryRecord;
+ */
+
+#define LA_SummaryRecordSize (SummarySize + 4)
+
+/* LANalyzer board types (which indicate the type of network on which
+ the capture was done). */
+#define BOARD_325 226 /* LANalyzer 325 (Ethernet) */
+#define BOARD_325TR 227 /* LANalyzer 325TR (Token-ring) */
+
+
+/*
+ * typedef struct {
+ * gint16 rid;
+ * gint16 rlen;
+ * gint16 seqno;
+ * gint32 totpktf;
+ * } LA_SubfileSummaryRecord;
+ */
+
+#define LA_SubfileSummaryRecordSize 10
+
+
+#define LA_IndexSize 500
+
+/*
+ * typedef struct {
+ * gint16 rid;
+ * gint16 rlen;
+ * gint16 idxsp; = LA_IndexSize
+ * gint16 idxct;
+ * gint8 idxgranu;
+ * gint8 idxvd;
+ * gint32 trcidx[LA_IndexSize + 2]; +2 undocumented but used by La 2.2
+ * } LA_IndexRecord;
+ */
+
+#define LA_IndexRecordSize (10 + 4 * (LA_IndexSize + 2))
+
+
+/*
+ * typedef struct {
+ * guint16 rx_channels;
+ * guint16 rx_errors;
+ * gint16 rx_frm_len;
+ * gint16 rx_frm_sln;
+ * TimeStamp rx_time;
+ * guint32 pktno;
+ * gint16 prvlen;
+ * gint16 offset;
+ * gint16 tx_errs;
+ * gint16 rx_filters;
+ * gint8 unused[2];
+ * gint16 hwcolls;
+ * gint16 hwcollschans;
+ * Packetdata ....;
+ * } LA_PacketRecord;
+ */
+
+#define LA_PacketRecordSize 32
+
+typedef struct {
+ gboolean init;
+ nstime_t start;
+ guint32 pkts;
+ int encap;
+ int lastlen;
+ } LA_TmpInfo;
+
+static const guint8 LA_HeaderRegularFake[] = {
+0x01,0x10,0x4c,0x00,0x01,0x05,0x54,0x72,0x61,0x63,0x65,0x20,0x44,0x69,0x73,0x70,
+0x6c,0x61,0x79,0x20,0x54,0x72,0x61,0x63,0x65,0x20,0x46,0x69,0x6c,0x65,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
+ };
+
+static const guint8 LA_RxChannelNameFake[] = {
+0x06,0x10,0x80,0x00,0x43,0x68,0x61,0x6e ,0x6e,0x65,0x6c,0x31,0x00,0x43,0x68,0x61,
+0x6e,0x6e,0x65,0x6c,0x32,0x00,0x43,0x68 ,0x61,0x6e,0x6e,0x65,0x6c,0x33,0x00,0x43,
+0x68,0x61,0x6e,0x6e,0x65,0x6c,0x34,0x00 ,0x43,0x68,0x61,0x6e,0x6e,0x65,0x6c,0x35,
+0x00,0x43,0x68,0x61,0x6e,0x6e,0x65,0x6c ,0x36,0x00,0x43,0x68,0x61,0x6e,0x6e,0x65,
+0x6c,0x37,0x00,0x43,0x68,0x61,0x6e,0x6e ,0x65,0x6c,0x38,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00
+ };
+
+static const guint8 LA_TxChannelNameFake[] = {
+ 0x0b,0x10,0x36,0x00 ,0x54,0x72,0x61,0x6e,0x73,0x31,0x00,0x00,
+0x00,0x54,0x72,0x61,0x6e,0x73,0x32,0x00 ,0x00,0x00,0x54,0x72,0x61,0x6e,0x73,0x33,
+0x00,0x00,0x00,0x54,0x72,0x61,0x6e,0x73 ,0x34,0x00,0x00,0x00,0x54,0x72,0x61,0x6e,
+0x73,0x35,0x00,0x00,0x00,0x54,0x72,0x61 ,0x6e,0x73,0x36,0x00,0x00,0x00
+ };
+
+static const guint8 LA_RxTemplateNameFake[] = {
+ 0x35,0x10,
+0x90,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00
+ };
+
+static const guint8 LA_TxTemplateNameFake[] = {
+ 0x36,0x10,0x36,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00
+ };
+
+static const guint8 LA_DisplayOptionsFake[] = {
+ 0x0a,0x10,0x0a,0x01,
+0x00,0x00,0x01,0x00,0x01,0x02,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00
+ };
+
+static const guint8 LA_CyclicInformationFake[] = {
+ 0x09,0x10,0x1a,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
+ };
+
+static const guint8 z64[64] = {
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
+ };
+
+typedef struct {
+ time_t start;
+} lanalyzer_t;
+
+static gboolean lanalyzer_read(wtap *wth, wtap_rec *rec,
+ Buffer *buf, int *err, gchar **err_info, gint64 *data_offset);
+static gboolean lanalyzer_seek_read(wtap *wth, gint64 seek_off,
+ wtap_rec *rec, Buffer *buf, int *err, gchar **err_info);
+static gboolean lanalyzer_dump_finish(wtap_dumper *wdh, int *err,
+ gchar **err_info);
+
+static int lanalyzer_file_type_subtype = -1;
+
+void register_lanalyzer(void);
+
+wtap_open_return_val lanalyzer_open(wtap *wth, int *err, gchar **err_info)
+{
+ LA_RecordHeader rec_header;
+ char header_fixed[2];
+ char *comment;
+ gboolean found_summary;
+ char summary[210];
+ guint16 board_type, mxslc;
+ guint16 record_type, record_length;
+ guint8 cr_day, cr_month;
+ guint16 cr_year;
+ struct tm tm;
+ time_t start;
+ int file_encap;
+ lanalyzer_t *lanalyzer;
+
+ if (!wtap_read_bytes(wth->fh, &rec_header, LA_RecordHeaderSize,
+ err, err_info)) {
+ if (*err != WTAP_ERR_SHORT_READ)
+ return WTAP_OPEN_ERROR;
+ return WTAP_OPEN_NOT_MINE;
+ }
+ record_type = pletoh16(rec_header.record_type);
+ record_length = pletoh16(rec_header.record_length); /* make sure to do this for while() loop */
+
+ if (record_type != RT_HeaderRegular && record_type != RT_HeaderCyclic) {
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ /* Read the major and minor version numbers */
+ if (record_length < sizeof header_fixed) {
+ /*
+ * Not enough room for the major and minor version numbers.
+ * Just treat that as a "not a LANalyzer file" indication.
+ */
+ return WTAP_OPEN_NOT_MINE;
+ }
+ if (!wtap_read_bytes(wth->fh, &header_fixed, sizeof header_fixed,
+ err, err_info)) {
+ if (*err != WTAP_ERR_SHORT_READ)
+ return WTAP_OPEN_ERROR;
+ return WTAP_OPEN_NOT_MINE;
+ }
+ record_length -= sizeof header_fixed;
+
+ if (record_length != 0) {
+ /* Read the rest of the record as a comment. */
+ comment = (char *)g_malloc(record_length + 1);
+ if (!wtap_read_bytes(wth->fh, comment, record_length,
+ err, err_info)) {
+ if (*err != WTAP_ERR_SHORT_READ) {
+ g_free(comment);
+ return WTAP_OPEN_ERROR;
+ }
+ g_free(comment);
+ return WTAP_OPEN_NOT_MINE;
+ }
+ wtap_block_add_string_option(g_array_index(wth->shb_hdrs, wtap_block_t, 0), OPT_COMMENT, comment, record_length);
+ g_free(comment);
+ }
+
+ /*
+ * Read records until we find the start of packets.
+ * The document cited above claims that the first 11 records are
+ * in a particular sequence of types, but at least one capture
+ * doesn't have all the types listed in the order listed.
+ *
+ * If we don't have a summary record, we don't know the link-layer
+ * header type, so we can't read the file.
+ */
+ found_summary = FALSE;
+ while (1) {
+ if (!wtap_read_bytes_or_eof(wth->fh, &rec_header,
+ LA_RecordHeaderSize, err, err_info)) {
+ if (*err == 0) {
+ /*
+ * End of file and no packets;
+ * accept this file.
+ */
+ break;
+ }
+ return WTAP_OPEN_ERROR;
+ }
+
+ record_type = pletoh16(rec_header.record_type);
+ record_length = pletoh16(rec_header.record_length);
+
+ /*ws_message("Record 0x%04X Length %d", record_type, record_length);*/
+ switch (record_type) {
+ /* Trace Summary Record */
+ case RT_Summary:
+ if (record_length < sizeof summary) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("lanalyzer: summary record length %u is too short",
+ record_length);
+ return WTAP_OPEN_ERROR;
+ }
+ if (!wtap_read_bytes(wth->fh, summary,
+ sizeof summary, err, err_info))
+ return WTAP_OPEN_ERROR;
+
+ /* Assume that the date of the creation of the trace file
+ * is the same date of the trace. Lanalyzer doesn't
+ * store the creation date/time of the trace, but only of
+ * the file. Unless you traced at 11:55 PM and saved at 00:05
+ * AM, the assumption that trace.date == file.date is true.
+ */
+ cr_day = summary[0];
+ cr_month = summary[1];
+ cr_year = pletoh16(&summary[2]);
+ /*ws_message("Day %d Month %d Year %d (%04X)", cr_day, cr_month,
+ cr_year, cr_year);*/
+
+ /* Get capture start time. I learned how to do
+ * this from Guy's code in ngsniffer.c
+ */
+ tm.tm_year = cr_year - 1900;
+ tm.tm_mon = cr_month - 1;
+ tm.tm_mday = cr_day;
+ tm.tm_hour = 0;
+ tm.tm_min = 0;
+ tm.tm_sec = 0;
+ tm.tm_isdst = -1;
+ start = mktime(&tm);
+ /*ws_message("Day %d Month %d Year %d", tm.tm_mday,
+ tm.tm_mon, tm.tm_year);*/
+ mxslc = pletoh16(&summary[30]);
+
+ board_type = pletoh16(&summary[188]);
+ switch (board_type) {
+ case BOARD_325:
+ file_encap = WTAP_ENCAP_ETHERNET;
+ break;
+ case BOARD_325TR:
+ file_encap = WTAP_ENCAP_TOKEN_RING;
+ break;
+ default:
+ *err = WTAP_ERR_UNSUPPORTED;
+ *err_info = ws_strdup_printf("lanalyzer: board type %u unknown",
+ board_type);
+ return WTAP_OPEN_ERROR;
+ }
+
+ if (found_summary) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("lanalyzer: file has more than one summary record");
+ return WTAP_OPEN_ERROR;
+ }
+ found_summary = TRUE;
+
+ /* Skip the rest of the record */
+ record_length -= sizeof summary;
+ if (record_length != 0) {
+ if (!wtap_read_bytes(wth->fh, NULL, record_length, err, err_info)) {
+ return WTAP_OPEN_ERROR;
+ }
+ }
+ break;
+
+ /* Trace Packet Data Record */
+ case RT_PacketData:
+ /* Go back header number of bytes so that lanalyzer_read
+ * can read this header */
+ if (file_seek(wth->fh, -LA_RecordHeaderSize, SEEK_CUR, err) == -1) {
+ return WTAP_OPEN_ERROR;
+ }
+ goto done;
+
+ default:
+ /* Unknown record type - skip it */
+ if (!wtap_read_bytes(wth->fh, NULL, record_length, err, err_info)) {
+ return WTAP_OPEN_ERROR;
+ }
+ break;
+ }
+ }
+
+done:
+ if (!found_summary) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("lanalyzer: file has no summary record");
+ return WTAP_OPEN_ERROR;
+ }
+
+ /* If we made it this far, then the file is a readable LANAlyzer file.
+ * Let's get some info from it. Note that we get wth->snapshot_length
+ * from a record later in the file. */
+ wth->file_type_subtype = lanalyzer_file_type_subtype;
+ lanalyzer = g_new(lanalyzer_t, 1);
+ lanalyzer->start = start;
+ wth->priv = (void *)lanalyzer;
+ wth->subtype_read = lanalyzer_read;
+ wth->subtype_seek_read = lanalyzer_seek_read;
+ wth->file_encap = file_encap;
+ wth->snapshot_length = mxslc;
+ wth->file_tsprec = WTAP_TSPREC_NSEC;
+
+ /*
+ * Add an IDB; we don't know how many interfaces were involved,
+ * so we just say one interface, about which we only know
+ * the link-layer type, snapshot length, and time stamp
+ * resolution.
+ */
+ wtap_add_generated_idb(wth);
+
+ return WTAP_OPEN_MINE;
+}
+
+#define DESCRIPTOR_LEN 32
+
+static gboolean lanalyzer_read_trace_record(wtap *wth, FILE_T fh,
+ wtap_rec *rec, Buffer *buf, int *err, gchar **err_info)
+{
+ char LE_record_type[2];
+ char LE_record_length[2];
+ guint16 record_type, record_length;
+ int record_data_size;
+ int packet_size;
+ gchar descriptor[DESCRIPTOR_LEN];
+ lanalyzer_t *lanalyzer;
+ guint16 time_low, time_med, time_high, true_size;
+ guint64 t;
+ time_t tsecs;
+
+ /* read the record type and length. */
+ if (!wtap_read_bytes_or_eof(fh, LE_record_type, 2, err, err_info))
+ return FALSE;
+ if (!wtap_read_bytes(fh, LE_record_length, 2, err, err_info))
+ return FALSE;
+
+ record_type = pletoh16(LE_record_type);
+ record_length = pletoh16(LE_record_length);
+
+ /* Only Trace Packet Data Records should occur now that we're in
+ * the middle of reading packets. If any other record type exists
+ * after a Trace Packet Data Record, mark it as an error. */
+ if (record_type != RT_PacketData) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("lanalyzer: record type %u seen after trace summary record",
+ record_type);
+ return FALSE;
+ }
+
+ if (record_length < DESCRIPTOR_LEN) {
+ /*
+ * Uh-oh, the record isn't big enough to even have a
+ * descriptor.
+ */
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("lanalyzer: file has a %u-byte record, too small to have even a packet descriptor",
+ record_length);
+ return FALSE;
+ }
+ record_data_size = record_length - DESCRIPTOR_LEN;
+
+ /* Read the descriptor data */
+ if (!wtap_read_bytes(fh, descriptor, DESCRIPTOR_LEN, err, err_info))
+ return FALSE;
+
+ true_size = pletoh16(&descriptor[4]);
+ packet_size = pletoh16(&descriptor[6]);
+ /*
+ * The maximum value of packet_size is 65535, which is less than
+ * WTAP_MAX_PACKET_SIZE_STANDARD will ever be, so we don't need to check
+ * it.
+ */
+
+ /*
+ * OK, is the frame data size greater than what's left of the
+ * record?
+ */
+ if (packet_size > record_data_size) {
+ /*
+ * Yes - treat this as an error.
+ */
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("lanalyzer: Record length is less than packet size");
+ return FALSE;
+ }
+
+ rec->rec_type = REC_TYPE_PACKET;
+ rec->block = wtap_block_create(WTAP_BLOCK_PACKET);
+ rec->presence_flags = WTAP_HAS_TS|WTAP_HAS_CAP_LEN;
+
+ time_low = pletoh16(&descriptor[8]);
+ time_med = pletoh16(&descriptor[10]);
+ time_high = pletoh16(&descriptor[12]);
+ t = (((guint64)time_low) << 0) + (((guint64)time_med) << 16) +
+ (((guint64)time_high) << 32);
+ tsecs = (time_t) (t/2000000);
+ lanalyzer = (lanalyzer_t *)wth->priv;
+ rec->ts.secs = tsecs + lanalyzer->start;
+ rec->ts.nsecs = ((guint32) (t - tsecs*2000000)) * 500;
+
+ if (true_size - 4 >= packet_size) {
+ /*
+ * It appears that the "true size" includes the FCS;
+ * make it reflect the non-FCS size (the "packet size"
+ * appears never to include the FCS, even if no slicing
+ * is done).
+ */
+ true_size -= 4;
+ }
+ rec->rec_header.packet_header.len = true_size;
+ rec->rec_header.packet_header.caplen = packet_size;
+
+ switch (wth->file_encap) {
+
+ case WTAP_ENCAP_ETHERNET:
+ /* We assume there's no FCS in this frame. */
+ rec->rec_header.packet_header.pseudo_header.eth.fcs_len = 0;
+ break;
+ }
+
+ /* Read the packet data */
+ return wtap_read_packet_bytes(fh, buf, packet_size, err, err_info);
+}
+
+/* Read the next packet */
+static gboolean lanalyzer_read(wtap *wth, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info, gint64 *data_offset)
+{
+ *data_offset = file_tell(wth->fh);
+
+ /* Read the record */
+ return lanalyzer_read_trace_record(wth, wth->fh, rec, buf, err,
+ err_info);
+}
+
+static gboolean lanalyzer_seek_read(wtap *wth, gint64 seek_off,
+ wtap_rec *rec, Buffer *buf, int *err, gchar **err_info)
+{
+ if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
+ return FALSE;
+
+ /* Read the record */
+ if (!lanalyzer_read_trace_record(wth, wth->random_fh, rec, buf,
+ err, err_info)) {
+ if (*err == 0)
+ *err = WTAP_ERR_SHORT_READ;
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/*---------------------------------------------------
+ * Returns TRUE on success, FALSE on error
+ * Write "cnt" bytes of zero with error control
+ *---------------------------------------------------*/
+static gboolean s0write(wtap_dumper *wdh, size_t cnt, int *err)
+{
+ size_t snack;
+
+ while (cnt) {
+ snack = cnt > 64 ? 64 : cnt;
+
+ if (!wtap_dump_file_write(wdh, z64, snack, err))
+ return FALSE;
+ cnt -= snack;
+ }
+ return TRUE; /* ok */
+}
+
+/*---------------------------------------------------
+ * Returns TRUE on success, FALSE on error
+ * Write an 8-bit value
+ *---------------------------------------------------*/
+static gboolean s8write(wtap_dumper *wdh, const guint8 s8, int *err)
+{
+ return wtap_dump_file_write(wdh, &s8, 1, err);
+}
+/*---------------------------------------------------
+ * Returns TRUE on success, FALSE on error
+ * Write a 16-bit value as little-endian
+ *---------------------------------------------------*/
+static gboolean s16write(wtap_dumper *wdh, const guint16 s16, int *err)
+{
+ guint16 s16_le = GUINT16_TO_LE(s16);
+ return wtap_dump_file_write(wdh, &s16_le, 2, err);
+}
+/*---------------------------------------------------
+ * Returns TRUE on success, FALSE on error
+ * Write a 32-bit value as little-endian
+ *---------------------------------------------------*/
+static gboolean s32write(wtap_dumper *wdh, const guint32 s32, int *err)
+{
+ guint32 s32_le = GUINT32_TO_LE(s32);
+ return wtap_dump_file_write(wdh, &s32_le, 4, err);
+}
+/*---------------------------------------------------
+ * Returns TRUE on success, FALSE on error
+ * Write a 48-bit value as little-endian
+ *---------------------------------------------------*/
+static gboolean s48write(wtap_dumper *wdh, const guint64 s48, int *err)
+{
+#if G_BYTE_ORDER == G_BIG_ENDIAN
+ guint16 s48_upper_le = GUINT16_SWAP_LE_BE((guint16) (s48 >> 32));
+ guint32 s48_lower_le = GUINT32_SWAP_LE_BE((guint32) (s48 & 0xFFFFFFFF));
+#else
+ guint16 s48_upper_le = (guint16) (s48 >> 32);
+ guint32 s48_lower_le = (guint32) (s48 & 0xFFFFFFFF);
+#endif
+ return wtap_dump_file_write(wdh, &s48_lower_le, 4, err) &&
+ wtap_dump_file_write(wdh, &s48_upper_le, 2, err);
+}
+/*---------------------------------------------------
+ * Write a record for a packet to a dump file.
+ * Returns TRUE on success, FALSE on failure.
+ *---------------------------------------------------*/
+static gboolean lanalyzer_dump(wtap_dumper *wdh,
+ const wtap_rec *rec,
+ const guint8 *pd, int *err, gchar **err_info _U_)
+{
+ guint64 x;
+ int len;
+
+ LA_TmpInfo *itmp = (LA_TmpInfo*)(wdh->priv);
+ nstime_t td;
+ int thisSize = rec->rec_header.packet_header.caplen + LA_PacketRecordSize + LA_RecordHeaderSize;
+
+ /* We can only write packet records. */
+ if (rec->rec_type != REC_TYPE_PACKET) {
+ *err = WTAP_ERR_UNWRITABLE_REC_TYPE;
+ return FALSE;
+ }
+
+ /*
+ * Make sure this packet doesn't have a link-layer type that
+ * differs from the one for the file.
+ */
+ if (wdh->file_encap != rec->rec_header.packet_header.pkt_encap) {
+ *err = WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
+ return FALSE;
+ }
+
+ if (wdh->bytes_dumped + thisSize > LA_ProFileLimit) {
+ /* printf(" LA_ProFileLimit reached\n"); */
+ *err = EFBIG;
+ return FALSE; /* and don't forget the header */
+ }
+
+ len = rec->rec_header.packet_header.caplen + (rec->rec_header.packet_header.caplen ? LA_PacketRecordSize : 0);
+
+ /* len goes into a 16-bit field, so there's a hard limit of 65535. */
+ if (len > 65535) {
+ *err = WTAP_ERR_PACKET_TOO_LARGE;
+ return FALSE;
+ }
+
+ if (!s16write(wdh, 0x1005, err))
+ return FALSE;
+ if (!s16write(wdh, (guint16)len, err))
+ return FALSE;
+
+ if (!itmp->init) {
+ /* collect some information for the
+ * finally written header
+ */
+ itmp->start = rec->ts;
+ itmp->pkts = 0;
+ itmp->init = TRUE;
+ itmp->encap = wdh->file_encap;
+ itmp->lastlen = 0;
+ }
+
+ if (!s16write(wdh, 0x0001, err)) /* pr.rx_channels */
+ return FALSE;
+ if (!s16write(wdh, 0x0008, err)) /* pr.rx_errors */
+ return FALSE;
+ if (!s16write(wdh, (guint16) (rec->rec_header.packet_header.len + 4), err)) /* pr.rx_frm_len */
+ return FALSE;
+ if (!s16write(wdh, (guint16) rec->rec_header.packet_header.caplen, err)) /* pr.rx_frm_sln */
+ return FALSE;
+
+ nstime_delta(&td, &rec->ts, &itmp->start);
+
+ /* Convert to half-microseconds, rounded up. */
+ x = (td.nsecs + 250) / 500; /* nanoseconds -> half-microseconds, rounded */
+ x += td.secs * 2000000; /* seconds -> half-microseconds */
+
+ if (!s48write(wdh, x, err)) /* pr.rx_time[i] */
+ return FALSE;
+
+ if (!s32write(wdh, ++itmp->pkts, err)) /* pr.pktno */
+ return FALSE;
+ if (!s16write(wdh, (guint16)itmp->lastlen, err)) /* pr.prlen */
+ return FALSE;
+ itmp->lastlen = len;
+
+ if (!s0write(wdh, 12, err))
+ return FALSE;
+
+ if (!wtap_dump_file_write(wdh, pd, rec->rec_header.packet_header.caplen, err))
+ return FALSE;
+
+ return TRUE;
+}
+
+/*---------------------------------------------------
+ * Returns 0 if we could write the specified encapsulation type,
+ * an error indication otherwise.
+ *---------------------------------------------------*/
+static int lanalyzer_dump_can_write_encap(int encap)
+{
+ /* Per-packet encapsulations aren't supported. */
+ if (encap == WTAP_ENCAP_PER_PACKET)
+ return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
+
+ if ( encap != WTAP_ENCAP_ETHERNET
+ && encap != WTAP_ENCAP_TOKEN_RING )
+ return WTAP_ERR_UNWRITABLE_ENCAP;
+ /*
+ * printf("lanalyzer_dump_can_write_encap(%d)\n",encap);
+ */
+ return 0;
+}
+
+/*---------------------------------------------------
+ * Returns TRUE on success, FALSE on failure; sets "*err" to an
+ * error code on failure
+ *---------------------------------------------------*/
+static gboolean lanalyzer_dump_open(wtap_dumper *wdh, int *err, gchar **err_info _U_)
+{
+ int jump;
+ void *tmp;
+
+ tmp = g_malloc(sizeof(LA_TmpInfo));
+ if (!tmp) {
+ *err = errno;
+ return FALSE;
+ }
+
+ ((LA_TmpInfo*)tmp)->init = FALSE;
+ wdh->priv = tmp;
+ wdh->subtype_write = lanalyzer_dump;
+ wdh->subtype_finish = lanalyzer_dump_finish;
+
+ /* Some of the fields in the file header aren't known yet so
+ just skip over it for now. It will be created after all
+ of the packets have been written. */
+
+ jump = sizeof (LA_HeaderRegularFake)
+ + sizeof (LA_RxChannelNameFake)
+ + sizeof (LA_TxChannelNameFake)
+ + sizeof (LA_RxTemplateNameFake)
+ + sizeof (LA_TxTemplateNameFake)
+ + sizeof (LA_DisplayOptionsFake)
+ + LA_SummaryRecordSize
+ + LA_SubfileSummaryRecordSize
+ + sizeof (LA_CyclicInformationFake)
+ + LA_IndexRecordSize;
+
+ if (wtap_dump_file_seek(wdh, jump, SEEK_SET, err) == -1)
+ return FALSE;
+
+ wdh->bytes_dumped = jump;
+ return TRUE;
+}
+
+/*---------------------------------------------------
+ *
+ *---------------------------------------------------*/
+static gboolean lanalyzer_dump_header(wtap_dumper *wdh, int *err)
+{
+ LA_TmpInfo *itmp = (LA_TmpInfo*)(wdh->priv);
+ guint16 board_type = itmp->encap == WTAP_ENCAP_TOKEN_RING
+ ? BOARD_325TR /* LANalyzer Board Type */
+ : BOARD_325; /* LANalyzer Board Type */
+ struct tm *fT;
+
+ fT = localtime(&itmp->start.secs);
+ if (fT == NULL)
+ return FALSE;
+
+ if (wtap_dump_file_seek(wdh, 0, SEEK_SET, err) == -1)
+ return FALSE;
+
+ if (!wtap_dump_file_write(wdh, &LA_HeaderRegularFake,
+ sizeof LA_HeaderRegularFake, err))
+ return FALSE;
+ if (!wtap_dump_file_write(wdh, &LA_RxChannelNameFake,
+ sizeof LA_RxChannelNameFake, err))
+ return FALSE;
+ if (!wtap_dump_file_write(wdh, &LA_TxChannelNameFake,
+ sizeof LA_TxChannelNameFake, err))
+ return FALSE;
+ if (!wtap_dump_file_write(wdh, &LA_RxTemplateNameFake,
+ sizeof LA_RxTemplateNameFake, err))
+ return FALSE;
+ if (!wtap_dump_file_write(wdh, &LA_TxTemplateNameFake,
+ sizeof LA_TxTemplateNameFake, err))
+ return FALSE;
+ if (!wtap_dump_file_write(wdh, &LA_DisplayOptionsFake,
+ sizeof LA_DisplayOptionsFake, err))
+ return FALSE;
+ /*-----------------------------------------------------------------*/
+ if (!s16write(wdh, RT_Summary, err)) /* rid */
+ return FALSE;
+ if (!s16write(wdh, SummarySize, err)) /* rlen */
+ return FALSE;
+ if (!s8write(wdh, (guint8) fT->tm_mday, err)) /* s.datcre.day */
+ return FALSE;
+ if (!s8write(wdh, (guint8) (fT->tm_mon+1), err)) /* s.datcre.mon */
+ return FALSE;
+ if (!s16write(wdh, (guint16) (fT->tm_year + 1900), err)) /* s.datcre.year */
+ return FALSE;
+ if (!s8write(wdh, (guint8) fT->tm_mday, err)) /* s.datclo.day */
+ return FALSE;
+ if (!s8write(wdh, (guint8) (fT->tm_mon+1), err)) /* s.datclo.mon */
+ return FALSE;
+ if (!s16write(wdh, (guint16) (fT->tm_year + 1900), err)) /* s.datclo.year */
+ return FALSE;
+ if (!s8write(wdh, (guint8) fT->tm_sec, err)) /* s.timeopn.second */
+ return FALSE;
+ if (!s8write(wdh, (guint8) fT->tm_min, err)) /* s.timeopn.minute */
+ return FALSE;
+ if (!s8write(wdh, (guint8) fT->tm_hour, err)) /* s.timeopn.hour */
+ return FALSE;
+ if (!s8write(wdh, (guint8) fT->tm_mday, err)) /* s.timeopn.mday */
+ return FALSE;
+ if (!s0write(wdh, 2, err))
+ return FALSE;
+ if (!s8write(wdh, (guint8) fT->tm_sec, err)) /* s.timeclo.second */
+ return FALSE;
+ if (!s8write(wdh, (guint8) fT->tm_min, err)) /* s.timeclo.minute */
+ return FALSE;
+ if (!s8write(wdh, (guint8) fT->tm_hour, err)) /* s.timeclo.hour */
+ return FALSE;
+ if (!s8write(wdh, (guint8) fT->tm_mday, err)) /* s.timeclo.mday */
+ return FALSE;
+ if (!s0write(wdh, 2, err))
+ return FALSE;
+ if (!s0write(wdh, 6, err)) /* EAddr == 0 */
+ return FALSE;
+ if (!s16write(wdh, 1, err)) /* s.mxseqno */
+ return FALSE;
+ if (!s16write(wdh, 0, err)) /* s.slcoffo */
+ return FALSE;
+ if (!s16write(wdh, 1514, err)) /* s.mxslc */
+ return FALSE;
+ if (!s32write(wdh, itmp->pkts, err)) /* s.totpktt */
+ return FALSE;
+ /*
+ * statrg == 0; ? -1
+ * stptrg == 0; ? -1
+ * s.mxpkta[0]=0
+ */
+ if (!s0write(wdh, 12, err))
+ return FALSE;
+ if (!s32write(wdh, itmp->pkts, err)) /* sr.s.mxpkta[1] */
+ return FALSE;
+ if (!s0write(wdh, 34*4, err)) /* s.mxpkta[2-33]=0 */
+ return FALSE;
+ if (!s16write(wdh, board_type, err))
+ return FALSE;
+ if (!s0write(wdh, 20, err)) /* board_version == 0 */
+ return FALSE;
+ /*-----------------------------------------------------------------*/
+ if (!s16write(wdh, RT_SubfileSummary, err)) /* ssr.rid */
+ return FALSE;
+ if (!s16write(wdh, LA_SubfileSummaryRecordSize-4, err)) /* ssr.rlen */
+ return FALSE;
+ if (!s16write(wdh, 1, err)) /* ssr.seqno */
+ return FALSE;
+ if (!s32write(wdh, itmp->pkts, err)) /* ssr.totpkts */
+ return FALSE;
+ /*-----------------------------------------------------------------*/
+ if (!wtap_dump_file_write(wdh, &LA_CyclicInformationFake,
+ sizeof LA_CyclicInformationFake, err))
+ return FALSE;
+ /*-----------------------------------------------------------------*/
+ if (!s16write(wdh, RT_Index, err)) /* rid */
+ return FALSE;
+ if (!s16write(wdh, LA_IndexRecordSize -4, err)) /* rlen */
+ return FALSE;
+ if (!s16write(wdh, LA_IndexSize, err)) /* idxsp */
+ return FALSE;
+ if (!s0write(wdh, LA_IndexRecordSize - 6, err))
+ return FALSE;
+
+ return TRUE;
+}
+
+/*---------------------------------------------------
+ * Finish writing to a dump file.
+ * Returns TRUE on success, FALSE on failure.
+ *---------------------------------------------------*/
+static gboolean lanalyzer_dump_finish(wtap_dumper *wdh, int *err,
+ gchar **err_info _U_)
+{
+ /* bytes_dumped already accounts for the size of the header,
+ * but lanalyzer_dump_header() (via wtap_dump_file_write())
+ * will keep incrementing it.
+ */
+ gint64 saved_bytes_dumped = wdh->bytes_dumped;
+ lanalyzer_dump_header(wdh,err);
+ wdh->bytes_dumped = saved_bytes_dumped;
+ return *err ? FALSE : TRUE;
+}
+
+static const struct supported_block_type lanalyzer_blocks_supported[] = {
+ /*
+ * We support packet blocks, with no comments or other options.
+ */
+ { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
+};
+
+static const struct file_type_subtype_info lanalyzer_info = {
+ "Novell LANalyzer","lanalyzer", "tr1", NULL,
+ TRUE, BLOCKS_SUPPORTED(lanalyzer_blocks_supported),
+ lanalyzer_dump_can_write_encap, lanalyzer_dump_open, NULL
+};
+
+void register_lanalyzer(void)
+{
+ lanalyzer_file_type_subtype = wtap_register_file_type_subtype(&lanalyzer_info);
+
+ /*
+ * Register name for backwards compatibility with the
+ * wtap_filetypes table in Lua.
+ */
+ wtap_register_backwards_compatibility_lua_name("LANALYZER",
+ lanalyzer_file_type_subtype);
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 6
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * vi: set shiftwidth=6 tabstop=8 expandtab:
+ * :indentSize=6:tabSize=8:noTabs=true:
+ */
diff --git a/wiretap/lanalyzer.h b/wiretap/lanalyzer.h
new file mode 100644
index 00000000..ffae61de
--- /dev/null
+++ b/wiretap/lanalyzer.h
@@ -0,0 +1,17 @@
+/** @file
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __LANALYZER_H__
+#define __LANALYZER_H__
+
+#include <glib.h>
+#include "wtap.h"
+
+wtap_open_return_val lanalyzer_open(wtap *wth, int *err, gchar **err_info);
+
+#endif
diff --git a/wiretap/libpcap.c b/wiretap/libpcap.c
new file mode 100644
index 00000000..f3538078
--- /dev/null
+++ b/wiretap/libpcap.c
@@ -0,0 +1,1590 @@
+/* libpcap.c
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include "wtap-int.h"
+#include "file_wrappers.h"
+#include "required_file_handlers.h"
+#include "pcap-common.h"
+#include "pcap-encap.h"
+#include "libpcap.h"
+#include "erf-common.h"
+#include <wsutil/ws_assert.h>
+
+/* See source to the "libpcap" library for information on the "libpcap"
+ file format. */
+
+/*
+ * Private per-wtap_t data needed to read a file.
+ */
+typedef enum {
+ NOT_SWAPPED,
+ SWAPPED,
+ MAYBE_SWAPPED
+} swapped_type_t;
+
+/*
+ * Variants of pcap, some distinguished by the magic number and some,
+ * alas, not.
+ *
+ * (Don't do that. Srsly.)
+ */
+typedef enum {
+ PCAP, /* OG pcap */
+ PCAP_NSEC, /* PCAP with nanosecond resolution */
+ PCAP_AIX, /* AIX pcap */
+ PCAP_SS990417, /* Modified, from 1999-04-17 patch */
+ PCAP_SS990915, /* Modified, from 1999-09-15 patch */
+ PCAP_SS991029, /* Modified, from 1999-10-29 patch */
+ PCAP_NOKIA, /* Nokia pcap */
+ PCAP_UNKNOWN /* Unknown as yet */
+} pcap_variant_t;
+
+typedef struct {
+ gboolean byte_swapped;
+ swapped_type_t lengths_swapped;
+ guint16 version_major;
+ guint16 version_minor;
+ pcap_variant_t variant;
+ int fcs_len;
+ void *encap_priv;
+} libpcap_t;
+
+/* Try to read the first few records of the capture file. */
+static gboolean libpcap_try_variants(wtap *wth, const pcap_variant_t *variants,
+ size_t n_variants, int *err, gchar **err_info);
+static int libpcap_try(wtap *wth, int *err, gchar **err_info);
+static int libpcap_try_record(wtap *wth, int *err, gchar **err_info);
+
+static gboolean libpcap_read(wtap *wth, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info, gint64 *data_offset);
+static gboolean libpcap_seek_read(wtap *wth, gint64 seek_off,
+ wtap_rec *rec, Buffer *buf, int *err, gchar **err_info);
+static gboolean libpcap_read_packet(wtap *wth, FILE_T fh,
+ wtap_rec *rec, Buffer *buf, int *err, gchar **err_info);
+static int libpcap_read_header(wtap *wth, FILE_T fh, int *err, gchar **err_info,
+ struct pcaprec_ss990915_hdr *hdr);
+static void libpcap_close(wtap *wth);
+
+static gboolean libpcap_dump_pcap(wtap_dumper *wdh, const wtap_rec *rec,
+ const guint8 *pd, int *err, gchar **err_info);
+static gboolean libpcap_dump_pcap_nsec(wtap_dumper *wdh, const wtap_rec *rec,
+ const guint8 *pd, int *err, gchar **err_info);
+static gboolean libpcap_dump_pcap_ss990417(wtap_dumper *wdh,
+ const wtap_rec *rec, const guint8 *pd, int *err, gchar **err_info);
+static gboolean libpcap_dump_pcap_ss990915(wtap_dumper *wdh,
+ const wtap_rec *rec, const guint8 *pd, int *err, gchar **err_info);
+static gboolean libpcap_dump_pcap_ss991029(wtap_dumper *wdh,
+ const wtap_rec *rec, const guint8 *pd, int *err, gchar **err_info);
+static gboolean libpcap_dump_pcap_nokia(wtap_dumper *wdh, const wtap_rec *rec,
+ const guint8 *pd, int *err, gchar **err_info);
+
+/*
+ * Subfields of the field containing the link-layer header type.
+ *
+ * Link-layer header types are assigned for both pcap and
+ * pcapng, and the same value must work with both. In pcapng,
+ * the link-layer header type field in an Interface Description
+ * Block is 16 bits, so only the bottommost 16 bits of the
+ * link-layer header type in a pcap file can be used for the
+ * header type value.
+ *
+ * In libpcap, the upper 16 bits, from the top down, are divided into:
+ *
+ * A 4-bit "FCS length" field, to allow the FCS length to
+ * be specified, just as it can be specified in the if_fcslen
+ * field of the pcapng IDB. The field is in units of 16 bits,
+ * i.e. 1 means 16 bits of FCS, 2 means 32 bits of FCS, etc..
+ *
+ * A reserved bit, which must be zero.
+ *
+ * An "FCS length present" flag; if 0, the "FCS length" field
+ * should be ignored, and if 1, the "FCS length" field should
+ * be used.
+ *
+ * 10 reserved bits, which must be zero. They were originally
+ * intended to be used as a "class" field, allowing additional
+ * classes of link-layer types to be defined, with a class value
+ * of 0 indicating that the link-layer type is a LINKTYPE_ value.
+ * A value of 0x224 was, at one point, used by NetBSD to define
+ * "raw" packet types, with the lower 16 bits containing a
+ * NetBSD AF_ value; see
+ *
+ * https://marc.info/?l=tcpdump-workers&m=98296750229149&w=2
+ *
+ * It's unknown whether those were ever used in capture files,
+ * or if the intent was just to use it as a link-layer type
+ * for BPF programs; NetBSD's libpcap used to support them in
+ * the BPF code generator, but it no longer does so. If it
+ * was ever used in capture files, or if classes other than
+ * "LINKTYPE_ value" are ever useful in capture files, we could
+ * re-enable this, and use the reserved 16 bits following the
+ * link-layer type in pcapng files to hold the class information
+ * there. (Note, BTW, that LINKTYPE_RAW/DLT_RAW is now being
+ * interpreted by libpcap, tcpdump, and Wireshark as "raw IP",
+ * including both IPv4 and IPv6, with the version number in the
+ * header being checked to see which it is, not just "raw IPv4";
+ * there are LINKTYPE_IPV4/DLT_IPV4 and LINKTYPE_IPV6/DLT_IPV6
+ * values if "these are IPv{4,6} and only IPv{4,6} packets"
+ * types are needed.)
+ *
+ * Or we might be able to use it for other purposes.
+ */
+#define LT_LINKTYPE(x) ((x) & 0x0000FFFF)
+#define LT_RESERVED1(x) ((x) & 0x03FF0000)
+#define LT_FCS_LENGTH_PRESENT(x) ((x) & 0x04000000)
+#define LT_FCS_LENGTH(x) (((x) & 0xF0000000) >> 28)
+#define LT_FCS_DATALINK_EXT(x) (((x) & 0xF) << 28) | 0x04000000)
+
+/*
+ * Private file type/subtype values; pcap and nanosecond-resolution
+ * pcap are imported from wiretap/file_access.c.
+ */
+static int pcap_aix_file_type_subtype = -1;
+static int pcap_ss990417_file_type_subtype = -1;
+static int pcap_ss990915_file_type_subtype = -1;
+static int pcap_ss991029_file_type_subtype = -1;
+static int pcap_nokia_file_type_subtype = -1;
+
+/*
+ * pcap variants that use the standard magic number.
+ */
+static const pcap_variant_t variants_standard[] = {
+ PCAP,
+ PCAP_SS990417,
+ PCAP_NOKIA
+};
+#define N_VARIANTS_STANDARD G_N_ELEMENTS(variants_standard)
+
+/*
+ * pcap variants that use the modified magic number.
+ */
+static const pcap_variant_t variants_modified[] = {
+ PCAP_SS991029,
+ PCAP_SS990915
+};
+#define N_VARIANTS_MODIFIED G_N_ELEMENTS(variants_modified)
+
+wtap_open_return_val libpcap_open(wtap *wth, int *err, gchar **err_info)
+{
+ guint32 magic;
+ struct pcap_hdr hdr;
+ gboolean byte_swapped;
+ pcap_variant_t variant;
+ libpcap_t *libpcap;
+ int skip_size = 0;
+ int sizebytes;
+
+ /* Read in the number that should be at the start of a "libpcap" file */
+ if (!wtap_read_bytes(wth->fh, &magic, sizeof magic, err, err_info)) {
+ if (*err != WTAP_ERR_SHORT_READ)
+ return WTAP_OPEN_ERROR;
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ switch (magic) {
+
+ case PCAP_MAGIC:
+ /* Host that wrote it has our byte order, and was running
+ a program using either standard or ss990417 libpcap,
+ or maybe it was written by AIX. That means we don't
+ yet know the variant. */
+ byte_swapped = FALSE;
+ variant = PCAP_UNKNOWN;
+ break;
+
+ case PCAP_SWAPPED_MAGIC:
+ /* Host that wrote it has a byte order opposite to ours,
+ and was running a program using either standard or
+ ss990417 libpcap, or maybe it was written by AIX.
+ That means we don't yet know the variant. */
+ byte_swapped = TRUE;
+ variant = PCAP_UNKNOWN;
+ break;
+
+ case PCAP_IXIAHW_MAGIC:
+ case PCAP_IXIASW_MAGIC:
+ /* Weird Ixia variant that has extra crud, written in our
+ byte order. It's otherwise like standard pcap. */
+ skip_size = 1;
+ byte_swapped = FALSE;
+ variant = PCAP;
+ break;
+
+ case PCAP_SWAPPED_IXIAHW_MAGIC:
+ case PCAP_SWAPPED_IXIASW_MAGIC:
+ /* Weird Ixia variant that has extra crud, written in a
+ byte order opposite to ours. It's otherwise like
+ standard pcap. */
+ skip_size = 1;
+ byte_swapped = TRUE;
+ variant = PCAP;
+ break;
+
+ case PCAP_MODIFIED_MAGIC:
+ /* Host that wrote it has our byte order, and was running
+ a program using either ss990915 or ss991029 libpcap.
+ That means we don't yet know the variant; there's
+ no obvious default, so default to "unknown". */
+ byte_swapped = FALSE;
+ variant = PCAP_UNKNOWN;
+ break;
+
+ case PCAP_SWAPPED_MODIFIED_MAGIC:
+ /* Host that wrote it out has a byte order opposite to
+ ours, and was running a program using either ss990915
+ or ss991029 libpcap. That means we don't yet know
+ the variant; there's no obvious default, so default
+ to "unknown". */
+ byte_swapped = TRUE;
+ variant = PCAP_UNKNOWN;
+ break;
+
+ case PCAP_NSEC_MAGIC:
+ /* Host that wrote it has our byte order, and was writing
+ the file in a format similar to standard libpcap
+ except that the time stamps have nanosecond resolution. */
+ byte_swapped = FALSE;
+ variant = PCAP_NSEC;
+ break;
+
+ case PCAP_SWAPPED_NSEC_MAGIC:
+ /* Host that wrote it out has a byte order opposite to
+ ours, and was writing the file in a format similar to
+ standard libpcap except that the time stamps have
+ nanosecond resolution. */
+ byte_swapped = TRUE;
+ variant = PCAP_NSEC;
+ break;
+
+ default:
+ /* Not a "libpcap" type we know about. */
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ /* Read the rest of the header. */
+ if (!wtap_read_bytes(wth->fh, &hdr, sizeof hdr, err, err_info))
+ return WTAP_OPEN_ERROR;
+ if (skip_size==1 && !wtap_read_bytes(wth->fh, &sizebytes, sizeof sizebytes, err, err_info))
+ return WTAP_OPEN_ERROR;
+
+ if (byte_swapped) {
+ /* Byte-swap the header fields about which we care. */
+ magic = GUINT32_SWAP_LE_BE(magic);
+ hdr.version_major = GUINT16_SWAP_LE_BE(hdr.version_major);
+ hdr.version_minor = GUINT16_SWAP_LE_BE(hdr.version_minor);
+ hdr.snaplen = GUINT32_SWAP_LE_BE(hdr.snaplen);
+ hdr.network = GUINT32_SWAP_LE_BE(hdr.network);
+ }
+ if (hdr.version_major < 2) {
+ /* We only support version 2.0 and later. */
+ *err = WTAP_ERR_UNSUPPORTED;
+ *err_info = ws_strdup_printf("pcap: major version %u unsupported",
+ hdr.version_major);
+ return WTAP_OPEN_ERROR;
+ }
+
+ /* This is a libpcap file */
+ wth->subtype_read = libpcap_read;
+ wth->subtype_seek_read = libpcap_seek_read;
+ wth->subtype_close = libpcap_close;
+ wth->snapshot_length = hdr.snaplen;
+ libpcap = g_new0(libpcap_t, 1);
+ wth->priv = (void *)libpcap;
+ /*
+ * Fill in the information we already know or can determine
+ * at this point, so the private data is usable by the code
+ * that tries reading packets as a heuristic to guess the
+ * variant.
+ */
+ libpcap->byte_swapped = byte_swapped;
+ /* In file format version 2.3, the order of the "incl_len" and
+ "orig_len" fields in the per-packet header was reversed,
+ in order to match the BPF header layout.
+
+ Therefore, in files with versions prior to that, we must swap
+ those two fields.
+
+ Unfortunately, some files were, according to a comment in the
+ "libpcap" source, written with version 2.3 in their headers
+ but without the interchanged fields, so if "incl_len" is
+ greater than "orig_len" - which would make no sense - we
+ assume that we need to swap them in version 2.3 files
+ as well.
+
+ In addition, DG/UX's tcpdump uses version 543.0, and writes
+ the two fields in the pre-2.3 order. */
+ switch (hdr.version_major) {
+
+ case 2:
+ if (hdr.version_minor < 3)
+ libpcap->lengths_swapped = SWAPPED;
+ else if (hdr.version_minor == 3)
+ libpcap->lengths_swapped = MAYBE_SWAPPED;
+ else
+ libpcap->lengths_swapped = NOT_SWAPPED;
+ break;
+
+ case 543:
+ libpcap->lengths_swapped = SWAPPED;
+ break;
+
+ default:
+ libpcap->lengths_swapped = NOT_SWAPPED;
+ break;
+ }
+ libpcap->version_major = hdr.version_major;
+ libpcap->version_minor = hdr.version_minor;
+ /*
+ * Check whether this is an AIX pcap before we convert the
+ * link-layer type in the header file to an encapsulation,
+ * because AIX pcaps use RFC 1573 ifType values in the header.
+ *
+ * AIX pcap files use the standard magic number, and have a
+ * major and minor version of 2.
+ *
+ * Unfortunately, that's also true of older versions of libpcap,
+ * so we need to do some heuristics to try to identify AIX pcap
+ * files.
+ */
+ if (magic == PCAP_MAGIC && hdr.version_major == 2 &&
+ hdr.version_minor == 2) {
+ /*
+ * The AIX libpcap uses RFC 1573 ifType values rather
+ * than LINKTYPE_/DLT_ values in the header; the ifType
+ * values for LAN devices are:
+ *
+ * Ethernet 6
+ * Token Ring 9
+ * FDDI 15
+ *
+ * which correspond to LINKTYPE_IEEE802_5/DLT_IEEE802 (used
+ * for Token Ring), LINKTYPE_PPP/DLT_PPP, and
+ * LINKTYPE_SLIP_BSDOS/DLT_SLIP_BSDOS, respectively, and
+ * the ifType value for a loopback interface is 24, which
+ * currently isn't used by any version of libpcap I know
+ * about (and, as tcpdump.org are assigning LINKTYPE_/DLT_
+ * values above 100, and NetBSD started assigning values
+ * starting at 50, and the values chosen by other libpcaps
+ * appear to stop at 19, it's probably not going to be used
+ * by any libpcap in the future).
+ *
+ * So we shall assume that if the network type is 6, 9, 15,
+ * or 24 it's AIX libpcap.
+ *
+ * We also assume those older versions of libpcap didn't use
+ * LINKTYPE_IEEE802_5/DLT_IEEE802 for Token Ring, and didn't
+ * use LINKTYPE_SLIP_BSDOS/DLT_SLIP_BSDOS as that came later.
+ * It may have used LINKTYPE_PPP/DLT_PPP, however, in which
+ * case we're out of luck; we assume it's Token Ring in AIX
+ * libpcap rather than PPP in standard libpcap, as you're
+ * probably more likely to be handing an AIX libpcap token-
+ *ring capture than an old (pre-libpcap 0.4) PPP capture to
+ * Wireshark.
+ *
+ * AIX pcap files didn't use the upper 16 bits, so we don't
+ * need to ignore them here - they'll be 0.
+ */
+ switch (hdr.network) {
+
+ case 6:
+ hdr.network = 1; /* LINKTYPE_EN10MB, Ethernet */
+ variant = PCAP_AIX;
+ break;
+
+ case 9:
+ hdr.network = 6; /* LINKTYPE_IEEE802_5, Token Ring */
+ variant = PCAP_AIX;
+ break;
+
+ case 15:
+ hdr.network = 10; /* LINKTYPE_FDDI, FDDI */
+ variant = PCAP_AIX;
+ break;
+
+ case 24:
+ hdr.network = 0; /* LINKTYPE_NULL, loopback */
+ variant = PCAP_AIX;
+ break;
+ }
+ }
+
+ /*
+ * Check the main reserved field.
+ */
+ if (LT_RESERVED1(hdr.network) != 0) {
+ *err = WTAP_ERR_UNSUPPORTED;
+ *err_info = ws_strdup_printf("pcap: network type reserved field not zero (0x%08x)",
+ LT_RESERVED1(hdr.network));
+ return WTAP_OPEN_ERROR;
+ }
+
+ /*
+ * Map the link-layer type from the "network" field in
+ * the header to a Wiretap encapsulation.
+ */
+ wth->file_encap = wtap_pcap_encap_to_wtap_encap(LT_LINKTYPE(hdr.network));
+ if (wth->file_encap == WTAP_ENCAP_UNKNOWN) {
+ *err = WTAP_ERR_UNSUPPORTED;
+ *err_info = ws_strdup_printf("pcap: network type %u unknown or unsupported",
+ hdr.network);
+ return WTAP_OPEN_ERROR;
+ }
+
+ /*
+ * Extract the FCS information, if present.
+ */
+ libpcap->fcs_len = -1;
+ if (LT_FCS_LENGTH_PRESENT(hdr.network)) {
+ /*
+ * We have an FCS length, in units of 16 bits.
+ * Convert it to bits.
+ */
+ libpcap->fcs_len = LT_FCS_LENGTH(hdr.network) * 16;
+ }
+
+ libpcap->encap_priv = NULL;
+
+ /*
+ * If this file has the standard magic number, it could be
+ * one of a number of variants, including regular pcap, the
+ * AIX variant, the ss990417 variant, and a Nokia variant.
+ * The ss990417 variant is used in, for example, Red Hat 6.1,
+ * so some versions of AIX, RH 6.1, and some Nokia devices
+ * write files that can't be read by any software that expects
+ * standard libpcap packet record headers if the magic number
+ * is the standard magic number (e.g., any program such as
+ * tcpdump that uses libpcap, when using the standard libpcap,
+ * and Wireshark if we don't do the heuristics below).
+ *
+ * If this file has the patched magic number, used by the
+ * ss990915 and ss991029 variants, then it could be either
+ * of those. The ss991029 variant uses the same packet
+ * record header as the ss990417 variant, but the ss990915
+ * variant uses a packet record header with some additional
+ * fields and it is used in, for example, SuSE 6.3, so SuSE
+ * 6.3 writes files that can't be read by any software that
+ * expects ss990417 packet record headers if the magic number
+ * is the modified magic number.
+ *
+ * So, for the standard and modified magic number:
+ *
+ * For the standard magic number, we first do some heuristic
+ * checks of data from the file header to see if it looks like
+ * an AIX libpcap file. If so, we choose PCAP_AIX as the variant,
+ * and we don't have to do any more guessing.
+ *
+ * Otherwise, we determine the variant by, for each variant,
+ * trying to read the first few packets as if that file were
+ * in that variant's format, and seeing whether the packet
+ * record headers make sense.
+ *
+ * But don't do the latter if the input is a pipe; that would mean
+ * the open won't complete until two packets have been written to
+ * the pipe, unless the pipe is closed after one packet has been
+ * written, so a program reading from the file won't see the
+ * first packet until the second packet has been written.
+ */
+ switch (magic) {
+
+ case PCAP_MAGIC:
+ /*
+ * Original libpcap magic.
+ *
+ * If we still don't know the variant, look at the first
+ * few packets to see what type of per-packet header they
+ * have.
+ *
+ * Default to PCAP, as that's probably what this is;
+ * libpcap_try_variants() will just give up if we're
+ * reading from a pipe.
+ */
+ if (variant == PCAP_UNKNOWN) {
+ if (wth->ispipe) {
+ /*
+ * We can't do the heuristics.
+ * Just go with standard libpcap.
+ */
+ libpcap->variant = PCAP;
+ } else {
+ /*
+ * Try the variants that use the standard
+ * pcap magic number.
+ */
+ if (!libpcap_try_variants(wth, variants_standard,
+ N_VARIANTS_STANDARD, err, err_info)) {
+ /*
+ * File read error.
+ */
+ return WTAP_OPEN_ERROR;
+ }
+ }
+ } else {
+ /*
+ * Use the variant we found.
+ */
+ libpcap->variant = variant;
+ }
+ break;
+
+ case PCAP_MODIFIED_MAGIC:
+ /*
+ * Modified libpcap magic, from Alexey's later two
+ * patches.
+ *
+ * This might be one of two different flavors of
+ * pcap file, with different modified per-packet
+ * headers.
+ *
+ * If we're reading from a pipe, we don't have an
+ * obvious choice to use as a default.
+ */
+ if (wth->ispipe) {
+ /*
+ * We can't do the heuristics.
+ * There's no obvious choice to use as a
+ * default, so just report an error.
+ */
+ *err = WTAP_ERR_UNSUPPORTED;
+ *err_info = g_strdup("pcap: that type of pcap file can't be read from a pipe");
+ return WTAP_OPEN_ERROR;
+ } else {
+ /*
+ * Try the variants that use the modified
+ * pcap magic number.
+ */
+ if (!libpcap_try_variants(wth, variants_modified,
+ N_VARIANTS_MODIFIED, err, err_info)) {
+ /*
+ * File read error.
+ */
+ return WTAP_OPEN_ERROR;
+ }
+ }
+ break;
+
+ default:
+ /*
+ * None of these require heuristics to guess the
+ * variant; just use the variant we found.
+ */
+ libpcap->variant = variant;
+ break;
+ }
+
+ /*
+ * Set the file type and subtype, and handle some variants
+ * specially.
+ */
+ switch (libpcap->variant) {
+
+ case PCAP:
+ wth->file_type_subtype = pcap_file_type_subtype;
+ wth->file_tsprec = WTAP_TSPREC_USEC;
+ break;
+
+ case PCAP_NSEC:
+ wth->file_type_subtype = pcap_nsec_file_type_subtype;
+ wth->file_tsprec = WTAP_TSPREC_NSEC;
+ break;
+
+ case PCAP_SS990417:
+ wth->file_type_subtype = pcap_ss990417_file_type_subtype;
+ wth->file_tsprec = WTAP_TSPREC_USEC;
+ break;
+
+ case PCAP_SS990915:
+ wth->file_type_subtype = pcap_ss990915_file_type_subtype;
+ wth->file_tsprec = WTAP_TSPREC_USEC;
+ break;
+
+ case PCAP_SS991029:
+ wth->file_type_subtype = pcap_ss991029_file_type_subtype;
+ wth->file_tsprec = WTAP_TSPREC_USEC;
+ break;
+
+ case PCAP_AIX:
+ wth->file_type_subtype = pcap_aix_file_type_subtype;
+ wth->file_tsprec = WTAP_TSPREC_NSEC;
+ break;
+
+ case PCAP_NOKIA:
+ wth->file_type_subtype = pcap_nokia_file_type_subtype;
+ wth->file_tsprec = WTAP_TSPREC_USEC;
+ /*
+ * We treat a DLT_ value of 13 specially - it appears
+ * that in Nokia libpcap format, it's some form of ATM
+ * with what I suspect is a pseudo-header (even though
+ * Nokia's IPSO is based on FreeBSD, which #defines
+ * DLT_SLIP_BSDOS as 13).
+ *
+ * Treat 13 as WTAP_ENCAP_ATM_PDUS, rather than as what
+ * we normally treat it.
+ */
+ if (hdr.network == 13)
+ wth->file_encap = WTAP_ENCAP_ATM_PDUS;
+ break;
+
+ default:
+ ws_assert_not_reached();
+ }
+
+ if (wth->file_encap == WTAP_ENCAP_ERF) {
+ /* Reset the ERF interface lookup table */
+ libpcap->encap_priv = erf_priv_create();
+ } else {
+ /*
+ * Add an IDB; we don't know how many interfaces were
+ * involved, so we just say one interface, about which
+ * we only know the link-layer type, snapshot length,
+ * and time stamp resolution.
+ */
+ wtap_add_generated_idb(wth);
+ }
+
+ return WTAP_OPEN_MINE;
+}
+
+static gboolean libpcap_try_variants(wtap *wth, const pcap_variant_t *variants,
+ size_t n_variants, int *err, gchar **err_info)
+{
+ libpcap_t *libpcap = (libpcap_t *)wth->priv;
+#define MAX_FIGURES_OF_MERIT \
+ MAX(N_VARIANTS_MODIFIED, N_VARIANTS_STANDARD)
+ int figures_of_merit[MAX_FIGURES_OF_MERIT];
+ int best_variant;
+ gint64 first_packet_offset;
+
+ first_packet_offset = file_tell(wth->fh);
+ for (size_t i = 0; i < n_variants; i++) {
+ libpcap->variant = variants[i];
+ figures_of_merit[i] = libpcap_try(wth, err, err_info);
+ if (figures_of_merit[i] == -1) {
+ /*
+ * Well, we couldn't even read it. Give up.
+ */
+ return FALSE;
+ }
+ if (figures_of_merit[i] == 0) {
+ /*
+ * This format doesn't have any issues.
+ * Put the seek pointer back, and finish,
+ * using that format as the subtype.
+ */
+ if (file_seek(wth->fh, first_packet_offset, SEEK_SET,
+ err) == -1) {
+ return FALSE;
+ }
+ return TRUE;
+ }
+
+ /*
+ * OK, we've recorded the figure of merit for this
+ * one; go back to the first packet and try the
+ * next one.
+ */
+ if (file_seek(wth->fh, first_packet_offset, SEEK_SET,
+ err) == -1) {
+ return FALSE;
+ }
+ }
+
+ /*
+ * OK, none are perfect; let's see which one is least bad.
+ */
+ best_variant = INT_MAX;
+ for (size_t i = 0; i < n_variants; i++) {
+ /*
+ * Is this subtype better than the last one we saw?
+ */
+ if (figures_of_merit[i] < best_variant) {
+ /*
+ * Yes. Choose it until we find a better one.
+ */
+ libpcap->variant = variants[i];
+ best_variant = figures_of_merit[i];
+ }
+ }
+ return TRUE;
+}
+
+/*
+ * Maximum number of records to try to read. Must be >= 2.
+ */
+#define MAX_RECORDS_TO_TRY 3
+
+/* Try to read the first MAX_RECORDS_TO_TRY records of the capture file. */
+static int libpcap_try(wtap *wth, int *err, gchar **err_info)
+{
+ int ret;
+ int i;
+
+ /*
+ * Attempt to read the first record.
+ */
+ ret = libpcap_try_record(wth, err, err_info);
+ if (ret != 0) {
+ /*
+ * Error or mismatch; return the error indication or
+ * the figure of merit (demerit?).
+ */
+ return ret;
+ }
+
+ /*
+ * Now attempt to read the next MAX_RECORDS_TO_TRY-1 records.
+ * Get the maximum figure of (de?)merit, as that represents the
+ * figure of merit for the record that had the most problems.
+ */
+ for (i = 1; i < MAX_RECORDS_TO_TRY; i++) {
+ /*
+ * Attempt to read this record.
+ */
+ ret = libpcap_try_record(wth, err, err_info);
+ if (ret != 0) {
+ /*
+ * Error or mismatch; return the error indication or
+ * the figure of merit (demerit?).
+ */
+ return ret;
+ }
+ }
+
+ /* They all succeeded. */
+ return 0;
+}
+
+/* Read the header of the next packet and, if that succeeds, read the
+ data of the next packet.
+
+ Return -1 on an I/O error, 0 on success, or a positive number if the
+ header looks corrupt. The higher the positive number, the more things
+ are wrong with the header; this is used by the heuristics that try to
+ guess what type of file it is, with the type with the fewest problems
+ being chosen. */
+static int libpcap_try_record(wtap *wth, int *err, gchar **err_info)
+{
+ /*
+ * pcaprec_ss990915_hdr is the largest header type.
+ */
+ struct pcaprec_ss990915_hdr rec_hdr;
+ int ret;
+
+ if (!libpcap_read_header(wth, wth->fh, err, err_info, &rec_hdr)) {
+ if (*err == 0) {
+ /*
+ * EOF - assume the file is in this format.
+ * This means it doesn't have all the
+ * records we're trying to read.
+ */
+ return 0;
+ }
+ if (*err == WTAP_ERR_SHORT_READ) {
+ /*
+ * Short read; this might be a corrupt
+ * file in this format or might not be
+ * in this format. Return a figure of
+ * merit of 1.
+ */
+ return 1;
+ }
+ /* Hard error. */
+ return -1;
+ }
+
+ ret = 0; /* start out presuming everything's OK */
+
+ /*
+ * The only file types for which we have to do variant
+ * determination by looking at packets have microsecond
+ * resolution; treat fractions-of-a-second values >= 1 000 000
+ * as an indication that the header format might not be
+ * what we think it is.
+ */
+ if (rec_hdr.hdr.ts_usec >= 1000000)
+ ret++;
+
+ if (rec_hdr.hdr.incl_len > wtap_max_snaplen_for_encap(wth->file_encap)) {
+ /*
+ * Probably either a corrupt capture file or a file
+ * of a type different from the one we're trying.
+ */
+ ret++;
+ }
+
+ if (rec_hdr.hdr.orig_len > 128*1024*1024) {
+ /*
+ * In theory I guess the on-the-wire packet size can be
+ * arbitrarily large, and it can certainly be larger than the
+ * maximum snapshot length which bounds the snapshot size,
+ * but any file claiming 128MB in a single packet is *probably*
+ * corrupt, and treating them as such makes the heuristics
+ * much more reliable. See, for example,
+ *
+ * https://gitlab.com/wireshark/wireshark/-/issues/9634
+ *
+ * (128MB is an arbitrary size at this point, chosen to be
+ * large enough for the largest D-Bus packet).
+ */
+ ret++;
+ }
+
+ if (rec_hdr.hdr.incl_len > wth->snapshot_length) {
+ /*
+ * This is not a fatal error, and packets that have one
+ * such packet probably have thousands. For discussion,
+ * see
+ * https://www.wireshark.org/lists/wireshark-dev/201307/msg00076.html
+ * and related messages.
+ *
+ * The packet contents will be copied to a Buffer, which
+ * expands as necessary to hold the contents; we don't have
+ * to worry about fixed-length buffers allocated based on
+ * the original snapshot length.
+ *
+ * We just treat this as an indication that we might be
+ * trying the wrong file type here.
+ */
+ ret++;
+ }
+
+ if (rec_hdr.hdr.incl_len > rec_hdr.hdr.orig_len) {
+ /*
+ * Another hint that this might be the wrong file type.
+ */
+ ret++;
+ }
+
+ if (ret != 0) {
+ /*
+ * Might be the wrong file type; stop trying, and give
+ * this as the figure of merit for this file type.
+ */
+ return ret;
+ }
+
+ /*
+ * Now skip over the record's data, under the assumption that
+ * the header is sane.
+ */
+ if (!wtap_read_bytes(wth->fh, NULL, rec_hdr.hdr.incl_len, err,
+ err_info)) {
+ if (*err == WTAP_ERR_SHORT_READ) {
+ /*
+ * Short read - treat that as a suggestion that
+ * the header isn't sane, and return a figure of
+ * merit value of 1.
+ */
+ return 1;
+ }
+ /* Hard error. */
+ return -1;
+ }
+
+ /* Success. */
+ return 0;
+}
+
+/* Read the next packet */
+static gboolean libpcap_read(wtap *wth, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info, gint64 *data_offset)
+{
+ *data_offset = file_tell(wth->fh);
+
+ return libpcap_read_packet(wth, wth->fh, rec, buf, err, err_info);
+}
+
+static gboolean
+libpcap_seek_read(wtap *wth, gint64 seek_off, wtap_rec *rec,
+ Buffer *buf, int *err, gchar **err_info)
+{
+ if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
+ return FALSE;
+
+ if (!libpcap_read_packet(wth, wth->random_fh, rec, buf, err,
+ err_info)) {
+ if (*err == 0)
+ *err = WTAP_ERR_SHORT_READ;
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static gboolean
+libpcap_read_packet(wtap *wth, FILE_T fh, wtap_rec *rec,
+ Buffer *buf, int *err, gchar **err_info)
+{
+ struct pcaprec_ss990915_hdr hdr;
+ guint packet_size;
+ guint orig_size;
+ int phdr_len;
+ libpcap_t *libpcap = (libpcap_t *)wth->priv;
+ gboolean is_nokia;
+
+ if (!libpcap_read_header(wth, fh, err, err_info, &hdr))
+ return FALSE;
+
+ if (hdr.hdr.incl_len > wtap_max_snaplen_for_encap(wth->file_encap)) {
+ /*
+ * Probably a corrupt capture file; return an error,
+ * so that our caller doesn't blow up trying to allocate
+ * space for an immensely-large packet.
+ */
+ *err = WTAP_ERR_BAD_FILE;
+ if (err_info != NULL) {
+ *err_info = ws_strdup_printf("pcap: File has %u-byte packet, bigger than maximum of %u",
+ hdr.hdr.incl_len,
+ wtap_max_snaplen_for_encap(wth->file_encap));
+ }
+ return FALSE;
+ }
+
+ packet_size = hdr.hdr.incl_len;
+ orig_size = hdr.hdr.orig_len;
+
+ /*
+ * AIX appears to put 3 bytes of padding in front of FDDI
+ * frames; strip that crap off.
+ */
+ if (libpcap->variant == PCAP_AIX &&
+ (wth->file_encap == WTAP_ENCAP_FDDI ||
+ wth->file_encap == WTAP_ENCAP_FDDI_BITSWAPPED)) {
+ /*
+ * The packet size is really a record size and includes
+ * the padding.
+ */
+ packet_size -= 3;
+ orig_size -= 3;
+
+ /*
+ * Skip the padding.
+ */
+ if (!wtap_read_bytes(fh, NULL, 3, err, err_info))
+ return FALSE;
+ }
+
+ is_nokia = (libpcap->variant == PCAP_NOKIA);
+ phdr_len = pcap_process_pseudo_header(fh, is_nokia,
+ wth->file_encap, packet_size, rec, err, err_info);
+ if (phdr_len < 0)
+ return FALSE; /* error */
+
+ /*
+ * Don't count any pseudo-header as part of the packet.
+ */
+ orig_size -= phdr_len;
+ packet_size -= phdr_len;
+
+ rec->rec_type = REC_TYPE_PACKET;
+ rec->block = wtap_block_create(WTAP_BLOCK_PACKET);
+ rec->presence_flags = WTAP_HAS_TS|WTAP_HAS_CAP_LEN;
+
+ /* Update the timestamp, if not already done */
+ if (wth->file_encap != WTAP_ENCAP_ERF) {
+ rec->ts.secs = hdr.hdr.ts_sec;
+ if (libpcap->variant == PCAP_NSEC ||
+ libpcap->variant == PCAP_AIX)
+ rec->ts.nsecs = hdr.hdr.ts_usec;
+ else
+ rec->ts.nsecs = hdr.hdr.ts_usec * 1000;
+ } else {
+ int interface_id;
+ /* Set interface ID for ERF format */
+ rec->presence_flags |= WTAP_HAS_INTERFACE_ID;
+ if ((interface_id = erf_populate_interface_from_header((erf_t*) libpcap->encap_priv, wth, &rec->rec_header.packet_header.pseudo_header, err, err_info)) < 0)
+ return FALSE;
+
+ rec->rec_header.packet_header.interface_id = (guint) interface_id;
+ }
+ rec->rec_header.packet_header.caplen = packet_size;
+ rec->rec_header.packet_header.len = orig_size;
+
+ /*
+ * Read the packet data.
+ */
+ if (!wtap_read_packet_bytes(fh, buf, packet_size, err, err_info))
+ return FALSE; /* failed */
+
+ pcap_read_post_process(is_nokia, wth->file_encap, rec,
+ ws_buffer_start_ptr(buf), libpcap->byte_swapped, libpcap->fcs_len);
+ return TRUE;
+}
+
+/* Read the header of the next packet.
+
+ Return FALSE on an error, TRUE on success. */
+static int libpcap_read_header(wtap *wth, FILE_T fh, int *err, gchar **err_info,
+ struct pcaprec_ss990915_hdr *hdr)
+{
+ int bytes_to_read;
+ guint32 temp;
+ libpcap_t *libpcap = (libpcap_t *)wth->priv;
+
+ switch (libpcap->variant) {
+
+ case PCAP:
+ case PCAP_AIX:
+ case PCAP_NSEC:
+ bytes_to_read = sizeof (struct pcaprec_hdr);
+ break;
+
+ case PCAP_SS990417:
+ case PCAP_SS991029:
+ bytes_to_read = sizeof (struct pcaprec_modified_hdr);
+ break;
+
+ case PCAP_SS990915:
+ bytes_to_read = sizeof (struct pcaprec_ss990915_hdr);
+ break;
+
+ case PCAP_NOKIA:
+ bytes_to_read = sizeof (struct pcaprec_nokia_hdr);
+ break;
+
+ default:
+ bytes_to_read = 0;
+ ws_assert_not_reached();
+ }
+ if (!wtap_read_bytes_or_eof(fh, hdr, bytes_to_read, err, err_info))
+ return FALSE;
+
+ if (libpcap->byte_swapped) {
+ /* Byte-swap the record header fields. */
+ hdr->hdr.ts_sec = GUINT32_SWAP_LE_BE(hdr->hdr.ts_sec);
+ hdr->hdr.ts_usec = GUINT32_SWAP_LE_BE(hdr->hdr.ts_usec);
+ hdr->hdr.incl_len = GUINT32_SWAP_LE_BE(hdr->hdr.incl_len);
+ hdr->hdr.orig_len = GUINT32_SWAP_LE_BE(hdr->hdr.orig_len);
+ }
+
+ /* Swap the "incl_len" and "orig_len" fields, if necessary. */
+ switch (libpcap->lengths_swapped) {
+
+ case NOT_SWAPPED:
+ break;
+
+ case MAYBE_SWAPPED:
+ if (hdr->hdr.incl_len <= hdr->hdr.orig_len) {
+ /*
+ * The captured length is <= the actual length,
+ * so presumably they weren't swapped.
+ */
+ break;
+ }
+ /* FALLTHROUGH */
+
+ case SWAPPED:
+ temp = hdr->hdr.orig_len;
+ hdr->hdr.orig_len = hdr->hdr.incl_len;
+ hdr->hdr.incl_len = temp;
+ break;
+ }
+
+ return TRUE;
+}
+
+/* Returns 0 if we could write the specified encapsulation type,
+ an error indication otherwise. */
+static int libpcap_dump_can_write_encap(int encap)
+{
+ /* Per-packet encapsulations aren't supported. */
+ if (encap == WTAP_ENCAP_PER_PACKET)
+ return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
+
+ if (wtap_wtap_encap_to_pcap_encap(encap) == -1)
+ return WTAP_ERR_UNWRITABLE_ENCAP;
+
+ return 0;
+}
+
+static gboolean libpcap_dump_write_file_header(wtap_dumper *wdh, guint32 magic,
+ int *err)
+{
+ struct pcap_hdr file_hdr;
+
+ if (!wtap_dump_file_write(wdh, &magic, sizeof magic, err))
+ return FALSE;
+
+ /* current "libpcap" format is 2.4 */
+ file_hdr.version_major = 2;
+ file_hdr.version_minor = 4;
+ file_hdr.thiszone = 0; /* XXX - current offset? */
+ file_hdr.sigfigs = 0; /* unknown, but also apparently unused */
+ /*
+ * Tcpdump cannot handle capture files with a snapshot length of 0,
+ * as BPF filters return either 0 if they fail or the snapshot length
+ * if they succeed, and a snapshot length of 0 means success is
+ * indistinguishable from failure and the filter expression would
+ * reject all packets.
+ *
+ * A snapshot length of 0, inside Wiretap, means "snapshot length
+ * unknown"; if the snapshot length supplied to us is 0, we make
+ * the snapshot length in the header file the maximum for the
+ * link-layer type we'll be writing.
+ */
+ file_hdr.snaplen = (wdh->snaplen != 0) ? (guint)wdh->snaplen :
+ wtap_max_snaplen_for_encap(wdh->file_encap);
+ file_hdr.network = wtap_wtap_encap_to_pcap_encap(wdh->file_encap);
+ if (!wtap_dump_file_write(wdh, &file_hdr, sizeof file_hdr, err))
+ return FALSE;
+
+ return TRUE;
+}
+
+/* Good old fashioned pcap.
+ Returns TRUE on success, FALSE on failure; sets "*err" to an error code on
+ failure */
+static gboolean
+libpcap_dump_open_pcap(wtap_dumper *wdh, int *err, gchar **err_info _U_)
+{
+ /* This is a libpcap file */
+ wdh->subtype_write = libpcap_dump_pcap;
+
+ /* Write the file header. */
+ return libpcap_dump_write_file_header(wdh, PCAP_MAGIC, err);
+}
+
+/* Like classic pcap, but with nanosecond resolution.
+ Returns TRUE on success, FALSE on failure; sets "*err" to an error code on
+ failure */
+static gboolean
+libpcap_dump_open_pcap_nsec(wtap_dumper *wdh, int *err, gchar **err_info _U_)
+{
+ /* This is a nanosecond-resolution libpcap file */
+ wdh->subtype_write = libpcap_dump_pcap_nsec;
+
+ /* Write the file header. */
+ return libpcap_dump_write_file_header(wdh, PCAP_NSEC_MAGIC, err);
+}
+
+/* Modified, but with the old magic, sigh.
+ Returns TRUE on success, FALSE on failure; sets "*err" to an error code on
+ failure */
+static gboolean
+libpcap_dump_open_pcap_ss990417(wtap_dumper *wdh, int *err,
+ gchar **err_info _U_)
+{
+ /* This is a modified-by-patch-SS990417 libpcap file */
+ wdh->subtype_write = libpcap_dump_pcap_ss990417;
+
+ /* Write the file header. */
+ return libpcap_dump_write_file_header(wdh, PCAP_MAGIC, err);
+}
+
+/* New magic, extra crap.
+ Returns TRUE on success, FALSE on failure; sets "*err" to an error code on
+ failure */
+static gboolean
+libpcap_dump_open_pcap_ss990915(wtap_dumper *wdh, int *err,
+ gchar **err_info _U_)
+{
+ /* This is a modified-by-patch-SS990915 libpcap file */
+ wdh->subtype_write = libpcap_dump_pcap_ss990915;
+
+ /* Write the file header. */
+ return libpcap_dump_write_file_header(wdh, PCAP_MODIFIED_MAGIC, err);
+}
+
+/* Same magic as SS990915, *different* extra crap, sigh.
+ Returns TRUE on success, FALSE on failure; sets "*err" to an error code on
+ failure */
+static gboolean
+libpcap_dump_open_pcap_ss991029(wtap_dumper *wdh, int *err,
+ gchar **err_info _U_)
+{
+ /* This is a modified-by-patch-SS991029 libpcap file */
+ wdh->subtype_write = libpcap_dump_pcap_ss991029;
+
+ /* Write the file header. */
+ return libpcap_dump_write_file_header(wdh, PCAP_MODIFIED_MAGIC, err);
+}
+
+static void libpcap_close(wtap *wth)
+{
+ libpcap_t *libpcap = (libpcap_t *)wth->priv;
+
+ if (libpcap->encap_priv) {
+ switch (wth->file_encap) {
+
+ case WTAP_ENCAP_ERF:
+ erf_priv_free((erf_t*) libpcap->encap_priv);
+ break;
+
+ default:
+ g_free(libpcap->encap_priv);
+ break;
+ }
+ }
+}
+
+/* Nokia libpcap of some sort.
+ Returns TRUE on success, FALSE on failure; sets "*err" to an error code on
+ failure */
+static gboolean
+libpcap_dump_open_pcap_nokia(wtap_dumper *wdh, int *err, gchar **err_info _U_)
+{
+ /* This is a Nokia libpcap file */
+ wdh->subtype_write = libpcap_dump_pcap_nokia;
+
+ /* Write the file header. */
+ return libpcap_dump_write_file_header(wdh, PCAP_MAGIC, err);
+}
+
+static gboolean
+libpcap_dump_write_packet(wtap_dumper *wdh, const wtap_rec *rec,
+ struct pcaprec_hdr *hdr, size_t hdr_size, const guint8 *pd, int *err)
+{
+ const union wtap_pseudo_header *pseudo_header = &rec->rec_header.packet_header.pseudo_header;
+ int phdrsize;
+
+ phdrsize = pcap_get_phdr_size(wdh->file_encap, pseudo_header);
+
+ /* We can only write packet records. */
+ if (rec->rec_type != REC_TYPE_PACKET) {
+ *err = WTAP_ERR_UNWRITABLE_REC_TYPE;
+ return FALSE;
+ }
+
+ /*
+ * Make sure this packet doesn't have a link-layer type that
+ * differs from the one for the file.
+ */
+ if (wdh->file_encap != rec->rec_header.packet_header.pkt_encap) {
+ *err = WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
+ return FALSE;
+ }
+
+ /*
+ * Don't write anything we're not willing to read.
+ * (The cast is to prevent an overflow.)
+ */
+ if ((guint64)rec->rec_header.packet_header.caplen + phdrsize > wtap_max_snaplen_for_encap(wdh->file_encap)) {
+ *err = WTAP_ERR_PACKET_TOO_LARGE;
+ return FALSE;
+ }
+
+ hdr->incl_len = rec->rec_header.packet_header.caplen + phdrsize;
+ hdr->orig_len = rec->rec_header.packet_header.len + phdrsize;
+
+ if (!wtap_dump_file_write(wdh, hdr, hdr_size, err))
+ return FALSE;
+
+ if (!pcap_write_phdr(wdh, wdh->file_encap, pseudo_header, err))
+ return FALSE;
+
+ if (!wtap_dump_file_write(wdh, pd, rec->rec_header.packet_header.caplen, err))
+ return FALSE;
+ return TRUE;
+}
+
+/* Good old fashioned pcap.
+ Write a record for a packet to a dump file.
+ Returns TRUE on success, FALSE on failure. */
+static gboolean
+libpcap_dump_pcap(wtap_dumper *wdh, const wtap_rec *rec, const guint8 *pd,
+ int *err, gchar **err_info _U_)
+{
+ struct pcaprec_hdr rec_hdr;
+
+ /*
+ * Some code that reads libpcap files may handle time
+ * stamps as unsigned, but most of it probably handles
+ * them as signed.
+ */
+ if (rec->ts.secs < 0 || rec->ts.secs > G_MAXINT32) {
+ *err = WTAP_ERR_TIME_STAMP_NOT_SUPPORTED;
+ return FALSE;
+ }
+ rec_hdr.ts_sec = (guint32) rec->ts.secs;
+ rec_hdr.ts_usec = rec->ts.nsecs / 1000;
+ return libpcap_dump_write_packet(wdh, rec, &rec_hdr, sizeof rec_hdr,
+ pd, err);
+}
+
+/* Like classic pcap, but with nanosecond resolution.
+ Write a record for a packet to a dump file.
+ Returns TRUE on success, FALSE on failure. */
+static gboolean
+libpcap_dump_pcap_nsec(wtap_dumper *wdh, const wtap_rec *rec, const guint8 *pd,
+ int *err, gchar **err_info _U_)
+{
+ struct pcaprec_hdr rec_hdr;
+
+ /*
+ * Some code that reads libpcap files may handle time
+ * stamps as unsigned, but most of it probably handles
+ * them as signed.
+ */
+ if (rec->ts.secs < 0 || rec->ts.secs > G_MAXINT32) {
+ *err = WTAP_ERR_TIME_STAMP_NOT_SUPPORTED;
+ return FALSE;
+ }
+ rec_hdr.ts_sec = (guint32) rec->ts.secs;
+ rec_hdr.ts_usec = rec->ts.nsecs;
+ return libpcap_dump_write_packet(wdh, rec, &rec_hdr, sizeof rec_hdr,
+ pd, err);
+}
+
+/* Modified, but with the old magic, sigh.
+ Write a record for a packet to a dump file.
+ Returns TRUE on success, FALSE on failure. */
+static gboolean
+libpcap_dump_pcap_ss990417(wtap_dumper *wdh, const wtap_rec *rec,
+ const guint8 *pd, int *err, gchar **err_info _U_)
+{
+ struct pcaprec_modified_hdr rec_hdr;
+
+ /*
+ * Some code that reads libpcap files may handle time
+ * stamps as unsigned, but most of it probably handles
+ * them as signed.
+ */
+ if (rec->ts.secs < 0 || rec->ts.secs > G_MAXINT32) {
+ *err = WTAP_ERR_TIME_STAMP_NOT_SUPPORTED;
+ return FALSE;
+ }
+ rec_hdr.hdr.ts_sec = (guint32) rec->ts.secs;
+ rec_hdr.hdr.ts_usec = rec->ts.nsecs / 1000;
+ /* XXX - what should we supply here?
+
+ Alexey's "libpcap" looks up the interface in the system's
+ interface list if "ifindex" is non-zero, and prints
+ the interface name. It ignores "protocol", and uses
+ "pkt_type" to tag the packet as "host", "broadcast",
+ "multicast", "other host", "outgoing", or "none of the
+ above", but that's it.
+
+ If the capture we're writing isn't a modified or
+ RH 6.1 capture, we'd have to do some work to
+ generate the packet type and interface index - and
+ we can't generate the interface index unless we
+ just did the capture ourselves in any case.
+
+ I'm inclined to continue to punt; systems other than
+ those with the older patch can read standard "libpcap"
+ files, and systems with the older patch, e.g. RH 6.1,
+ will just have to live with this. */
+ rec_hdr.ifindex = 0;
+ rec_hdr.protocol = 0;
+ rec_hdr.pkt_type = 0;
+ return libpcap_dump_write_packet(wdh, rec, &rec_hdr.hdr, sizeof rec_hdr,
+ pd, err);
+}
+
+/* New magic, extra crap.
+ Write a record for a packet to a dump file.
+ Returns TRUE on success, FALSE on failure. */
+static gboolean
+libpcap_dump_pcap_ss990915(wtap_dumper *wdh, const wtap_rec *rec,
+ const guint8 *pd, int *err, gchar **err_info _U_)
+{
+ struct pcaprec_ss990915_hdr rec_hdr;
+
+ /*
+ * Some code that reads libpcap files may handle time
+ * stamps as unsigned, but most of it probably handles
+ * them as signed.
+ */
+ if (rec->ts.secs < 0 || rec->ts.secs > G_MAXINT32) {
+ *err = WTAP_ERR_TIME_STAMP_NOT_SUPPORTED;
+ return FALSE;
+ }
+ rec_hdr.hdr.ts_sec = (guint32) rec->ts.secs;
+ rec_hdr.hdr.ts_usec = rec->ts.nsecs / 1000;
+ rec_hdr.ifindex = 0;
+ rec_hdr.protocol = 0;
+ rec_hdr.pkt_type = 0;
+ rec_hdr.cpu1 = 0;
+ rec_hdr.cpu2 = 0;
+ return libpcap_dump_write_packet(wdh, rec, &rec_hdr.hdr, sizeof rec_hdr,
+ pd, err);
+}
+
+/* Same magic as SS990915, *different* extra crap, sigh.
+ Write a record for a packet to a dump file.
+ Returns TRUE on success, FALSE on failure. */
+static gboolean
+libpcap_dump_pcap_ss991029(wtap_dumper *wdh, const wtap_rec *rec,
+ const guint8 *pd, int *err, gchar **err_info _U_)
+{
+ struct pcaprec_modified_hdr rec_hdr;
+
+ /*
+ * Some code that reads libpcap files may handle time
+ * stamps as unsigned, but most of it probably handles
+ * them as signed.
+ */
+ if (rec->ts.secs < 0 || rec->ts.secs > G_MAXINT32) {
+ *err = WTAP_ERR_TIME_STAMP_NOT_SUPPORTED;
+ return FALSE;
+ }
+ rec_hdr.hdr.ts_sec = (guint32) rec->ts.secs;
+ rec_hdr.hdr.ts_usec = rec->ts.nsecs / 1000;
+ /* XXX - what should we supply here?
+
+ Alexey's "libpcap" looks up the interface in the system's
+ interface list if "ifindex" is non-zero, and prints
+ the interface name. It ignores "protocol", and uses
+ "pkt_type" to tag the packet as "host", "broadcast",
+ "multicast", "other host", "outgoing", or "none of the
+ above", but that's it.
+
+ If the capture we're writing isn't a modified or
+ RH 6.1 capture, we'd have to do some work to
+ generate the packet type and interface index - and
+ we can't generate the interface index unless we
+ just did the capture ourselves in any case.
+
+ I'm inclined to continue to punt; systems other than
+ those with the older patch can read standard "libpcap"
+ files, and systems with the older patch, e.g. RH 6.1,
+ will just have to live with this. */
+ rec_hdr.ifindex = 0;
+ rec_hdr.protocol = 0;
+ rec_hdr.pkt_type = 0;
+ return libpcap_dump_write_packet(wdh, rec, &rec_hdr.hdr, sizeof rec_hdr,
+ pd, err);
+}
+
+/* Nokia libpcap of some sort.
+ Write a record for a packet to a dump file.
+ Returns TRUE on success, FALSE on failure. */
+static gboolean
+libpcap_dump_pcap_nokia(wtap_dumper *wdh, const wtap_rec *rec,
+ const guint8 *pd, int *err, gchar **err_info _U_)
+{
+ struct pcaprec_nokia_hdr rec_hdr;
+ const union wtap_pseudo_header *pseudo_header = &rec->rec_header.packet_header.pseudo_header;
+
+ /*
+ * Some code that reads libpcap files may handle time
+ * stamps as unsigned, but most of it probably handles
+ * them as signed.
+ */
+ if (rec->ts.secs < 0 || rec->ts.secs > G_MAXINT32) {
+ *err = WTAP_ERR_TIME_STAMP_NOT_SUPPORTED;
+ return FALSE;
+ }
+ rec_hdr.hdr.ts_sec = (guint32) rec->ts.secs;
+ rec_hdr.hdr.ts_usec = rec->ts.nsecs / 1000;
+ /* restore the "mysterious stuff" that came with the packet */
+ memcpy(rec_hdr.stuff, pseudo_header->nokia.stuff, 4);
+ return libpcap_dump_write_packet(wdh, rec, &rec_hdr.hdr, sizeof rec_hdr,
+ pd, err);
+}
+
+static const struct supported_block_type pcap_blocks_supported[] = {
+ /*
+ * We support packet blocks, with no comments or other options.
+ */
+ { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
+};
+
+static const struct file_type_subtype_info pcap_info = {
+ /* Gianluca Varenni suggests that we add "deprecated" to the description. */
+ "Wireshark/tcpdump/... - pcap", "pcap", "pcap", "cap;dmp",
+ FALSE, BLOCKS_SUPPORTED(pcap_blocks_supported),
+ libpcap_dump_can_write_encap, libpcap_dump_open_pcap, NULL
+};
+
+static const struct file_type_subtype_info pcap_nsec_info = {
+ "Wireshark/tcpdump/... - nanosecond pcap", "nsecpcap", "pcap", "cap;dmp",
+ FALSE, BLOCKS_SUPPORTED(pcap_blocks_supported),
+ libpcap_dump_can_write_encap, libpcap_dump_open_pcap_nsec, NULL
+};
+
+static const struct file_type_subtype_info pcap_aix_info = {
+ "AIX tcpdump - pcap", "aixpcap", "pcap", "cap;dmp",
+ FALSE, BLOCKS_SUPPORTED(pcap_blocks_supported),
+ NULL, NULL, NULL
+};
+
+static const struct file_type_subtype_info pcap_ss990417_info = {
+ "RedHat 6.1 tcpdump - pcap", "rh6_1pcap", "pcap", "cap;dmp",
+ FALSE, BLOCKS_SUPPORTED(pcap_blocks_supported),
+ libpcap_dump_can_write_encap, libpcap_dump_open_pcap_ss990417, NULL
+};
+
+static const struct file_type_subtype_info pcap_ss990915_info = {
+ "SuSE 6.3 tcpdump - pcap", "suse6_3pcap", "pcap", "cap;dmp",
+ FALSE, BLOCKS_SUPPORTED(pcap_blocks_supported),
+ libpcap_dump_can_write_encap, libpcap_dump_open_pcap_ss990915, NULL
+};
+
+static const struct file_type_subtype_info pcap_ss991029_info = {
+ "Modified tcpdump - pcap", "modpcap", "pcap", "cap;dmp",
+ FALSE, BLOCKS_SUPPORTED(pcap_blocks_supported),
+ libpcap_dump_can_write_encap, libpcap_dump_open_pcap_ss991029, NULL
+};
+
+static const struct file_type_subtype_info pcap_nokia_info = {
+ "Nokia tcpdump - pcap", "nokiapcap", "pcap", "cap;dmp",
+ FALSE, BLOCKS_SUPPORTED(pcap_blocks_supported),
+ libpcap_dump_can_write_encap, libpcap_dump_open_pcap_nokia, NULL
+};
+
+void register_pcap(void)
+{
+ pcap_file_type_subtype = wtap_register_file_type_subtype(&pcap_info);
+ pcap_nsec_file_type_subtype = wtap_register_file_type_subtype(&pcap_nsec_info);
+ pcap_aix_file_type_subtype = wtap_register_file_type_subtype(&pcap_aix_info);
+ pcap_ss990417_file_type_subtype = wtap_register_file_type_subtype(&pcap_ss990417_info);
+ pcap_ss990915_file_type_subtype = wtap_register_file_type_subtype(&pcap_ss990915_info);
+ pcap_ss991029_file_type_subtype = wtap_register_file_type_subtype(&pcap_ss991029_info);
+ pcap_nokia_file_type_subtype = wtap_register_file_type_subtype(&pcap_nokia_info);
+
+ /*
+ * We now call the libpcap file format just pcap, but we allow
+ * the various variants of it to be specified using names
+ * containing "libpcap" as well as "pcap", for backwards
+ * compatibility.
+ *
+ * Register names for that purpose.
+ */
+ wtap_register_compatibility_file_subtype_name("libpcap", "pcap");
+ wtap_register_compatibility_file_subtype_name("nseclibpcap", "nsecpcap");
+ wtap_register_compatibility_file_subtype_name("aixlibpcap", "aixpcap");
+ wtap_register_compatibility_file_subtype_name("modlibpcap", "modpcap");
+ wtap_register_compatibility_file_subtype_name("nokialibpcap", "nokiapcap");
+ wtap_register_compatibility_file_subtype_name("rh6_1libpcap", "rh6_1pcap");
+ wtap_register_compatibility_file_subtype_name("suse6_3libpcap", "suse6_3pcap");
+
+ /*
+ * Register names for backwards compatibility with the
+ * wtap_filetypes table in Lua.
+ */
+ wtap_register_backwards_compatibility_lua_name("PCAP",
+ pcap_file_type_subtype);
+ wtap_register_backwards_compatibility_lua_name("PCAP_NSEC",
+ pcap_nsec_file_type_subtype);
+ wtap_register_backwards_compatibility_lua_name("PCAP_AIX",
+ pcap_aix_file_type_subtype);
+ wtap_register_backwards_compatibility_lua_name("PCAP_SS990417",
+ pcap_ss990417_file_type_subtype);
+ wtap_register_backwards_compatibility_lua_name("PCAP_SS990915",
+ pcap_ss990915_file_type_subtype);
+ wtap_register_backwards_compatibility_lua_name("PCAP_SS991029",
+ pcap_ss991029_file_type_subtype);
+ wtap_register_backwards_compatibility_lua_name("PCAP_NOKIA",
+ pcap_nokia_file_type_subtype);
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/wiretap/libpcap.h b/wiretap/libpcap.h
new file mode 100644
index 00000000..4042ec7c
--- /dev/null
+++ b/wiretap/libpcap.h
@@ -0,0 +1,101 @@
+/** @file
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __W_LIBPCAP_H__
+#define __W_LIBPCAP_H__
+
+#include <glib.h>
+#include <wiretap/wtap.h>
+#include "ws_symbol_export.h"
+
+/* Magic numbers in "libpcap" files.
+
+ "libpcap" file records are written in the byte order of the host that
+ writes them, and the reader is expected to fix this up.
+
+ PCAP_MAGIC is the magic number, in host byte order; PCAP_SWAPPED_MAGIC
+ is a byte-swapped version of that.
+
+ PCAP_MODIFIED_MAGIC is for Alexey Kuznetsov's modified "libpcap"
+ format, as generated on Linux systems that have a "libpcap" with
+ his patches, at
+
+ http://ftp.sunet.se/pub/os/Linux/ip-routing/lbl-tools/
+
+ applied; PCAP_SWAPPED_MODIFIED_MAGIC is the byte-swapped version.
+
+ PCAP_IXIAMODIFIED_MAGIC is used by IXIA's lcap file format. It adds
+ a length field at the end of the file header (size of all records).
+ PCAP_SWAPPED_IXIAMODIFIED_MAGIC is the byte-swapped version.
+
+ PCAP_NSEC_MAGIC is for Ulf Lamping's modified "libpcap" format,
+ which uses the same common file format as PCAP_MAGIC, but the
+ timestamps are saved in nanosecond resolution instead of microseconds.
+ PCAP_SWAPPED_NSEC_MAGIC is a byte-swapped version of that. */
+#define PCAP_MAGIC 0xa1b2c3d4
+#define PCAP_SWAPPED_MAGIC 0xd4c3b2a1
+#define PCAP_MODIFIED_MAGIC 0xa1b2cd34
+#define PCAP_SWAPPED_MODIFIED_MAGIC 0x34cdb2a1
+#define PCAP_IXIAHW_MAGIC 0x1c0001ac
+#define PCAP_SWAPPED_IXIAHW_MAGIC 0xac01001c
+#define PCAP_IXIASW_MAGIC 0x1c0001ab
+#define PCAP_SWAPPED_IXIASW_MAGIC 0xab01001c
+#define PCAP_NSEC_MAGIC 0xa1b23c4d
+#define PCAP_SWAPPED_NSEC_MAGIC 0x4d3cb2a1
+
+/* "libpcap" file header (minus magic number). */
+struct pcap_hdr {
+ guint16 version_major; /* major version number */
+ guint16 version_minor; /* minor version number */
+ gint32 thiszone; /* GMT to local correction */
+ guint32 sigfigs; /* accuracy of timestamps */
+ guint32 snaplen; /* max length of captured packets, in octets */
+ guint32 network; /* data link type */
+};
+
+/* "libpcap" record header. */
+struct pcaprec_hdr {
+ guint32 ts_sec; /* timestamp seconds */
+ guint32 ts_usec; /* timestamp microseconds (nsecs for PCAP_NSEC_MAGIC) */
+ guint32 incl_len; /* number of octets of packet saved in file */
+ guint32 orig_len; /* actual length of packet */
+};
+
+/* "libpcap" record header for Alexey's patched version. */
+struct pcaprec_modified_hdr {
+ struct pcaprec_hdr hdr; /* the regular header */
+ guint32 ifindex; /* index, in *capturing* machine's list of
+ interfaces, of the interface on which this
+ packet came in. */
+ guint16 protocol; /* Ethernet packet type */
+ guint8 pkt_type; /* broadcast/multicast/etc. indication */
+ guint8 pad; /* pad to a 4-byte boundary */
+};
+
+/* "libpcap" record header for Alexey's patched version in its ss990915
+ incarnation; this version shows up in SuSE Linux 6.3. */
+struct pcaprec_ss990915_hdr {
+ struct pcaprec_hdr hdr; /* the regular header */
+ guint32 ifindex; /* index, in *capturing* machine's list of
+ interfaces, of the interface on which this
+ packet came in. */
+ guint16 protocol; /* Ethernet packet type */
+ guint8 pkt_type; /* broadcast/multicast/etc. indication */
+ guint8 cpu1, cpu2; /* SMP debugging gunk? */
+ guint8 pad[3]; /* pad to a 4-byte boundary */
+};
+
+/* "libpcap" record header for version used on some Nokia boxes (firewalls?) */
+struct pcaprec_nokia_hdr {
+ struct pcaprec_hdr hdr; /* the regular header */
+ guint8 stuff[4]; /* mysterious stuff */
+};
+
+wtap_open_return_val libpcap_open(wtap *wth, int *err, gchar **err_info);
+
+#endif
diff --git a/wiretap/log3gpp.c b/wiretap/log3gpp.c
new file mode 100644
index 00000000..4754d6c2
--- /dev/null
+++ b/wiretap/log3gpp.c
@@ -0,0 +1,858 @@
+/* log3gpp.c
+ * Routines encapsulating/dumping 3gpp protocol logs.
+ * The purpose of this format is to be able to log the 3GPP protocol stack on a mobile phone.
+ * Copyright 2008, Vincent Helfre
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "config.h"
+
+#define WS_LOG_DOMAIN LOG_DOMAIN_WIRETAP
+
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "wtap-int.h"
+#include "file_wrappers.h"
+
+#include "log3gpp.h"
+
+#define MAX_FIRST_LINE_LENGTH 200
+#define MAX_TIMESTAMP_LINE_LENGTH 100
+#define MAX_LINE_LENGTH 65536
+#define MAX_TIMESTAMP_LEN 32
+#define MAX_SECONDS_CHARS 16
+#define MAX_SUBSECOND_DECIMALS 4
+#define MAX_PROTOCOL_NAME 64
+#define MAX_PROTOCOL_PAR_STRING 64
+
+/* 'u' or 'd' of a packet as read from file */
+typedef enum packet_direction_t
+{
+ uplink,
+ downlink
+} packet_direction_t;
+
+typedef struct {
+ time_t start_secs;
+ guint32 start_usecs;
+} log3gpp_t;
+
+gint first_packet_offset;
+gchar firstline[MAX_FIRST_LINE_LENGTH];
+gchar secondline[MAX_TIMESTAMP_LINE_LENGTH];
+gint secondline_length = 0;
+
+/***********************************************************/
+/* Transient data used for parsing */
+
+/* 'Magic number' at start of 3gpp log files. */
+static const gchar log3gpp_magic[] = "3GPP protocols transcript";
+
+/* Protocol name of the packet that the packet was captured at */
+static gchar protocol_name[MAX_PROTOCOL_NAME+1];
+
+/* Optional string parameter giving info required for the protocol dissector */
+static gchar protocol_parameters[MAX_PROTOCOL_PAR_STRING+1];
+/************************************************************/
+/* Functions called from wiretap core */
+static gboolean log3gpp_read( wtap* wth, wtap_rec* rec, Buffer* buf,
+ int* err, gchar** err_info, gint64* data_offset);
+static gboolean log3gpp_seek_read(struct wtap *wth, gint64 seek_off,
+ wtap_rec *rec,
+ Buffer *buf,
+ int *err, gchar **err_info);
+
+/************************************************************/
+/* Private helper functions */
+static gboolean read_new_line(FILE_T fh, gint* length,
+ gchar* buf, size_t bufsize, int* err,
+ gchar** err_info);
+
+static gboolean parse_line(char* linebuff, gint line_length, gint *seconds, gint *useconds,
+ long *data_offset,
+ gint *data_chars,
+ packet_direction_t *direction,
+ gboolean *is_text_data);
+static int write_stub_header(guchar *frame_buffer, char *timestamp_string,
+ packet_direction_t direction);
+static guchar hex_from_char(gchar c);
+/*not used static gchar char_from_hex(guchar hex);*/
+
+static gboolean get_file_time_stamp(gchar* linebuff, time_t *secs, guint32 *usecs);
+
+
+static int log3gpp_file_type_subtype = -1;
+
+void register_log3gpp(void);
+
+/***************************************************************************/
+/* Free log3gpp-specific capture info from file that was open for reading */
+/***************************************************************************/
+static void log3gpp_close(wtap* wth)
+{
+ log3gpp_t* log3gpp = (log3gpp_t*)wth->priv;
+ /* Also free this capture info */
+ g_free(log3gpp);
+ wth->priv = NULL;
+}
+
+/********************************************/
+/* Open file (for reading) */
+/********************************************/
+wtap_open_return_val
+log3gpp_open(wtap *wth, int *err, gchar **err_info _U_)
+{
+ time_t timestamp;
+ guint32 usecs;
+ log3gpp_t *log3gpp;
+ wtap_open_return_val retval;
+ /* Buffer to hold a single text line read from the file */
+ static gchar linebuff[MAX_LINE_LENGTH];
+ gint firstline_length = 0;
+
+ /* Clear errno before reading from the file */
+ errno = 0;
+
+ /********************************************************************/
+ /* First line needs to contain at least as many characters as magic */
+
+ /*ws_warning("Open file"); */
+
+ if (!read_new_line(wth->fh, &firstline_length, linebuff,
+ sizeof linebuff, err, err_info)) {
+ if (*err != 0 && *err != WTAP_ERR_SHORT_READ) {
+ return WTAP_OPEN_ERROR;
+ }
+ else {
+ return WTAP_OPEN_NOT_MINE;
+ }
+ }
+
+ if (((size_t)firstline_length < strlen(log3gpp_magic)) ||
+ firstline_length >= MAX_FIRST_LINE_LENGTH)
+ {
+ retval = WTAP_OPEN_NOT_MINE;
+ return retval;
+ }
+
+ /* This file is not for us if it doesn't match our signature */
+ if (memcmp(log3gpp_magic, linebuff, strlen(log3gpp_magic)) != 0)
+ {
+ retval = WTAP_OPEN_NOT_MINE;
+ return retval;
+ }
+
+ /***********************************************************/
+ /* Second line contains file timestamp */
+ if (!read_new_line(wth->fh, &secondline_length,
+ linebuff, sizeof linebuff, err, err_info)) {
+ if (*err != 0 && *err != WTAP_ERR_SHORT_READ) {
+ return WTAP_OPEN_ERROR;
+ }
+ else {
+ return WTAP_OPEN_NOT_MINE;
+ }
+ }
+
+ first_packet_offset = firstline_length + secondline_length;
+
+ if ((secondline_length >= MAX_TIMESTAMP_LINE_LENGTH) ||
+ (!get_file_time_stamp(linebuff, &timestamp, &usecs)))
+ {
+ /* Give up if file time line wasn't valid */
+ retval = WTAP_OPEN_NOT_MINE;
+ return retval;
+ }
+
+ /* Allocate struct and fill in timestamp (netmon re used)*/
+ log3gpp = g_new(log3gpp_t, 1);
+ log3gpp->start_secs = timestamp;
+ log3gpp->start_usecs = usecs;
+ wth->priv = (void *)log3gpp;
+
+ /************************************************************/
+ /* File is for us. Fill in details so packets can be read */
+
+ /* Set our file type */
+ wth->file_type_subtype = log3gpp_file_type_subtype;
+
+ /* Use our own encapsulation to send all packets to our stub dissector */
+ wth->file_encap = WTAP_ENCAP_LOG_3GPP;
+
+ /* Callbacks for reading operations */
+ wth->subtype_read = log3gpp_read;
+ wth->subtype_seek_read = log3gpp_seek_read;
+ wth->subtype_close = log3gpp_close;
+
+ /* Choose microseconds (have 4 decimal places...) */
+ wth->file_tsprec = WTAP_TSPREC_USEC;
+
+ *err = errno;
+
+ /*
+ * Add an IDB; we don't know how many interfaces were
+ * involved, so we just say one interface, about which
+ * we only know the link-layer type, snapshot length,
+ * and time stamp resolution.
+ */
+ wtap_add_generated_idb(wth);
+
+ retval = WTAP_OPEN_MINE;
+ return retval;
+}
+
+
+/**************************************************/
+/* Read packet function. */
+/* Look for and read the next usable packet */
+/* - return TRUE and details if found */
+/**************************************************/
+gboolean log3gpp_read(wtap* wth, wtap_rec* rec, Buffer* buf,
+ int* err, gchar** err_info, gint64* data_offset)
+{
+ gint64 offset = file_tell(wth->fh);
+ static gchar linebuff[MAX_LINE_LENGTH + 1];
+ long dollar_offset;
+ packet_direction_t direction;
+ gboolean is_text_data;
+ log3gpp_t *log3gpp = (log3gpp_t *)wth->priv;
+
+ /* Search for a line containing a usable packet */
+ while (1)
+ {
+ int line_length, seconds, useconds, data_chars;
+ gint64 this_offset = offset;
+
+ /* Are looking for first packet after 2nd line */
+ if (file_tell(wth->fh) == 0)
+ {
+ this_offset += (gint64)first_packet_offset +1+1;
+ }
+
+ /* Clear errno before reading from the file */
+ errno = 0;
+
+ /* Read a new line from file into linebuff */
+ if (!read_new_line(wth->fh, &line_length, linebuff,
+ sizeof linebuff, err, err_info)) {
+ if (*err != 0) {
+ return FALSE; /* error */
+ }
+ /* No more lines can be read, so quit. */
+ break;
+ }
+
+ /* Try to parse the line as a frame record */
+ if (parse_line(linebuff, line_length, &seconds, &useconds,
+ &dollar_offset,
+ &data_chars,
+ &direction,
+ &is_text_data))
+ {
+ guchar *frame_buffer;
+ int n;
+ int stub_offset = 0;
+ char timestamp_string[MAX_TIMESTAMP_LEN+1];
+ /*not used gint64 *pkey = NULL;*/
+
+ snprintf(timestamp_string, 32, "%d.%04d", seconds, useconds/100);
+
+ /* All packets go to 3GPP protocol stub dissector */
+ rec->rec_header.packet_header.pkt_encap = WTAP_ENCAP_LOG_3GPP;
+ rec->rec_type = REC_TYPE_PACKET;
+ rec->block = wtap_block_create(WTAP_BLOCK_PACKET);
+ rec->presence_flags = WTAP_HAS_TS;
+
+ /* Set data_offset to the beginning of the line we're returning.
+ This will be the seek_off parameter when this frame is re-read.
+ */
+ *data_offset = this_offset;
+
+ /* Fill in timestamp (capture base + packet offset) */
+ rec->ts.secs = log3gpp->start_secs + seconds;
+ if ((log3gpp->start_usecs + useconds) >= 1000000)
+ {
+ rec->ts.secs++;
+ }
+ rec->ts.nsecs =
+ ((log3gpp->start_usecs + useconds) % 1000000) *1000;
+
+ if (!is_text_data)
+ {
+ /* Get buffer pointer ready */
+ ws_buffer_assure_space(buf,
+ strlen(timestamp_string)+1 + /* timestamp */
+ strlen(protocol_name)+1 + /* Protocol name */
+ 1 + /* direction */
+ (size_t)(data_chars/2));
+
+ frame_buffer = ws_buffer_start_ptr(buf);
+ /*********************/
+ /* Write stub header */
+ stub_offset = write_stub_header(frame_buffer, timestamp_string,
+ direction);
+
+ /* Binary data length is half bytestring length + stub header */
+ rec->rec_header.packet_header.len = data_chars/2 + stub_offset;
+ rec->rec_header.packet_header.caplen = data_chars/2 + stub_offset;
+ /********************************/
+ /* Copy packet data into buffer */
+ for (n=0; n <= data_chars; n+=2)
+ {
+ frame_buffer[stub_offset + n/2] = (hex_from_char(linebuff[dollar_offset+n]) << 4) |
+ hex_from_char(linebuff[dollar_offset+n+1]);
+ }
+ *err = errno = 0;
+ return TRUE;
+ }
+ else
+ {
+ /* Get buffer pointer ready */
+ ws_buffer_assure_space(buf,
+ strlen(timestamp_string)+1 + /* timestamp */
+ strlen(protocol_name)+1 + /* Protocol name */
+ 1 + /* direction */
+ data_chars);
+ frame_buffer = ws_buffer_start_ptr(buf);
+
+ /*********************/
+ /* Write stub header */
+ stub_offset = write_stub_header(frame_buffer, timestamp_string,
+ direction);
+
+ /* Binary data length is bytestring length + stub header */
+ rec->rec_header.packet_header.len = data_chars + stub_offset;
+ rec->rec_header.packet_header.caplen = data_chars + stub_offset;
+
+ /* do not convert the ascii char */
+ memcpy(&frame_buffer[stub_offset],&linebuff[dollar_offset],data_chars);
+ frame_buffer[stub_offset+data_chars-1]= '\0';
+ *err = errno = 0;
+ return TRUE;
+ }
+ }
+ }
+
+ /* No packet details to return... */
+ *err = errno;
+ return FALSE;
+}
+
+
+/**************************************************/
+/* Read & seek function. */
+/**************************************************/
+static gboolean
+log3gpp_seek_read(wtap *wth, gint64 seek_off,
+ wtap_rec *rec _U_ , Buffer *buf,
+ int *err, gchar **err_info)
+{
+ long dollar_offset;
+ static gchar linebuff[MAX_LINE_LENGTH + 1];
+ packet_direction_t direction;
+ int seconds, useconds, data_chars;
+ gboolean is_text_data;
+ log3gpp_t* log3gpp = (log3gpp_t*)wth->priv;
+ int length = 0;
+ guchar *frame_buffer;
+
+ /* Reset errno */
+ *err = errno = 0;
+
+ /* Seek to beginning of packet */
+ if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
+ {
+ return FALSE;
+ }
+
+ /* Re-read whole line (this really should succeed) */
+ if (!read_new_line(wth->random_fh, &length, linebuff,
+ sizeof linebuff, err, err_info)) {
+ return FALSE;
+ }
+
+ /* Try to parse this line again (should succeed as re-reading...) */
+ if (parse_line(linebuff, length, &seconds, &useconds,
+ &dollar_offset,
+ &data_chars,
+ &direction,
+ &is_text_data))
+ {
+ int n;
+ int stub_offset = 0;
+ char timestamp_string[32];
+ snprintf(timestamp_string, 32, "%d.%04d", seconds, useconds/100);
+
+ /* Make sure all packets go to log3gpp dissector */
+ rec->rec_header.packet_header.pkt_encap = WTAP_ENCAP_LOG_3GPP;
+ rec->rec_type = REC_TYPE_PACKET;
+ rec->block = wtap_block_create(WTAP_BLOCK_PACKET);
+ rec->presence_flags = WTAP_HAS_TS;
+
+ /* Fill in timestamp (capture base + packet offset) */
+ rec->ts.secs = log3gpp->start_secs + seconds;
+ if ((log3gpp->start_usecs + useconds) >= 1000000)
+ {
+ rec->ts.secs++;
+ }
+ rec->ts.nsecs =
+ ((log3gpp->start_usecs + useconds) % 1000000) * 1000;
+
+ /*********************/
+ /* Write stub header */
+ ws_buffer_assure_space(buf,
+ strlen(timestamp_string)+1 + /* timestamp */
+ strlen(protocol_name)+1 + /* Protocol name */
+ 1 + /* direction */
+ data_chars);
+ frame_buffer = ws_buffer_start_ptr(buf);
+ stub_offset = write_stub_header(frame_buffer, timestamp_string,
+ direction);
+
+ if (!is_text_data)
+ {
+ /********************************/
+ /* Copy packet data into buffer */
+ for (n=0; n <= data_chars; n+=2)
+ {
+ frame_buffer[stub_offset + n/2] = (hex_from_char(linebuff[dollar_offset+n]) << 4) |
+ hex_from_char(linebuff[dollar_offset+n+1]);
+ }
+ *err = errno = 0;
+ return TRUE;
+ }
+ else
+ {
+ /* do not convert the ascii char */
+ memcpy(&frame_buffer[stub_offset],&linebuff[dollar_offset],data_chars);
+ frame_buffer[stub_offset+data_chars-1] = '\0';
+ *err = errno = 0;
+ return TRUE;
+ }
+ }
+
+ /* If get here, must have failed */
+ *err = errno;
+ *err_info = ws_strdup_printf("prot 3gpp: seek_read failed to read/parse "
+ "line at position %" PRId64,
+ seek_off);
+ return FALSE;
+}
+
+/****************************/
+/* Private helper functions */
+/****************************/
+
+/**********************************************************************/
+/* Read a new line from the file, starting at offset. */
+/* - writes data to static var linebuff */
+/* - on return 'offset' will point to the next position to read from */
+/* - return TRUE if this read is successful */
+/**********************************************************************/
+static gboolean
+read_new_line(FILE_T fh, gint* length,
+ gchar* linebuff, size_t linebuffsize, int* err, gchar** err_info)
+{
+ /* Read in a line */
+ gint64 pos_before = file_tell(fh);
+
+ if (file_gets(linebuff, (int)linebuffsize - 1, fh) == NULL) {
+ /* No characters found, or error */
+ *err = file_error(fh, err_info);
+ return FALSE;
+ }
+
+ /* Set length (avoiding strlen()) and offset.. */
+ *length = (gint)(file_tell(fh) - pos_before);
+
+ /* ...but don't want to include newline in line length */
+ if (*length > 0 && linebuff[*length - 1] == '\n') {
+ linebuff[*length - 1] = '\0';
+ *length = *length - 1;
+ }
+ /* Nor do we want '\r' (as will be written when log is created on windows) */
+ if (*length > 0 && linebuff[*length - 1] == '\r') {
+ linebuff[*length - 1] = '\0';
+ *length = *length - 1;
+ }
+
+ return TRUE;
+}
+
+
+/**********************************************************************/
+/* Parse a line from buffer, by identifying: */
+/* - timestamp */
+/* - data position and length */
+/* Return TRUE if this packet looks valid and can be displayed */
+/**********************************************************************/
+gboolean parse_line(gchar* linebuff, gint line_length, gint *seconds, gint *useconds,
+ long *data_offset, gint *data_chars,
+ packet_direction_t *direction,
+ gboolean *is_text_data)
+{
+ int n = 0;
+ int protocol_chars = 0;
+ int prot_option_chars = 0;
+ char seconds_buff[MAX_SECONDS_CHARS+1];
+ int seconds_chars;
+ char subsecond_decimals_buff[MAX_SUBSECOND_DECIMALS+1];
+ int subsecond_decimals_chars;
+
+ /*********************************************************************/
+ /* Find and read the timestamp */
+ /*********************************************************************/
+ /* Now scan to the next digit, which should be the start of the timestamp */
+ for (; !g_ascii_isdigit((guchar)linebuff[n]) && (n < line_length); n++);
+ if (n >= line_length)
+ {
+ return FALSE;
+ }
+
+ /* Seconds */
+ for (seconds_chars = 0;
+ (linebuff[n] != '.') &&
+ (seconds_chars <= MAX_SECONDS_CHARS) &&
+ (n < line_length);
+ n++, seconds_chars++)
+ {
+ if (!g_ascii_isdigit((guchar)linebuff[n]))
+ {
+ /* Found a non-digit before decimal point. Fail */
+ return FALSE;
+ }
+ seconds_buff[seconds_chars] = linebuff[n];
+ }
+ if (seconds_chars > MAX_SECONDS_CHARS || n >= line_length)
+ {
+ /* Didn't fit in buffer. Fail rather than use truncated */
+ return FALSE;
+ }
+
+ /* Convert found value into number */
+ seconds_buff[seconds_chars] = '\0';
+
+ /* Already know they are digits, so avoid expense of ws_strtoi32() */
+ int multiplier = 1;
+ *seconds = 0;
+ for (int d = seconds_chars - 1; d >= 0; d--) {
+ *seconds += ((seconds_buff[d] - '0') * multiplier);
+ multiplier *= 10;
+ }
+
+ /* The decimal point must follow the last of the seconds digits */
+ if (linebuff[n] != '.')
+ {
+ return FALSE;
+ }
+ /* Skip it */
+ n++;
+
+ /* Subsecond decimal digits (expect 4-digit accuracy) */
+ for (subsecond_decimals_chars = 0;
+ (linebuff[n] != ' ') && (subsecond_decimals_chars < MAX_SUBSECOND_DECIMALS) && (n < line_length);
+ n++, subsecond_decimals_chars++)
+ {
+ if (!g_ascii_isdigit((guchar)linebuff[n]))
+ {
+ return FALSE;
+ }
+ subsecond_decimals_buff[subsecond_decimals_chars] = linebuff[n];
+ }
+
+ if (subsecond_decimals_chars > MAX_SUBSECOND_DECIMALS || n >= line_length)
+ {
+ /* More numbers than expected - give up */
+ return FALSE;
+ }
+
+ /* Convert found value into microseconds */
+ subsecond_decimals_buff[subsecond_decimals_chars] = '\0';
+ /* Already know they are digits, so avoid expense of ws_strtoi32() */
+ *useconds = ((subsecond_decimals_buff[0] - '0') * 100000) +
+ ((subsecond_decimals_buff[1] - '0') * 10000) +
+ ((subsecond_decimals_buff[2] - '0') * 1000) +
+ ((subsecond_decimals_buff[3] - '0') * 100);
+
+ /* Space character must follow end of timestamp */
+ if (linebuff[n] != ' ')
+ {
+ return FALSE;
+ }
+ n++;
+
+ /*********************************************************************/
+ /* Find and read protocol name */
+ /*********************************************************************/
+ for (protocol_chars = 0;
+ (linebuff[n] != ' ') && (protocol_chars < MAX_PROTOCOL_NAME) && (n < line_length);
+ n++, protocol_chars++)
+ {
+ if (!g_ascii_isalnum((guchar)linebuff[n]) && linebuff[n] != '_' && linebuff[n] != '.' && linebuff[n] != '-')
+ {
+ return FALSE;
+ }
+ protocol_name[protocol_chars] = linebuff[n];
+ }
+ if (protocol_chars == MAX_PROTOCOL_NAME || n >= line_length)
+ {
+ /* If doesn't fit, fail rather than truncate */
+ return FALSE;
+ }
+ protocol_name[protocol_chars] = '\0';
+
+ /* Space char must follow protocol name */
+ if (linebuff[n] != ' ')
+ {
+ return FALSE;
+ }
+ /* Skip it */
+ n++;
+
+ /* Scan ahead to the next space */
+ for (; (!g_ascii_isalnum((guchar)linebuff[n])) && (n < line_length); n++);
+ if (n >= line_length)
+ {
+ return FALSE;
+ }
+
+
+ if (strcmp(protocol_name,"TXT") == 0)
+ {
+ *direction = uplink;
+ *data_offset = n;
+ *data_chars = line_length - n;
+ *is_text_data = TRUE;
+ }
+ else
+ {
+ /* Next character gives direction of message (must be 'u' or 'd') */
+ if (linebuff[n] == 'u')
+ {
+ *direction = uplink;
+ }
+ else if (linebuff[n] == 'd')
+ {
+ *direction = downlink;
+ }
+ else
+ {
+ return FALSE;
+ }
+ n++;
+
+ /* Now skip ahead to find start of data (marked by '$') */
+ for (; (n <= line_length) && (linebuff[n] != '$') && (prot_option_chars <= MAX_PROTOCOL_PAR_STRING);
+ n++,prot_option_chars++)
+ {
+ protocol_parameters[prot_option_chars] = linebuff[n];
+ }
+ protocol_parameters[prot_option_chars] = '\0';
+ if (prot_option_chars == MAX_PROTOCOL_PAR_STRING || n >= line_length)
+ {
+ /* If doesn't fit, fail rather than truncate */
+ return FALSE;
+ }
+
+ /* Skip it */
+ n++;
+
+ /* Set offset to data start within line */
+ *data_offset = n;
+
+ /* Set number of chars that comprise the hex string protocol data */
+ *data_chars = line_length - n;
+
+ *is_text_data = FALSE;
+ }
+ return TRUE;
+}
+
+/*****************************************************************/
+/* Write the stub info to the data buffer while reading a packet */
+/*****************************************************************/
+int write_stub_header(guchar *frame_buffer, char *timestamp_string,
+ packet_direction_t direction)
+{
+ int stub_offset = 0;
+
+ /* Timestamp within file */
+ (void) g_strlcpy((char*)&frame_buffer[stub_offset], timestamp_string, MAX_TIMESTAMP_LEN+1);
+ stub_offset += (int)(strlen(timestamp_string) + 1);
+
+ /* Protocol name */
+ (void) g_strlcpy((char*)&frame_buffer[stub_offset], protocol_name, MAX_PROTOCOL_NAME+1);
+ stub_offset += (int)(strlen(protocol_name) + 1);
+
+ /* Direction */
+ frame_buffer[stub_offset] = direction;
+ stub_offset++;
+
+ /* Option string (might be string of length 0) */
+ (void) g_strlcpy((char*)&frame_buffer[stub_offset], protocol_parameters,MAX_PROTOCOL_PAR_STRING+1);
+ stub_offset += (int)(strlen(protocol_parameters) + 1);
+ return stub_offset;
+}
+
+
+/********************************************************/
+/* Return hex nibble equivalent of hex string character */
+/********************************************************/
+guchar hex_from_char(gchar c)
+{
+ if ((c >= '0') && (c <= '9'))
+ {
+ return c - '0';
+ }
+
+ if ((c >= 'a') && (c <= 'f'))
+ {
+ return 0x0a + (c - 'a');
+ }
+
+ if ((c >= 'A') && (c <= 'F'))
+ {
+ return 0x0a + (c - 'A');
+ }
+ /* Not a valid hex string character */
+ return 0xff;
+}
+
+
+/********************************************************/
+/* Return character corresponding to hex nibble value */
+/********************************************************/
+/*gchar char_from_hex(guchar hex)
+{
+ static char hex_lookup[16] =
+ { '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
+
+ if (hex > 15)
+ {
+ return '?';
+ }
+
+ return hex_lookup[hex];
+}*/
+
+/************************************************************************/
+/* Parse year, month, day, hour, minute, seconds out of formatted line. */
+/* Set secs and usecs as output */
+/* Return FALSE if no valid time can be read */
+/************************************************************************/
+gboolean get_file_time_stamp(gchar* linebuff, time_t *secs, guint32 *usecs)
+{
+ int n;
+ struct tm tm;
+ #define MAX_MONTH_LETTERS 9
+ char month[MAX_MONTH_LETTERS+1];
+
+ int day, year, hour, minute, second;
+ int scan_found;
+
+ /* If line longer than expected, file is probably not correctly formatted */
+ if (strlen(linebuff) > MAX_TIMESTAMP_LINE_LENGTH)
+ {
+ return FALSE;
+ }
+
+ /**************************************************************/
+ /* First is month. Read until get a space following the month */
+ for (n=0; (n < MAX_MONTH_LETTERS) && (linebuff[n] != ' '); n++)
+ {
+ month[n] = linebuff[n];
+ }
+ month[n] = '\0';
+
+ if (strcmp(month, "January" ) == 0) tm.tm_mon = 0;
+ else if (strcmp(month, "February" ) == 0) tm.tm_mon = 1;
+ else if (strcmp(month, "March" ) == 0) tm.tm_mon = 2;
+ else if (strcmp(month, "April" ) == 0) tm.tm_mon = 3;
+ else if (strcmp(month, "May" ) == 0) tm.tm_mon = 4;
+ else if (strcmp(month, "June" ) == 0) tm.tm_mon = 5;
+ else if (strcmp(month, "July" ) == 0) tm.tm_mon = 6;
+ else if (strcmp(month, "August" ) == 0) tm.tm_mon = 7;
+ else if (strcmp(month, "September") == 0) tm.tm_mon = 8;
+ else if (strcmp(month, "October" ) == 0) tm.tm_mon = 9;
+ else if (strcmp(month, "November" ) == 0) tm.tm_mon = 10;
+ else if (strcmp(month, "December" ) == 0) tm.tm_mon = 11;
+ else
+ {
+ /* Give up if not found a properly-formatted date */
+ return FALSE;
+ }
+ /* Skip space char */
+ n++;
+
+ /********************************************************/
+ /* Scan for remaining numerical fields */
+ scan_found = sscanf(linebuff+n, "%d, %d %d:%d:%d.%u",
+ &day, &year, &hour, &minute, &second, usecs);
+ if (scan_found != 6)
+ {
+ /* Give up if not all found */
+ return FALSE;
+ }
+
+ /******************************************************/
+ /* Fill in remaining fields and return it in a time_t */
+ tm.tm_year = year - 1900;
+ tm.tm_mday = day;
+ tm.tm_hour = hour;
+ tm.tm_min = minute;
+ tm.tm_sec = second;
+ tm.tm_isdst = -1; /* daylight saving time info not known */
+
+ /* Get seconds from this time */
+ *secs = mktime(&tm);
+
+ /* Multiply 4 digits given to get micro-seconds */
+ *usecs = *usecs * 100;
+
+ return TRUE;
+}
+
+static const struct supported_block_type log3gpp_blocks_supported[] = {
+ /*
+ * We support packet blocks, with no comments or other options.
+ */
+ { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
+};
+
+static const struct file_type_subtype_info log3gpp_info = {
+ "3GPP Log", "3gpp_log", "*.log", NULL,
+ TRUE, BLOCKS_SUPPORTED(log3gpp_blocks_supported),
+ NULL, NULL, NULL
+};
+
+void register_log3gpp(void)
+{
+ log3gpp_file_type_subtype = wtap_register_file_type_subtype(&log3gpp_info);
+
+ /*
+ * Register name for backwards compatibility with the
+ * wtap_filetypes table in Lua.
+ */
+ wtap_register_backwards_compatibility_lua_name("LOG_3GPP",
+ log3gpp_file_type_subtype);
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * vi: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */
diff --git a/wiretap/log3gpp.h b/wiretap/log3gpp.h
new file mode 100644
index 00000000..ff3b3ca8
--- /dev/null
+++ b/wiretap/log3gpp.h
@@ -0,0 +1,16 @@
+/** @file
+*
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+*/
+#ifndef __LOG_3GPP_H__
+#define __LOG_3GPP_H__
+
+#include <wtap.h>
+
+wtap_open_return_val log3gpp_open(wtap* wth, int* err, gchar** err_info);
+
+#endif /* __LOG_3GPP_H__ */
diff --git a/wiretap/logcat.c b/wiretap/logcat.c
new file mode 100644
index 00000000..fc4eb87b
--- /dev/null
+++ b/wiretap/logcat.c
@@ -0,0 +1,401 @@
+/* logcat.c
+ *
+ * Copyright 2014, Michal Labedzki for Tieto Corporation
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include "wtap-int.h"
+#include "file_wrappers.h"
+
+#include "logcat.h"
+
+static int logcat_file_type_subtype = -1;
+
+void register_logcat(void);
+
+/* Returns '?' for invalid priorities */
+static gchar get_priority(const guint8 priority) {
+ static gchar priorities[] = "??VDIWEFS";
+
+ if (priority >= (guint8) sizeof(priorities))
+ return '?';
+
+ return priorities[priority];
+}
+
+/*
+ * Returns:
+ *
+ * -2 if we get an EOF at the beginning;
+ * -1 on an I/O error;
+ * 0 if the record doesn't appear to be valid;
+ * 1-{max gint} as a version number if we got a valid record.
+ */
+static gint detect_version(FILE_T fh, int *err, gchar **err_info)
+{
+ guint16 payload_length;
+ guint16 hdr_size;
+ guint16 read_sofar;
+ guint16 entry_len;
+ gint version;
+ struct logger_entry *log_entry;
+ struct logger_entry_v2 *log_entry_v2;
+ guint8 *buffer;
+ guint16 tmp;
+ guint8 *msg_payload;
+ guint8 *msg_part;
+ guint8 *msg_end;
+ guint16 msg_len;
+
+ /* 16-bit payload length */
+ if (!wtap_read_bytes_or_eof(fh, &tmp, 2, err, err_info)) {
+ if (*err == 0) {
+ /*
+ * Got an EOF at the beginning.
+ */
+ return -2;
+ }
+ if (*err != WTAP_ERR_SHORT_READ)
+ return -1;
+ return 0;
+ }
+ payload_length = pletoh16(&tmp);
+
+ /* must contain at least priority and two nulls as separator */
+ if (payload_length < 3)
+ return 0;
+ /* payload length may not exceed the maximum payload size */
+ if (payload_length > LOGGER_ENTRY_MAX_PAYLOAD)
+ return 0;
+
+ /* 16-bit header length (or padding, equal to 0x0000) */
+ if (!wtap_read_bytes(fh, &tmp, 2, err, err_info)) {
+ if (*err != WTAP_ERR_SHORT_READ)
+ return -1;
+ return 0;
+ }
+ hdr_size = pletoh16(&tmp);
+ read_sofar = 4;
+
+ /* ensure buffer is large enough for all versions */
+ buffer = (guint8 *) g_malloc(sizeof(*log_entry_v2) + payload_length);
+ log_entry_v2 = (struct logger_entry_v2 *)(void *) buffer;
+ log_entry = (struct logger_entry *)(void *) buffer;
+
+ /* cannot rely on __pad being 0 for v1, use heuristics to find out what
+ * version is in use. First assume the smallest msg. */
+ for (version = 1; version <= 2; ++version) {
+ if (version == 1) {
+ msg_payload = (guint8 *) (log_entry + 1);
+ entry_len = sizeof(*log_entry) + payload_length;
+ } else if (version == 2) {
+ /* v2 is 4 bytes longer */
+ msg_payload = (guint8 *) (log_entry_v2 + 1);
+ entry_len = sizeof(*log_entry_v2) + payload_length;
+ if (hdr_size != sizeof(*log_entry_v2))
+ continue;
+ } else {
+ continue;
+ }
+
+ if (!wtap_read_bytes(fh, buffer + read_sofar, entry_len - read_sofar, err, err_info)) {
+ g_free(buffer);
+ if (*err != WTAP_ERR_SHORT_READ)
+ return -1;
+ return 0;
+ }
+ read_sofar += entry_len - read_sofar;
+
+ /* A v2 msg has a 32-bit userid instead of v1 priority */
+ if (get_priority(msg_payload[0]) == '?')
+ continue;
+
+ /* Is there a terminating '\0' for the tag? */
+ msg_part = (guint8 *) memchr(msg_payload, '\0', payload_length - 1);
+ if (msg_part == NULL)
+ continue;
+
+ /* if msg is '\0'-terminated, is it equal to the payload len? */
+ ++msg_part;
+ msg_len = (guint16)(payload_length - (msg_part - msg_payload));
+ msg_end = (guint8 *) memchr(msg_part, '\0', msg_len);
+ /* is the end of the buffer (-1) equal to the end of msg? */
+ if (msg_end && (msg_payload + payload_length - 1 != msg_end))
+ continue;
+
+ g_free(buffer);
+ return version;
+ }
+
+ /* No version number is valid */
+ g_free(buffer);
+ return 0;
+}
+
+gint logcat_exported_pdu_length(const guint8 *pd) {
+ const guint16 *tag;
+ const guint16 *tag_length;
+ gint length = 0;
+
+ tag = (const guint16 *)(const void *) pd;
+
+ while(GINT16_FROM_BE(*tag)) {
+ tag_length = (const guint16 *)(const void *) (pd + 2);
+ length += 2 + 2 + GINT16_FROM_BE(*tag_length);
+
+ pd += 2 + 2 + GINT16_FROM_BE(*tag_length);
+ tag = (const guint16 *)(const void *) pd;
+ }
+
+ length += 2 + 2;
+
+ return length;
+}
+
+static gboolean logcat_read_packet(struct logcat_phdr *logcat, FILE_T fh,
+ wtap_rec *rec, Buffer *buf, int *err, gchar **err_info)
+{
+ gint packet_size;
+ guint16 payload_length;
+ guint tmp[2];
+ guint8 *pd;
+ struct logger_entry *log_entry;
+
+ if (!wtap_read_bytes_or_eof(fh, &tmp, 2, err, err_info)) {
+ return FALSE;
+ }
+ payload_length = pletoh16(tmp);
+
+ if (logcat->version == 1) {
+ packet_size = (gint)sizeof(struct logger_entry) + payload_length;
+ } else if (logcat->version == 2) {
+ packet_size = (gint)sizeof(struct logger_entry_v2) + payload_length;
+ } else {
+ return FALSE;
+ }
+ /*
+ * The maximum value of payload_length is 65535, which, even after
+ * the size of the logger entry structure is added to it, is less
+ * than WTAP_MAX_PACKET_SIZE_STANDARD will ever be, so we don't need to check
+ * it.
+ */
+
+ ws_buffer_assure_space(buf, packet_size);
+ pd = ws_buffer_start_ptr(buf);
+ log_entry = (struct logger_entry *)(void *) pd;
+
+ /* Copy the first two bytes of the packet. */
+ memcpy(pd, tmp, 2);
+
+ /* Read the rest of the packet. */
+ if (!wtap_read_bytes(fh, pd + 2, packet_size - 2, err, err_info)) {
+ return FALSE;
+ }
+
+ rec->rec_type = REC_TYPE_PACKET;
+ rec->block = wtap_block_create(WTAP_BLOCK_PACKET);
+ rec->presence_flags = WTAP_HAS_TS;
+ rec->ts.secs = (time_t) GINT32_FROM_LE(log_entry->sec);
+ rec->ts.nsecs = GINT32_FROM_LE(log_entry->nsec);
+ rec->rec_header.packet_header.caplen = packet_size;
+ rec->rec_header.packet_header.len = packet_size;
+
+ rec->rec_header.packet_header.pseudo_header.logcat.version = logcat->version;
+
+ return TRUE;
+}
+
+static gboolean logcat_read(wtap *wth, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info, gint64 *data_offset)
+{
+ *data_offset = file_tell(wth->fh);
+
+ return logcat_read_packet((struct logcat_phdr *) wth->priv, wth->fh,
+ rec, buf, err, err_info);
+}
+
+static gboolean logcat_seek_read(wtap *wth, gint64 seek_off,
+ wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info)
+{
+ if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
+ return FALSE;
+
+ if (!logcat_read_packet((struct logcat_phdr *) wth->priv, wth->random_fh,
+ rec, buf, err, err_info)) {
+ if (*err == 0)
+ *err = WTAP_ERR_SHORT_READ;
+ return FALSE;
+ }
+ return TRUE;
+}
+
+wtap_open_return_val logcat_open(wtap *wth, int *err, gchar **err_info)
+{
+ gint version;
+ gint tmp_version;
+ struct logcat_phdr *logcat;
+
+ /* check first 3 packets (or 2 or 1 if EOF) versions to check file format is correct */
+ version = detect_version(wth->fh, err, err_info); /* first packet */
+ if (version == -1)
+ return WTAP_OPEN_ERROR; /* I/O error */
+ if (version == 0)
+ return WTAP_OPEN_NOT_MINE; /* not a logcat file */
+ if (version == -2)
+ return WTAP_OPEN_NOT_MINE; /* empty file, so not any type of file */
+
+ tmp_version = detect_version(wth->fh, err, err_info); /* second packet */
+ if (tmp_version == -1)
+ return WTAP_OPEN_ERROR; /* I/O error */
+ if (tmp_version == 0)
+ return WTAP_OPEN_NOT_MINE; /* not a logcat file */
+ if (tmp_version != -2) {
+ /* we've read two packets; do they have the same version? */
+ if (tmp_version != version) {
+ /* no, so this is presumably not a logcat file */
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ tmp_version = detect_version(wth->fh, err, err_info); /* third packet */
+ if (tmp_version < 0)
+ return WTAP_OPEN_ERROR; /* I/O error */
+ if (tmp_version == 0)
+ return WTAP_OPEN_NOT_MINE; /* not a logcat file */
+
+ /*
+ * we've read three packets and the first two have the same
+ * version; does the third have the same version?
+ */
+ if (tmp_version != version) {
+ /* no, so this is presumably not a logcat file */
+ return WTAP_OPEN_NOT_MINE;
+ }
+ }
+
+ if (file_seek(wth->fh, 0, SEEK_SET, err) == -1)
+ return WTAP_OPEN_ERROR;
+
+ logcat = g_new(struct logcat_phdr, 1);
+ logcat->version = version;
+
+ wth->priv = logcat;
+
+ wth->file_type_subtype = logcat_file_type_subtype;
+ wth->file_encap = WTAP_ENCAP_LOGCAT;
+ wth->snapshot_length = 0;
+
+ wth->subtype_read = logcat_read;
+ wth->subtype_seek_read = logcat_seek_read;
+ wth->file_tsprec = WTAP_TSPREC_USEC;
+
+ /*
+ * Add an IDB; we don't know how many interfaces were
+ * involved, so we just say one interface, about which
+ * we only know the link-layer type, snapshot length,
+ * and time stamp resolution.
+ */
+ wtap_add_generated_idb(wth);
+
+ return WTAP_OPEN_MINE;
+}
+
+static int logcat_dump_can_write_encap(int encap)
+{
+ if (encap == WTAP_ENCAP_PER_PACKET)
+ return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
+
+ if (encap != WTAP_ENCAP_LOGCAT && encap != WTAP_ENCAP_WIRESHARK_UPPER_PDU)
+ return WTAP_ERR_UNWRITABLE_ENCAP;
+
+ return 0;
+}
+
+static gboolean logcat_binary_dump(wtap_dumper *wdh,
+ const wtap_rec *rec,
+ const guint8 *pd, int *err, gchar **err_info _U_)
+{
+ int caplen;
+
+ /* We can only write packet records. */
+ if (rec->rec_type != REC_TYPE_PACKET) {
+ *err = WTAP_ERR_UNWRITABLE_REC_TYPE;
+ return FALSE;
+ }
+
+ /*
+ * Make sure this packet doesn't have a link-layer type that
+ * differs from the one for the file.
+ */
+ if (wdh->file_encap != rec->rec_header.packet_header.pkt_encap) {
+ *err = WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
+ return FALSE;
+ }
+
+ caplen = rec->rec_header.packet_header.caplen;
+
+ /* Skip EXPORTED_PDU*/
+ if (wdh->file_encap == WTAP_ENCAP_WIRESHARK_UPPER_PDU) {
+ gint skipped_length;
+
+ skipped_length = logcat_exported_pdu_length(pd);
+ pd += skipped_length;
+ caplen -= skipped_length;
+ }
+
+ if (!wtap_dump_file_write(wdh, pd, caplen, err))
+ return FALSE;
+
+ return TRUE;
+}
+
+static gboolean logcat_binary_dump_open(wtap_dumper *wdh, int *err _U_,
+ gchar **err_info _U_)
+{
+ wdh->subtype_write = logcat_binary_dump;
+
+ return TRUE;
+}
+
+static const struct supported_block_type logcat_blocks_supported[] = {
+ /*
+ * We support packet blocks, with no comments or other options.
+ */
+ { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
+};
+
+static const struct file_type_subtype_info logcat_info = {
+ "Android Logcat Binary format", "logcat", "logcat", NULL,
+ FALSE, BLOCKS_SUPPORTED(logcat_blocks_supported),
+ logcat_dump_can_write_encap, logcat_binary_dump_open, NULL
+};
+
+void register_logcat(void)
+{
+ logcat_file_type_subtype = wtap_register_file_type_subtype(&logcat_info);
+
+ /*
+ * Register name for backwards compatibility with the
+ * wtap_filetypes table in Lua.
+ */
+ wtap_register_backwards_compatibility_lua_name("LOGCAT",
+ logcat_file_type_subtype);
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * vi: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */
diff --git a/wiretap/logcat.h b/wiretap/logcat.h
new file mode 100644
index 00000000..29ea6d61
--- /dev/null
+++ b/wiretap/logcat.h
@@ -0,0 +1,64 @@
+/** @file
+ *
+ * Copyright 2014, Michal Labedzki for Tieto Corporation
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ */
+
+#ifndef __LOGCAT_H__
+#define __LOGCAT_H__
+
+#include <glib.h>
+
+#include "wtap.h"
+
+/* The log format can be found on:
+ * https://android.googlesource.com/platform/system/core/+/master/include/log/logger.h
+ * Log format is assumed to be little-endian (Android platform).
+ */
+/* maximum size of a message payload in a log entry */
+#define LOGGER_ENTRY_MAX_PAYLOAD 4076
+
+struct logger_entry {
+ guint16 len; /* length of the payload */
+ guint16 __pad; /* no matter what, we get 2 bytes of padding */
+ gint32 pid; /* generating process's pid */
+ gint32 tid; /* generating process's tid */
+ gint32 sec; /* seconds since Epoch */
+ gint32 nsec; /* nanoseconds */
+/* char msg[0]; *//* the entry's payload */
+};
+
+struct logger_entry_v2 {
+ guint16 len; /* length of the payload */
+ guint16 hdr_size; /* sizeof(struct logger_entry_v2) */
+ gint32 pid; /* generating process's pid */
+ gint32 tid; /* generating process's tid */
+ gint32 sec; /* seconds since Epoch */
+ gint32 nsec; /* nanoseconds */
+ union {
+ /* v1: not present */
+ guint32 euid; /* v2: effective UID of logger */
+ guint32 lid; /* v3: log id of the payload */
+ } id;
+/* char msg[0]; *//* the entry's payload */
+};
+
+wtap_open_return_val logcat_open(wtap *wth, int *err, gchar **err_info);
+
+gint logcat_exported_pdu_length(const guint8 *pd);
+#endif
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * vi: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */
diff --git a/wiretap/logcat_text.c b/wiretap/logcat_text.c
new file mode 100644
index 00000000..ced74396
--- /dev/null
+++ b/wiretap/logcat_text.c
@@ -0,0 +1,762 @@
+/* logcat_text.c
+ *
+ * Copyright 2014, Michal Orynicz for Tieto Corporation
+ * Copyright 2014, Michal Labedzki for Tieto Corporation
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include "wtap-int.h"
+#include "file_wrappers.h"
+
+#include "logcat_text.h"
+#include "logcat.h"
+
+struct dumper_t {
+ int type;
+};
+
+static int logcat_text_brief_file_type_subtype = -1;
+static int logcat_text_process_file_type_subtype = -1;
+static int logcat_text_tag_file_type_subtype = -1;
+static int logcat_text_thread_file_type_subtype = -1;
+static int logcat_text_time_file_type_subtype = -1;
+static int logcat_text_threadtime_file_type_subtype = -1;
+static int logcat_text_long_file_type_subtype = -1;
+
+void register_logcat_text(void);
+
+/* Returns '?' for invalid priorities */
+static gchar get_priority(const guint8 priority) {
+ static gchar priorities[] = "??VDIWEFS";
+
+ if (priority >= (guint8) sizeof(priorities))
+ return '?';
+
+ return priorities[priority];
+}
+
+static gint buffered_detect_version(const guint8 *pd)
+{
+ const struct logger_entry *log_entry;
+ const struct logger_entry_v2 *log_entry_v2;
+ gint version;
+ const guint8 *msg_payload = NULL;
+ guint8 *msg_part;
+ guint8 *msg_end;
+ guint16 msg_len;
+
+ log_entry = (const struct logger_entry *)(const void *) pd;
+ log_entry_v2 = (const struct logger_entry_v2 *)(const void *) pd;
+
+ /* must contain at least priority and two nulls as separator */
+ if (log_entry->len < 3)
+ return -1;
+
+ /* payload length may not exceed the maximum payload size */
+ if (log_entry->len > LOGGER_ENTRY_MAX_PAYLOAD)
+ return -1;
+
+ /* cannot rely on __pad being 0 for v1, use heuristics to find out what
+ * version is in use. First assume the smallest msg. */
+ for (version = 1; version <= 2; ++version) {
+ if (version == 1) {
+ msg_payload = (const guint8 *) (log_entry + 1);
+ } else if (version == 2) {
+ /* v2 is 4 bytes longer */
+ msg_payload = (const guint8 *) (log_entry_v2 + 1);
+ if (log_entry_v2->hdr_size != sizeof(*log_entry_v2))
+ continue;
+ }
+
+ /* A v2 msg has a 32-bit userid instead of v1 priority */
+ if (get_priority(msg_payload[0]) == '?')
+ continue;
+
+ /* Is there a terminating '\0' for the tag? */
+ msg_part = (guint8 *) memchr(msg_payload, '\0', log_entry->len - 1);
+ if (msg_part == NULL)
+ continue;
+
+ /* if msg is '\0'-terminated, is it equal to the payload len? */
+ ++msg_part;
+ msg_len = (guint16)(log_entry->len - (msg_part - msg_payload));
+ msg_end = (guint8 *) memchr(msg_part, '\0', msg_len);
+ /* is the end of the buffer (-1) equal to the end of msg? */
+ if (msg_end && (msg_payload + log_entry->len - 1 != msg_end))
+ continue;
+
+ return version;
+ }
+
+ return -1;
+}
+
+static gchar *logcat_log(const struct dumper_t *dumper, guint32 seconds,
+ gint milliseconds, gint pid, gint tid, gchar priority, const gchar *tag,
+ const gchar *log)
+{
+ gchar time_buffer[15];
+ time_t datetime;
+ struct tm *tm;
+
+ datetime = (time_t) seconds;
+
+ switch (dumper->type) {
+ case WTAP_ENCAP_LOGCAT_BRIEF:
+ return ws_strdup_printf("%c/%-8s(%5i): %s\n",
+ priority, tag, pid, log);
+ case WTAP_ENCAP_LOGCAT_PROCESS:
+ /* NOTE: Last parameter should be "process name", not tag;
+ Unfortunately, we do not have process name */
+ return ws_strdup_printf("%c(%5i) %s (%s)\n",
+ priority, pid, log, "");
+ case WTAP_ENCAP_LOGCAT_TAG:
+ return ws_strdup_printf("%c/%-8s: %s\n",
+ priority, tag, log);
+ case WTAP_ENCAP_LOGCAT_THREAD:
+ return ws_strdup_printf("%c(%5i:%5i) %s\n",
+ priority, pid, tid, log);
+ case WTAP_ENCAP_LOGCAT_TIME:
+ tm = gmtime(&datetime);
+ if (tm != NULL) {
+ strftime(time_buffer, sizeof(time_buffer), "%m-%d %H:%M:%S",
+ tm);
+ return ws_strdup_printf("%s.%03i %c/%-8s(%5i): %s\n",
+ time_buffer, milliseconds, priority, tag, pid, log);
+ } else {
+ return ws_strdup_printf("Not representable %c/%-8s(%5i): %s\n",
+ priority, tag, pid, log);
+ }
+ case WTAP_ENCAP_LOGCAT_THREADTIME:
+ tm = gmtime(&datetime);
+ if (tm != NULL) {
+ strftime(time_buffer, sizeof(time_buffer), "%m-%d %H:%M:%S",
+ tm);
+ return ws_strdup_printf("%s.%03i %5i %5i %c %-8s: %s\n",
+ time_buffer, milliseconds, pid, tid, priority, tag, log);
+ } else {
+ return ws_strdup_printf("Not representable %5i %5i %c %-8s: %s\n",
+ pid, tid, priority, tag, log);
+ }
+ case WTAP_ENCAP_LOGCAT_LONG:
+ tm = gmtime(&datetime);
+ if (tm != NULL) {
+ strftime(time_buffer, sizeof(time_buffer), "%m-%d %H:%M:%S",
+ tm);
+ return ws_strdup_printf("[ %s.%03i %5i:%5i %c/%-8s ]\n%s\n\n",
+ time_buffer, milliseconds, pid, tid, priority, tag, log);
+ } else {
+ return ws_strdup_printf("[ Not representable %5i:%5i %c/%-8s ]\n%s\n\n",
+ pid, tid, priority, tag, log);
+ }
+ default:
+ return NULL;
+ }
+
+}
+
+static void get_time(gchar *string, wtap_rec *rec) {
+ gint ms;
+ struct tm date;
+ time_t seconds;
+
+ if (6 == sscanf(string, "%d-%d %d:%d:%d.%d", &date.tm_mon, &date.tm_mday, &date.tm_hour,
+ &date.tm_min, &date.tm_sec, &ms)) {
+ date.tm_year = 70;
+ date.tm_mon -= 1;
+ date.tm_isdst = -1;
+ seconds = mktime(&date);
+ rec->ts.secs = seconds;
+ rec->ts.nsecs = (int) (ms * 1e6);
+ rec->presence_flags = WTAP_HAS_TS;
+ } else {
+ rec->presence_flags = 0;
+ rec->ts.secs = (time_t) 0;
+ rec->ts.nsecs = 0;
+ }
+}
+
+static gboolean logcat_text_read_packet(FILE_T fh, wtap_rec *rec,
+ Buffer *buf, gint file_type) {
+ gint8 *pd;
+ gchar *cbuff;
+ gchar *ret = NULL;
+
+ cbuff = (gchar*)g_malloc(WTAP_MAX_PACKET_SIZE_STANDARD);
+ do {
+ ret = file_gets(cbuff, WTAP_MAX_PACKET_SIZE_STANDARD, fh);
+ } while (NULL != ret && 3 > strlen(cbuff) && !file_eof(fh));
+
+ if (NULL == ret || 3 > strlen(cbuff)) {
+ g_free(cbuff);
+ return FALSE;
+ }
+
+ if (logcat_text_long_file_type_subtype == file_type &&
+ !g_regex_match_simple(SPECIAL_STRING, cbuff, (GRegexCompileFlags)(G_REGEX_ANCHORED | G_REGEX_RAW), G_REGEX_MATCH_NOTEMPTY)) {
+ gint64 file_off = 0;
+ gchar *lbuff;
+ int err;
+ gchar *ret2 = NULL;
+
+ lbuff = (gchar*)g_malloc(WTAP_MAX_PACKET_SIZE_STANDARD);
+ file_off = file_tell(fh);
+ ret2 = file_gets(lbuff,WTAP_MAX_PACKET_SIZE_STANDARD, fh);
+ while (NULL != ret2 && 2 < strlen(lbuff) && !file_eof(fh)) {
+ (void) g_strlcat(cbuff,lbuff,WTAP_MAX_PACKET_SIZE_STANDARD);
+ file_off = file_tell(fh);
+ ret2 = file_gets(lbuff,WTAP_MAX_PACKET_SIZE_STANDARD, fh);
+ }
+
+ if(NULL == ret2 || 2 < strlen(lbuff)) {
+ g_free(cbuff);
+ g_free(lbuff);
+ return FALSE;
+ }
+
+ file_seek(fh,file_off,SEEK_SET,&err);
+ g_free(lbuff);
+ }
+
+ rec->rec_type = REC_TYPE_PACKET;
+ rec->block = wtap_block_create(WTAP_BLOCK_PACKET);
+ rec->rec_header.packet_header.caplen = (guint32)strlen(cbuff);
+ rec->rec_header.packet_header.len = rec->rec_header.packet_header.caplen;
+
+ ws_buffer_assure_space(buf, rec->rec_header.packet_header.caplen + 1);
+ pd = ws_buffer_start_ptr(buf);
+ if ((logcat_text_time_file_type_subtype == file_type
+ || logcat_text_threadtime_file_type_subtype == file_type
+ || logcat_text_long_file_type_subtype == file_type)
+ && '-' != cbuff[0]) { /* the last part filters out the -- beginning of... lines */
+ if (logcat_text_long_file_type_subtype == file_type) {
+ get_time(cbuff+2, rec);
+ } else {
+ get_time(cbuff, rec);
+ }
+ } else {
+ rec->presence_flags = 0;
+ rec->ts.secs = (time_t) 0;
+ rec->ts.nsecs = 0;
+ }
+ memcpy(pd, cbuff, rec->rec_header.packet_header.caplen + 1);
+ g_free(cbuff);
+ return TRUE;
+}
+
+static gboolean logcat_text_read(wtap *wth, wtap_rec *rec,
+ Buffer *buf, int *err _U_ , gchar **err_info _U_, gint64 *data_offset) {
+ *data_offset = file_tell(wth->fh);
+
+ return logcat_text_read_packet(wth->fh, rec, buf, wth->file_type_subtype);
+}
+
+static gboolean logcat_text_seek_read(wtap *wth, gint64 seek_off,
+ wtap_rec *rec, Buffer *buf, int *err, gchar **err_info _U_) {
+ if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
+ return FALSE;
+
+ if (!logcat_text_read_packet(wth->random_fh, rec, buf,
+ wth->file_type_subtype)) {
+ if (*err == 0)
+ *err = WTAP_ERR_SHORT_READ;
+ return FALSE;
+ }
+ return TRUE;
+}
+
+wtap_open_return_val logcat_text_open(wtap *wth, int *err, gchar **err_info _U_) {
+ gchar *cbuff;
+ gchar *ret = NULL;
+
+ if (file_seek(wth->fh, 0, SEEK_SET, err) == -1)
+ return WTAP_OPEN_ERROR;
+
+ cbuff = (gchar*)g_malloc(WTAP_MAX_PACKET_SIZE_STANDARD);
+ do {
+ ret = file_gets(cbuff, WTAP_MAX_PACKET_SIZE_STANDARD, wth->fh);
+ } while (NULL != ret && !file_eof(wth->fh)
+ && ((3 > strlen(cbuff))
+ || g_regex_match_simple(SPECIAL_STRING, cbuff, (GRegexCompileFlags)(G_REGEX_ANCHORED | G_REGEX_RAW),
+ G_REGEX_MATCH_NOTEMPTY)));
+
+ if (g_regex_match_simple(BRIEF_STRING, cbuff, (GRegexCompileFlags)(G_REGEX_ANCHORED | G_REGEX_RAW),
+ G_REGEX_MATCH_NOTEMPTY)) {
+ wth->file_type_subtype = logcat_text_brief_file_type_subtype;
+ wth->file_encap = WTAP_ENCAP_LOGCAT_BRIEF;
+ } else if (g_regex_match_simple(TAG_STRING, cbuff, (GRegexCompileFlags)(G_REGEX_ANCHORED | G_REGEX_RAW),
+ G_REGEX_MATCH_NOTEMPTY)) {
+ wth->file_type_subtype = logcat_text_tag_file_type_subtype;
+ wth->file_encap = WTAP_ENCAP_LOGCAT_TAG;
+ } else if (g_regex_match_simple(PROCESS_STRING, cbuff, (GRegexCompileFlags)(G_REGEX_ANCHORED | G_REGEX_RAW),
+ G_REGEX_MATCH_NOTEMPTY)) {
+ wth->file_type_subtype = logcat_text_process_file_type_subtype;
+ wth->file_encap = WTAP_ENCAP_LOGCAT_PROCESS;
+ } else if (g_regex_match_simple(TIME_STRING, cbuff, (GRegexCompileFlags)(G_REGEX_ANCHORED | G_REGEX_RAW),
+ G_REGEX_MATCH_NOTEMPTY)) {
+ wth->file_type_subtype = logcat_text_time_file_type_subtype;
+ wth->file_encap = WTAP_ENCAP_LOGCAT_TIME;
+ } else if (g_regex_match_simple(THREAD_STRING, cbuff, (GRegexCompileFlags)(G_REGEX_ANCHORED | G_REGEX_RAW),
+ G_REGEX_MATCH_NOTEMPTY)) {
+ wth->file_type_subtype = logcat_text_thread_file_type_subtype;
+ wth->file_encap = WTAP_ENCAP_LOGCAT_THREAD;
+ } else if (g_regex_match_simple(THREADTIME_STRING, cbuff, (GRegexCompileFlags)(G_REGEX_ANCHORED | G_REGEX_RAW),
+ G_REGEX_MATCH_NOTEMPTY)) {
+ wth->file_type_subtype = logcat_text_threadtime_file_type_subtype;
+ wth->file_encap = WTAP_ENCAP_LOGCAT_THREADTIME;
+ } else if (g_regex_match_simple(LONG_STRING, cbuff, (GRegexCompileFlags)(G_REGEX_ANCHORED | G_REGEX_RAW),
+ G_REGEX_MATCH_NOTEMPTY)) {
+ wth->file_type_subtype = logcat_text_long_file_type_subtype;
+ wth->file_encap = WTAP_ENCAP_LOGCAT_LONG;
+ } else {
+ g_free(cbuff);
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ if (file_seek(wth->fh, 0, SEEK_SET, err) == -1) {
+ g_free(cbuff);
+ return WTAP_OPEN_ERROR;
+ }
+ wth->snapshot_length = 0;
+
+ wth->subtype_read = logcat_text_read;
+ wth->subtype_seek_read = logcat_text_seek_read;
+ wth->file_tsprec = WTAP_TSPREC_USEC;
+ g_free(cbuff);
+ return WTAP_OPEN_MINE;
+}
+
+static int logcat_text_brief_dump_can_write_encap(int encap) {
+ if (encap == WTAP_ENCAP_PER_PACKET)
+ return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
+
+ switch (encap) {
+ case WTAP_ENCAP_LOGCAT:
+ case WTAP_ENCAP_LOGCAT_BRIEF:
+ case WTAP_ENCAP_WIRESHARK_UPPER_PDU:
+ return 0;
+ default:
+ return WTAP_ERR_UNWRITABLE_ENCAP;
+ }
+}
+
+static int logcat_text_process_dump_can_write_encap(int encap) {
+ if (encap == WTAP_ENCAP_PER_PACKET)
+ return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
+
+ switch (encap) {
+ case WTAP_ENCAP_LOGCAT:
+ case WTAP_ENCAP_LOGCAT_PROCESS:
+ case WTAP_ENCAP_WIRESHARK_UPPER_PDU:
+ return 0;
+ default:
+ return WTAP_ERR_UNWRITABLE_ENCAP;
+ }
+}
+
+static int logcat_text_tag_dump_can_write_encap(int encap) {
+ if (encap == WTAP_ENCAP_PER_PACKET)
+ return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
+
+ switch (encap) {
+ case WTAP_ENCAP_LOGCAT:
+ case WTAP_ENCAP_LOGCAT_TAG:
+ case WTAP_ENCAP_WIRESHARK_UPPER_PDU:
+ return 0;
+ default:
+ return WTAP_ERR_UNWRITABLE_ENCAP;
+ }
+}
+
+static int logcat_text_time_dump_can_write_encap(int encap) {
+ if (encap == WTAP_ENCAP_PER_PACKET)
+ return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
+
+ switch (encap) {
+ case WTAP_ENCAP_LOGCAT:
+ case WTAP_ENCAP_LOGCAT_TIME:
+ case WTAP_ENCAP_WIRESHARK_UPPER_PDU:
+ return 0;
+ default:
+ return WTAP_ERR_UNWRITABLE_ENCAP;
+ }
+}
+
+static int logcat_text_thread_dump_can_write_encap(int encap) {
+ if (encap == WTAP_ENCAP_PER_PACKET)
+ return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
+
+ switch (encap) {
+ case WTAP_ENCAP_LOGCAT:
+ case WTAP_ENCAP_LOGCAT_THREAD:
+ case WTAP_ENCAP_WIRESHARK_UPPER_PDU:
+ return 0;
+ default:
+ return WTAP_ERR_UNWRITABLE_ENCAP;
+ }
+}
+
+static int logcat_text_threadtime_dump_can_write_encap(int encap) {
+ if (encap == WTAP_ENCAP_PER_PACKET)
+ return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
+
+ switch (encap) {
+ case WTAP_ENCAP_LOGCAT:
+ case WTAP_ENCAP_LOGCAT_THREADTIME:
+ case WTAP_ENCAP_WIRESHARK_UPPER_PDU:
+ return 0;
+ default:
+ return WTAP_ERR_UNWRITABLE_ENCAP;
+ }
+}
+
+static int logcat_text_long_dump_can_write_encap(int encap) {
+ if (encap == WTAP_ENCAP_PER_PACKET)
+ return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
+
+ switch (encap) {
+ case WTAP_ENCAP_LOGCAT:
+ case WTAP_ENCAP_LOGCAT_LONG:
+ case WTAP_ENCAP_WIRESHARK_UPPER_PDU:
+ return 0;
+ default:
+ return WTAP_ERR_UNWRITABLE_ENCAP;
+ }
+}
+
+static gboolean logcat_text_dump_text(wtap_dumper *wdh,
+ const wtap_rec *rec,
+ const guint8 *pd, int *err, gchar **err_info)
+{
+ gchar *buf;
+ gint length;
+ gchar priority;
+ const struct logger_entry *log_entry;
+ const struct logger_entry_v2 *log_entry_v2;
+ gint payload_length;
+ const gchar *tag;
+ gint32 pid;
+ gint32 tid;
+ gint32 seconds;
+ gint32 milliseconds;
+ const guint8 *msg_payload = NULL;
+ const gchar *msg_begin;
+ gint msg_pre_skip;
+ gchar *log;
+ gchar *log_part;
+ gchar *log_next;
+ gint logcat_version;
+ const struct dumper_t *dumper = (const struct dumper_t *) wdh->priv;
+
+ /* We can only write packet records. */
+ if (rec->rec_type != REC_TYPE_PACKET) {
+ *err = WTAP_ERR_UNWRITABLE_REC_TYPE;
+ return FALSE;
+ }
+
+ /*
+ * Make sure this packet doesn't have a link-layer type that
+ * differs from the one for the file.
+ */
+ if (wdh->file_encap != rec->rec_header.packet_header.pkt_encap) {
+ *err = WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
+ return FALSE;
+ }
+
+ switch (wdh->file_encap) {
+ case WTAP_ENCAP_WIRESHARK_UPPER_PDU:
+ {
+ gint skipped_length;
+
+ skipped_length = logcat_exported_pdu_length(pd);
+ pd += skipped_length;
+
+ if (!wtap_dump_file_write(wdh, (const gchar*) pd, rec->rec_header.packet_header.caplen - skipped_length, err)) {
+ return FALSE;
+ }
+ }
+ break;
+ case WTAP_ENCAP_LOGCAT:
+ /* Skip EXPORTED_PDU*/
+ if (wdh->file_encap == WTAP_ENCAP_WIRESHARK_UPPER_PDU) {
+ gint skipped_length;
+
+ skipped_length = logcat_exported_pdu_length(pd);
+ pd += skipped_length;
+
+ logcat_version = buffered_detect_version(pd);
+ } else {
+ const union wtap_pseudo_header *pseudo_header = &rec->rec_header.packet_header.pseudo_header;
+
+ logcat_version = pseudo_header->logcat.version;
+ }
+
+ log_entry = (const struct logger_entry *)(const void *) pd;
+ log_entry_v2 = (const struct logger_entry_v2 *)(const void *) pd;
+
+ payload_length = GINT32_FROM_LE(log_entry->len);
+ pid = GINT32_FROM_LE(log_entry->pid);
+ tid = GINT32_FROM_LE(log_entry->tid);
+ seconds = GINT32_FROM_LE(log_entry->sec);
+ milliseconds = GINT32_FROM_LE(log_entry->nsec) / 1000000;
+
+ /* msg: <prio:1><tag:N>\0<msg:N>\0 with N >= 0, last \0 can be missing */
+ if (logcat_version == 1) {
+ msg_payload = (const guint8 *) (log_entry + 1);
+
+ priority = get_priority(msg_payload[0]);
+ tag = msg_payload + 1;
+ msg_pre_skip = 1 + (gint) strlen(tag) + 1;
+ msg_begin = msg_payload + msg_pre_skip;
+ } else if (logcat_version == 2) {
+ msg_payload = (const guint8 *) (log_entry_v2 + 1);
+
+ priority = get_priority(msg_payload[0]);
+ tag = msg_payload + 1;
+ msg_pre_skip = 1 + (gint) strlen(tag) + 1;
+ msg_begin = msg_payload + msg_pre_skip;
+ } else {
+ *err = WTAP_ERR_UNWRITABLE_REC_DATA;
+ *err_info = ws_strdup_printf("logcat: version %d isn't supported",
+ logcat_version);
+ return FALSE;
+ }
+
+ /* copy the message part. If a nul byte was missing, it will be added. */
+ log = g_strndup(msg_begin, payload_length - msg_pre_skip);
+
+ /* long format: display one header followed by the whole message (which may
+ * contain new lines). Other formats: include tag, etc. with each line */
+ log_next = log;
+ do {
+ log_part = log_next;
+ if (dumper->type == WTAP_ENCAP_LOGCAT_LONG) {
+ /* read until end, there is no next string */
+ log_next = NULL;
+ } else {
+ /* read until next newline */
+ log_next = strchr(log_part, '\n');
+ if (log_next != NULL) {
+ *log_next = '\0';
+ ++log_next;
+ /* ignore trailing newline */
+ if (*log_next == '\0') {
+ log_next = NULL;
+ }
+ }
+ }
+
+ buf = logcat_log(dumper, seconds, milliseconds, pid, tid, priority, tag, log_part);
+ if (!buf) {
+ g_free(log);
+ return FALSE;
+ }
+ length = (guint32) strlen(buf);
+
+ if (!wtap_dump_file_write(wdh, buf, length, err)) {
+ g_free(log);
+ return FALSE;
+ }
+ } while (log_next != NULL );
+
+ g_free(log);
+
+ break;
+ case WTAP_ENCAP_LOGCAT_BRIEF:
+ case WTAP_ENCAP_LOGCAT_TAG:
+ case WTAP_ENCAP_LOGCAT_PROCESS:
+ case WTAP_ENCAP_LOGCAT_TIME:
+ case WTAP_ENCAP_LOGCAT_THREAD:
+ case WTAP_ENCAP_LOGCAT_THREADTIME:
+ case WTAP_ENCAP_LOGCAT_LONG:
+ if (dumper->type == wdh->file_encap) {
+ if (!wtap_dump_file_write(wdh, (const gchar*) pd, rec->rec_header.packet_header.caplen, err)) {
+ return FALSE;
+ }
+ } else {
+ *err = WTAP_ERR_UNWRITABLE_FILE_TYPE;
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+static gboolean logcat_text_dump_open(wtap_dumper *wdh, guint dump_type) {
+ struct dumper_t *dumper;
+
+ dumper = g_new(struct dumper_t, 1);
+ dumper->type = dump_type;
+
+ wdh->priv = dumper;
+ wdh->subtype_write = logcat_text_dump_text;
+
+ return TRUE;
+}
+
+static gboolean logcat_text_brief_dump_open(wtap_dumper *wdh, int *err _U_, gchar **err_info _U_) {
+ return logcat_text_dump_open(wdh, WTAP_ENCAP_LOGCAT_BRIEF);
+}
+
+static gboolean logcat_text_process_dump_open(wtap_dumper *wdh, int *err _U_, gchar **err_info _U_) {
+ return logcat_text_dump_open(wdh, WTAP_ENCAP_LOGCAT_PROCESS);
+}
+
+static gboolean logcat_text_tag_dump_open(wtap_dumper *wdh, int *err _U_, gchar **err_info _U_) {
+ return logcat_text_dump_open(wdh, WTAP_ENCAP_LOGCAT_TAG);
+}
+
+static gboolean logcat_text_time_dump_open(wtap_dumper *wdh, int *err _U_, gchar **err_info _U_) {
+ return logcat_text_dump_open(wdh, WTAP_ENCAP_LOGCAT_TIME);
+}
+
+static gboolean logcat_text_thread_dump_open(wtap_dumper *wdh, int *err _U_, gchar **err_info _U_) {
+ return logcat_text_dump_open(wdh, WTAP_ENCAP_LOGCAT_THREAD);
+}
+
+static gboolean logcat_text_threadtime_dump_open(wtap_dumper *wdh, int *err _U_, gchar **err_info _U_) {
+ return logcat_text_dump_open(wdh, WTAP_ENCAP_LOGCAT_THREADTIME);
+}
+
+static gboolean logcat_text_long_dump_open(wtap_dumper *wdh, int *err _U_, gchar **err_info _U_) {
+ return logcat_text_dump_open(wdh, WTAP_ENCAP_LOGCAT_LONG);
+}
+
+static const struct supported_block_type logcat_text_brief_blocks_supported[] = {
+ /*
+ * We support packet blocks, with no comments or other options.
+ */
+ { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
+};
+
+static const struct file_type_subtype_info logcat_text_brief_info = {
+ "Android Logcat Brief text format", "logcat-brief", NULL, NULL,
+ FALSE, BLOCKS_SUPPORTED(logcat_text_brief_blocks_supported),
+ logcat_text_brief_dump_can_write_encap, logcat_text_brief_dump_open, NULL
+};
+
+static const struct supported_block_type logcat_text_process_blocks_supported[] = {
+ /*
+ * We support packet blocks, with no comments or other options.
+ */
+ { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
+};
+
+static const struct file_type_subtype_info logcat_text_process_info = {
+ "Android Logcat Process text format", "logcat-process", NULL, NULL,
+ FALSE, BLOCKS_SUPPORTED(logcat_text_process_blocks_supported),
+ logcat_text_process_dump_can_write_encap, logcat_text_process_dump_open, NULL
+};
+
+static const struct supported_block_type logcat_text_tag_blocks_supported[] = {
+ /*
+ * We support packet blocks, with no comments or other options.
+ */
+ { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
+};
+
+static const struct file_type_subtype_info logcat_text_tag_info = {
+ "Android Logcat Tag text format", "logcat-tag", NULL, NULL,
+ FALSE, BLOCKS_SUPPORTED(logcat_text_tag_blocks_supported),
+ logcat_text_tag_dump_can_write_encap, logcat_text_tag_dump_open, NULL
+};
+
+static const struct supported_block_type logcat_text_thread_blocks_supported[] = {
+ /*
+ * We support packet blocks, with no comments or other options.
+ */
+ { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
+};
+
+static const struct file_type_subtype_info logcat_text_thread_info = {
+ "Android Logcat Thread text format", "logcat-thread", NULL, NULL,
+ FALSE, BLOCKS_SUPPORTED(logcat_text_thread_blocks_supported),
+ logcat_text_thread_dump_can_write_encap, logcat_text_thread_dump_open, NULL
+};
+
+static const struct supported_block_type logcat_text_time_blocks_supported[] = {
+ /*
+ * We support packet blocks, with no comments or other options.
+ */
+ { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
+};
+
+static const struct file_type_subtype_info logcat_text_time_info = {
+ "Android Logcat Time text format", "logcat-time", NULL, NULL,
+ FALSE, BLOCKS_SUPPORTED(logcat_text_time_blocks_supported),
+ logcat_text_time_dump_can_write_encap, logcat_text_time_dump_open, NULL
+};
+
+static const struct supported_block_type logcat_text_threadtime_blocks_supported[] = {
+ /*
+ * We support packet blocks, with no comments or other options.
+ */
+ { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
+};
+
+static const struct file_type_subtype_info logcat_text_threadtime_info = {
+ "Android Logcat Threadtime text format", "logcat-threadtime", NULL, NULL,
+ FALSE, BLOCKS_SUPPORTED(logcat_text_threadtime_blocks_supported),
+ logcat_text_threadtime_dump_can_write_encap, logcat_text_threadtime_dump_open, NULL
+};
+
+static const struct supported_block_type logcat_text_long_blocks_supported[] = {
+ /*
+ * We support packet blocks, with no comments or other options.
+ */
+ { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
+};
+
+static const struct file_type_subtype_info logcat_text_long_info = {
+ "Android Logcat Long text format", "logcat-long", NULL, NULL,
+ FALSE, BLOCKS_SUPPORTED(logcat_text_long_blocks_supported),
+ logcat_text_long_dump_can_write_encap, logcat_text_long_dump_open, NULL
+};
+
+void register_logcat_text(void)
+{
+ logcat_text_brief_file_type_subtype = wtap_register_file_type_subtype(&logcat_text_brief_info);
+ logcat_text_process_file_type_subtype = wtap_register_file_type_subtype(&logcat_text_process_info);
+ logcat_text_tag_file_type_subtype = wtap_register_file_type_subtype(&logcat_text_tag_info);
+ logcat_text_thread_file_type_subtype = wtap_register_file_type_subtype(&logcat_text_thread_info);
+ logcat_text_time_file_type_subtype = wtap_register_file_type_subtype(&logcat_text_time_info);
+ logcat_text_threadtime_file_type_subtype = wtap_register_file_type_subtype(&logcat_text_threadtime_info);
+ logcat_text_long_file_type_subtype = wtap_register_file_type_subtype(&logcat_text_long_info);
+
+ /*
+ * Register names for backwards compatibility with the
+ * wtap_filetypes table in Lua.
+ */
+ wtap_register_backwards_compatibility_lua_name("LOGCAT_BRIEF",
+ logcat_text_brief_file_type_subtype);
+ wtap_register_backwards_compatibility_lua_name("LOGCAT_PROCESS",
+ logcat_text_process_file_type_subtype);
+ wtap_register_backwards_compatibility_lua_name("LOGCAT_TAG",
+ logcat_text_tag_file_type_subtype);
+ wtap_register_backwards_compatibility_lua_name("LOGCAT_THREAD",
+ logcat_text_thread_file_type_subtype);
+ wtap_register_backwards_compatibility_lua_name("LOGCAT_TIME",
+ logcat_text_time_file_type_subtype);
+ wtap_register_backwards_compatibility_lua_name("LOGCAT_THREADTIME",
+ logcat_text_threadtime_file_type_subtype);
+ wtap_register_backwards_compatibility_lua_name("LOGCAT_LONG",
+ logcat_text_long_file_type_subtype);
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * vi: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */
diff --git a/wiretap/logcat_text.h b/wiretap/logcat_text.h
new file mode 100644
index 00000000..c8bd5026
--- /dev/null
+++ b/wiretap/logcat_text.h
@@ -0,0 +1,40 @@
+/** @file
+ *
+ * Copyright 2014, Michal Orynicz for Tieto Corporation
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ */
+
+#ifndef __LOGCAT_TEXT_H__
+#define __LOGCAT_TEXT_H__
+
+#include <glib.h>
+
+#include "wtap.h"
+
+#define SPECIAL_STRING "[-]+ (beginning of \\/?.+)"
+#define BRIEF_STRING "([IVDWEF])/(.*?)\\( *(\\d+)\\): (.*)"
+#define TAG_STRING "([IVDWEF])/(.*?): (.*)"
+#define TIME_STRING "(\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}\\.\\d{3}) ([IVDWEF])/(.*?)\\( *(\\d+)\\): (.*)"
+#define THREAD_STRING "([IVDWEF])\\( *(\\d+): *(\\d+)\\) (.*)"
+#define PROCESS_STRING "([IVDWEF])\\( *(\\d+)\\) (.*)"
+#define THREADTIME_STRING "(\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}\\.\\d{3}) +(\\d+) +(\\d+) ([IVDWEF]) (.*?): (.*)"
+#define LONG_STRING "\\[ (\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}\\.\\d{3}) +(\\d+): *(\\d+) ([IVDWEF])/(.+) ]\\R(.*)"
+
+wtap_open_return_val logcat_text_open(wtap *wth, int *err, gchar **err_info);
+
+#endif
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * vi: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */
diff --git a/wiretap/merge.c b/wiretap/merge.c
new file mode 100644
index 00000000..82154f9b
--- /dev/null
+++ b/wiretap/merge.c
@@ -0,0 +1,1485 @@
+/* Combine multiple dump files, either by appending or by merging by timestamp
+ *
+ * Written by Scott Renfro <scott@renfro.org> based on
+ * editcap by Richard Sharpe and Guy Harris
+ *
+ * Copyright 2013, Scott Renfro <scott[AT]renfro.org>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "config.h"
+
+#define WS_LOG_DOMAIN LOG_DOMAIN_WIRETAP
+
+#include <stdlib.h>
+#include <errno.h>
+
+#ifndef _WIN32
+#include <sys/resource.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#if defined(__APPLE__)
+#include <sys/sysctl.h>
+#endif
+
+#include <string.h>
+#include "merge.h"
+#include "wtap_opttypes.h"
+#include "wtap-int.h"
+
+#include <wsutil/filesystem.h>
+#include "wsutil/os_version_info.h"
+#include <wsutil/report_message.h>
+#include <wsutil/wslog.h>
+#include <wsutil/ws_assert.h>
+
+
+static const char* idb_merge_mode_strings[] = {
+ /* IDB_MERGE_MODE_NONE */
+ "none",
+ /* IDB_MERGE_MODE_ALL_SAME */
+ "all",
+ /* IDB_MERGE_MODE_ANY_SAME */
+ "any",
+ /* IDB_MERGE_MODE_MAX */
+ "UNKNOWN"
+};
+
+idb_merge_mode
+merge_string_to_idb_merge_mode(const char *name)
+{
+ int i;
+ for (i = 0; i < IDB_MERGE_MODE_MAX; i++) {
+ if (g_strcmp0(name, idb_merge_mode_strings[i]) == 0) {
+ return (idb_merge_mode) i;
+ }
+ }
+ return IDB_MERGE_MODE_MAX;
+}
+
+const char *
+merge_idb_merge_mode_to_string(const int mode)
+{
+ if (mode >= 0 && mode < IDB_MERGE_MODE_MAX) {
+ return idb_merge_mode_strings[mode];
+ }
+ return idb_merge_mode_strings[(int)IDB_MERGE_MODE_MAX];
+}
+
+
+static void
+cleanup_in_file(merge_in_file_t *in_file)
+{
+ ws_assert(in_file != NULL);
+
+ wtap_close(in_file->wth);
+ in_file->wth = NULL;
+
+ g_array_free(in_file->idb_index_map, TRUE);
+ in_file->idb_index_map = NULL;
+
+ wtap_rec_cleanup(&in_file->rec);
+ ws_buffer_free(&in_file->frame_buffer);
+}
+
+static void
+add_idb_index_map(merge_in_file_t *in_file, const guint orig_index _U_, const guint found_index)
+{
+ ws_assert(in_file != NULL);
+ ws_assert(in_file->idb_index_map != NULL);
+
+ /*
+ * we didn't really need the orig_index, since just appending to the array
+ * should result in the orig_index being its location in the array; but we
+ * pass it into this function to do a sanity check here
+ */
+ ws_assert(orig_index == in_file->idb_index_map->len);
+
+ g_array_append_val(in_file->idb_index_map, found_index);
+}
+
+#ifndef _WIN32
+static bool
+raise_limit(int resource, unsigned add)
+{
+ struct rlimit rl;
+ /* For now don't try to raise the hard limit; we could if we
+ * have the appropriate privileges (CAP_SYS_RESOURCE on Linux).
+ */
+ if ((getrlimit(resource, &rl) == 0) && rl.rlim_cur < rl.rlim_max) {
+ rlim_t old_cur = rl.rlim_cur;
+ /* What open file descriptor limit do we need? */
+ rl.rlim_cur = rl.rlim_cur + add;
+ /* Check for overflow (unlikely). */
+ rl.rlim_cur = MAX(old_cur, rl.rlim_cur);
+ rl.rlim_cur = MIN(rl.rlim_cur, rl.rlim_max);
+ if (setrlimit(resource, &rl) == 0) {
+ return true;
+ }
+#if defined(__APPLE__)
+ /* On Leopard, setrlimit(RLIMIT_NOFILE, ...) fails
+ * on attempts to set rlim_cur above OPEN_MAX, even
+ * if rlim_max > OPEN_MAX. OPEN_MAX is 10240.
+ *
+ * Starting with some later version (at least Mojave,
+ * possibly earlier), it can be set to kern.maxfilesperproc
+ * from sysctl, which is _usually_ higher than 10240.
+ *
+ * In Big Sur and later, it can always be set to rlim_max.
+ * (That is, setrlimit() will return 0 and getrlimit() will
+ * subsequently return the set value; the enforced limit
+ * is the lesser of that and kern.maxfilesperproc.)
+ */
+ if (resource == RLIMIT_NOFILE) {
+ unsigned int nlimit = 0;
+ size_t limit_len = sizeof(nlimit);
+ if (sysctlbyname("kern.maxfilesperproc", &nlimit, &limit_len, NULL, 0) != 0 || nlimit < OPEN_MAX) {
+ rl.rlim_cur = OPEN_MAX;
+ } else {
+ rl.rlim_cur = nlimit;
+ }
+ if (setrlimit(RLIMIT_NOFILE, &rl) == 0) {
+ return true;
+ }
+ if (rl.rlim_cur > OPEN_MAX) {
+ rl.rlim_cur = OPEN_MAX;
+ if (setrlimit(RLIMIT_NOFILE, &rl) == 0) {
+ return true;
+ }
+ }
+ }
+#endif
+ }
+ return false;
+}
+#endif
+
+/** Open a number of input files to merge.
+ *
+ * @param in_file_count number of entries in in_file_names
+ * @param in_file_names filenames of the input files
+ * @param out_files output pointer with filled file array, or NULL
+ * @param err wiretap error, if failed
+ * @param err_info wiretap error string, if failed
+ * @param err_fileno file on which open failed, if failed
+ * @return The number of input files opened, which can be less than
+ * the number requested if the limit of open file descriptors is reached.
+ */
+static guint
+merge_open_in_files(guint in_file_count, const char *const *in_file_names,
+ merge_in_file_t **out_files, merge_progress_callback_t* cb,
+ int *err, gchar **err_info, guint *err_fileno)
+{
+ guint i = 0;
+ guint j;
+ size_t files_size = in_file_count * sizeof(merge_in_file_t);
+ merge_in_file_t *files;
+ gint64 size;
+#ifndef _WIN32
+ bool try_raise_nofile = false;
+#endif
+
+ files = (merge_in_file_t *)g_malloc0(files_size);
+ *out_files = NULL;
+
+ while (i < in_file_count) {
+ files[i].filename = in_file_names[i];
+ files[i].wth = wtap_open_offline(in_file_names[i], WTAP_TYPE_AUTO, err, err_info, FALSE);
+ files[i].state = RECORD_NOT_PRESENT;
+ files[i].packet_num = 0;
+
+ if (!files[i].wth) {
+ if (*err == EMFILE && i > 2) {
+ /* We need at least two opened files to merge things if we
+ * are batch processing. (If there was only one file to open
+ * then we can "merge" a single file so long as we don't get
+ * EMFILE, even though that's pointless.)
+ */
+#ifdef _WIN32
+ report_warning("Requested opening %u files but could only open %u: %s\nUsing temporary files to batch process.", in_file_count, i, g_strerror(*err));
+#else
+ if (!try_raise_nofile) {
+ try_raise_nofile = true;
+ if (raise_limit(RLIMIT_NOFILE, in_file_count - i)) {
+ continue;
+ }
+ }
+ report_warning("Requested opening %u files but could only open %u: %s\nUsing temporary files to batch process (try ulimit -n to adjust the limit).", in_file_count, i, g_strerror(*err));
+#endif
+ in_file_count = i;
+ files_size = in_file_count * sizeof(merge_in_file_t);
+ files = (merge_in_file_t *)g_realloc(files, files_size);
+ *err = 0;
+ break;
+ } else {
+ /* Close the files we've already opened. */
+ for (j = 0; j < i; j++)
+ cleanup_in_file(&files[j]);
+ g_free(files);
+ *err_fileno = i;
+ return 0;
+ }
+ }
+ size = wtap_file_size(files[i].wth, err);
+ if (size == -1) {
+ for (j = 0; j != G_MAXUINT && j <= i; j++)
+ cleanup_in_file(&files[j]);
+ g_free(files);
+ *err_fileno = i;
+ return 0;
+ }
+ wtap_rec_init(&files[i].rec);
+ ws_buffer_init(&files[i].frame_buffer, 1514);
+ files[i].size = size;
+ files[i].idb_index_map = g_array_new(FALSE, FALSE, sizeof(guint));
+
+ i++;
+ }
+
+ if (cb)
+ cb->callback_func(MERGE_EVENT_INPUT_FILES_OPENED, 0, files, in_file_count, cb->data);
+
+ *out_files = files;
+ return in_file_count;
+}
+
+/** Close the input files again.
+ *
+ * @param in_file_count number of entries in in_files
+ * @param in_files input file array to be closed
+ */
+static void
+merge_close_in_files(int in_file_count, merge_in_file_t in_files[])
+{
+ int i;
+ for (i = 0; i < in_file_count; i++) {
+ cleanup_in_file(&in_files[i]);
+ }
+}
+
+/** Select an output frame type based on the input files
+ *
+ * If all files have the same frame type, then use that.
+ * Otherwise select WTAP_ENCAP_PER_PACKET. If the selected
+ * output file type doesn't support per packet frame types,
+ * then the wtap_dump_open call will fail with a reasonable
+ * error condition.
+ *
+ * @param file_type output file type
+ * @param in_file_count number of entries in in_files
+ * @param in_files input file array
+ * @return the frame type
+ */
+static int
+merge_select_frame_type(const int file_type, int in_file_count, merge_in_file_t in_files[])
+{
+ int i;
+ int selected_frame_type;
+
+ selected_frame_type = wtap_file_encap(in_files[0].wth);
+ if (!wtap_dump_can_write_encap(file_type, selected_frame_type)) {
+ return WTAP_ENCAP_UNKNOWN;
+ }
+
+ for (i = 1; i < in_file_count; i++) {
+ int this_frame_type = wtap_file_encap(in_files[i].wth);
+ if (!wtap_dump_can_write_encap(file_type, this_frame_type)) {
+ return WTAP_ENCAP_UNKNOWN;
+ }
+ if (selected_frame_type != this_frame_type) {
+ selected_frame_type = WTAP_ENCAP_PER_PACKET;
+ break;
+ }
+ }
+
+ return selected_frame_type;
+}
+
+/*
+ * returns TRUE if first argument is earlier than second
+ */
+static gboolean
+is_earlier(nstime_t *l, nstime_t *r) /* XXX, move to nstime.c */
+{
+ if (l->secs > r->secs) { /* left is later */
+ return FALSE;
+ } else if (l->secs < r->secs) { /* left is earlier */
+ return TRUE;
+ } else if (l->nsecs > r->nsecs) { /* tv_sec equal, l.usec later */
+ return FALSE;
+ }
+ /* either one < two or one == two
+ * either way, return one
+ */
+ return TRUE;
+}
+
+/** Read the next packet, in chronological order, from the set of files to
+ * be merged.
+ *
+ * On success, set *err to 0 and return a pointer to the merge_in_file_t
+ * for the file from which the packet was read.
+ *
+ * On a read error, set *err to the error and return a pointer to the
+ * merge_in_file_t for the file on which we got an error.
+ *
+ * On an EOF (meaning all the files are at EOF), set *err to 0 and return
+ * NULL.
+ *
+ * @param in_file_count number of entries in in_files
+ * @param in_files input file array
+ * @param err wiretap error, if failed
+ * @param err_info wiretap error string, if failed
+ * @return pointer to merge_in_file_t for file from which that packet
+ * came or on which we got a read error, or NULL if we're at EOF on
+ * all files
+ */
+static merge_in_file_t *
+merge_read_packet(int in_file_count, merge_in_file_t in_files[],
+ int *err, gchar **err_info)
+{
+ int i;
+ int ei = -1;
+ nstime_t tv = NSTIME_INIT_MAX;
+ wtap_rec *rec;
+
+ /*
+ * Make sure we have a record available from each file that's not at
+ * EOF, and search for the record with the earliest time stamp or
+ * with no time stamp (those records are treated as earlier than
+ * all other records). Yes, this means you won't get a chronological
+ * merge of those records, but you obviously *can't* get that.
+ */
+ for (i = 0; i < in_file_count; i++) {
+ gint64 data_offset;
+
+ if (in_files[i].state == RECORD_NOT_PRESENT) {
+ /*
+ * No packet available, and we haven't seen an error or EOF yet,
+ * so try to read the next packet.
+ */
+ if (!wtap_read(in_files[i].wth, &in_files[i].rec,
+ &in_files[i].frame_buffer, err, err_info,
+ &data_offset)) {
+ if (*err != 0) {
+ in_files[i].state = GOT_ERROR;
+ return &in_files[i];
+ }
+ in_files[i].state = AT_EOF;
+ } else
+ in_files[i].state = RECORD_PRESENT;
+ }
+
+ if (in_files[i].state == RECORD_PRESENT) {
+ rec = &in_files[i].rec;
+ if (!(rec->presence_flags & WTAP_HAS_TS)) {
+ /*
+ * No time stamp. Pick this record, and stop looking.
+ */
+ ei = i;
+ break;
+ }
+ if (is_earlier(&rec->ts, &tv)) {
+ /*
+ * This record's time stamp is earlier than any of the
+ * records we've seen so far. Pick it, for now, but
+ * keep looking.
+ */
+ tv = rec->ts;
+ ei = i;
+ }
+ }
+ }
+
+ if (ei == -1) {
+ /* All the streams are at EOF. Return an EOF indication. */
+ *err = 0;
+ return NULL;
+ }
+
+ /* We'll need to read another packet from this file. */
+ in_files[ei].state = RECORD_NOT_PRESENT;
+
+ /* Count this packet. */
+ in_files[ei].packet_num++;
+
+ /*
+ * Return a pointer to the merge_in_file_t of the file from which the
+ * packet was read.
+ */
+ *err = 0;
+ return &in_files[ei];
+}
+
+/** Read the next packet, in file sequence order, from the set of files
+ * to be merged.
+ *
+ * On success, set *err to 0 and return a pointer to the merge_in_file_t
+ * for the file from which the packet was read.
+ *
+ * On a read error, set *err to the error and return a pointer to the
+ * merge_in_file_t for the file on which we got an error.
+ *
+ * On an EOF (meaning all the files are at EOF), set *err to 0 and return
+ * NULL.
+ *
+ * @param in_file_count number of entries in in_files
+ * @param in_files input file array
+ * @param err wiretap error, if failed
+ * @param err_info wiretap error string, if failed
+ * @return pointer to merge_in_file_t for file from which that packet
+ * came or on which we got a read error, or NULL if we're at EOF on
+ * all files
+ */
+static merge_in_file_t *
+merge_append_read_packet(int in_file_count, merge_in_file_t in_files[],
+ int *err, gchar **err_info)
+{
+ int i;
+ gint64 data_offset;
+
+ /*
+ * Find the first file not at EOF, and read the next packet from it.
+ */
+ for (i = 0; i < in_file_count; i++) {
+ if (in_files[i].state == AT_EOF)
+ continue; /* This file is already at EOF */
+ if (wtap_read(in_files[i].wth, &in_files[i].rec,
+ &in_files[i].frame_buffer, err, err_info,
+ &data_offset))
+ break; /* We have a packet */
+ if (*err != 0) {
+ /* Read error - quit immediately. */
+ in_files[i].state = GOT_ERROR;
+ return &in_files[i];
+ }
+ /* EOF - flag this file as being at EOF, and try the next one. */
+ in_files[i].state = AT_EOF;
+ }
+ if (i == in_file_count) {
+ /* All the streams are at EOF. Return an EOF indication. */
+ *err = 0;
+ return NULL;
+ }
+
+ /*
+ * Return a pointer to the merge_in_file_t of the file from which the
+ * packet was read.
+ */
+ *err = 0;
+ return &in_files[i];
+}
+
+
+/* creates a section header block for the new output file */
+static GArray*
+create_shb_header(const merge_in_file_t *in_files, const guint in_file_count,
+ const gchar *app_name)
+{
+ GArray *shb_hdrs;
+ wtap_block_t shb_hdr;
+ GString *comment_gstr;
+ GString *os_info_str;
+ guint i;
+ char* shb_comment = NULL;
+ wtapng_section_mandatory_t* shb_data;
+ gsize opt_len;
+ gchar *opt_str;
+
+ shb_hdrs = wtap_file_get_shb_for_new_file(in_files[0].wth);
+ shb_hdr = g_array_index(shb_hdrs, wtap_block_t, 0);
+
+ comment_gstr = g_string_new("");
+
+ /*
+ * TODO: merge comments from all files
+ *
+ * XXX - do we want some way to record which comments, hardware/OS/app
+ * descriptions, IDBs, etc.? came from which files?
+ *
+ * XXX - fix this to handle multiple comments from a single file.
+ */
+ if (wtap_block_get_nth_string_option_value(shb_hdr, OPT_COMMENT, 0, &shb_comment) == WTAP_OPTTYPE_SUCCESS &&
+ strlen(shb_comment) > 0) {
+ /* very lame way to save comments - does not save them from the other files */
+ g_string_append_printf(comment_gstr, "%s \n",shb_comment);
+ }
+
+ g_string_append_printf(comment_gstr, "File created by merging: \n");
+
+ for (i = 0; i < in_file_count; i++) {
+ g_string_append_printf(comment_gstr, "File%d: %s \n",i+1,in_files[i].filename);
+ }
+
+ os_info_str = g_string_new("");
+ get_os_version_info(os_info_str);
+
+ shb_data = (wtapng_section_mandatory_t*)wtap_block_get_mandatory_data(shb_hdr);
+ shb_data->section_length = -1;
+ /* TODO: handle comments from each file being merged */
+ opt_len = comment_gstr->len;
+ opt_str = g_string_free(comment_gstr, FALSE);
+ wtap_block_set_nth_string_option_value(shb_hdr, OPT_COMMENT, 0, opt_str, opt_len); /* section comment */
+ g_free(opt_str);
+ /*
+ * XXX - and how do we preserve all the OPT_SHB_HARDWARE, OPT_SHB_OS,
+ * and OPT_SHB_USERAPPL values from all the previous files?
+ */
+ wtap_block_remove_option(shb_hdr, OPT_SHB_HARDWARE);
+ opt_len = os_info_str->len;
+ opt_str = g_string_free(os_info_str, FALSE);
+ if (opt_str) {
+ wtap_block_set_string_option_value(shb_hdr, OPT_SHB_OS, opt_str, opt_len); /* UTF-8 string containing the name */
+ /* of the operating system used to create this section. */
+ g_free(opt_str);
+ } else {
+ /*
+ * No OS information; remove the old version.
+ */
+ wtap_block_remove_option(shb_hdr, OPT_SHB_OS);
+ }
+ wtap_block_set_string_option_value(shb_hdr, OPT_SHB_USERAPPL, app_name, app_name ? strlen(app_name): 0 ); /* NULL if not available, UTF-8 string containing the name */
+ /* of the application used to create this section. */
+
+ return shb_hdrs;
+}
+
+static gboolean
+is_duplicate_idb(const wtap_block_t idb1, const wtap_block_t idb2)
+{
+ wtapng_if_descr_mandatory_t *idb1_mand, *idb2_mand;
+ gboolean have_idb1_value, have_idb2_value;
+ guint64 idb1_if_speed, idb2_if_speed;
+ guint8 idb1_if_tsresol, idb2_if_tsresol;
+ guint8 idb1_if_fcslen, idb2_if_fcslen;
+ char *idb1_opt_comment, *idb2_opt_comment;
+ char *idb1_if_name, *idb2_if_name;
+ char *idb1_if_description, *idb2_if_description;
+ char *idb1_if_hardware, *idb2_if_hardware;
+ char *idb1_if_os, *idb2_if_os;
+
+ ws_assert(idb1 && idb2);
+ idb1_mand = (wtapng_if_descr_mandatory_t*)wtap_block_get_mandatory_data(idb1);
+ idb2_mand = (wtapng_if_descr_mandatory_t*)wtap_block_get_mandatory_data(idb2);
+
+ ws_debug("merge::is_duplicate_idb() called");
+ ws_debug("idb1_mand->wtap_encap == idb2_mand->wtap_encap: %s",
+ (idb1_mand->wtap_encap == idb2_mand->wtap_encap) ? "TRUE":"FALSE");
+ if (idb1_mand->wtap_encap != idb2_mand->wtap_encap) {
+ /* Clearly not the same interface. */
+ ws_debug("returning FALSE");
+ return FALSE;
+ }
+
+ ws_debug("idb1_mand->time_units_per_second == idb2_mand->time_units_per_second: %s",
+ (idb1_mand->time_units_per_second == idb2_mand->time_units_per_second) ? "TRUE":"FALSE");
+ if (idb1_mand->time_units_per_second != idb2_mand->time_units_per_second) {
+ /*
+ * Probably not the same interface, and we can't combine them
+ * in any case.
+ */
+ ws_debug("returning FALSE");
+ return FALSE;
+ }
+
+ ws_debug("idb1_mand->tsprecision == idb2_mand->tsprecision: %s",
+ (idb1_mand->tsprecision == idb2_mand->tsprecision) ? "TRUE":"FALSE");
+ if (idb1_mand->tsprecision != idb2_mand->tsprecision) {
+ /*
+ * Probably not the same interface, and we can't combine them
+ * in any case.
+ */
+ ws_debug("returning FALSE");
+ return FALSE;
+ }
+
+ /* XXX: should snaplen not be compared? */
+ ws_debug("idb1_mand->snap_len == idb2_mand->snap_len: %s",
+ (idb1_mand->snap_len == idb2_mand->snap_len) ? "TRUE":"FALSE");
+ if (idb1_mand->snap_len != idb2_mand->snap_len) {
+ ws_debug("returning FALSE");
+ return FALSE;
+ }
+
+ /* XXX - what do to if we have only one value? */
+ have_idb1_value = (wtap_block_get_uint64_option_value(idb1, OPT_IDB_SPEED, &idb1_if_speed) == WTAP_OPTTYPE_SUCCESS);
+ have_idb2_value = (wtap_block_get_uint64_option_value(idb2, OPT_IDB_SPEED, &idb2_if_speed) == WTAP_OPTTYPE_SUCCESS);
+ if (have_idb1_value && have_idb2_value) {
+ ws_debug("idb1_if_speed == idb2_if_speed: %s",
+ (idb1_if_speed == idb2_if_speed) ? "TRUE":"FALSE");
+ if (idb1_if_speed != idb2_if_speed) {
+ ws_debug("returning FALSE");
+ return FALSE;
+ }
+ }
+
+ /* XXX - what do to if we have only one value? */
+ have_idb1_value = (wtap_block_get_uint8_option_value(idb1, OPT_IDB_TSRESOL, &idb1_if_tsresol) == WTAP_OPTTYPE_SUCCESS);
+ have_idb2_value = (wtap_block_get_uint8_option_value(idb2, OPT_IDB_TSRESOL, &idb2_if_tsresol) == WTAP_OPTTYPE_SUCCESS);
+ if (have_idb1_value && have_idb2_value) {
+ ws_debug("idb1_if_tsresol == idb2_if_tsresol: %s",
+ (idb1_if_tsresol == idb2_if_tsresol) ? "TRUE":"FALSE");
+ if (idb1_if_tsresol != idb2_if_tsresol) {
+ ws_debug("returning FALSE");
+ return FALSE;
+ }
+ }
+
+ /* XXX - what do to if we have only one value? */
+ have_idb1_value = (wtap_block_get_uint8_option_value(idb1, OPT_IDB_FCSLEN, &idb1_if_fcslen) == WTAP_OPTTYPE_SUCCESS);
+ have_idb2_value = (wtap_block_get_uint8_option_value(idb2, OPT_IDB_FCSLEN, &idb2_if_fcslen) == WTAP_OPTTYPE_SUCCESS);
+ if (have_idb1_value && have_idb2_value) {
+ ws_debug("idb1_if_fcslen == idb2_if_fcslen: %s",
+ (idb1_if_fcslen == idb2_if_fcslen) ? "TRUE":"FALSE");
+ if (idb1_if_fcslen == idb2_if_fcslen) {
+ ws_debug("returning FALSE");
+ return FALSE;
+ }
+ }
+
+ /*
+ * XXX - handle multiple comments?
+ * XXX - if the comments are different, just combine them if we
+ * decide the two interfaces are really the same? As comments
+ * can be arbitrary strings added by people, the fact that they're
+ * different doesn't necessarily mean the interfaces are different.
+ */
+ have_idb1_value = (wtap_block_get_nth_string_option_value(idb1, OPT_COMMENT, 0, &idb1_opt_comment) == WTAP_OPTTYPE_SUCCESS);
+ have_idb2_value = (wtap_block_get_nth_string_option_value(idb2, OPT_COMMENT, 0, &idb2_opt_comment) == WTAP_OPTTYPE_SUCCESS);
+ if (have_idb1_value && have_idb2_value) {
+ ws_debug("g_strcmp0(idb1_opt_comment, idb2_opt_comment) == 0: %s",
+ (g_strcmp0(idb1_opt_comment, idb2_opt_comment) == 0) ? "TRUE":"FALSE");
+ if (g_strcmp0(idb1_opt_comment, idb2_opt_comment) != 0) {
+ ws_debug("returning FALSE");
+ return FALSE;
+ }
+ }
+
+ /* XXX - what do to if we have only one value? */
+ have_idb1_value = (wtap_block_get_string_option_value(idb1, OPT_IDB_NAME, &idb1_if_name) == WTAP_OPTTYPE_SUCCESS);
+ have_idb2_value = (wtap_block_get_string_option_value(idb2, OPT_IDB_NAME, &idb2_if_name) == WTAP_OPTTYPE_SUCCESS);
+ if (have_idb1_value && have_idb2_value) {
+ ws_debug("g_strcmp0(idb1_if_name, idb2_if_name) == 0: %s",
+ (g_strcmp0(idb1_if_name, idb2_if_name) == 0) ? "TRUE":"FALSE");
+ if (g_strcmp0(idb1_if_name, idb2_if_name) != 0) {
+ ws_debug("returning FALSE");
+ return FALSE;
+ }
+ }
+
+ /* XXX - what do to if we have only one value? */
+ have_idb1_value = (wtap_block_get_string_option_value(idb1, OPT_IDB_DESCRIPTION, &idb1_if_description) == WTAP_OPTTYPE_SUCCESS);
+ have_idb2_value = (wtap_block_get_string_option_value(idb2, OPT_IDB_DESCRIPTION, &idb2_if_description) == WTAP_OPTTYPE_SUCCESS);
+ if (have_idb1_value && have_idb2_value) {
+ ws_debug("g_strcmp0(idb1_if_description, idb2_if_description) == 0: %s",
+ (g_strcmp0(idb1_if_description, idb2_if_description) == 0) ? "TRUE":"FALSE");
+ if (g_strcmp0(idb1_if_description, idb2_if_description) != 0) {
+ ws_debug("returning FALSE");
+ return FALSE;
+ }
+ }
+
+ /* XXX - what do to if we have only one value? */
+ have_idb1_value = (wtap_block_get_string_option_value(idb1, OPT_IDB_HARDWARE, &idb1_if_hardware) == WTAP_OPTTYPE_SUCCESS);
+ have_idb2_value = (wtap_block_get_string_option_value(idb2, OPT_IDB_HARDWARE, &idb2_if_hardware) == WTAP_OPTTYPE_SUCCESS);
+ if (have_idb1_value && have_idb2_value) {
+ ws_debug("g_strcmp0(idb1_if_hardware, idb2_if_hardware) == 0: %s",
+ (g_strcmp0(idb1_if_hardware, idb2_if_hardware) == 0) ? "TRUE":"FALSE");
+ if (g_strcmp0(idb1_if_hardware, idb2_if_hardware) != 0) {
+ ws_debug("returning FALSE");
+ return FALSE;
+ }
+ }
+
+ /* XXX - what do to if we have only one value? */
+ have_idb1_value = (wtap_block_get_string_option_value(idb1, OPT_IDB_OS, &idb1_if_os) == WTAP_OPTTYPE_SUCCESS);
+ have_idb2_value = (wtap_block_get_string_option_value(idb2, OPT_IDB_OS, &idb2_if_os) == WTAP_OPTTYPE_SUCCESS);
+ if (have_idb1_value && have_idb2_value) {
+ ws_debug("g_strcmp0(idb1_if_os, idb2_if_os) == 0: %s",
+ (g_strcmp0(idb1_if_os, idb2_if_os) == 0) ? "TRUE":"FALSE");
+ if (g_strcmp0(idb1_if_os, idb2_if_os) != 0) {
+ ws_debug("returning FALSE");
+ return FALSE;
+ }
+ }
+
+ /* does not compare filters nor interface statistics */
+ ws_debug("returning TRUE");
+ return TRUE;
+}
+
+/*
+ * Returns true if all of the input files have duplicate IDBs to the other files.
+ */
+static gboolean
+all_idbs_are_duplicates(const merge_in_file_t *in_files, const guint in_file_count)
+{
+ wtapng_iface_descriptions_t *first_idb_list = NULL;
+ wtapng_iface_descriptions_t *other_idb_list = NULL;
+ guint first_idb_list_size, other_idb_list_size;
+ wtap_block_t first_file_idb, other_file_idb;
+ guint i, j;
+
+ ws_assert(in_files != NULL);
+
+ /* get the first file's info */
+ first_idb_list = wtap_file_get_idb_info(in_files[0].wth);
+ ws_assert(first_idb_list->interface_data);
+
+ first_idb_list_size = first_idb_list->interface_data->len;
+
+ /* now compare the other input files with that */
+ for (i = 1; i < in_file_count; i++) {
+ other_idb_list = wtap_file_get_idb_info(in_files[i].wth);
+ ws_assert(other_idb_list->interface_data);
+ other_idb_list_size = other_idb_list->interface_data->len;
+
+ if (other_idb_list_size != first_idb_list_size) {
+ ws_debug("sizes of IDB lists don't match: first=%u, other=%u",
+ first_idb_list_size, other_idb_list_size);
+ g_free(other_idb_list);
+ g_free(first_idb_list);
+ return FALSE;
+ }
+
+ for (j = 0; j < other_idb_list_size; j++) {
+ first_file_idb = g_array_index(first_idb_list->interface_data, wtap_block_t, j);
+ other_file_idb = g_array_index(other_idb_list->interface_data, wtap_block_t, j);
+
+ if (!is_duplicate_idb(first_file_idb, other_file_idb)) {
+ ws_debug("IDBs at index %d do not match, returning FALSE", j);
+ g_free(other_idb_list);
+ g_free(first_idb_list);
+ return FALSE;
+ }
+ }
+ g_free(other_idb_list);
+ }
+
+ ws_debug("returning TRUE");
+
+ g_free(first_idb_list);
+
+ return TRUE;
+}
+
+/*
+ * Returns true if the given input_file_idb is a duplicate of an existing one
+ * in the merged_idb_list; it's a duplicate if the interface description data
+ * is all identical to a previous one in another input file. For this
+ * function, the input file IDB's index does NOT need to match the index
+ * location of a previous one to be considered a duplicate; any match is
+ * considered a success. That means it will even match another IDB from its
+ * own (same) input file.
+ */
+static gboolean
+find_duplicate_idb(const wtap_block_t input_file_idb,
+ const wtapng_iface_descriptions_t *merged_idb_list,
+ guint *found_index)
+{
+ wtap_block_t merged_idb;
+ guint i;
+
+ ws_assert(input_file_idb != NULL);
+ ws_assert(merged_idb_list != NULL);
+ ws_assert(merged_idb_list->interface_data != NULL);
+ ws_assert(found_index != NULL);
+
+ for (i = 0; i < merged_idb_list->interface_data->len; i++) {
+ merged_idb = g_array_index(merged_idb_list->interface_data, wtap_block_t, i);
+
+ if (is_duplicate_idb(input_file_idb, merged_idb)) {
+ *found_index = i;
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+/* Adds IDB to merged file info. If pdh is not NULL, also tries to
+ * add the IDB to the file (if the file type supports writing IDBs).
+ * returns TRUE on success
+ * (merged_idb_list->interface_data->len - 1 is the new index) */
+static gboolean
+add_idb_to_merged_file(wtapng_iface_descriptions_t *merged_idb_list,
+ const wtap_block_t input_file_idb, wtap_dumper *pdh,
+ int *err, gchar **err_info)
+{
+ wtap_block_t idb;
+ wtapng_if_descr_mandatory_t* idb_mand;
+
+ ws_assert(merged_idb_list != NULL);
+ ws_assert(merged_idb_list->interface_data != NULL);
+ ws_assert(input_file_idb != NULL);
+
+ idb = wtap_block_make_copy(input_file_idb);
+ idb_mand = (wtapng_if_descr_mandatory_t*)wtap_block_get_mandatory_data(idb);
+
+ /* Don't copy filter or stat information */
+ idb_mand->num_stat_entries = 0; /* Number of ISB:s */
+ idb_mand->interface_statistics = NULL;
+
+ if (pdh != NULL) {
+ if (wtap_file_type_subtype_supports_block(wtap_dump_file_type_subtype(pdh), WTAP_BLOCK_IF_ID_AND_INFO) != BLOCK_NOT_SUPPORTED) {
+ if (!wtap_dump_add_idb(pdh, input_file_idb, err, err_info)) {
+ return FALSE;
+ }
+ }
+ }
+ g_array_append_val(merged_idb_list->interface_data, idb);
+
+ return TRUE;
+}
+
+/*
+ * Create clone IDBs for the merge file for IDBs found in the middle of
+ * input files while processing.
+ */
+static gboolean
+process_new_idbs(wtap_dumper *pdh, merge_in_file_t *in_files, const guint in_file_count, const idb_merge_mode mode, wtapng_iface_descriptions_t *merged_idb_list, int *err, gchar **err_info)
+{
+ wtap_block_t input_file_idb;
+ guint itf_count, merged_index;
+ guint i;
+
+ for (i = 0; i < in_file_count; i++) {
+
+ itf_count = in_files[i].wth->next_interface_data;
+ while ((input_file_idb = wtap_get_next_interface_description(in_files[i].wth)) != NULL) {
+
+ /* If we were initially in ALL mode and all the interfaces
+ * did match, then we set the mode to ANY (merge duplicates).
+ * If the interfaces didn't match, then we are still in ALL
+ * mode, but treat that as NONE (write out all IDBs.)
+ * XXX: Should there be separate modes for "match ALL at the start
+ * and ANY later" vs "match ALL at the beginning and NONE later"?
+ * Should there be a two-pass mode for people who want ALL mode to
+ * work for IDBs in the middle of the file? (See #16542)
+ */
+
+ if (mode == IDB_MERGE_MODE_ANY_SAME &&
+ find_duplicate_idb(input_file_idb, merged_idb_list, &merged_index))
+ {
+ ws_debug("mode ANY set and found a duplicate");
+ /*
+ * It's the same as a previous IDB, so we're going to "merge"
+ * them into one by adding a map from its old IDB index to the
+ * new one. This will be used later to change the rec
+ * interface_id.
+ */
+ add_idb_index_map(&in_files[i], itf_count, merged_index);
+ }
+ else {
+ ws_debug("mode NONE or ALL set or did not find a duplicate");
+ /*
+ * This IDB does not match a previous (or we want to save all
+ * IDBs), so add the IDB to the merge file, and add a map of
+ * the indices.
+ */
+ if (add_idb_to_merged_file(merged_idb_list, input_file_idb, pdh, err, err_info)) {
+ merged_index = merged_idb_list->interface_data->len - 1;
+ add_idb_index_map(&in_files[i], itf_count, merged_index);
+ } else {
+ return FALSE;
+ }
+ }
+ itf_count = in_files[i].wth->next_interface_data;
+ }
+ }
+
+ return TRUE;
+}
+
+/*
+ * Create clone IDBs for the merge file, based on the input files and mode.
+ */
+static wtapng_iface_descriptions_t *
+generate_merged_idbs(merge_in_file_t *in_files, const guint in_file_count, idb_merge_mode * const mode)
+{
+ wtapng_iface_descriptions_t *merged_idb_list = NULL;
+ wtap_block_t input_file_idb;
+ guint itf_count, merged_index;
+ guint i;
+
+ /* create new IDB info */
+ merged_idb_list = g_new(wtapng_iface_descriptions_t,1);
+ merged_idb_list->interface_data = g_array_new(FALSE, FALSE, sizeof(wtap_block_t));
+
+ if (*mode == IDB_MERGE_MODE_ALL_SAME && all_idbs_are_duplicates(in_files, in_file_count)) {
+ ws_debug("mode ALL set and all IDBs are duplicates");
+
+ /* All files have the same interfaces are the same, so merge any
+ * IDBs found later in the files together with duplicates.
+ * (Note this is also the right thing to do if we have some kind
+ * of two-pass mode and all_idbs_are_duplicates actually did
+ * compare all the IDBs instead of just the ones before any packets.)
+ */
+ *mode = IDB_MERGE_MODE_ANY_SAME;
+
+ /* they're all the same, so just get the first file's IDBs */
+ itf_count = in_files[0].wth->next_interface_data;
+ /* put them in the merged file */
+ while ((input_file_idb = wtap_get_next_interface_description(in_files[0].wth)) != NULL) {
+ add_idb_to_merged_file(merged_idb_list, input_file_idb, NULL, NULL, NULL);
+ merged_index = merged_idb_list->interface_data->len - 1;
+ add_idb_index_map(&in_files[0], itf_count, merged_index);
+ /* and set all the other file index maps the same way */
+ for (i = 1; i < in_file_count; i++) {
+ if (wtap_get_next_interface_description(in_files[i].wth) != NULL) {
+ add_idb_index_map(&in_files[i], itf_count, merged_index);
+ } else {
+ ws_assert_not_reached();
+ }
+ }
+ itf_count = in_files[0].wth->next_interface_data;
+ }
+ }
+ else {
+ for (i = 0; i < in_file_count; i++) {
+
+ itf_count = in_files[i].wth->next_interface_data;
+ while ((input_file_idb = wtap_get_next_interface_description(in_files[i].wth)) != NULL) {
+
+ if (*mode == IDB_MERGE_MODE_ANY_SAME &&
+ find_duplicate_idb(input_file_idb, merged_idb_list, &merged_index))
+ {
+ ws_debug("mode ANY set and found a duplicate");
+ /*
+ * It's the same as a previous IDB, so we're going to "merge"
+ * them into one by adding a map from its old IDB index to the new
+ * one. This will be used later to change the rec interface_id.
+ */
+ add_idb_index_map(&in_files[i], itf_count, merged_index);
+ }
+ else {
+ ws_debug("mode NONE set or did not find a duplicate");
+ /*
+ * This IDB does not match a previous (or we want to save all IDBs),
+ * so add the IDB to the merge file, and add a map of the indices.
+ */
+ add_idb_to_merged_file(merged_idb_list, input_file_idb, NULL, NULL, NULL);
+ merged_index = merged_idb_list->interface_data->len - 1;
+ add_idb_index_map(&in_files[i], itf_count, merged_index);
+ }
+ itf_count = in_files[i].wth->next_interface_data;
+ }
+ }
+ }
+
+ return merged_idb_list;
+}
+
+static gboolean
+map_rec_interface_id(wtap_rec *rec, const merge_in_file_t *in_file)
+{
+ guint current_interface_id = 0;
+ ws_assert(rec != NULL);
+ ws_assert(in_file != NULL);
+ ws_assert(in_file->idb_index_map != NULL);
+
+ if (rec->presence_flags & WTAP_HAS_INTERFACE_ID) {
+ current_interface_id = rec->rec_header.packet_header.interface_id;
+ }
+
+ if (current_interface_id >= in_file->idb_index_map->len) {
+ /* this shouldn't happen, but in a malformed input file it could */
+ ws_debug("current_interface_id (%u) >= in_file->idb_index_map->len (%u) (ERROR?)",
+ current_interface_id, in_file->idb_index_map->len);
+ return FALSE;
+ }
+
+ rec->rec_header.packet_header.interface_id = g_array_index(in_file->idb_index_map, guint, current_interface_id);
+ rec->presence_flags |= WTAP_HAS_INTERFACE_ID;
+
+ return TRUE;
+}
+
+static merge_result
+merge_process_packets(wtap_dumper *pdh, const int file_type,
+ merge_in_file_t *in_files, const guint in_file_count,
+ const gboolean do_append,
+ const idb_merge_mode mode, guint snaplen,
+ merge_progress_callback_t* cb,
+ wtapng_iface_descriptions_t *idb_inf,
+ GArray *nrb_combined, GArray *dsb_combined,
+ int *err, gchar **err_info, guint *err_fileno,
+ guint32 *err_framenum)
+{
+ merge_result status = MERGE_OK;
+ merge_in_file_t *in_file;
+ int count = 0;
+ gboolean stop_flag = FALSE;
+ wtap_rec *rec, snap_rec;
+
+ for (;;) {
+ *err = 0;
+
+ if (do_append) {
+ in_file = merge_append_read_packet(in_file_count, in_files, err,
+ err_info);
+ }
+ else {
+ in_file = merge_read_packet(in_file_count, in_files, err,
+ err_info);
+ }
+
+ if (in_file == NULL) {
+ /* We're at EOF on all input files */
+ break;
+ }
+
+ if (*err != 0) {
+ /* I/O error reading from in_file */
+ status = MERGE_ERR_CANT_READ_INFILE;
+ break;
+ }
+
+ count++;
+ if (cb)
+ stop_flag = cb->callback_func(MERGE_EVENT_RECORD_WAS_READ, count, in_files, in_file_count, cb->data);
+
+ if (stop_flag) {
+ /* The user decided to abort the merge. */
+ status = MERGE_USER_ABORTED;
+ break;
+ }
+
+ rec = &in_file->rec;
+
+ if (wtap_file_type_subtype_supports_block(file_type,
+ WTAP_BLOCK_IF_ID_AND_INFO) != BLOCK_NOT_SUPPORTED) {
+ if (!process_new_idbs(pdh, in_files, in_file_count, mode, idb_inf, err, err_info)) {
+ status = MERGE_ERR_CANT_WRITE_OUTFILE;
+ break;
+ }
+ }
+
+ switch (rec->rec_type) {
+
+ case REC_TYPE_PACKET:
+ if (rec->presence_flags & WTAP_HAS_CAP_LEN) {
+ if (snaplen != 0 &&
+ rec->rec_header.packet_header.caplen > snaplen) {
+ /*
+ * The dumper will only write up to caplen bytes out,
+ * so we only need to change that value, instead of
+ * cloning the whole packet with fewer bytes.
+ *
+ * XXX: but do we need to change the IDBs' snap_len?
+ */
+ snap_rec = *rec;
+ snap_rec.rec_header.packet_header.caplen = snaplen;
+ rec = &snap_rec;
+ }
+ }
+ break;
+ }
+
+ /*
+ * Does this file type support identifying the interfaces on
+ * which packets arrive?
+ *
+ * That mean that the abstract interface provided by libwiretap
+ * involves WTAP_BLOCK_IF_ID_AND_INFO blocks.
+ */
+ if (wtap_file_type_subtype_supports_block(file_type,
+ WTAP_BLOCK_IF_ID_AND_INFO) != BLOCK_NOT_SUPPORTED) {
+ /*
+ * XXX - We should do this only for record types
+ * that pertain to a particular interface; for
+ * now, we hardcode that, but we need to figure
+ * out a more general way to handle this.
+ */
+ if (rec->rec_type == REC_TYPE_PACKET) {
+ if (!map_rec_interface_id(rec, in_file)) {
+ status = MERGE_ERR_BAD_PHDR_INTERFACE_ID;
+ break;
+ }
+ }
+ }
+ /*
+ * If any DSBs were read before this record, be sure to pass those now
+ * such that wtap_dump can pick it up.
+ */
+ if (nrb_combined && in_file->wth->nrbs) {
+ GArray *in_nrb = in_file->wth->nrbs;
+ for (guint i = in_file->nrbs_seen; i < in_nrb->len; i++) {
+ wtap_block_t wblock = g_array_index(in_nrb, wtap_block_t, i);
+ g_array_append_val(nrb_combined, wblock);
+ in_file->nrbs_seen++;
+ }
+ }
+ if (dsb_combined && in_file->wth->dsbs) {
+ GArray *in_dsb = in_file->wth->dsbs;
+ for (guint i = in_file->dsbs_seen; i < in_dsb->len; i++) {
+ wtap_block_t wblock = g_array_index(in_dsb, wtap_block_t, i);
+ g_array_append_val(dsb_combined, wblock);
+ in_file->dsbs_seen++;
+ }
+ }
+
+ if (!wtap_dump(pdh, rec, ws_buffer_start_ptr(&in_file->frame_buffer),
+ err, err_info)) {
+ status = MERGE_ERR_CANT_WRITE_OUTFILE;
+ break;
+ }
+ wtap_rec_reset(rec);
+ }
+
+ if (cb)
+ cb->callback_func(MERGE_EVENT_DONE, count, in_files, in_file_count, cb->data);
+
+ if (status == MERGE_OK || status == MERGE_USER_ABORTED) {
+ /* Check for IDBs, NRBs, or DSBs read after the last packet records. */
+ if (wtap_file_type_subtype_supports_block(file_type,
+ WTAP_BLOCK_IF_ID_AND_INFO) != BLOCK_NOT_SUPPORTED) {
+ if (!process_new_idbs(pdh, in_files, in_file_count, mode, idb_inf, err, err_info)) {
+ status = MERGE_ERR_CANT_WRITE_OUTFILE;
+ }
+ }
+ if (nrb_combined) {
+ for (guint j = 0; j < in_file_count; j++) {
+ in_file = &in_files[j];
+ GArray *in_nrb = in_file->wth->nrbs;
+ if (in_nrb) {
+ for (guint i = in_file->nrbs_seen; i < in_nrb->len; i++) {
+ wtap_block_t wblock = g_array_index(in_nrb, wtap_block_t, i);
+ g_array_append_val(nrb_combined, wblock);
+ in_file->nrbs_seen++;
+ }
+ }
+ }
+ }
+ if (dsb_combined) {
+ for (guint j = 0; j < in_file_count; j++) {
+ in_file = &in_files[j];
+ GArray *in_dsb = in_file->wth->dsbs;
+ if (in_dsb) {
+ for (guint i = in_file->dsbs_seen; i < in_dsb->len; i++) {
+ wtap_block_t wblock = g_array_index(in_dsb, wtap_block_t, i);
+ g_array_append_val(dsb_combined, wblock);
+ in_file->dsbs_seen++;
+ }
+ }
+ }
+ }
+ }
+ if (status == MERGE_OK || status == MERGE_USER_ABORTED) {
+ if (!wtap_dump_close(pdh, NULL, err, err_info))
+ status = MERGE_ERR_CANT_CLOSE_OUTFILE;
+ } else {
+ /*
+ * We already got some error; no need to report another error on
+ * close.
+ *
+ * Don't overwrite the earlier error.
+ */
+ int close_err = 0;
+ gchar *close_err_info = NULL;
+ (void)wtap_dump_close(pdh, NULL, &close_err, &close_err_info);
+ g_free(close_err_info);
+ }
+
+ /* Close the input files after the output file in case the latter still
+ * holds references to blocks in the input file (such as the DSB). Even if
+ * those DSBs are only written when wtap_dump is called and nothing bad will
+ * happen now, let's keep all pointers in pdh valid for correctness sake. */
+ merge_close_in_files(in_file_count, in_files);
+
+ if (status == MERGE_OK || in_file == NULL) {
+ *err_fileno = 0;
+ *err_framenum = 0;
+ } else {
+ *err_fileno = (guint)(in_file - in_files);
+ *err_framenum = in_file->packet_num;
+ }
+
+ return status;
+}
+
+static void
+tempfile_free(gpointer data) {
+ char *filename = (char*)data;
+ ws_unlink(filename);
+ g_free(filename);
+}
+
+static merge_result
+merge_files_common(const gchar* out_filename, /* filename in normal output mode,
+ optional tempdir in tempfile mode (NULL for OS default) */
+ gchar **out_filenamep, const char *pfx, /* tempfile mode */
+ const int file_type, const char *const *in_filenames,
+ const guint in_file_count, const gboolean do_append,
+ idb_merge_mode mode, guint snaplen,
+ const gchar *app_name, merge_progress_callback_t* cb,
+ int *err, gchar **err_info, guint *err_fileno,
+ guint32 *err_framenum)
+{
+ merge_in_file_t *in_files = NULL;
+ int frame_type = WTAP_ENCAP_PER_PACKET;
+ unsigned open_file_count;
+ merge_result status = MERGE_OK;
+ wtap_dumper *pdh;
+ GArray *shb_hdrs = NULL;
+ wtapng_iface_descriptions_t *idb_inf = NULL;
+ GArray *nrb_combined = NULL;
+ GArray *dsb_combined = NULL;
+ GPtrArray *temp_files = NULL;
+ int dup_fd;
+
+ ws_assert(in_file_count > 0);
+ ws_assert(in_filenames != NULL);
+ ws_assert(err != NULL);
+ ws_assert(err_info != NULL);
+ ws_assert(err_fileno != NULL);
+ ws_assert(err_framenum != NULL);
+
+ /* if a callback was given, it has to have a callback function ptr */
+ ws_assert((cb != NULL) ? (cb->callback_func != NULL) : TRUE);
+
+ ws_debug("merge_files: begin");
+
+ for (unsigned total_file_count = 0; total_file_count < in_file_count && status == MERGE_OK; total_file_count += open_file_count) {
+
+ /* Reserve a file descriptor for the output; if we run out of file
+ * descriptors we will end up writing to a temp file instead of the
+ * file or stdout originally requested, but this simplifies EMFILE
+ * handling.
+ */
+ dup_fd = ws_dup(1);
+ if (dup_fd == -1) {
+ return MERGE_ERR_CANT_OPEN_OUTFILE;
+ }
+
+ /* open the input files */
+ open_file_count = merge_open_in_files(in_file_count - total_file_count, &in_filenames[total_file_count], &in_files, cb, err, err_info, err_fileno);
+ if (open_file_count == 0) {
+ ws_debug("merge_open_in_files() failed with err=%d", *err);
+ *err_framenum = 0;
+ return MERGE_ERR_CANT_OPEN_INFILE;
+ }
+
+ if (snaplen == 0) {
+ /* Snapshot length not specified - default to the maximum. */
+ snaplen = WTAP_MAX_PACKET_SIZE_STANDARD;
+ }
+
+ /*
+ * This doesn't tell us that much. It tells us what to set the outfile's
+ * encap type to, but that's all - for example, it does *not* tell us
+ * whether the input files had the same number of IDBs, for the same exact
+ * interfaces, and only one IDB each, so it doesn't actually tell us
+ * whether we can merge IDBs into one or not.
+ *
+ * XXX: If an input file is WTAP_ENCAP_PER_PACKET, just because the
+ * output file format (e.g. pcapng) can write WTAP_ENCAP_PER_PACKET,
+ * that doesn't mean that the format can actually write all the IDBs.
+ */
+ frame_type = merge_select_frame_type(file_type, open_file_count, in_files);
+ ws_debug("got frame_type=%d", frame_type);
+
+ if (cb)
+ cb->callback_func(MERGE_EVENT_FRAME_TYPE_SELECTED, frame_type, in_files, open_file_count, cb->data);
+
+ /* prepare the outfile */
+ wtap_dump_params params = WTAP_DUMP_PARAMS_INIT;
+ params.encap = frame_type;
+ params.snaplen = snaplen;
+ /*
+ * Does this file type support identifying the interfaces on
+ * which packets arrive?
+ *
+ * That mean that the abstract interface provided by libwiretap
+ * involves WTAP_BLOCK_IF_ID_AND_INFO blocks.
+ */
+ if (wtap_file_type_subtype_supports_block(file_type,
+ WTAP_BLOCK_IF_ID_AND_INFO) != BLOCK_NOT_SUPPORTED) {
+ shb_hdrs = create_shb_header(in_files, open_file_count, app_name);
+ ws_debug("SHB created");
+
+ idb_inf = generate_merged_idbs(in_files, open_file_count, &mode);
+ ws_debug("IDB merge operation complete, got %u IDBs", idb_inf ? idb_inf->interface_data->len : 0);
+
+ /* XXX other blocks like ISB are now discarded. */
+ params.shb_hdrs = shb_hdrs;
+ params.idb_inf = idb_inf;
+ }
+ if (wtap_file_type_subtype_supports_block(file_type,
+ WTAP_BLOCK_NAME_RESOLUTION) != BLOCK_NOT_SUPPORTED) {
+ nrb_combined = g_array_new(FALSE, FALSE, sizeof(wtap_block_t));
+ params.nrbs_growing = nrb_combined;
+ }
+ if (wtap_file_type_subtype_supports_block(file_type,
+ WTAP_BLOCK_DECRYPTION_SECRETS) != BLOCK_NOT_SUPPORTED) {
+ dsb_combined = g_array_new(FALSE, FALSE, sizeof(wtap_block_t));
+ params.dsbs_growing = dsb_combined;
+ }
+ ws_close(dup_fd);
+ if (open_file_count < in_file_count) {
+ if (temp_files == NULL) {
+ temp_files = g_ptr_array_new_with_free_func(tempfile_free);
+ }
+ char* temp_filename;
+ /* If out_filenamep is not null, then out_filename is the
+ * desired tempdir, so let's use that.
+ */
+ pdh = wtap_dump_open_tempfile(out_filenamep ? out_filename : NULL,
+ &temp_filename,
+ pfx ? pfx : "mergecap", file_type,
+ WTAP_UNCOMPRESSED, &params, err,
+ err_info);
+ if (pdh) {
+ g_ptr_array_add(temp_files, temp_filename);
+ }
+ } else if (out_filenamep) {
+ pdh = wtap_dump_open_tempfile(out_filename, out_filenamep, pfx, file_type,
+ WTAP_UNCOMPRESSED, &params, err,
+ err_info);
+ } else if (out_filename) {
+ pdh = wtap_dump_open(out_filename, file_type, WTAP_UNCOMPRESSED,
+ &params, err, err_info);
+ } else {
+ pdh = wtap_dump_open_stdout(file_type, WTAP_UNCOMPRESSED, &params, err,
+ err_info);
+ }
+ if (pdh == NULL) {
+ merge_close_in_files(open_file_count, in_files);
+ g_free(in_files);
+ wtap_block_array_free(shb_hdrs);
+ wtap_free_idb_info(idb_inf);
+ if (nrb_combined) {
+ g_array_free(nrb_combined, TRUE);
+ }
+ if (dsb_combined) {
+ g_array_free(dsb_combined, TRUE);
+ }
+ if (temp_files) {
+ g_ptr_array_free(temp_files, TRUE);
+ }
+ *err_framenum = 0;
+ return MERGE_ERR_CANT_OPEN_OUTFILE;
+ }
+
+ if (cb)
+ cb->callback_func(MERGE_EVENT_READY_TO_MERGE, 0, in_files, open_file_count, cb->data);
+
+ status = merge_process_packets(pdh, file_type, in_files, open_file_count,
+ do_append, mode, snaplen, cb,
+ idb_inf, nrb_combined, dsb_combined,
+ err, err_info,
+ err_fileno, err_framenum);
+
+ g_free(in_files);
+ wtap_block_array_free(shb_hdrs);
+ wtap_free_idb_info(idb_inf);
+ if (nrb_combined) {
+ g_array_free(nrb_combined, TRUE);
+ nrb_combined = NULL;
+ }
+ if (dsb_combined) {
+ g_array_free(dsb_combined, TRUE);
+ dsb_combined = NULL;
+ }
+
+ }
+
+ if (temp_files != NULL) {
+ if (status == MERGE_OK) {
+ status = merge_files_common(out_filename, out_filenamep, pfx,
+ file_type, (const char**)temp_files->pdata,
+ temp_files->len, do_append, mode, snaplen, app_name,
+ cb, err, err_info, err_fileno, err_framenum);
+ }
+ g_ptr_array_free(temp_files, TRUE);
+ }
+
+ return status;
+}
+
+/*
+ * Merges the files to an output file whose name is supplied as an argument,
+ * based on given input, and invokes callback during execution. Returns
+ * MERGE_OK on success, or a MERGE_ERR_XXX on failure.
+ */
+merge_result
+merge_files(const gchar* out_filename, const int file_type,
+ const char *const *in_filenames, const guint in_file_count,
+ const gboolean do_append, const idb_merge_mode mode,
+ guint snaplen, const gchar *app_name, merge_progress_callback_t* cb,
+ int *err, gchar **err_info, guint *err_fileno,
+ guint32 *err_framenum)
+{
+ ws_assert(out_filename != NULL);
+
+ return merge_files_common(out_filename, NULL, NULL,
+ file_type, in_filenames, in_file_count,
+ do_append, mode, snaplen, app_name, cb, err,
+ err_info, err_fileno, err_framenum);
+}
+
+/*
+ * Merges the files to a temporary file based on given input, and invokes
+ * callback during execution. Returns MERGE_OK on success, or a MERGE_ERR_XXX
+ * on failure.
+ */
+merge_result
+merge_files_to_tempfile(const char *tmpdir, gchar **out_filenamep, const char *pfx,
+ const int file_type, const char *const *in_filenames,
+ const guint in_file_count, const gboolean do_append,
+ const idb_merge_mode mode, guint snaplen,
+ const gchar *app_name, merge_progress_callback_t* cb,
+ int *err, gchar **err_info, guint *err_fileno,
+ guint32 *err_framenum)
+{
+ ws_assert(out_filenamep != NULL);
+
+ /* no temporary file name yet */
+ *out_filenamep = NULL;
+
+ return merge_files_common(tmpdir, out_filenamep, pfx,
+ file_type, in_filenames, in_file_count,
+ do_append, mode, snaplen, app_name, cb, err,
+ err_info, err_fileno, err_framenum);
+}
+
+/*
+ * Merges the files to the standard output based on given input, and invokes
+ * callback during execution. Returns MERGE_OK on success, or a MERGE_ERR_XXX
+ * on failure.
+ */
+merge_result
+merge_files_to_stdout(const int file_type, const char *const *in_filenames,
+ const guint in_file_count, const gboolean do_append,
+ const idb_merge_mode mode, guint snaplen,
+ const gchar *app_name, merge_progress_callback_t* cb,
+ int *err, gchar **err_info, guint *err_fileno,
+ guint32 *err_framenum)
+{
+ return merge_files_common(NULL, NULL, NULL,
+ file_type, in_filenames, in_file_count,
+ do_append, mode, snaplen, app_name, cb, err,
+ err_info, err_fileno, err_framenum);
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local Variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * vi: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */
diff --git a/wiretap/merge.h b/wiretap/merge.h
new file mode 100644
index 00000000..b069f3ff
--- /dev/null
+++ b/wiretap/merge.h
@@ -0,0 +1,212 @@
+/** @file
+ * Definitions for routines for merging files.
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __MERGE_H__
+#define __MERGE_H__
+
+#include "wiretap/wtap.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+typedef enum {
+ RECORD_PRESENT,
+ RECORD_NOT_PRESENT,
+ AT_EOF,
+ GOT_ERROR
+} in_file_state_e;
+
+/**
+ * Structures to manage our input files.
+ */
+typedef struct merge_in_file_s {
+ const char *filename;
+ wtap *wth;
+ wtap_rec rec;
+ Buffer frame_buffer;
+ in_file_state_e state;
+ guint32 packet_num; /* current packet number */
+ gint64 size; /* file size */
+ GArray *idb_index_map; /* used for mapping the old phdr interface_id values to new during merge */
+ guint nrbs_seen; /* number of elements processed so far from wth->nrbs */
+ guint dsbs_seen; /* number of elements processed so far from wth->dsbs */
+} merge_in_file_t;
+
+/** Return values from merge_files(). */
+typedef enum {
+ MERGE_OK,
+ MERGE_USER_ABORTED,
+ /* below here are true errors */
+ MERGE_ERR_CANT_OPEN_INFILE,
+ MERGE_ERR_CANT_OPEN_OUTFILE,
+ MERGE_ERR_CANT_READ_INFILE,
+ MERGE_ERR_BAD_PHDR_INTERFACE_ID,
+ MERGE_ERR_CANT_WRITE_OUTFILE,
+ MERGE_ERR_CANT_CLOSE_OUTFILE,
+ MERGE_ERR_INVALID_OPTION
+} merge_result;
+
+
+/** Merge events, used as an arg in the callback function - indicates when the callback was invoked. */
+typedef enum {
+ MERGE_EVENT_INPUT_FILES_OPENED,
+ MERGE_EVENT_FRAME_TYPE_SELECTED,
+ MERGE_EVENT_READY_TO_MERGE,
+ MERGE_EVENT_RECORD_WAS_READ,
+ MERGE_EVENT_DONE
+} merge_event;
+
+
+/** Merge mode for IDB info. */
+typedef enum {
+ IDB_MERGE_MODE_NONE = 0, /**< no merging of IDBs is done, all IDBs are copied into merged file */
+ IDB_MERGE_MODE_ALL_SAME,/**< duplicate IDBs merged only if all the files have the same set of IDBs */
+ IDB_MERGE_MODE_ANY_SAME, /**< any and all duplicate IDBs are merged into one IDB, even within a file */
+ IDB_MERGE_MODE_MAX
+} idb_merge_mode;
+
+
+/** Returns the idb_merge_mode for the given string name.
+ *
+ * @param name The name of the mode.
+ * @return The idb_merge_mode, or IDB_MERGE_MODE_MAX on failure.
+ */
+WS_DLL_PUBLIC idb_merge_mode
+merge_string_to_idb_merge_mode(const char *name);
+
+
+/** Returns the string name for the given number.
+ *
+ * @param mode The number of the mode, representing the idb_merge_mode enum value.
+ * @return The string name, or "UNKNOWN" on failure.
+ */
+WS_DLL_PUBLIC const char*
+merge_idb_merge_mode_to_string(const int mode);
+
+
+/** @struct merge_progress_callback_t
+ *
+ * @brief Callback information for merging.
+ *
+ * @details The merge_files() routine can invoke a callback during its execution,
+ * to enable verbose printing or progress bar updating, for example. This struct
+ * provides merge_files() with the callback routine to invoke, and optionally
+ * private data to pass through to the callback each time it is invoked.
+ * For the callback_func routine's arguments: the event is when the callback
+ * was invoked, the num is an int specific to the event, in_files is an array
+ * of the created merge info, in_file_count is the size of the array, data is
+ * whatever was passed in the data member of this struct. The callback_func
+ * routine's return value should be TRUE if merging should be aborted.
+ */
+typedef struct {
+ gboolean (*callback_func)(merge_event event, int num,
+ const merge_in_file_t in_files[], const guint in_file_count,
+ void *data);
+ void *data; /**< private data to use for passing through to the callback function */
+} merge_progress_callback_t;
+
+
+/** Merge the given input files to a file with the given filename
+ *
+ * @param out_filename The output filename
+ * @param file_type The WTAP_FILE_TYPE_SUBTYPE_XXX output file type
+ * @param in_filenames An array of input filenames to merge from
+ * @param in_file_count The number of entries in in_filenames
+ * @param do_append Whether to append by file order instead of chronological order
+ * @param mode The IDB_MERGE_MODE_XXX merge mode for interface data
+ * @param snaplen The snaplen to limit it to, or 0 to leave as it is in the files
+ * @param app_name The application name performing the merge, used in SHB info
+ * @param cb The callback information to use during execution
+ * @param[out] err Set to the internal WTAP_ERR_XXX error code if it failed
+ * with MERGE_ERR_CANT_OPEN_INFILE, MERGE_ERR_CANT_OPEN_OUTFILE,
+ * MERGE_ERR_CANT_READ_INFILE, MERGE_ERR_CANT_WRITE_OUTFILE, or
+ * MERGE_ERR_CANT_CLOSE_OUTFILE
+ * @param[out] err_info Additional information for some WTAP_ERR_XXX codes
+ * @param[out] err_fileno Set to the input file number which failed, if it
+ * failed
+ * @param[out] err_framenum Set to the input frame number if it failed
+ * @return the frame type
+ */
+WS_DLL_PUBLIC merge_result
+merge_files(const gchar* out_filename, const int file_type,
+ const char *const *in_filenames, const guint in_file_count,
+ const gboolean do_append, const idb_merge_mode mode,
+ guint snaplen, const gchar *app_name, merge_progress_callback_t* cb,
+ int *err, gchar **err_info, guint *err_fileno,
+ guint32 *err_framenum);
+
+/** Merge the given input files to a temporary file
+ *
+ * @param tmpdir Points to the directory in which to write the temporary file
+ * @param out_filenamep Points to a pointer that's set to point to the
+ * pathname of the temporary file; it's allocated with g_malloc()
+ * @param pfx A string to be used as the prefix for the temporary file name
+ * @param file_type The WTAP_FILE_TYPE_SUBTYPE_XXX output file type
+ * @param in_filenames An array of input filenames to merge from
+ * @param in_file_count The number of entries in in_filenames
+ * @param do_append Whether to append by file order instead of chronological order
+ * @param mode The IDB_MERGE_MODE_XXX merge mode for interface data
+ * @param snaplen The snaplen to limit it to, or 0 to leave as it is in the files
+ * @param app_name The application name performing the merge, used in SHB info
+ * @param cb The callback information to use during execution
+ * @param[out] err Set to the internal WTAP_ERR_XXX error code if it failed
+ * with MERGE_ERR_CANT_OPEN_INFILE, MERGE_ERR_CANT_OPEN_OUTFILE,
+ * MERGE_ERR_CANT_READ_INFILE, MERGE_ERR_CANT_WRITE_OUTFILE, or
+ * MERGE_ERR_CANT_CLOSE_OUTFILE
+ * @param[out] err_info Additional information for some WTAP_ERR_XXX codes
+ * @param[out] err_fileno Set to the input file number which failed, if it
+ * failed
+ * @param[out] err_framenum Set to the input frame number if it failed
+ * @return the frame type
+ */
+WS_DLL_PUBLIC merge_result
+merge_files_to_tempfile(const char *tmpdir, gchar **out_filenamep, const char *pfx,
+ const int file_type, const char *const *in_filenames,
+ const guint in_file_count, const gboolean do_append,
+ const idb_merge_mode mode, guint snaplen,
+ const gchar *app_name, merge_progress_callback_t* cb,
+ int *err, gchar **err_info, guint *err_fileno,
+ guint32 *err_framenum);
+
+/** Merge the given input files to the standard output
+ *
+ * @param file_type The WTAP_FILE_TYPE_SUBTYPE_XXX output file type
+ * @param in_filenames An array of input filenames to merge from
+ * @param in_file_count The number of entries in in_filenames
+ * @param do_append Whether to append by file order instead of chronological order
+ * @param mode The IDB_MERGE_MODE_XXX merge mode for interface data
+ * @param snaplen The snaplen to limit it to, or 0 to leave as it is in the files
+ * @param app_name The application name performing the merge, used in SHB info
+ * @param cb The callback information to use during execution
+ * @param[out] err Set to the internal WTAP_ERR_XXX error code if it failed
+ * with MERGE_ERR_CANT_OPEN_INFILE, MERGE_ERR_CANT_OPEN_OUTFILE,
+ * MERGE_ERR_CANT_READ_INFILE, MERGE_ERR_CANT_WRITE_OUTFILE, or
+ * MERGE_ERR_CANT_CLOSE_OUTFILE
+ * @param[out] err_info Additional information for some WTAP_ERR_XXX codes
+ * @param[out] err_fileno Set to the input file number which failed, if it
+ * failed
+ * @param[out] err_framenum Set to the input frame number if it failed
+ * @return the frame type
+ */
+WS_DLL_PUBLIC merge_result
+merge_files_to_stdout(const int file_type, const char *const *in_filenames,
+ const guint in_file_count, const gboolean do_append,
+ const idb_merge_mode mode, guint snaplen,
+ const gchar *app_name, merge_progress_callback_t* cb,
+ int *err, gchar **err_info, guint *err_fileno,
+ guint32 *err_framenum);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __MERGE_H__ */
+
diff --git a/wiretap/mime_file.c b/wiretap/mime_file.c
new file mode 100644
index 00000000..a3cce0d1
--- /dev/null
+++ b/wiretap/mime_file.c
@@ -0,0 +1,229 @@
+/* mime_file.c
+ *
+ * MIME file format decoder for the Wiretap library.
+ *
+ * This is for use with Wireshark dissectors that handle file
+ * formats (e.g., because they handle a particular MIME media type).
+ * It breaks the file into chunks of at most WTAP_MAX_PACKET_SIZE_STANDARD,
+ * each of which is reported as a packet, so that files larger than
+ * WTAP_MAX_PACKET_SIZE_STANDARD can be handled by reassembly.
+ *
+ * The "MIME file" dissector does the reassembly, and hands the result
+ * off to heuristic dissectors to try to identify the file's contents.
+ *
+ * Wiretap Library
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "config.h"
+
+#include <sys/types.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include "wtap-int.h"
+#include "file_wrappers.h"
+#include <wsutil/buffer.h>
+#include "mime_file.h"
+
+typedef struct {
+ const guint8 *magic;
+ guint magic_len;
+} mime_files_t;
+
+/*
+ * Written by Marton Nemeth <nm127@freemail.hu>
+ * Copyright 2009 Marton Nemeth
+ * The JPEG specification can be found at:
+ *
+ * https://www.w3.org/Graphics/JPEG/itu-t81.pdf
+ * https://www.itu.int/rec/T-REC-T.81/en (but you have to pay for it)
+ *
+ * and the JFIF specification can be found at:
+ *
+ * https://www.itu.int/rec/T-REC-T.871-201105-I/en
+ * https://www.w3.org/Graphics/JPEG/jfif3.pdf
+ */
+static const guint8 jpeg_jfif_magic[] = { 0xFF, 0xD8, /* SOF */
+ 0xFF /* start of the next marker */
+ };
+
+/* <?xml */
+static const guint8 xml_magic[] = { '<', '?', 'x', 'm', 'l' };
+static const guint8 png_magic[] = { 0x89, 'P', 'N', 'G', '\r', '\n', 0x1A, '\n' };
+static const guint8 gif87a_magic[] = { 'G', 'I', 'F', '8', '7', 'a'};
+static const guint8 gif89a_magic[] = { 'G', 'I', 'F', '8', '9', 'a'};
+static const guint8 elf_magic[] = { 0x7F, 'E', 'L', 'F'};
+static const guint8 tiff_le_magic[] = { 'I', 'I', 42, 0 };
+static const guint8 tiff_be_magic[] = { 'M', 'M', 0, 42 };
+static const guint8 btsnoop_magic[] = { 'b', 't', 's', 'n', 'o', 'o', 'p', 0};
+static const guint8 pcap_magic[] = { 0xA1, 0xB2, 0xC3, 0xD4 };
+static const guint8 pcap_swapped_magic[] = { 0xD4, 0xC3, 0xB2, 0xA1 };
+static const guint8 pcap_nsec_magic[] = { 0xA1, 0xB2, 0x3C, 0x4D };
+static const guint8 pcap_nsec_swapped_magic[] = { 0x4D, 0x3C, 0xB2, 0xA1 };
+static const guint8 pcapng_premagic[] = { 0x0A, 0x0D, 0x0D, 0x0A };
+static const guint8 blf_magic[] = { 'L', 'O', 'G', 'G' };
+static const guint8 autosar_dlt_magic[] = { 'D', 'L', 'T', 0x01 };
+static const guint8 rtpdump_magic[] = { '#', '!', 'r', 't', 'p', 'p', 'l', 'a', 'y', '1', '.', '0', ' ' };
+
+/* File does not start with it */
+static const guint8 pcapng_xmagic[] = { 0x1A, 0x2B, 0x3C, 0x4D };
+static const guint8 pcapng_swapped_xmagic[] = { 0x4D, 0x3C, 0x2B, 0x1A };
+
+static const mime_files_t magic_files[] = {
+ { jpeg_jfif_magic, sizeof(jpeg_jfif_magic) },
+ { xml_magic, sizeof(xml_magic) },
+ { png_magic, sizeof(png_magic) },
+ { gif87a_magic, sizeof(gif87a_magic) },
+ { gif89a_magic, sizeof(gif89a_magic) },
+ { elf_magic, sizeof(elf_magic) },
+ { tiff_le_magic, sizeof(tiff_le_magic) },
+ { tiff_be_magic, sizeof(tiff_be_magic) },
+ { btsnoop_magic, sizeof(btsnoop_magic) },
+ { pcap_magic, sizeof(pcap_magic) },
+ { pcap_swapped_magic, sizeof(pcap_swapped_magic) },
+ { pcap_nsec_magic, sizeof(pcap_nsec_magic) },
+ { pcap_nsec_swapped_magic, sizeof(pcap_nsec_swapped_magic) },
+ { pcapng_premagic, sizeof(pcapng_premagic) },
+ { blf_magic, sizeof(blf_magic) },
+ { autosar_dlt_magic, sizeof(autosar_dlt_magic) },
+ { rtpdump_magic, sizeof(rtpdump_magic) },
+};
+
+#define N_MAGIC_TYPES (sizeof(magic_files) / sizeof(magic_files[0]))
+
+static int mime_file_type_subtype = -1;
+
+void register_mime(void);
+
+wtap_open_return_val
+mime_file_open(wtap *wth, int *err, gchar **err_info)
+{
+ char magic_buf[128]; /* increase buffer size when needed */
+ int bytes_read;
+ gboolean found_file;
+ /* guint file_ok; */
+ guint i;
+
+ guint read_bytes = 12;
+
+ for (i = 0; i < N_MAGIC_TYPES; i++)
+ read_bytes = MAX(read_bytes, magic_files[i].magic_len);
+
+ read_bytes = (guint)MIN(read_bytes, sizeof(magic_buf));
+ bytes_read = file_read(magic_buf, read_bytes, wth->fh);
+
+ if (bytes_read < 0) {
+ *err = file_error(wth->fh, err_info);
+ return WTAP_OPEN_ERROR;
+ }
+ if (bytes_read == 0)
+ return WTAP_OPEN_NOT_MINE;
+
+ found_file = FALSE;
+ for (i = 0; i < N_MAGIC_TYPES; i++) {
+ if ((guint) bytes_read >= magic_files[i].magic_len && !memcmp(magic_buf, magic_files[i].magic, MIN(magic_files[i].magic_len, (guint) bytes_read))) {
+ if (!found_file) {
+ if (magic_files[i].magic == pcapng_premagic) {
+ if (memcmp(magic_buf + 8, pcapng_xmagic, sizeof(pcapng_xmagic)) &&
+ memcmp(magic_buf + 8, pcapng_swapped_xmagic, sizeof(pcapng_swapped_xmagic)))
+ continue;
+ }
+ found_file = TRUE;
+ } else
+ return WTAP_OPEN_NOT_MINE; /* many files matched, bad file */
+ }
+ }
+
+ if (!found_file)
+ return WTAP_OPEN_NOT_MINE;
+
+ if (file_seek(wth->fh, 0, SEEK_SET, err) == -1)
+ return WTAP_OPEN_ERROR;
+
+ wth->file_type_subtype = mime_file_type_subtype;
+ wth->file_encap = WTAP_ENCAP_MIME;
+ wth->file_tsprec = WTAP_TSPREC_SEC;
+ wth->subtype_read = wtap_full_file_read;
+ wth->subtype_seek_read = wtap_full_file_seek_read;
+ wth->snapshot_length = 0;
+
+ return WTAP_OPEN_MINE;
+}
+
+static const struct supported_block_type mime_blocks_supported[] = {
+ /*
+ * This is a file format that we dissect, so we provide
+ * only one "packet" with the file's contents, and don't
+ * support any options.
+ */
+ { WTAP_BLOCK_PACKET, ONE_BLOCK_SUPPORTED, NO_OPTIONS_SUPPORTED }
+};
+
+static const struct file_type_subtype_info mime_info = {
+ "MIME File Format", "mime", NULL, NULL,
+ FALSE, BLOCKS_SUPPORTED(mime_blocks_supported),
+ NULL, NULL, NULL
+};
+
+/*
+ * XXX - registered solely for the benefit of Lua scripts that
+ * look for the file type "JPEG_JFIF"; it may be removed once
+ * we get rid of wtap_filetypes.
+ */
+static const struct supported_block_type jpeg_jfif_blocks_supported[] = {
+ /*
+ * This is a file format that we dissect, so we provide
+ * only one "packet" with the file's contents, and don't
+ * support any options.
+ */
+ { WTAP_BLOCK_PACKET, ONE_BLOCK_SUPPORTED, NO_OPTIONS_SUPPORTED }
+};
+
+static const struct file_type_subtype_info jpeg_jfif_info = {
+ "JPEG/JFIF", "jpeg", "jpg", "jpeg;jfif",
+ FALSE, BLOCKS_SUPPORTED(jpeg_jfif_blocks_supported),
+ NULL, NULL, NULL
+};
+
+void register_mime(void)
+{
+ int jpeg_jfif_file_type_subtype;
+
+ mime_file_type_subtype = wtap_register_file_type_subtype(&mime_info);
+
+ /*
+ * Obsoleted by "mime", but we want it for the backwards-
+ * compatibility table for Lua.
+ */
+ jpeg_jfif_file_type_subtype = wtap_register_file_type_subtype(&jpeg_jfif_info);
+
+ /*
+ * Register names for backwards compatibility with the
+ * wtap_filetypes table in Lua.
+ */
+ wtap_register_backwards_compatibility_lua_name("MIME",
+ mime_file_type_subtype);
+ wtap_register_backwards_compatibility_lua_name("JPEG_JFIF",
+ jpeg_jfif_file_type_subtype);
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/wiretap/mime_file.h b/wiretap/mime_file.h
new file mode 100644
index 00000000..dd6820fb
--- /dev/null
+++ b/wiretap/mime_file.h
@@ -0,0 +1,17 @@
+/** @file
+ *
+ * MIME file format decoder for the Wiretap library.
+ *
+ * Wiretap Library
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __W_JPEG_JFIF_H__
+#define __W_JPEG_JFIF_H__
+
+#include <glib.h>
+#include "wtap.h"
+
+wtap_open_return_val mime_file_open(wtap *wth, int *err, gchar **err_info);
+
+#endif
diff --git a/wiretap/mp2t.c b/wiretap/mp2t.c
new file mode 100644
index 00000000..a0f3f611
--- /dev/null
+++ b/wiretap/mp2t.c
@@ -0,0 +1,434 @@
+/* mp2t.c
+ *
+ * ISO/IEC 13818-1 MPEG2-TS file format decoder for the Wiretap library.
+ * Written by Weston Schmidt <weston_schmidt@alumni.purdue.edu>
+ * Copyright 2012 Weston Schmidt
+ *
+ * Wiretap Library
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "config.h"
+
+#include <sys/types.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "mp2t.h"
+
+#include "wtap-int.h"
+#include <wsutil/buffer.h>
+#include "file_wrappers.h"
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#define MP2T_SYNC_BYTE 0x47
+#define MP2T_SIZE 188
+#define MP2T_QAM64_BITRATE 26970350 /* bits per second */
+#define MP2T_PCR_CLOCK 27000000 /* cycles per second - 27MHz */
+
+/* we try to detect trailing data up to 40 bytes after each packet */
+#define TRAILER_LEN_MAX 40
+
+/* number of consecutive packets we must read to decide that a file
+ is actually an mpeg2 ts */
+#define SYNC_STEPS 10
+
+
+typedef struct {
+ guint32 start_offset;
+ guint64 bitrate;
+ /* length of trailing data (e.g. FEC) that's appended after each packet */
+ guint8 trailer_len;
+} mp2t_filetype_t;
+
+static int mp2t_file_type_subtype = -1;
+
+void register_mp2t(void);
+
+static gboolean
+mp2t_read_packet(mp2t_filetype_t *mp2t, FILE_T fh, gint64 offset,
+ wtap_rec *rec, Buffer *buf, int *err,
+ gchar **err_info)
+{
+ guint64 tmp;
+
+ /*
+ * MP2T_SIZE will always be less than WTAP_MAX_PACKET_SIZE_STANDARD, so
+ * we don't have to worry about the packet being too big.
+ */
+ ws_buffer_assure_space(buf, MP2T_SIZE);
+ if (!wtap_read_bytes_or_eof(fh, ws_buffer_start_ptr(buf), MP2T_SIZE, err, err_info))
+ return FALSE;
+
+ rec->rec_type = REC_TYPE_PACKET;
+ rec->block = wtap_block_create(WTAP_BLOCK_PACKET);
+
+ /* XXX - relative, not absolute, time stamps */
+ rec->presence_flags = WTAP_HAS_TS;
+
+ /*
+ * Every packet in an MPEG2-TS stream is has a fixed size of
+ * MP2T_SIZE plus the number of trailer bytes.
+ *
+ * We assume that the bits in the transport stream are supplied at
+ * a constant rate; is that guaranteed by all media that use
+ * MPEG2-TS? If so, the time offset, from the beginning of the
+ * stream, of a given packet is the packet offset, in bits, divided
+ * by the bitrate.
+ *
+ * It would be really cool to be able to configure the bitrate, in
+ * case our attempt to guess it from the PCRs of one of the programs
+ * doesn't get the right answer.
+ */
+ tmp = ((guint64)(offset - mp2t->start_offset) * 8); /* offset, in bits */
+ rec->ts.secs = (time_t)(tmp / mp2t->bitrate);
+ rec->ts.nsecs = (int)((tmp % mp2t->bitrate) * 1000000000 / mp2t->bitrate);
+
+ rec->rec_header.packet_header.caplen = MP2T_SIZE;
+ rec->rec_header.packet_header.len = MP2T_SIZE;
+
+ return TRUE;
+}
+
+static gboolean
+mp2t_read(wtap *wth, wtap_rec *rec, Buffer *buf, int *err,
+ gchar **err_info, gint64 *data_offset)
+{
+ mp2t_filetype_t *mp2t;
+
+ mp2t = (mp2t_filetype_t*) wth->priv;
+
+ *data_offset = file_tell(wth->fh);
+
+ if (!mp2t_read_packet(mp2t, wth->fh, *data_offset, rec, buf, err,
+ err_info)) {
+ return FALSE;
+ }
+
+ /* if there's a trailer, skip it and go to the start of the next packet */
+ if (mp2t->trailer_len!=0) {
+ if (!wtap_read_bytes(wth->fh, NULL, mp2t->trailer_len, err, err_info)) {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+static gboolean
+mp2t_seek_read(wtap *wth, gint64 seek_off, wtap_rec *rec,
+ Buffer *buf, int *err, gchar **err_info)
+{
+ mp2t_filetype_t *mp2t;
+
+ if (-1 == file_seek(wth->random_fh, seek_off, SEEK_SET, err)) {
+ return FALSE;
+ }
+
+ mp2t = (mp2t_filetype_t*) wth->priv;
+
+ if (!mp2t_read_packet(mp2t, wth->random_fh, seek_off, rec, buf,
+ err, err_info)) {
+ if (*err == 0)
+ *err = WTAP_ERR_SHORT_READ;
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static guint64
+mp2t_read_pcr(guint8 *buffer)
+{
+ guint64 base;
+ guint64 ext;
+
+ base = pntoh40(buffer);
+ base >>= 7;
+
+ ext = pntoh16(&buffer[4]);
+ ext &= 0x01ff;
+
+ return (base * 300 + ext);
+}
+
+static gboolean
+mp2t_find_next_pcr(wtap *wth, guint8 trailer_len,
+ int *err, gchar **err_info, guint32 *idx, guint64 *pcr, guint16 *pid)
+{
+ guint8 buffer[MP2T_SIZE+TRAILER_LEN_MAX];
+ gboolean found;
+ guint8 afc;
+ guint timeout = 0;
+
+ found = FALSE;
+ while (FALSE == found && timeout++ < SYNC_STEPS * SYNC_STEPS) {
+ (*idx)++;
+ if (!wtap_read_bytes_or_eof(
+ wth->fh, buffer, MP2T_SIZE+trailer_len, err, err_info)) {
+ /* Read error, short read, or EOF */
+ return FALSE;
+ }
+
+ if (MP2T_SYNC_BYTE != buffer[0]) {
+ continue;
+ }
+
+ /* Read out the AFC value. */
+ afc = 3 & (buffer[3] >> 4);
+ if (afc < 2) {
+ continue;
+ }
+
+ /* Check the length. */
+ if (buffer[4] < 7) {
+ continue;
+ }
+
+ /* Check that there is the PCR flag. */
+ if (0x10 != (0x10 & buffer[5])) {
+ continue;
+ }
+
+ /* We have a PCR value! */
+ *pcr = mp2t_read_pcr(&buffer[6]);
+ *pid = 0x01ff & pntoh16(&buffer[1]);
+ found = TRUE;
+ }
+
+ return found;
+}
+
+static wtap_open_return_val
+mp2t_bits_per_second(wtap *wth, guint32 first, guint8 trailer_len,
+ guint64 *bitrate, int *err, gchar **err_info)
+{
+ guint32 pn1, pn2;
+ guint64 pcr1, pcr2;
+ guint16 pid1, pid2;
+ guint32 idx;
+ guint64 pcr_delta, bits_passed;
+
+ /* Find the first PCR + PID.
+ * Then find another PCR in that PID.
+ * Take the difference and that's our bitrate.
+ * All the different PCRs in different PIDs 'should' be the same.
+ *
+ * XXX - is this assuming that the time stamps in the PCRs correspond
+ * to the time scale of the underlying transport stream?
+ */
+ idx = first;
+
+ if (!mp2t_find_next_pcr(wth, trailer_len, err, err_info, &idx, &pcr1, &pid1)) {
+ /* Read error, short read, or EOF */
+ if (*err == WTAP_ERR_SHORT_READ)
+ return WTAP_OPEN_NOT_MINE; /* not a full frame */
+ if (*err != 0)
+ return WTAP_OPEN_ERROR;
+
+ /* We don't have any PCRs, so we can't guess the bit rate.
+ * Default to something reasonable.
+ */
+ *bitrate = MP2T_QAM64_BITRATE;
+ return WTAP_OPEN_MINE;
+ }
+
+ pn1 = idx;
+ pn2 = pn1;
+
+ while (pn1 == pn2) {
+ if (!mp2t_find_next_pcr(wth, trailer_len, err, err_info, &idx, &pcr2, &pid2)) {
+ /* Read error, short read, or EOF */
+ if (*err == WTAP_ERR_SHORT_READ)
+ return WTAP_OPEN_NOT_MINE; /* not a full frame */
+ if (*err != 0)
+ return WTAP_OPEN_ERROR;
+
+ /* We don't have two PCRs for the same PID, so we can't guess
+ * the bit rate.
+ * Default to something reasonable.
+ */
+ *bitrate = MP2T_QAM64_BITRATE;
+ return WTAP_OPEN_MINE;
+ }
+
+ if (pid1 == pid2) {
+ pn2 = idx;
+ }
+ }
+
+ if (pcr2 <= pcr1) {
+ /* The PCRs for that PID didn't go forward; treat that as an
+ * indication that this isn't an MPEG-2 TS.
+ */
+ return WTAP_OPEN_NOT_MINE;
+ }
+ pcr_delta = pcr2 - pcr1;
+ /* cast one of the factors to guint64
+ otherwise, the multiplication would use guint32 and could
+ overflow before the result is assigned to the guint64 bits_passed */
+ bits_passed = (guint64)MP2T_SIZE * (pn2 - pn1) * 8;
+
+ *bitrate = ((MP2T_PCR_CLOCK * bits_passed) / pcr_delta);
+ if (*bitrate == 0) {
+ /* pcr_delta < MP2T_PCR_CLOCK * bits_passed (pn2 != pn1,
+ * as that's the test for the loop above, so bits_passed
+ * is non-zero).
+ *
+ * That will produce a fractional bitrate, which turns
+ * into zero, causing a zero divide later.
+ *
+ * XXX - should we report this as "not ours"? A bitrate
+ * of less than 1 bit per second is not very useful for any
+ * form of audio/video, so presumably that's unlikely to
+ * be an MP2T file.
+ */
+ return WTAP_OPEN_ERROR;
+ }
+ return WTAP_OPEN_MINE;
+}
+
+wtap_open_return_val
+mp2t_open(wtap *wth, int *err, gchar **err_info)
+{
+ guint8 buffer[MP2T_SIZE+TRAILER_LEN_MAX];
+ guint8 trailer_len = 0;
+ guint sync_steps = 0;
+ guint i;
+ guint32 first = 0;
+ mp2t_filetype_t *mp2t;
+ wtap_open_return_val status;
+ guint64 bitrate;
+
+
+ if (!wtap_read_bytes(wth->fh, buffer, MP2T_SIZE, err, err_info)) {
+ if (*err != WTAP_ERR_SHORT_READ)
+ return WTAP_OPEN_ERROR;
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ for (i = 0; i < MP2T_SIZE; i++) {
+ if (MP2T_SYNC_BYTE == buffer[i]) {
+ first = i;
+ goto found;
+ }
+ }
+ /*
+ * No sync bytes found, so not an MPEG-2 Transport Stream file.
+ */
+ return WTAP_OPEN_NOT_MINE; /* wrong file type - not an mpeg2 ts file */
+
+found:
+ if (-1 == file_seek(wth->fh, first, SEEK_SET, err)) {
+ return WTAP_OPEN_ERROR;
+ }
+
+ /* read some packets and make sure they all start with a sync byte */
+ do {
+ if (!wtap_read_bytes(wth->fh, buffer, MP2T_SIZE+trailer_len, err, err_info)) {
+ if (*err != WTAP_ERR_SHORT_READ)
+ return WTAP_OPEN_ERROR; /* read error */
+ if(sync_steps<2) return WTAP_OPEN_NOT_MINE; /* wrong file type - not an mpeg2 ts file */
+ break; /* end of file, that's ok if we're still in sync */
+ }
+ if (buffer[0] == MP2T_SYNC_BYTE) {
+ sync_steps++;
+ }
+ else {
+ /* no sync byte found, check if trailing data is appended
+ and we have to increase the packet size */
+
+ /* if we've already detected a trailer field, we must remain in sync
+ another mismatch means we have no mpeg2 ts file */
+ if (trailer_len>0)
+ return WTAP_OPEN_NOT_MINE;
+
+ /* check if a trailer is appended to the packet */
+ for (i=0; i<TRAILER_LEN_MAX; i++) {
+ if (buffer[i] == MP2T_SYNC_BYTE) {
+ trailer_len = i;
+ if (-1 == file_seek(wth->fh, first, SEEK_SET, err)) {
+ return WTAP_OPEN_ERROR;
+ }
+ sync_steps = 0;
+ break;
+ }
+ }
+ /* no sync byte found in the vicinity, this is no mpeg2 ts file */
+ if (i==TRAILER_LEN_MAX)
+ return WTAP_OPEN_NOT_MINE;
+ }
+ } while (sync_steps < SYNC_STEPS);
+
+ if (-1 == file_seek(wth->fh, first, SEEK_SET, err)) {
+ return WTAP_OPEN_ERROR;
+ }
+
+ /* Ensure there is a valid bitrate */
+ status = mp2t_bits_per_second(wth, first, trailer_len,
+ &bitrate, err, err_info);
+ if (status != WTAP_OPEN_MINE) {
+ return status;
+ }
+
+ if (-1 == file_seek(wth->fh, first, SEEK_SET, err)) {
+ return WTAP_OPEN_ERROR;
+ }
+
+ wth->file_type_subtype = mp2t_file_type_subtype;
+ wth->file_encap = WTAP_ENCAP_MPEG_2_TS;
+ wth->file_tsprec = WTAP_TSPREC_NSEC;
+ wth->subtype_read = mp2t_read;
+ wth->subtype_seek_read = mp2t_seek_read;
+ wth->snapshot_length = 0;
+
+ mp2t = g_new(mp2t_filetype_t, 1);
+
+ wth->priv = mp2t;
+ mp2t->start_offset = first;
+ mp2t->trailer_len = trailer_len;
+ mp2t->bitrate = bitrate;
+
+ return WTAP_OPEN_MINE;
+}
+
+static const struct supported_block_type mp2t_blocks_supported[] = {
+ /*
+ * We support packet blocks, with no comments or other options.
+ */
+ { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
+};
+
+static const struct file_type_subtype_info mp2t_info = {
+ "MPEG2 transport stream", "mp2t", "mp2t", "ts;mpg",
+ FALSE, BLOCKS_SUPPORTED(mp2t_blocks_supported),
+ NULL, NULL, NULL
+};
+
+void register_mp2t(void)
+{
+ mp2t_file_type_subtype = wtap_register_file_type_subtype(&mp2t_info);
+
+ /*
+ * Register name for backwards compatibility with the
+ * wtap_filetypes table in Lua.
+ */
+ wtap_register_backwards_compatibility_lua_name("MPEG_2_TS",
+ mp2t_file_type_subtype);
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * vi: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */
diff --git a/wiretap/mp2t.h b/wiretap/mp2t.h
new file mode 100644
index 00000000..76bd7f49
--- /dev/null
+++ b/wiretap/mp2t.h
@@ -0,0 +1,19 @@
+/** @file
+ *
+ * ISO/IEC 13818-1 MPEG2-TS file format decoder for the Wiretap library.
+ * Written by Weston Schmidt <weston_schmidt@alumni.purdue.edu>
+ * Copyright 2012 Weston Schmidt
+ *
+ * Wiretap Library
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __W_MP2T_H__
+#define __W_MP2T_H__
+
+#include <glib.h>
+#include "wtap.h"
+
+wtap_open_return_val mp2t_open(wtap *wth, int *err, gchar **err_info);
+
+#endif
diff --git a/wiretap/mp4.c b/wiretap/mp4.c
new file mode 100644
index 00000000..36a43a71
--- /dev/null
+++ b/wiretap/mp4.c
@@ -0,0 +1,97 @@
+/* mp4.c
+ *
+ * MP4 (ISO/IEC 14496-12) file format decoder for the Wiretap library.
+ *
+ * Wiretap Library
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "mp4.h"
+
+#include <string.h>
+
+#include "file_wrappers.h"
+#include "wtap-int.h"
+
+static const guint8 mp4_magic[] = { 'f', 't', 'y', 'p' };
+static const guint8 mp4_magic_sidx[] = { 's', 'i', 'd', 'x' };
+static const guint8 mp4_magic_styp[] = { 's', 't', 'y', 'p' };
+
+static int mp4_file_type_subtype = -1;
+
+void register_mp4(void);
+
+wtap_open_return_val
+mp4_open(wtap *wth, int *err, gchar **err_info)
+{
+ char magic_buf[8];
+ int bytes_read;
+
+ bytes_read = file_read(magic_buf, sizeof (magic_buf), wth->fh);
+
+ if (bytes_read < 0) {
+ *err = file_error(wth->fh, err_info);
+ return WTAP_OPEN_ERROR;
+ }
+ if (bytes_read == 0)
+ return WTAP_OPEN_NOT_MINE;
+
+ if (bytes_read == sizeof (magic_buf) &&
+ memcmp(magic_buf + 4, mp4_magic, sizeof (mp4_magic)) &&
+ memcmp(magic_buf + 4, mp4_magic_sidx, sizeof (mp4_magic_sidx)) &&
+ memcmp(magic_buf + 4, mp4_magic_styp, sizeof (mp4_magic_styp)))
+ return WTAP_OPEN_NOT_MINE;
+
+ if (file_seek(wth->fh, 0, SEEK_SET, err) == -1)
+ return WTAP_OPEN_ERROR;
+
+ wth->file_type_subtype = mp4_file_type_subtype;
+ wth->file_encap = WTAP_ENCAP_MP4;
+ wth->file_tsprec = WTAP_TSPREC_SEC;
+ wth->subtype_read = wtap_full_file_read;
+ wth->subtype_seek_read = wtap_full_file_seek_read;
+ wth->snapshot_length = 0;
+
+ return WTAP_OPEN_MINE;
+}
+
+static const struct supported_block_type mp4_blocks_supported[] = {
+ /*
+ * This is a file format that we dissect, so we provide
+ * only one "packet" with the file's contents, and don't
+ * support any options.
+ */
+ { WTAP_BLOCK_PACKET, ONE_BLOCK_SUPPORTED, NO_OPTIONS_SUPPORTED }
+};
+
+static const struct file_type_subtype_info mp4_info = {
+ "MP4 media", "mp4", "mp4", NULL,
+ FALSE, BLOCKS_SUPPORTED(mp4_blocks_supported),
+ NULL, NULL, NULL
+};
+
+void register_mp4(void)
+{
+ mp4_file_type_subtype = wtap_register_file_type_subtype(&mp4_info);
+
+ /*
+ * Register name for backwards compatibility with the
+ * wtap_filetypes table in Lua.
+ */
+ wtap_register_backwards_compatibility_lua_name("MP4",
+ mp4_file_type_subtype);
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/wiretap/mp4.h b/wiretap/mp4.h
new file mode 100644
index 00000000..5dbcd96f
--- /dev/null
+++ b/wiretap/mp4.h
@@ -0,0 +1,16 @@
+/** @file
+ *
+ * MP4 (ISO/IEC 14496-12) file format decoder for the Wiretap library.
+ *
+ * Wiretap Library
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __W_MP4_H__
+#define __W_MP4_H__
+
+#include "wtap.h"
+
+wtap_open_return_val mp4_open(wtap *wth, int *err, gchar **err_info);
+
+#endif
diff --git a/wiretap/mpeg.c b/wiretap/mpeg.c
new file mode 100644
index 00000000..c4219926
--- /dev/null
+++ b/wiretap/mpeg.c
@@ -0,0 +1,415 @@
+/* mpeg.c
+ *
+ * MPEG-1/2 file format decoder for the Wiretap library.
+ * Written by Shaun Jackman <sjackman@gmail.com>
+ * Copyright 2007 Shaun Jackman
+ *
+ * MPEG-1/2 Program Streams (ISO/IEC 11172-1, ISO/IEC 13818-1 / ITU-T H.220.0)
+ * MPEG-1/2 Video bitstream (ISO/IEC 11172-2, ISO/IEC 13818-2 / ITU-T H.262)
+ * MPEG-1/2 Audio files (ISO/IEC 11172-3, ISO/IEC 13818-3)
+ *
+ * Does not handle other MPEG-2 container formats such as Transport Streams
+ * (also ISO/IEC 13818-1 / ITU-T H.222.0) or MPEG-4 containers such as
+ * MPEG-4 Part 14 / MP4 (ISO/IEC 14496-14). Support in wiretap for those
+ * two formats is provided in mp2t.c and mp4.c, respectively.
+ *
+ * Wiretap Library
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "config.h"
+
+#include <sys/types.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "mpeg.h"
+#include "wsutil/mpeg-audio.h"
+
+#include "wtap-int.h"
+#include <wsutil/buffer.h>
+#include "file_wrappers.h"
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#define PES_PREFIX 1
+#define PES_VALID(n) (((n) >> 8 & 0xffffff) == PES_PREFIX)
+
+typedef struct {
+ nstime_t now;
+ time_t t0;
+ gboolean is_audio;
+} mpeg_t;
+
+static int mpeg_file_type_subtype = -1;
+
+void register_mpeg(void);
+
+static int
+mpeg_resync(FILE_T fh, int *err)
+{
+ gint64 offset = file_tell(fh);
+ int count = 0;
+ int byte = file_getc(fh);
+
+ while (byte != EOF) {
+ if (byte == 0xff && count > 0) {
+ byte = file_getc(fh);
+ if (byte != EOF && (byte & 0xe0) == 0xe0)
+ break;
+ } else
+ byte = file_getc(fh);
+ count++;
+ }
+ if (file_seek(fh, offset, SEEK_SET, err) == -1)
+ return 0;
+ return count;
+}
+
+#define SCRHZ 27000000
+
+static unsigned int
+mpeg_read_audio_packet(wtap *wth, FILE_T fh, gboolean is_random, int *err, gchar **err_info)
+{
+ mpeg_t *mpeg = (mpeg_t *)wth->priv;
+ unsigned int packet_size;
+ guint32 n;
+ if (!wtap_read_bytes_or_eof(fh, &n, sizeof n, err, err_info))
+ return 0;
+ if (file_seek(fh, -(gint64)(sizeof n), SEEK_CUR, err) == -1)
+ return 0;
+ n = g_ntohl(n);
+ struct mpa mpa;
+
+ MPA_UNMARSHAL(&mpa, n);
+ if (MPA_VALID(&mpa)) {
+ packet_size = MPA_BYTES(&mpa);
+ if (!is_random) {
+ mpeg->now.nsecs += MPA_DURATION_NS(&mpa);
+ if (mpeg->now.nsecs >= 1000000000) {
+ mpeg->now.secs++;
+ mpeg->now.nsecs -= 1000000000;
+ }
+ }
+ } else {
+ if ((n & 0xffffff00) == 0x49443300) {
+ /* We have an ID3v2 header; read the size */
+ if (file_seek(fh, 6, SEEK_CUR, err) == -1)
+ return 0;
+ if (!wtap_read_bytes_or_eof(fh, &n, sizeof n, err, err_info))
+ return 0;
+ if (file_seek(fh, -(gint64)(6+sizeof(n)), SEEK_CUR, err) == -1)
+ return 0;
+ n = g_ntohl(n);
+
+ /* ID3v2 size does not include the 10-byte header */
+ packet_size = decode_synchsafe_int(n) + 10;
+ } else {
+ packet_size = mpeg_resync(fh, err);
+ }
+ }
+ return packet_size;
+}
+
+static unsigned int
+mpeg_read_pes_packet(wtap *wth, FILE_T fh, gboolean is_random, int *err, gchar **err_info)
+{
+ mpeg_t *mpeg = (mpeg_t *)wth->priv;
+ unsigned int packet_size = 0;
+ guint32 n;
+ while (1) {
+ if (!wtap_read_bytes_or_eof(fh, &n, sizeof n, err, err_info))
+ return 0;
+ if (file_seek(fh, -(gint64)(sizeof n), SEEK_CUR, err) == -1)
+ return 0;
+ n = g_ntohl(n);
+ if (PES_VALID(n)) {
+ break;
+ } else if (n == PES_PREFIX) {
+ if (!wtap_read_bytes(fh, NULL, 1, err, err_info))
+ return 0;
+ break;
+ } else if (n != 0) {
+ /* XXX: We could try to recover from errors and
+ * resynchronize to the next start code.
+ */
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup("mpeg: Non-zero stuffing bytes before start code");
+ return 0;
+ }
+ if (!wtap_read_bytes(fh, NULL, 2, err, err_info))
+ return 0;
+ }
+
+ gint64 offset = file_tell(fh);
+ guint8 stream;
+
+ if (!wtap_read_bytes(fh, NULL, 3, err, err_info))
+ return 0;
+
+ if (!wtap_read_bytes(fh, &stream, sizeof stream, err, err_info))
+ return 0;
+
+ if (stream == 0xba) {
+ guint32 pack1;
+ guint32 pack0;
+ guint64 pack;
+ guint8 stuffing;
+
+ if (!wtap_read_bytes(fh, &pack1, sizeof pack1, err, err_info))
+ return 0;
+ if (!wtap_read_bytes(fh, &pack0, sizeof pack0, err, err_info))
+ return 0;
+ pack = (guint64)g_ntohl(pack1) << 32 | g_ntohl(pack0);
+
+ switch (pack >> 62) {
+ case 1:
+ if (!wtap_read_bytes(fh, NULL, 1, err,
+ err_info))
+ return FALSE;
+ if (!wtap_read_bytes(fh, &stuffing,
+ sizeof stuffing, err, err_info))
+ return FALSE;
+ stuffing &= 0x07;
+ packet_size = 14 + stuffing;
+
+ if (!is_random) {
+ guint64 bytes = pack >> 16;
+ guint64 ts_val =
+ (bytes >> 43 & 0x0007) << 30 |
+ (bytes >> 27 & 0x7fff) << 15 |
+ (bytes >> 11 & 0x7fff) << 0;
+ guint ext = (guint)((bytes >> 1) & 0x1ff);
+ guint64 cr = 300 * ts_val + ext;
+ guint rem = (guint)(cr % SCRHZ);
+ mpeg->now.secs
+ = mpeg->t0 + (time_t)(cr / SCRHZ);
+ mpeg->now.nsecs
+ = (int)(G_GINT64_CONSTANT(1000000000) * rem / SCRHZ);
+ }
+ break;
+ default:
+ packet_size = 12;
+ }
+ } else if (stream == 0xb9) {
+ /* MPEG_program_end_code */
+ packet_size = 4;
+ } else {
+ guint16 length;
+ if (!wtap_read_bytes(fh, &length, sizeof length, err, err_info))
+ return FALSE;
+ length = g_ntohs(length);
+ packet_size = 6 + length;
+ }
+
+ if (file_seek(fh, offset, SEEK_SET, err) == -1)
+ return 0;
+
+ return packet_size;
+}
+
+static gboolean
+mpeg_read_packet(wtap *wth, FILE_T fh, wtap_rec *rec, Buffer *buf,
+ gboolean is_random, int *err, gchar **err_info)
+{
+ mpeg_t *mpeg = (mpeg_t *)wth->priv;
+ unsigned int packet_size;
+ nstime_t ts = mpeg->now;
+
+ if (mpeg->is_audio) {
+ /* mpeg_read_audio_packet calculates the duration of this
+ * packet to determine an updated relative timestamp for the
+ * next packet, if possible.
+ */
+ packet_size = mpeg_read_audio_packet(wth, fh, is_random, err, err_info);
+ } else {
+ /* mpeg_read_pes_packet uses the System Clock Reference counter
+ * to produce a relative timestamp for this packet, if possible.
+ */
+ packet_size = mpeg_read_pes_packet(wth, fh, is_random, err, err_info);
+ }
+
+ if (packet_size == 0)
+ return FALSE;
+
+ if (!wtap_read_packet_bytes(fh, buf, packet_size, err, err_info))
+ return FALSE;
+
+ rec->rec_type = REC_TYPE_PACKET;
+ rec->block = wtap_block_create(WTAP_BLOCK_PACKET);
+
+ rec->presence_flags = 0; /* we may or may not have a time stamp */
+ if (!is_random) {
+ /* XXX - relative, not absolute, time stamps */
+ rec->presence_flags = WTAP_HAS_TS;
+ rec->ts = ts;
+ }
+ rec->rec_header.packet_header.caplen = packet_size;
+ rec->rec_header.packet_header.len = packet_size;
+
+ return TRUE;
+}
+
+static gboolean
+mpeg_read(wtap *wth, wtap_rec *rec, Buffer *buf, int *err,
+ gchar **err_info, gint64 *data_offset)
+{
+ *data_offset = file_tell(wth->fh);
+
+ return mpeg_read_packet(wth, wth->fh, rec, buf, FALSE, err, err_info);
+}
+
+static gboolean
+mpeg_seek_read(wtap *wth, gint64 seek_off,
+ wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info)
+{
+ if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
+ return FALSE;
+
+ if (!mpeg_read_packet(wth, wth->random_fh, rec, buf, TRUE, err,
+ err_info)) {
+ if (*err == 0)
+ *err = WTAP_ERR_SHORT_READ;
+ return FALSE;
+ }
+ return TRUE;
+}
+
+struct _mpeg_magic {
+ size_t len;
+ const gchar* match;
+ gboolean is_audio;
+} magic[] = {
+ { 3, "TAG", TRUE }, /* ID3v1 */
+ /* XXX: ID3v1 tags come at the end of MP3 files, so in practice the
+ * untagged magic number is used instead.
+ */
+ { 3, "ID3", TRUE }, /* ID3v2 */
+ { 3, "\0\0\1", FALSE }, /* MPEG PES */
+ { 2, "\xff\xfb", TRUE }, /* MP3 (MPEG-1 Audio Layer 3, no CRC), taken from https://en.wikipedia.org/wiki/MP3#File_structure */
+#if 0
+ /* XXX: The value above is for MPEG-1 Audio Layer 3 with no CRC.
+ * Only the first three nibbles are the guaranteed sync byte.
+ * For the fourth nibble, the first bit is '1' for MPEG-1 and
+ * '0' for MPEG-2 (i.e., extension to lower sampling rates),
+ * the next two bits indicate the layer (1 for layer 3, 2 for
+ * layer 2, 3 for layer 1, 0 reserved), and the last ("protection")
+ * bit is 1 if there is no CRC and 0 if there is a CRC.
+ *
+ * The mpeg-audio dissector handles these, so wiretap should open
+ * them. Including all of them might increase false positives though.
+ */
+ { 2, "\xff\xf2", TRUE }, /* MPEG-2 Audio Layer 3, CRC */
+ { 2, "\xff\xf3", TRUE }, /* MPEG-2 Audio Layer 3, No CRC */
+ { 2, "\xff\xf4", TRUE }, /* MPEG-2 Audio Layer 2, CRC */
+ { 2, "\xff\xf5", TRUE }, /* MPEG-2 Audio Layer 2, No CRC */
+ { 2, "\xff\xf6", TRUE }, /* MPEG-2 Audio Layer 1, CRC */
+ { 2, "\xff\xf7", TRUE }, /* MPEG-2 Audio Layer 1, No CRC */
+ { 2, "\xff\xfa", TRUE }, /* MPEG-1 Audio Layer 3, CRC */
+ { 2, "\xff\xfc", TRUE }, /* MPEG-1 Audio Layer 2, CRC */
+ { 2, "\xff\xfd", TRUE }, /* MPEG-1 Audio Layer 2, No CRC */
+ { 2, "\xff\xfe", TRUE }, /* MPEG-1 Audio Layer 1, CRC */
+ { 2, "\xff\xff", TRUE }, /* MPEG-1 Audio Layer 1, No CRC */
+#endif
+ { 0, NULL, FALSE }
+};
+
+/*
+ * Even though this dissector uses magic numbers, it is registered in
+ * file_access.c as OPEN_INFO_HEURISTIC because the magic numbers are
+ * short and prone to false positives.
+ *
+ * XXX: There's room for improvement in detection if needed. A Program Stream
+ * starts with the pack_start_code, \x00\x00\x01\xba, and an uncontainered
+ * Video bitstream starts with the sequence_header_code, \x00\x00\x01\xb3.
+ * We could use those instead of matching any PES packet, which would greatly
+ * reduce false positives with e.g. PacketLogger files. (Unlike Transport
+ * Streams, unaligned file starts are unlikely with PS.)
+ *
+ * Untagged MPEG Audio files would still have to be heuristics, though.
+ */
+wtap_open_return_val
+mpeg_open(wtap *wth, int *err, gchar **err_info)
+{
+ char magic_buf[16];
+ struct _mpeg_magic* m;
+ mpeg_t *mpeg;
+
+ if (!wtap_read_bytes(wth->fh, magic_buf, sizeof magic_buf,
+ err, err_info)) {
+ if (*err != WTAP_ERR_SHORT_READ)
+ return WTAP_OPEN_ERROR;
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ for (m=magic; m->match; m++) {
+ if (memcmp(magic_buf, m->match, m->len) == 0)
+ goto good_magic;
+ }
+
+ return WTAP_OPEN_NOT_MINE;
+
+good_magic:
+ /* This appears to be a file with MPEG data. */
+ if (file_seek(wth->fh, 0, SEEK_SET, err) == -1)
+ return WTAP_OPEN_ERROR;
+
+ wth->file_type_subtype = mpeg_file_type_subtype;
+ wth->file_encap = WTAP_ENCAP_MPEG;
+ wth->file_tsprec = WTAP_TSPREC_NSEC;
+ wth->subtype_read = mpeg_read;
+ wth->subtype_seek_read = mpeg_seek_read;
+ wth->snapshot_length = 0;
+
+ mpeg = g_new(mpeg_t, 1);
+ wth->priv = (void *)mpeg;
+ mpeg->now.secs = 0;
+ mpeg->now.nsecs = 0;
+ mpeg->t0 = mpeg->now.secs;
+ mpeg->is_audio = m->is_audio;
+
+ return WTAP_OPEN_MINE;
+}
+
+static const struct supported_block_type mpeg_blocks_supported[] = {
+ /*
+ * This file format divides the file up into a "packet" for
+ * each frame, and doesn't support any options.
+ */
+ { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
+};
+
+static const struct file_type_subtype_info mpeg_info = {
+ "MPEG", "mpeg", "mpeg", "mpg;mp3",
+ FALSE, BLOCKS_SUPPORTED(mpeg_blocks_supported),
+ NULL, NULL, NULL
+};
+
+void register_mpeg(void)
+{
+ mpeg_file_type_subtype = wtap_register_file_type_subtype(&mpeg_info);
+
+ /*
+ * Register name for backwards compatibility with the
+ * wtap_filetypes table in Lua.
+ */
+ wtap_register_backwards_compatibility_lua_name("MPEG",
+ mpeg_file_type_subtype);
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/wiretap/mpeg.h b/wiretap/mpeg.h
new file mode 100644
index 00000000..5b1b220c
--- /dev/null
+++ b/wiretap/mpeg.h
@@ -0,0 +1,19 @@
+/** @file
+ *
+ * MPEG file format decoder for the Wiretap library.
+ * Written by Shaun Jackman <sjackman@gmail.com>
+ * Copyright 2007 Shaun Jackman
+ *
+ * Wiretap Library
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __W_MPEG_H__
+#define __W_MPEG_H__
+
+#include <glib.h>
+#include "wtap.h"
+
+wtap_open_return_val mpeg_open(wtap *wth, int *err, gchar **err_info);
+
+#endif
diff --git a/wiretap/mplog.c b/wiretap/mplog.c
new file mode 100644
index 00000000..e27400ad
--- /dev/null
+++ b/wiretap/mplog.c
@@ -0,0 +1,294 @@
+/* mplog.c
+ *
+ * File format support for Micropross mplog files
+ * Copyright (c) 2016 by Martin Kaiser <martin@kaiser.cx>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+
+/*
+ The mplog file format logs the communication between a contactless
+ smartcard and a card reader. Such files contain information about the
+ physical layer as well as the bytes exchanged between devices.
+ Some commercial logging and testing tools by the French company Micropross
+ use this format.
+
+ The information used for implementing this wiretap module were
+ obtained from reverse-engineering. There is no publicly available
+ documentation of the mplog file format.
+
+ Mplog files start with the string "MPCSII". This string is part of
+ the header which is in total 0x80 bytes long.
+
+ Following the header, the file is a sequence of 8 byte-blocks.
+ data (one byte)
+ block type (one byte)
+ timestamp (six bytes)
+
+ The timestamp is a counter in little-endian format. The counter is in
+ units of 10ns.
+*/
+
+#include "config.h"
+
+#include <string.h>
+#include <wtap-int.h>
+#include <file_wrappers.h>
+
+#include "mplog.h"
+
+/* the block types */
+#define TYPE_PCD_PICC_A 0x70
+#define TYPE_PICC_PCD_A 0x71
+#define TYPE_PCD_PICC_B 0x72
+#define TYPE_PICC_PCD_B 0x73
+#define TYPE_UNKNOWN 0xFF
+
+#define KNOWN_TYPE(x) \
+( \
+ ((x) == TYPE_PCD_PICC_A) || \
+ ((x) == TYPE_PICC_PCD_A) || \
+ ((x) == TYPE_PCD_PICC_B) || \
+ ((x) == TYPE_PICC_PCD_B) \
+)
+
+#define MPLOG_BLOCK_SIZE 8
+
+/* ISO14443 pseudo-header, see https://www.kaiser.cx/pcap-iso14443.html */
+#define ISO14443_PSEUDO_HDR_VER 0
+#define ISO14443_PSEUDO_HDR_LEN 4
+/* the two transfer events are the types that include a trailing CRC
+ the CRC is always present in mplog files */
+#define ISO14443_PSEUDO_HDR_PICC_TO_PCD 0xFF
+#define ISO14443_PSEUDO_HDR_PCD_TO_PICC 0xFE
+
+
+#define ISO14443_MAX_PKT_LEN 4096
+
+#define PKT_BUF_LEN (ISO14443_PSEUDO_HDR_LEN + ISO14443_MAX_PKT_LEN)
+
+
+static int mplog_file_type_subtype = -1;
+
+void register_mplog(void);
+
+/* read the next packet, starting at the current position of fh
+ as we know very little about the file format, our approach is rather simple:
+ - we read block-by-block until a known block-type is found
+ - this block's type is the type of the next packet
+ - this block's timestamp will become the packet's timestamp
+ - the data byte will be our packet's first byte
+ - we carry on reading blocks and add the data bytes
+ of all blocks of "our" type
+ - if a different well-known block type is found, this is the end of
+ our packet, we go back one block so that this block can be picked
+ up as the start of the next packet
+ - if two blocks of our packet's block type are more than 200us apart,
+ we treat this as a packet boundary as described above
+ */
+static gboolean mplog_read_packet(FILE_T fh, wtap_rec *rec,
+ Buffer *buf, int *err, gchar **err_info)
+{
+ guint8 *p, *start_p;
+ /* --- the last block of a known type --- */
+ guint64 last_ctr = 0;
+ /* --- the current block --- */
+ guint8 block[MPLOG_BLOCK_SIZE]; /* the entire block */
+ guint8 data, type; /* its data and block type bytes */
+ guint64 ctr; /* its timestamp counter */
+ /* --- the packet we're assembling --- */
+ gint pkt_bytes = 0;
+ guint8 pkt_type = TYPE_UNKNOWN;
+ /* the timestamp of the packet's first block,
+ this will become the packet's timestamp */
+ guint64 pkt_ctr = 0;
+
+
+ ws_buffer_assure_space(buf, PKT_BUF_LEN);
+ p = ws_buffer_start_ptr(buf);
+ start_p = p;
+
+ /* leave space for the iso14443 pseudo header
+ we can't create it until we've seen the entire packet */
+ p += ISO14443_PSEUDO_HDR_LEN;
+
+ do {
+ if (!wtap_read_bytes_or_eof(fh, block, sizeof(block), err, err_info)) {
+ /* If we've already read some data, if this failed with an EOF,
+ so that *err is 0, it's a short read. */
+ if (pkt_bytes != 0) {
+ if (*err == 0)
+ *err = WTAP_ERR_SHORT_READ;
+ }
+ break;
+ }
+ data = block[0];
+ type = block[1];
+ ctr = pletoh48(&block[2]);
+
+ if (pkt_type == TYPE_UNKNOWN) {
+ if (KNOWN_TYPE(type)) {
+ pkt_type = type;
+ pkt_ctr = ctr;
+ }
+ }
+
+ if (type == pkt_type) {
+ if (last_ctr != 0) {
+ /* if the distance to the last byte of the
+ same type is larger than 200us, this is very likely the
+ first byte of a new packet -> go back one block and exit
+ ctr and last_ctr are in units of 10ns
+ at 106kbit/s, it takes approx 75us to send one byte */
+ if (ctr - last_ctr > 200*100) {
+ file_seek(fh, -MPLOG_BLOCK_SIZE, SEEK_CUR, err);
+ break;
+ }
+ }
+
+ *p++ = data;
+ pkt_bytes++;
+ last_ctr = ctr;
+ }
+ else if (KNOWN_TYPE(type)) {
+ file_seek(fh, -MPLOG_BLOCK_SIZE, SEEK_CUR, err);
+ break;
+ }
+ } while (pkt_bytes < ISO14443_MAX_PKT_LEN);
+
+ if (pkt_type == TYPE_UNKNOWN)
+ return FALSE;
+
+ start_p[0] = ISO14443_PSEUDO_HDR_VER;
+
+ if (pkt_type==TYPE_PCD_PICC_A || pkt_type==TYPE_PCD_PICC_B)
+ start_p[1] = ISO14443_PSEUDO_HDR_PCD_TO_PICC;
+ else
+ start_p[1] = ISO14443_PSEUDO_HDR_PICC_TO_PCD;
+
+ start_p[2] = pkt_bytes >> 8;
+ start_p[3] = pkt_bytes & 0xFF;
+
+ rec->rec_type = REC_TYPE_PACKET;
+ rec->block = wtap_block_create(WTAP_BLOCK_PACKET);
+ rec->rec_header.packet_header.pkt_encap = WTAP_ENCAP_ISO14443;
+ rec->presence_flags = WTAP_HAS_TS | WTAP_HAS_CAP_LEN;
+ rec->ts.secs = (time_t)((pkt_ctr*10)/(1000*1000*1000));
+ rec->ts.nsecs = (int)((pkt_ctr*10)%(1000*1000*1000));
+ rec->rec_header.packet_header.caplen = ISO14443_PSEUDO_HDR_LEN + pkt_bytes;
+ rec->rec_header.packet_header.len = rec->rec_header.packet_header.caplen;
+
+ return TRUE;
+}
+
+
+static gboolean
+mplog_read(wtap *wth, wtap_rec *rec, Buffer *buf, int *err,
+ gchar **err_info, gint64 *data_offset)
+{
+ *data_offset = file_tell(wth->fh);
+
+ return mplog_read_packet(wth->fh, rec, buf, err, err_info);
+}
+
+
+static gboolean
+mplog_seek_read(wtap *wth, gint64 seek_off, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info)
+{
+ if (-1 == file_seek(wth->random_fh, seek_off, SEEK_SET, err))
+ return FALSE;
+
+ if (!mplog_read_packet(wth->random_fh, rec, buf, err, err_info)) {
+ /* Even if we got an immediate EOF, that's an error. */
+ if (*err == 0)
+ *err = WTAP_ERR_SHORT_READ;
+ return FALSE;
+ }
+ return TRUE;
+}
+
+
+wtap_open_return_val mplog_open(wtap *wth, int *err, gchar **err_info)
+{
+ gboolean ok;
+ guint8 magic[6];
+
+ ok = wtap_read_bytes(wth->fh, magic, 6, err, err_info);
+ if (!ok) {
+ if (*err != WTAP_ERR_SHORT_READ)
+ return WTAP_OPEN_ERROR;
+ return WTAP_OPEN_NOT_MINE;
+ }
+ if (memcmp(magic, "MPCSII", 6) != 0)
+ return WTAP_OPEN_NOT_MINE;
+
+ wth->file_encap = WTAP_ENCAP_ISO14443;
+ wth->snapshot_length = 0;
+ wth->file_tsprec = WTAP_TSPREC_NSEC;
+
+ wth->priv = NULL;
+
+ wth->subtype_read = mplog_read;
+ wth->subtype_seek_read = mplog_seek_read;
+ wth->file_type_subtype = mplog_file_type_subtype;
+
+ /* skip the file header */
+ if (-1 == file_seek(wth->fh, 0x80, SEEK_SET, err))
+ return WTAP_OPEN_ERROR;
+
+ *err = 0;
+
+ /*
+ * Add an IDB; we don't know how many interfaces were
+ * involved, so we just say one interface, about which
+ * we only know the link-layer type, snapshot length,
+ * and time stamp resolution.
+ */
+ wtap_add_generated_idb(wth);
+
+ return WTAP_OPEN_MINE;
+}
+
+static const struct supported_block_type mplog_blocks_supported[] = {
+ /*
+ * We support packet blocks, with no comments or other options.
+ */
+ { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
+};
+
+static const struct file_type_subtype_info mplog_info = {
+ "Micropross mplog", "mplog", "mplog", NULL,
+ FALSE, BLOCKS_SUPPORTED(mplog_blocks_supported),
+ NULL, NULL, NULL
+};
+
+void register_mplog(void)
+{
+ mplog_file_type_subtype = wtap_register_file_type_subtype(&mplog_info);
+
+ /*
+ * Register name for backwards compatibility with the
+ * wtap_filetypes table in Lua.
+ */
+ wtap_register_backwards_compatibility_lua_name("MPLOG",
+ mplog_file_type_subtype);
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * vi: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */
diff --git a/wiretap/mplog.h b/wiretap/mplog.h
new file mode 100644
index 00000000..2a1ba18c
--- /dev/null
+++ b/wiretap/mplog.h
@@ -0,0 +1,21 @@
+/** @file
+ *
+ * File format support for Micropross mplog files
+ * Copyright (c) 2016 by Martin Kaiser <martin@kaiser.cx>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef _MPLOG_H
+#define _MPLOG_H
+
+#include <glib.h>
+#include <wiretap/wtap.h>
+
+wtap_open_return_val mplog_open(wtap *wth, int *err, gchar **err_info);
+
+#endif /* _MPLOG_H */
diff --git a/wiretap/netmon.c b/wiretap/netmon.c
new file mode 100644
index 00000000..861b1f90
--- /dev/null
+++ b/wiretap/netmon.c
@@ -0,0 +1,2038 @@
+/* netmon.c
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "config.h"
+#include <errno.h>
+#include <string.h>
+#include <wsutil/unicode-utils.h>
+#include "wtap-int.h"
+#include "file_wrappers.h"
+#include "atm.h"
+#include "pcap-encap.h"
+#include "netmon.h"
+
+/* The file at
+ *
+ * ftp://ftp.microsoft.com/developr/drg/cifs/cifs/Bhfile.zip
+ *
+ * contains "STRUCT.H", which declares the typedef CAPTUREFILE_HEADER
+ * for the header of a Microsoft Network Monitor 1.x capture file.
+ *
+ * The help files for Network Monitor 3.x document the 2.x file format.
+ */
+
+/* Capture file header, *including* magic number, is padded to 128 bytes. */
+#define CAPTUREFILE_HEADER_SIZE 128
+
+/* Magic number size, for both 1.x and 2.x. */
+#define MAGIC_SIZE 4
+
+/* Magic number in Network Monitor 1.x files. */
+static const char netmon_1_x_magic[MAGIC_SIZE] = {
+ 'R', 'T', 'S', 'S'
+};
+
+/* Magic number in Network Monitor 2.x files. */
+static const char netmon_2_x_magic[MAGIC_SIZE] = {
+ 'G', 'M', 'B', 'U'
+};
+
+/* Network Monitor file header (minus magic number). */
+struct netmon_hdr {
+ guint8 ver_minor; /* minor version number */
+ guint8 ver_major; /* major version number */
+ guint16 network; /* network type */
+ guint16 ts_year; /* year of capture start */
+ guint16 ts_month; /* month of capture start (January = 1) */
+ guint16 ts_dow; /* day of week of capture start (Sun = 0) */
+ guint16 ts_day; /* day of month of capture start */
+ guint16 ts_hour; /* hour of capture start */
+ guint16 ts_min; /* minute of capture start */
+ guint16 ts_sec; /* second of capture start */
+ guint16 ts_msec; /* millisecond of capture start */
+ guint32 frametableoffset; /* frame index table offset */
+ guint32 frametablelength; /* frame index table size */
+ guint32 userdataoffset; /* user data offset */
+ guint32 userdatalength; /* user data size */
+ guint32 commentdataoffset; /* comment data offset */
+ guint32 commentdatalength; /* comment data size */
+ guint32 processinfooffset; /* offset to process info structure */
+ guint32 processinfocount; /* number of process info structures */
+ guint32 networkinfooffset; /* offset to network info structure */
+ guint32 networkinfolength; /* length of network info structure */
+};
+
+/* Network Monitor 1.x record header; not defined in STRUCT.H, but deduced by
+ * looking at capture files. */
+struct netmonrec_1_x_hdr {
+ guint32 ts_delta; /* time stamp - msecs since start of capture */
+ guint16 orig_len; /* actual length of packet */
+ guint16 incl_len; /* number of octets captured in file */
+};
+
+/*
+ * Network Monitor 2.x record header, as documented in NetMon 3.x's
+ * help files.
+ */
+struct netmonrec_2_x_hdr {
+ guint64 ts_delta; /* time stamp - usecs since start of capture */
+ guint32 orig_len; /* actual length of packet */
+ guint32 incl_len; /* number of octets captured in file */
+};
+
+/*
+ * Network Monitor 2.1 and later record trailers; documented in the Network
+ * Monitor 3.x help files, for 3.3 and later, although they don't clearly
+ * state how the trailer format changes from version to version.
+ *
+ * Some fields are multi-byte integers, but they're not aligned on their
+ * natural boundaries.
+ */
+struct netmonrec_2_1_trlr {
+ guint8 network[2]; /* network type for this packet */
+};
+
+struct netmonrec_2_2_trlr {
+ guint8 network[2]; /* network type for this packet */
+ guint8 process_info_index[4]; /* index into the process info table */
+};
+
+struct netmonrec_2_3_trlr {
+ guint8 network[2]; /* network type for this packet */
+ guint8 process_info_index[4]; /* index into the process info table */
+ guint8 utc_timestamp[8]; /* packet time stamp, as .1 us units since January 1, 1601, 00:00:00 UTC */
+ guint8 timezone_index; /* index of time zone information */
+};
+
+struct netmonrec_comment {
+ guint32 numFramePerComment; /* Currently, this is always set to 1. Each comment is attached to only one frame. */
+ guint32 frameOffset; /* Offset in the capture file table that indicates the beginning of the frame. Key used to match comment with frame */
+ guint8* title; /* Comment title */
+ guint32 descLength; /* Number of bytes in the comment description. Must be at least zero. */
+ guint8* description; /* Comment description */
+};
+
+/* Just the first few fields of netmonrec_comment so it can be read sequentially from file */
+struct netmonrec_comment_header {
+ guint32 numFramePerComment;
+ guint32 frameOffset;
+ guint32 titleLength;
+};
+
+union ip_address {
+ guint32 ipv4;
+ ws_in6_addr ipv6;
+};
+
+struct netmonrec_process_info {
+ guint8* path; /* A Unicode string of length PathSize */
+ guint32 iconSize;
+ guint8* iconData;
+ guint32 pid;
+ guint16 localPort;
+ guint16 remotePort;
+ gboolean isIPv6;
+ union ip_address localAddr;
+ union ip_address remoteAddr;
+};
+
+/*
+ * The link-layer header on ATM packets.
+ */
+struct netmon_atm_hdr {
+ guint8 dest[6]; /* "Destination address" - what is it? */
+ guint8 src[6]; /* "Source address" - what is it? */
+ guint16 vpi; /* VPI */
+ guint16 vci; /* VCI */
+};
+
+typedef struct {
+ time_t start_secs;
+ guint32 start_nsecs;
+ guint8 version_major;
+ guint8 version_minor;
+ guint32 *frame_table;
+ guint32 frame_table_size;
+ GHashTable* comment_table;
+ GHashTable* process_info_table;
+ guint current_frame;
+} netmon_t;
+
+/*
+ * Maximum pathname length supported in the process table; the length
+ * is in a 32-bit field, so we impose a limit to prevent attempts to
+ * allocate too much memory.
+ *
+ * See
+ *
+ * https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file#maximum-path-length-limitation
+ *
+ * The NetMon 3.4 "Capture File Format" documentation says "PathSize must be
+ * greater than 0, and less than MAX_PATH (260 characters)", but, as per that
+ * link above, that limit has been raised in more recent systems.
+ *
+ * We pick a limit of 65536, as that should handle a path length of 32767
+ * UTF-16 octet pairs plus a trailing NUL octet pair.
+ */
+#define MATH_PROCINFO_PATH_SIZE 65536
+
+/*
+ * XXX - at least in some NetMon 3.4 VPN captures, the per-packet
+ * link-layer type is 0, but the packets have Ethernet headers.
+ * We handle this by mapping 0 to WTAP_ENCAP_ETHERNET; should we,
+ * instead, use the per-file link-layer type?
+ */
+static const int netmon_encap[] = {
+ WTAP_ENCAP_ETHERNET,
+ WTAP_ENCAP_ETHERNET,
+ WTAP_ENCAP_TOKEN_RING,
+ WTAP_ENCAP_FDDI_BITSWAPPED,
+ WTAP_ENCAP_ATM_PDUS, /* NDIS WAN - this is what's used for ATM */
+ WTAP_ENCAP_UNKNOWN, /* NDIS LocalTalk, but format 2.x uses it for IP-over-IEEE 1394 */
+ WTAP_ENCAP_IEEE_802_11_NETMON,
+ /* NDIS "DIX", but format 2.x uses it for 802.11 */
+ WTAP_ENCAP_RAW_IP, /* NDIS ARCNET raw, but format 2.x uses it for "Tunneling interfaces" */
+ WTAP_ENCAP_RAW_IP, /* NDIS ARCNET 878.2, but format 2.x uses it for "Wireless WAN" */
+ WTAP_ENCAP_RAW_IP, /* NDIS ATM (no, this is NOT used for ATM); format 2.x uses it for "Raw IP Frames" */
+ WTAP_ENCAP_UNKNOWN, /* NDIS Wireless WAN */
+ WTAP_ENCAP_UNKNOWN /* NDIS IrDA */
+};
+#define NUM_NETMON_ENCAPS (sizeof netmon_encap / sizeof netmon_encap[0])
+
+/*
+ * Special link-layer types.
+ */
+#define NETMON_NET_PCAP_BASE 0xE000
+#define NETMON_NET_NETEVENT 0xFFE0
+#define NETMON_NET_NETWORK_INFO_EX 0xFFFB
+#define NETMON_NET_PAYLOAD_HEADER 0xFFFC
+#define NETMON_NET_NETWORK_INFO 0xFFFD
+#define NETMON_NET_DNS_CACHE 0xFFFE
+#define NETMON_NET_NETMON_FILTER 0xFFFF
+
+static gboolean netmon_read(wtap *wth, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info, gint64 *data_offset);
+static gboolean netmon_seek_read(wtap *wth, gint64 seek_off,
+ wtap_rec *rec, Buffer *buf, int *err, gchar **err_info);
+static gboolean netmon_read_atm_pseudoheader(FILE_T fh,
+ union wtap_pseudo_header *pseudo_header, int *err, gchar **err_info);
+static void netmon_close(wtap *wth);
+static gboolean netmon_dump(wtap_dumper *wdh, const wtap_rec *rec,
+ const guint8 *pd, int *err, gchar **err_info);
+static gboolean netmon_dump_finish(wtap_dumper *wdh, int *err,
+ gchar **err_info);
+
+static int netmon_1_x_file_type_subtype = -1;
+static int netmon_2_x_file_type_subtype = -1;
+
+void register_netmon(void);
+
+/*
+ * Convert a counted UTF-16 string, which is probably also null-terminated
+ * but is not guaranteed to be null-terminated (as it came from a file),
+ * to a null-terminated UTF-8 string.
+ */
+static guint8 *
+utf_16_to_utf_8(const guint8 *in, guint32 length)
+{
+ guint8 *result, *out;
+ gunichar2 uchar2;
+ gunichar uchar;
+ size_t n_bytes;
+ guint32 i;
+
+ /*
+ * Get the length of the resulting UTF-8 string, and validate
+ * the input string in the process.
+ */
+ n_bytes = 0;
+ for (i = 0; i + 1 < length && (uchar2 = pletoh16(in + i)) != '\0';
+ i += 2) {
+ if (IS_LEAD_SURROGATE(uchar2)) {
+ /*
+ * Lead surrogate. Must be followed by a trail
+ * surrogate.
+ */
+ gunichar2 lead_surrogate;
+
+ i += 2;
+ if (i + 1 >= length) {
+ /*
+ * Oops, string ends with a lead surrogate.
+ * Ignore this for now.
+ * XXX - insert "substitute" character?
+ * Report the error in some other fashion?
+ */
+ break;
+ }
+ lead_surrogate = uchar2;
+ uchar2 = pletoh16(in + i);
+ if (uchar2 == '\0') {
+ /*
+ * Oops, string ends with a lead surrogate.
+ * Ignore this for now.
+ * XXX - insert "substitute" character?
+ * Report the error in some other fashion?
+ */
+ break;
+ }
+ if (IS_TRAIL_SURROGATE(uchar2)) {
+ /* Trail surrogate. */
+ uchar = SURROGATE_VALUE(lead_surrogate, uchar2);
+ n_bytes += g_unichar_to_utf8(uchar, NULL);
+ } else {
+ /*
+ * Not a trail surrogate.
+ * Ignore the entire pair.
+ * XXX - insert "substitute" character?
+ * Report the error in some other fashion?
+ */
+ ;
+ }
+ } else {
+ if (IS_TRAIL_SURROGATE(uchar2)) {
+ /*
+ * Trail surrogate without a preceding
+ * lead surrogate. Ignore it.
+ * XXX - insert "substitute" character?
+ * Report the error in some other fashion?
+ */
+ ;
+ } else {
+ /*
+ * Non-surrogate; just count it.
+ */
+ n_bytes += g_unichar_to_utf8(uchar2, NULL);
+ }
+ }
+ }
+
+ /*
+ * Now allocate a buffer big enough for the UTF-8 string plus a
+ * trailing NUL, and generate the string.
+ */
+ result = (guint8 *)g_malloc(n_bytes + 1);
+
+ out = result;
+ for (i = 0; i + 1 < length && (uchar2 = pletoh16(in + i)) != '\0';
+ i += 2) {
+ if (IS_LEAD_SURROGATE(uchar2)) {
+ /*
+ * Lead surrogate. Must be followed by a trail
+ * surrogate.
+ */
+ gunichar2 lead_surrogate;
+
+ i += 2;
+ if (i + 1 >= length) {
+ /*
+ * Oops, string ends with a lead surrogate.
+ * Ignore this for now.
+ * XXX - insert "substitute" character?
+ * Report the error in some other fashion?
+ */
+ break;
+ }
+ lead_surrogate = uchar2;
+ uchar2 = pletoh16(in + i);
+ if (uchar2 == '\0') {
+ /*
+ * Oops, string ends with a lead surrogate.
+ * Ignore this for now.
+ * XXX - insert "substitute" character?
+ * Report the error in some other fashion?
+ */
+ break;
+ }
+ if (IS_TRAIL_SURROGATE(uchar2)) {
+ /* Trail surrogate. */
+ uchar = SURROGATE_VALUE(lead_surrogate, uchar2);
+ out += g_unichar_to_utf8(uchar, out);
+ } else {
+ /*
+ * Not a trail surrogate.
+ * Ignore the entire pair.
+ * XXX - insert "substitute" character?
+ * Report the error in some other fashion?
+ */
+ ;
+ }
+ } else {
+ if (IS_TRAIL_SURROGATE(uchar2)) {
+ /*
+ * Trail surrogate without a preceding
+ * lead surrogate. Ignore it.
+ * XXX - insert "substitute" character?
+ * Report the error in some other fashion?
+ */
+ ;
+ } else {
+ /*
+ * Non-surrogate; just count it.
+ */
+ out += g_unichar_to_utf8(uchar2, out);
+ }
+ }
+ }
+ *out = '\0';
+
+ /*
+ * XXX - if i < length, this means we were handed an odd
+ * number of bytes, so it was not a valid UTF-16 string.
+ */
+ return result;
+}
+
+
+static void netmonrec_comment_destroy(gpointer key) {
+ struct netmonrec_comment *comment = (struct netmonrec_comment*) key;
+
+ g_free(comment->title);
+ g_free(comment->description);
+ g_free(comment);
+}
+
+static void netmonrec_process_info_destroy(gpointer key) {
+ struct netmonrec_process_info *process_info = (struct netmonrec_process_info*) key;
+
+ g_free(process_info->path);
+ g_free(process_info->iconData);
+ g_free(process_info);
+}
+
+wtap_open_return_val netmon_open(wtap *wth, int *err, gchar **err_info)
+{
+ char magic[MAGIC_SIZE];
+ struct netmon_hdr hdr;
+ int file_type;
+ struct tm tm;
+ guint32 frame_table_offset;
+ guint32 frame_table_length;
+ guint32 frame_table_size;
+ guint32 *frame_table;
+ guint32 comment_table_offset, process_info_table_offset;
+ guint32 comment_table_size, process_info_table_count;
+ GHashTable *comment_table, *process_info_table;
+ struct netmonrec_comment* comment_rec;
+ gint64 file_size = wtap_file_size(wth, err);
+#if G_BYTE_ORDER == G_BIG_ENDIAN
+ unsigned int i;
+#endif
+ netmon_t *netmon;
+
+ /* Read in the string that should be at the start of a Network
+ * Monitor file */
+ if (!wtap_read_bytes(wth->fh, magic, MAGIC_SIZE, err, err_info)) {
+ if (*err != WTAP_ERR_SHORT_READ)
+ return WTAP_OPEN_ERROR;
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ if (memcmp(magic, netmon_1_x_magic, MAGIC_SIZE) != 0 &&
+ memcmp(magic, netmon_2_x_magic, MAGIC_SIZE) != 0) {
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ /* Read the rest of the header. */
+ if (!wtap_read_bytes(wth->fh, &hdr, sizeof hdr, err, err_info))
+ return WTAP_OPEN_ERROR;
+
+ switch (hdr.ver_major) {
+
+ case 1:
+ file_type = netmon_1_x_file_type_subtype;
+ break;
+
+ case 2:
+ file_type = netmon_2_x_file_type_subtype;
+ break;
+
+ default:
+ *err = WTAP_ERR_UNSUPPORTED;
+ *err_info = ws_strdup_printf("netmon: major version %u unsupported", hdr.ver_major);
+ return WTAP_OPEN_ERROR;
+ }
+
+ hdr.network = pletoh16(&hdr.network);
+ if (hdr.network >= NUM_NETMON_ENCAPS
+ || netmon_encap[hdr.network] == WTAP_ENCAP_UNKNOWN) {
+ *err = WTAP_ERR_UNSUPPORTED;
+ *err_info = ws_strdup_printf("netmon: network type %u unknown or unsupported",
+ hdr.network);
+ return WTAP_OPEN_ERROR;
+ }
+
+ /* This is a netmon file */
+ wth->file_type_subtype = file_type;
+ netmon = g_new0(netmon_t, 1);
+ wth->priv = (void *)netmon;
+ wth->subtype_read = netmon_read;
+ wth->subtype_seek_read = netmon_seek_read;
+ wth->subtype_close = netmon_close;
+
+ /* NetMon capture file formats v2.1+ use per-packet encapsulation types. NetMon 3 sets the value in
+ * the header to 1 (Ethernet) for backwards compatibility. */
+ if((hdr.ver_major == 2 && hdr.ver_minor >= 1) || hdr.ver_major > 2)
+ wth->file_encap = WTAP_ENCAP_PER_PACKET;
+ else
+ wth->file_encap = netmon_encap[hdr.network];
+
+ wth->snapshot_length = 0; /* not available in header */
+ /*
+ * Convert the time stamp to a "time_t" and a number of
+ * milliseconds.
+ */
+ tm.tm_year = pletoh16(&hdr.ts_year) - 1900;
+ tm.tm_mon = pletoh16(&hdr.ts_month) - 1;
+ tm.tm_mday = pletoh16(&hdr.ts_day);
+ tm.tm_hour = pletoh16(&hdr.ts_hour);
+ tm.tm_min = pletoh16(&hdr.ts_min);
+ tm.tm_sec = pletoh16(&hdr.ts_sec);
+ tm.tm_isdst = -1;
+ netmon->start_secs = mktime(&tm);
+ /*
+ * XXX - what if "secs" is -1? Unlikely, but if the capture was
+ * done in a time zone that switches between standard and summer
+ * time sometime other than when we do, and thus the time was one
+ * that doesn't exist here because a switch from standard to summer
+ * time zips over it, it could happen.
+ *
+ * On the other hand, if the capture was done in a different time
+ * zone, this won't work right anyway; unfortunately, the time
+ * zone isn't stored in the capture file (why the hell didn't
+ * they stuff a FILETIME, which is the number of 100-nanosecond
+ * intervals since 1601-01-01 00:00:00 "UTC", there, instead
+ * of stuffing a SYSTEMTIME, which is time-zone-dependent, there?).
+ *
+ * Eventually they went with per-packet FILETIMEs in a later
+ * version.
+ */
+ netmon->start_nsecs = pletoh16(&hdr.ts_msec)*1000000;
+
+ netmon->version_major = hdr.ver_major;
+ netmon->version_minor = hdr.ver_minor;
+
+ /*
+ * Get the offset of the frame index table.
+ */
+ frame_table_offset = pletoh32(&hdr.frametableoffset);
+
+ /*
+ * For NetMon 2.2 format and later, get the offset and length of
+ * the comment index table and process info table.
+ *
+ * For earlier versions, set them to zero; they appear to be
+ * uninitialized, so they're not necessarily zero.
+ */
+ if ((netmon->version_major == 2 && netmon->version_minor >= 2) ||
+ netmon->version_major > 2) {
+ comment_table_offset = pletoh32(&hdr.commentdataoffset);
+ comment_table_size = pletoh32(&hdr.commentdatalength);
+ process_info_table_offset = pletoh32(&hdr.processinfooffset);
+ process_info_table_count = pletoh32(&hdr.processinfocount);
+ } else {
+ comment_table_offset = 0;
+ comment_table_size = 0;
+ process_info_table_offset = 0;
+ process_info_table_count = 0;
+ }
+
+ /*
+ * It appears that some NetMon 2.x files don't have the
+ * first packet starting exactly 128 bytes into the file.
+ *
+ * Furthermore, it also appears that there are "holes" in
+ * the file, i.e. frame N+1 doesn't always follow immediately
+ * after frame N.
+ *
+ * Therefore, we must read the frame table, and use the offsets
+ * in it as the offsets of the frames.
+ */
+ frame_table_length = pletoh32(&hdr.frametablelength);
+ frame_table_size = frame_table_length / (guint32)sizeof (guint32);
+ if ((frame_table_size * sizeof (guint32)) != frame_table_length) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("netmon: frame table length is %u, which is not a multiple of the size of an entry",
+ frame_table_length);
+ return WTAP_OPEN_ERROR;
+ }
+ if (frame_table_size == 0) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("netmon: frame table length is %u, which means it's less than one entry in size",
+ frame_table_length);
+ return WTAP_OPEN_ERROR;
+ }
+ /*
+ * XXX - clamp the size of the frame table, so that we don't
+ * attempt to allocate a huge frame table and fail.
+ *
+ * Given that file offsets in the frame table are 32-bit,
+ * a NetMon file cannot be bigger than 2^32 bytes.
+ * Given that a NetMon 1.x-format packet header is 8 bytes,
+ * that means a NetMon file cannot have more than
+ * 512*2^20 packets. We'll pick that as the limit for
+ * now; it's 1/8th of a 32-bit address space, which is
+ * probably not going to exhaust the address space all by
+ * itself, and probably won't exhaust the backing store.
+ */
+ if (frame_table_size > 512*1024*1024) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("netmon: frame table length is %u, which is larger than we support",
+ frame_table_length);
+ return WTAP_OPEN_ERROR;
+ }
+ if (file_seek(wth->fh, frame_table_offset, SEEK_SET, err) == -1) {
+ return WTAP_OPEN_ERROR;
+ }
+
+ /*
+ * Sanity check the comment table information before we bother to allocate
+ * large chunks of memory for the frame table
+ */
+ if (comment_table_size > 0) {
+ /*
+ * XXX - clamp the size of the comment table, so that we don't
+ * attempt to allocate a huge comment table and fail.
+ *
+ * Just use same size requires as frame table
+ */
+ if (comment_table_size > 512*1024*1024) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("netmon: comment table size is %u, which is larger than we support",
+ comment_table_size);
+ return WTAP_OPEN_ERROR;
+ }
+
+ if (comment_table_size < 17) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("netmon: comment table size is %u, which is too small to use",
+ comment_table_size);
+ return WTAP_OPEN_ERROR;
+ }
+
+ if (comment_table_offset > file_size) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("netmon: comment table offset (%u) is larger than file",
+ comment_table_offset);
+ return WTAP_OPEN_ERROR;
+ }
+ }
+
+ /*
+ * Sanity check the process info table information before we bother to allocate
+ * large chunks of memory for the frame table
+ */
+ if ((process_info_table_offset > 0) && (process_info_table_count > 0)) {
+ /*
+ * XXX - clamp the size of the process info table, so that we don't
+ * attempt to allocate a huge process info table and fail.
+ */
+ if (process_info_table_count > 512*1024) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("netmon: process info table size is %u, which is larger than we support",
+ process_info_table_count);
+ return WTAP_OPEN_ERROR;
+ }
+
+ if (process_info_table_offset > file_size) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("netmon: process info table offset (%u) is larger than file",
+ process_info_table_offset);
+ return WTAP_OPEN_ERROR;
+ }
+ }
+
+ /*
+ * Return back to the frame table offset
+ */
+ if (file_seek(wth->fh, frame_table_offset, SEEK_SET, err) == -1) {
+ return WTAP_OPEN_ERROR;
+ }
+
+ /*
+ * Sanity check the process info table information before we bother to allocate
+ * large chunks of memory for the frame table
+ */
+
+ frame_table = (guint32 *)g_try_malloc(frame_table_length);
+ if (frame_table_length != 0 && frame_table == NULL) {
+ *err = ENOMEM; /* we assume we're out of memory */
+ return WTAP_OPEN_ERROR;
+ }
+ if (!wtap_read_bytes(wth->fh, frame_table, frame_table_length,
+ err, err_info)) {
+ g_free(frame_table);
+ return WTAP_OPEN_ERROR;
+ }
+ netmon->frame_table_size = frame_table_size;
+ netmon->frame_table = frame_table;
+
+ if (comment_table_size > 0) {
+ comment_table = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, netmonrec_comment_destroy);
+ if (comment_table == NULL) {
+ *err = ENOMEM; /* we assume we're out of memory */
+ return WTAP_OPEN_ERROR;
+ }
+
+ /* Make sure the file contains the full comment section */
+ if (file_seek(wth->fh, comment_table_offset+comment_table_size, SEEK_SET, err) == -1) {
+ g_hash_table_destroy(comment_table);
+ return WTAP_OPEN_ERROR;
+ }
+
+ if (file_seek(wth->fh, comment_table_offset, SEEK_SET, err) == -1) {
+ /* Shouldn't fail... */
+ g_hash_table_destroy(comment_table);
+ return WTAP_OPEN_ERROR;
+ }
+
+ while (comment_table_size > 16) {
+ struct netmonrec_comment_header comment_header;
+ guint32 title_length;
+ guint32 desc_length;
+ guint8 *utf16_str;
+
+ /* Read the first 12 bytes of the structure */
+ if (!wtap_read_bytes(wth->fh, &comment_header, 12, err, err_info)) {
+ g_hash_table_destroy(comment_table);
+ return WTAP_OPEN_ERROR;
+ }
+ comment_table_size -= 12;
+
+ /* Make sure comment size is sane */
+ title_length = pletoh32(&comment_header.titleLength);
+ if (title_length == 0) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("netmon: comment title size can't be 0");
+ g_hash_table_destroy(comment_table);
+ return WTAP_OPEN_ERROR;
+ }
+ if (title_length > comment_table_size) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("netmon: comment title size is %u, which is larger than the amount remaining in the comment section (%u)",
+ title_length, comment_table_size);
+ g_hash_table_destroy(comment_table);
+ return WTAP_OPEN_ERROR;
+ }
+
+ comment_rec = g_new0(struct netmonrec_comment, 1);
+ comment_rec->numFramePerComment = pletoh32(&comment_header.numFramePerComment);
+ comment_rec->frameOffset = pletoh32(&comment_header.frameOffset);
+
+ g_hash_table_insert(comment_table, GUINT_TO_POINTER(comment_rec->frameOffset), comment_rec);
+
+ /*
+ * Read in the comment title.
+ *
+ * It is in UTF-16-encoded Unicode, and the title
+ * size is a count of octets, not octet pairs or
+ * Unicode characters.
+ */
+ utf16_str = (guint8*)g_malloc(title_length);
+ if (!wtap_read_bytes(wth->fh, utf16_str, title_length,
+ err, err_info)) {
+ g_hash_table_destroy(comment_table);
+ return WTAP_OPEN_ERROR;
+ }
+ comment_table_size -= title_length;
+
+ /*
+ * Now convert it to UTF-8 for internal use.
+ */
+ comment_rec->title = utf_16_to_utf_8(utf16_str,
+ title_length);
+ g_free(utf16_str);
+
+ if (comment_table_size < 4) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("netmon: corrupt comment section");
+ g_hash_table_destroy(comment_table);
+ return WTAP_OPEN_ERROR;
+ }
+
+ if (!wtap_read_bytes(wth->fh, &desc_length, 4, err, err_info)) {
+ g_hash_table_destroy(comment_table);
+ return WTAP_OPEN_ERROR;
+ }
+ comment_table_size -= 4;
+
+ comment_rec->descLength = pletoh32(&desc_length);
+ if (comment_rec->descLength > 0) {
+ /* Make sure comment size is sane */
+ if (comment_rec->descLength > comment_table_size) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("netmon: comment description size is %u, which is larger than the amount remaining in the comment section (%u)",
+ comment_rec->descLength, comment_table_size);
+ g_hash_table_destroy(comment_table);
+ return WTAP_OPEN_ERROR;
+ }
+
+ comment_rec->description = (guint8*)g_malloc(comment_rec->descLength);
+
+ /* Read the comment description */
+ if (!wtap_read_bytes(wth->fh, comment_rec->description, comment_rec->descLength, err, err_info)) {
+ g_hash_table_destroy(comment_table);
+ return WTAP_OPEN_ERROR;
+ }
+
+ comment_table_size -= comment_rec->descLength;
+ }
+ }
+ netmon->comment_table = comment_table;
+ }
+
+ if ((process_info_table_offset > 0) && (process_info_table_count > 0)) {
+ guint16 version;
+
+ /* Go to the process table offset */
+ if (file_seek(wth->fh, process_info_table_offset, SEEK_SET, err) == -1) {
+ return WTAP_OPEN_ERROR;
+ }
+
+ process_info_table = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, netmonrec_process_info_destroy);
+ if (process_info_table == NULL) {
+ *err = ENOMEM; /* we assume we're out of memory */
+ return WTAP_OPEN_ERROR;
+ }
+
+ /* Read the version (ignored for now) */
+ if (!wtap_read_bytes(wth->fh, &version, 2, err, err_info)) {
+ g_hash_table_destroy(process_info_table);
+ return WTAP_OPEN_ERROR;
+ }
+
+ while (process_info_table_count > 0)
+ {
+ struct netmonrec_process_info* process_info;
+ guint32 tmp32;
+ guint16 tmp16;
+ guint32 path_size;
+ guint8 *utf16_str;
+
+ process_info = g_new0(struct netmonrec_process_info, 1);
+
+ /* Read path */
+ if (!wtap_read_bytes(wth->fh, &tmp32, 4, err, err_info)) {
+ g_free(process_info);
+ g_hash_table_destroy(process_info_table);
+ return WTAP_OPEN_ERROR;
+ }
+
+ path_size = pletoh32(&tmp32);
+ if (path_size > MATH_PROCINFO_PATH_SIZE) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("netmon: Path size for process info record is %u, which is larger than allowed max value (%u)",
+ path_size, MATH_PROCINFO_PATH_SIZE);
+ g_free(process_info);
+ g_hash_table_destroy(process_info_table);
+ return WTAP_OPEN_ERROR;
+ }
+
+ /*
+ * Read in the path string.
+ *
+ * It is in UTF-16-encoded Unicode, and the path
+ * size is a count of octets, not octet pairs or
+ * Unicode characters.
+ */
+ utf16_str = (guint8*)g_malloc(path_size);
+ if (!wtap_read_bytes(wth->fh, utf16_str, path_size,
+ err, err_info)) {
+ g_free(process_info);
+ g_hash_table_destroy(process_info_table);
+ return WTAP_OPEN_ERROR;
+ }
+
+ /*
+ * Now convert it to UTF-8 for internal use.
+ */
+ process_info->path = utf_16_to_utf_8(utf16_str,
+ path_size);
+ g_free(utf16_str);
+
+ /* Read icon (currently not saved) */
+ if (!wtap_read_bytes(wth->fh, &tmp32, 4, err, err_info)) {
+ g_free(process_info);
+ g_hash_table_destroy(process_info_table);
+ return WTAP_OPEN_ERROR;
+ }
+
+ process_info->iconSize = pletoh32(&tmp32);
+
+ /* XXX - skip the icon for now */
+ if (file_seek(wth->fh, process_info->iconSize, SEEK_CUR, err) == -1) {
+ g_free(process_info);
+ g_hash_table_destroy(process_info_table);
+ return WTAP_OPEN_ERROR;
+ }
+ process_info->iconSize = 0;
+
+ if (!wtap_read_bytes(wth->fh, &tmp32, 4, err, err_info)) {
+ g_free(process_info);
+ g_hash_table_destroy(process_info_table);
+ return WTAP_OPEN_ERROR;
+ }
+ process_info->pid = pletoh32(&tmp32);
+
+ /* XXX - Currently index process information by PID */
+ g_hash_table_insert(process_info_table, GUINT_TO_POINTER(process_info->pid), process_info);
+
+ /* Read local port */
+ if (!wtap_read_bytes(wth->fh, &tmp16, 2, err, err_info)) {
+ g_hash_table_destroy(process_info_table);
+ return WTAP_OPEN_ERROR;
+ }
+ process_info->localPort = pletoh16(&tmp16);
+
+ /* Skip padding */
+ if (!wtap_read_bytes(wth->fh, &tmp16, 2, err, err_info)) {
+ g_hash_table_destroy(process_info_table);
+ return WTAP_OPEN_ERROR;
+ }
+
+ /* Read remote port */
+ if (!wtap_read_bytes(wth->fh, &tmp16, 2, err, err_info)) {
+ g_hash_table_destroy(process_info_table);
+ return WTAP_OPEN_ERROR;
+ }
+ process_info->remotePort = pletoh16(&tmp16);
+
+ /* Skip padding */
+ if (!wtap_read_bytes(wth->fh, &tmp16, 2, err, err_info)) {
+ g_hash_table_destroy(process_info_table);
+ return WTAP_OPEN_ERROR;
+ }
+
+ /* Determine IP version */
+ if (!wtap_read_bytes(wth->fh, &tmp32, 4, err, err_info)) {
+ g_hash_table_destroy(process_info_table);
+ return WTAP_OPEN_ERROR;
+ }
+ process_info->isIPv6 = ((pletoh32(&tmp32) == 0) ? FALSE : TRUE);
+
+ if (process_info->isIPv6) {
+ if (!wtap_read_bytes(wth->fh, &process_info->localAddr.ipv6, 16, err, err_info)) {
+ g_hash_table_destroy(process_info_table);
+ return WTAP_OPEN_ERROR;
+ }
+ if (!wtap_read_bytes(wth->fh, &process_info->remoteAddr.ipv6, 16, err, err_info)) {
+ g_hash_table_destroy(process_info_table);
+ return WTAP_OPEN_ERROR;
+ }
+ } else {
+ guint8 ipbuffer[16];
+ if (!wtap_read_bytes(wth->fh, ipbuffer, 16, err, err_info)) {
+ g_hash_table_destroy(process_info_table);
+ return WTAP_OPEN_ERROR;
+ }
+ process_info->localAddr.ipv4 = pletoh32(ipbuffer);
+
+ if (!wtap_read_bytes(wth->fh, ipbuffer, 16, err, err_info)) {
+ g_hash_table_destroy(process_info_table);
+ return WTAP_OPEN_ERROR;
+ }
+ process_info->remoteAddr.ipv4 = pletoh32(ipbuffer);
+ }
+
+ process_info_table_count--;
+ }
+
+ netmon->process_info_table = process_info_table;
+ }
+
+#if G_BYTE_ORDER == G_BIG_ENDIAN
+ /*
+ * OK, now byte-swap the frame table.
+ */
+ for (i = 0; i < frame_table_size; i++)
+ frame_table[i] = pletoh32(&frame_table[i]);
+#endif
+
+ /* Set up to start reading at the first frame. */
+ netmon->current_frame = 0;
+ switch (netmon->version_major) {
+
+ case 1:
+ /*
+ * Version 1.x of the file format supports
+ * millisecond precision.
+ */
+ wth->file_tsprec = WTAP_TSPREC_MSEC;
+ break;
+
+ case 2:
+ /*
+ * Versions 2.0 through 2.2 support microsecond
+ * precision; version 2.3 supports 100-nanosecond
+ * precision (2.3 was the last version).
+ */
+ if (netmon->version_minor >= 3)
+ wth->file_tsprec = WTAP_TSPREC_100_NSEC;
+ else
+ wth->file_tsprec = WTAP_TSPREC_USEC;
+ break;
+ }
+ return WTAP_OPEN_MINE;
+}
+
+static void
+netmon_set_pseudo_header_info(wtap_rec *rec, Buffer *buf)
+{
+ switch (rec->rec_header.packet_header.pkt_encap) {
+
+ case WTAP_ENCAP_ATM_PDUS:
+ /*
+ * Attempt to guess from the packet data, the VPI, and
+ * the VCI information about the type of traffic.
+ */
+ atm_guess_traffic_type(rec, ws_buffer_start_ptr(buf));
+ break;
+
+ case WTAP_ENCAP_ETHERNET:
+ /*
+ * We assume there's no FCS in this frame.
+ */
+ rec->rec_header.packet_header.pseudo_header.eth.fcs_len = 0;
+ break;
+
+ case WTAP_ENCAP_IEEE_802_11_NETMON:
+ /*
+ * The 802.11 metadata at the beginnning of the frame data
+ * is processed by a dissector, which fills in a pseudo-
+ * header and passes it to the 802.11 radio dissector,
+ * just as is done with other 802.11 radio metadata headers
+ * that are part of the packet data, such as radiotap.
+ */
+ break;
+ }
+}
+
+typedef enum {
+ SUCCESS,
+ FAILURE,
+ RETRY
+} process_record_retval;
+
+static process_record_retval
+netmon_process_record(wtap *wth, FILE_T fh, wtap_rec *rec,
+ Buffer *buf, int *err, gchar **err_info)
+{
+ netmon_t *netmon = (netmon_t *)wth->priv;
+ int hdr_size = 0;
+ union {
+ struct netmonrec_1_x_hdr hdr_1_x;
+ struct netmonrec_2_x_hdr hdr_2_x;
+ } hdr;
+ gint64 delta = 0; /* signed - frame times can be before the nominal start */
+ gint64 t;
+ time_t secs;
+ int nsecs;
+ guint32 packet_size = 0;
+ guint32 orig_size = 0;
+ int trlr_size;
+ union {
+ struct netmonrec_2_1_trlr trlr_2_1;
+ struct netmonrec_2_2_trlr trlr_2_2;
+ struct netmonrec_2_3_trlr trlr_2_3;
+ } trlr;
+ guint16 network;
+ int pkt_encap;
+ struct netmonrec_comment* comment_rec = NULL;
+
+ /* Read record header. */
+ switch (netmon->version_major) {
+
+ case 1:
+ hdr_size = sizeof (struct netmonrec_1_x_hdr);
+ break;
+
+ case 2:
+ hdr_size = sizeof (struct netmonrec_2_x_hdr);
+ break;
+ }
+ if (!wtap_read_bytes_or_eof(fh, &hdr, hdr_size, err, err_info))
+ return FAILURE;
+
+ switch (netmon->version_major) {
+
+ case 1:
+ orig_size = pletoh16(&hdr.hdr_1_x.orig_len);
+ packet_size = pletoh16(&hdr.hdr_1_x.incl_len);
+ break;
+
+ case 2:
+ orig_size = pletoh32(&hdr.hdr_2_x.orig_len);
+ packet_size = pletoh32(&hdr.hdr_2_x.incl_len);
+ break;
+ }
+ if (packet_size > WTAP_MAX_PACKET_SIZE_STANDARD) {
+ /*
+ * Probably a corrupt capture file; don't blow up trying
+ * to allocate space for an immensely-large packet.
+ */
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("netmon: File has %u-byte packet, bigger than maximum of %u",
+ packet_size, WTAP_MAX_PACKET_SIZE_STANDARD);
+ return FAILURE;
+ }
+
+ rec->rec_type = REC_TYPE_PACKET;
+ rec->block = wtap_block_create(WTAP_BLOCK_PACKET);
+
+ /*
+ * If this is an ATM packet, the first
+ * "sizeof (struct netmon_atm_hdr)" bytes have destination and
+ * source addresses (6 bytes - MAC addresses of some sort?)
+ * and the VPI and VCI; read them and generate the pseudo-header
+ * from them.
+ */
+ switch (wth->file_encap) {
+
+ case WTAP_ENCAP_ATM_PDUS:
+ if (packet_size < sizeof (struct netmon_atm_hdr)) {
+ /*
+ * Uh-oh, the packet isn't big enough to even
+ * have a pseudo-header.
+ */
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("netmon: ATM file has a %u-byte packet, too small to have even an ATM pseudo-header",
+ packet_size);
+ return FAILURE;
+ }
+ if (!netmon_read_atm_pseudoheader(fh, &rec->rec_header.packet_header.pseudo_header,
+ err, err_info))
+ return FAILURE; /* Read error */
+
+ /*
+ * Don't count the pseudo-header as part of the packet.
+ */
+ orig_size -= (guint)sizeof (struct netmon_atm_hdr);
+ packet_size -= (guint)sizeof (struct netmon_atm_hdr);
+ break;
+
+ default:
+ break;
+ }
+
+ switch (netmon->version_major) {
+
+ case 1:
+ /*
+ * According to Paul Long, this offset is unsigned.
+ * It's 32 bits, so the maximum value will fit in
+ * a gint64 such as delta, even after multiplying
+ * it by 1000000.
+ *
+ * pletoh32() returns a guint32; we cast it to gint64
+ * before multiplying, so that the product doesn't
+ * overflow a guint32.
+ */
+ delta = ((gint64)pletoh32(&hdr.hdr_1_x.ts_delta))*1000000;
+ break;
+
+ case 2:
+ /*
+ * OK, this is weird. Microsoft's documentation
+ * says this is in microseconds and is a 64-bit
+ * unsigned number, but it can be negative; they
+ * say what appears to amount to "treat it as an
+ * unsigned number, multiply it by 10, and then
+ * interpret the resulting 64-bit quantity as a
+ * signed number". That operation can turn a
+ * value with the uppermost bit 0 to a value with
+ * the uppermost bit 1, hence turning a large
+ * positive number-of-microseconds into a small
+ * negative number-of-100-nanosecond-increments.
+ */
+ delta = pletoh64(&hdr.hdr_2_x.ts_delta)*10;
+
+ /*
+ * OK, it's now a signed value in 100-nanosecond
+ * units. Now convert it to nanosecond units.
+ */
+ delta *= 100;
+ break;
+ }
+ secs = 0;
+ t = netmon->start_nsecs + delta;
+ while (t < 0) {
+ /*
+ * Propagate a borrow into the seconds.
+ * The seconds is a time_t, and can be < 0
+ * (unlikely, as Windows didn't exist before
+ * January 1, 1970, 00:00:00 UTC), while the
+ * nanoseconds should be positive, as in
+ * "nanoseconds since the instant of time
+ * represented by the seconds".
+ *
+ * We do not want t to be negative, as, according
+ * to the C90 standard, "if either operand [of /
+ * or %] is negative, whether the result of the
+ * / operator is the largest integer less than or
+ * equal to the algebraic quotient or the smallest
+ * greater than or equal to the algebraic quotient
+ * is implementation-defined, as is the sign of
+ * the result of the % operator", and we want
+ * the result of the division and remainder
+ * operations to be the same on all platforms.
+ */
+ t += 1000000000;
+ secs--;
+ }
+ secs += (time_t)(t/1000000000);
+ nsecs = (int)(t%1000000000);
+ rec->presence_flags = WTAP_HAS_TS|WTAP_HAS_CAP_LEN;
+ rec->ts.secs = netmon->start_secs + secs;
+ rec->ts.nsecs = nsecs;
+ rec->rec_header.packet_header.caplen = packet_size;
+ rec->rec_header.packet_header.len = orig_size;
+
+ /*
+ * Read the packet data.
+ */
+ if (!wtap_read_packet_bytes(fh, buf, rec->rec_header.packet_header.caplen, err, err_info))
+ return FAILURE;
+
+ /*
+ * For version 2.1 and later, there's additional information
+ * after the frame data.
+ */
+ if (netmon->version_major == 2 && netmon->version_minor >= 1) {
+ switch (netmon->version_minor) {
+
+ case 1:
+ trlr_size = (int)sizeof (struct netmonrec_2_1_trlr);
+ break;
+
+ case 2:
+ trlr_size = (int)sizeof (struct netmonrec_2_2_trlr);
+ break;
+
+ default:
+ trlr_size = (int)sizeof (struct netmonrec_2_3_trlr);
+ break;
+ }
+
+ if (!wtap_read_bytes(fh, &trlr, trlr_size, err, err_info))
+ return FAILURE;
+
+ network = pletoh16(trlr.trlr_2_1.network);
+ if ((network >= 0xE080) && (network <= 0xE08A)) {
+ /* These values "violate" the LINKTYPE_ media type values
+ * in Microsoft Analyzer and are considered a MAExportedMediaType,
+ * so they need their own WTAP_ types
+ */
+ switch (network)
+ {
+ case 0xE080: // "WiFi Message"
+ pkt_encap = WTAP_ENCAP_IEEE_802_11;
+ break;
+ case 0xE081: // "Ndis Etw WiFi Channel Message"
+ case 0xE082: // "Fiddler Netmon Message"
+ case 0xE089: // "Pef Ndis Msg";
+ case 0xE08A: // "Pef Ndis Wifi Meta Msg";
+ *err = WTAP_ERR_UNSUPPORTED;
+ *err_info = ws_strdup_printf("netmon: network type %u unknown or unsupported", network);
+ return FAILURE;
+ case 0xE083:
+ pkt_encap = WTAP_ENCAP_MA_WFP_CAPTURE_V4;
+ break;
+ case 0xE084:
+ pkt_encap = WTAP_ENCAP_MA_WFP_CAPTURE_V6;
+ break;
+ case 0xE085:
+ pkt_encap = WTAP_ENCAP_MA_WFP_CAPTURE_2V4;
+ break;
+ case 0xE086:
+ pkt_encap = WTAP_ENCAP_MA_WFP_CAPTURE_2V6;
+ break;
+ case 0xE087:
+ pkt_encap = WTAP_ENCAP_MA_WFP_CAPTURE_AUTH_V4;
+ break;
+ case 0xE088:
+ pkt_encap = WTAP_ENCAP_MA_WFP_CAPTURE_AUTH_V6;
+ break;
+ default:
+ pkt_encap = WTAP_ENCAP_UNKNOWN;
+ break;
+ }
+ } else if ((network & 0xF000) == NETMON_NET_PCAP_BASE) {
+ /*
+ * Converted pcap file - the LINKTYPE_ value
+ * is the network value with 0xF000 masked off.
+ */
+ network &= 0x0FFF;
+ pkt_encap = wtap_pcap_encap_to_wtap_encap(network);
+ if (pkt_encap == WTAP_ENCAP_UNKNOWN) {
+ *err = WTAP_ERR_UNSUPPORTED;
+ *err_info = ws_strdup_printf("netmon: converted pcap network type %u unknown or unsupported",
+ network);
+ return FAILURE;
+ }
+ } else if (network < NUM_NETMON_ENCAPS) {
+ /*
+ * Regular NetMon encapsulation.
+ */
+ pkt_encap = netmon_encap[network];
+ if (pkt_encap == WTAP_ENCAP_UNKNOWN) {
+ *err = WTAP_ERR_UNSUPPORTED;
+ *err_info = ws_strdup_printf("netmon: network type %u unknown or unsupported",
+ network);
+ return FAILURE;
+ }
+ } else {
+ /*
+ * Special packet type for metadata.
+ */
+ switch (network) {
+
+ case NETMON_NET_NETEVENT:
+ /*
+ * Event Tracing event.
+ *
+ * https://docs.microsoft.com/en-us/windows/win32/api/evntcons/ns-evntcons-event_header
+ */
+ pkt_encap = WTAP_ENCAP_NETMON_NET_NETEVENT;
+ break;
+
+ case NETMON_NET_NETWORK_INFO_EX:
+ /*
+ * List of adapters on which the capture
+ * was done.
+ * XXX - this could be translated into pcapng
+ * blocks but for now, just treat as a frame.
+ */
+ pkt_encap = WTAP_ENCAP_NETMON_NETWORK_INFO_EX;
+ break;
+
+ case NETMON_NET_PAYLOAD_HEADER:
+ /*
+ * Header for a fake frame constructed
+ * by reassembly.
+ */
+ return RETRY;
+
+ case NETMON_NET_NETWORK_INFO:
+ /*
+ * List of adapters on which the capture
+ * was done.
+ */
+ return RETRY;
+
+ case NETMON_NET_DNS_CACHE:
+ /*
+ * List of resolved IP addresses.
+ */
+ return RETRY;
+
+ case NETMON_NET_NETMON_FILTER:
+ /*
+ * NetMon capture or display filter
+ * string.
+ */
+ pkt_encap = WTAP_ENCAP_NETMON_NET_FILTER;
+ break;
+
+ default:
+ *err = WTAP_ERR_UNSUPPORTED;
+ *err_info = ws_strdup_printf("netmon: network type %u unknown or unsupported",
+ network);
+ return FAILURE;
+ }
+ }
+
+ rec->rec_header.packet_header.pkt_encap = pkt_encap;
+ if (netmon->version_minor >= 3) {
+ /*
+ * This is a 2.3 or later file. That format
+ * contains a UTC per-packet time stamp; use
+ * that instead of the start time and offset.
+ */
+ guint64 d;
+
+ d = pletoh64(trlr.trlr_2_3.utc_timestamp);
+
+ /*
+ * Get the time as seconds and nanoseconds.
+ * and overwrite the time stamp obtained
+ * from the record header.
+ */
+ if (!filetime_to_nstime(&rec->ts, d)) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("netmon: time stamp outside supported range");
+ return FAILURE;
+ }
+ }
+ }
+
+ netmon_set_pseudo_header_info(rec, buf);
+
+ /* If any header specific information is present, set it as pseudo header data
+ * and set the encapsulation type, so it can be handled to the netmon_header
+ * dissector for further processing
+ */
+ if (netmon->comment_table != NULL) {
+ comment_rec = (struct netmonrec_comment*)g_hash_table_lookup(netmon->comment_table, GUINT_TO_POINTER(netmon->frame_table[netmon->current_frame-1]));
+ }
+
+ if (comment_rec != NULL) {
+ union wtap_pseudo_header temp_header;
+
+ /* These are the current encapsulation types that NetMon uses.
+ * Save them off so they can be copied to the NetMon pseudoheader
+ */
+ switch (rec->rec_header.packet_header.pkt_encap)
+ {
+ case WTAP_ENCAP_ATM_PDUS:
+ memcpy(&temp_header.atm, &rec->rec_header.packet_header.pseudo_header.atm, sizeof(temp_header.atm));
+ break;
+ case WTAP_ENCAP_ETHERNET:
+ memcpy(&temp_header.eth, &rec->rec_header.packet_header.pseudo_header.eth, sizeof(temp_header.eth));
+ break;
+ case WTAP_ENCAP_IEEE_802_11_NETMON:
+ memcpy(&temp_header.ieee_802_11, &rec->rec_header.packet_header.pseudo_header.ieee_802_11, sizeof(temp_header.ieee_802_11));
+ break;
+ }
+ memset(&rec->rec_header.packet_header.pseudo_header.netmon, 0, sizeof(rec->rec_header.packet_header.pseudo_header.netmon));
+
+ /* Save the current encapsulation type to the NetMon pseudoheader */
+ rec->rec_header.packet_header.pseudo_header.netmon.sub_encap = rec->rec_header.packet_header.pkt_encap;
+
+ /* Copy the comment data */
+ rec->rec_header.packet_header.pseudo_header.netmon.title = comment_rec->title;
+ rec->rec_header.packet_header.pseudo_header.netmon.descLength = comment_rec->descLength;
+ rec->rec_header.packet_header.pseudo_header.netmon.description = comment_rec->description;
+
+ /* Copy the saved pseudoheaders to the netmon pseudoheader structure */
+ switch (rec->rec_header.packet_header.pkt_encap)
+ {
+ case WTAP_ENCAP_ATM_PDUS:
+ memcpy(&rec->rec_header.packet_header.pseudo_header.netmon.subheader.atm, &temp_header.atm, sizeof(temp_header.atm));
+ break;
+ case WTAP_ENCAP_ETHERNET:
+ memcpy(&rec->rec_header.packet_header.pseudo_header.netmon.subheader.eth, &temp_header.eth, sizeof(temp_header.eth));
+ break;
+ case WTAP_ENCAP_IEEE_802_11_NETMON:
+ memcpy(&rec->rec_header.packet_header.pseudo_header.netmon.subheader.ieee_802_11, &temp_header.ieee_802_11, sizeof(temp_header.ieee_802_11));
+ break;
+ }
+
+ /* Encapsulation type is now something that can be passed to netmon_header dissector */
+ rec->rec_header.packet_header.pkt_encap = WTAP_ENCAP_NETMON_HEADER;
+ }
+
+ return SUCCESS;
+}
+
+/* Read the next packet */
+static gboolean netmon_read(wtap *wth, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info, gint64 *data_offset)
+{
+ netmon_t *netmon = (netmon_t *)wth->priv;
+ gint64 rec_offset;
+
+ for (;;) {
+ /* Have we reached the end of the packet data? */
+ if (netmon->current_frame >= netmon->frame_table_size) {
+ *err = 0; /* it's just an EOF, not an error */
+ return FALSE;
+ }
+
+ /* Seek to the beginning of the current record, if we're
+ not there already (seeking to the current position
+ may still cause a seek and a read of the underlying file,
+ so we don't want to do it unconditionally).
+
+ Yes, the current record could be before the previous
+ record. At least some captures put the trailer record
+ with statistics as the first physical record in the
+ file, but set the frame table up so it's the last
+ record in sequence. */
+ rec_offset = netmon->frame_table[netmon->current_frame];
+ if (file_tell(wth->fh) != rec_offset) {
+ if (file_seek(wth->fh, rec_offset, SEEK_SET, err) == -1)
+ return FALSE;
+ }
+ netmon->current_frame++;
+
+ *data_offset = file_tell(wth->fh);
+
+ switch (netmon_process_record(wth, wth->fh, rec, buf, err,
+ err_info)) {
+
+ case RETRY:
+ continue;
+
+ case SUCCESS:
+ return TRUE;
+
+ case FAILURE:
+ return FALSE;
+ }
+ }
+}
+
+static gboolean
+netmon_seek_read(wtap *wth, gint64 seek_off,
+ wtap_rec *rec, Buffer *buf, int *err, gchar **err_info)
+{
+ if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
+ return FALSE;
+
+ switch (netmon_process_record(wth, wth->random_fh, rec, buf, err,
+ err_info)) {
+
+ default:
+ /*
+ * This should not happen.
+ */
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("netmon: saw metadata in netmon_seek_read");
+ return FALSE;
+
+ case SUCCESS:
+ return TRUE;
+
+ case FAILURE:
+ return FALSE;
+ }
+}
+
+static gboolean
+netmon_read_atm_pseudoheader(FILE_T fh, union wtap_pseudo_header *pseudo_header,
+ int *err, gchar **err_info)
+{
+ struct netmon_atm_hdr atm_phdr;
+ guint16 vpi, vci;
+
+ if (!wtap_read_bytes(fh, &atm_phdr, sizeof (struct netmon_atm_hdr),
+ err, err_info))
+ return FALSE;
+
+ vpi = g_ntohs(atm_phdr.vpi);
+ vci = g_ntohs(atm_phdr.vci);
+
+ pseudo_header->atm.vpi = vpi;
+ pseudo_header->atm.vci = vci;
+
+ /* We don't have this information */
+ pseudo_header->atm.flags = 0;
+ pseudo_header->atm.channel = 0;
+ pseudo_header->atm.cells = 0;
+ pseudo_header->atm.aal5t_u2u = 0;
+ pseudo_header->atm.aal5t_len = 0;
+ pseudo_header->atm.aal5t_chksum = 0;
+
+ return TRUE;
+}
+
+/* Throw away the frame table used by the sequential I/O stream. */
+static void
+netmon_close(wtap *wth)
+{
+ netmon_t *netmon = (netmon_t *)wth->priv;
+
+ if (netmon->frame_table != NULL) {
+ g_free(netmon->frame_table);
+ netmon->frame_table = NULL;
+ }
+
+ if (netmon->comment_table != NULL) {
+ g_hash_table_destroy(netmon->comment_table);
+ netmon->comment_table = NULL;
+ }
+
+ if (netmon->process_info_table != NULL) {
+ g_hash_table_destroy(netmon->process_info_table);
+ netmon->process_info_table = NULL;
+ }
+}
+
+typedef struct {
+ gboolean is_v2;
+ gboolean got_first_record_time;
+ nstime_t first_record_time;
+ guint32 frame_table_offset;
+ guint32 *frame_table;
+ guint frame_table_index;
+ guint frame_table_size;
+ gboolean no_more_room; /* TRUE if no more records can be written */
+} netmon_dump_t;
+
+static const int wtap_encap[] = {
+ -1, /* WTAP_ENCAP_UNKNOWN -> unsupported */
+ 1, /* WTAP_ENCAP_ETHERNET -> NDIS Ethernet */
+ 2, /* WTAP_ENCAP_TOKEN_RING -> NDIS Token Ring */
+ -1, /* WTAP_ENCAP_SLIP -> unsupported */
+ -1, /* WTAP_ENCAP_PPP -> unsupported */
+ 3, /* WTAP_ENCAP_FDDI -> NDIS FDDI */
+ 3, /* WTAP_ENCAP_FDDI_BITSWAPPED -> NDIS FDDI */
+ -1, /* WTAP_ENCAP_RAW_IP -> unsupported */
+ -1, /* WTAP_ENCAP_ARCNET -> unsupported */
+ -1, /* WTAP_ENCAP_ARCNET_LINUX -> unsupported */
+ -1, /* WTAP_ENCAP_ATM_RFC1483 -> unsupported */
+ -1, /* WTAP_ENCAP_LINUX_ATM_CLIP -> unsupported */
+ -1, /* WTAP_ENCAP_LAPB -> unsupported*/
+ 4, /* WTAP_ENCAP_ATM_PDUS -> NDIS WAN (*NOT* ATM!) */
+};
+#define NUM_WTAP_ENCAPS (sizeof wtap_encap / sizeof wtap_encap[0])
+
+/* Returns 0 if we could write the specified encapsulation type,
+ an error indication otherwise. */
+static int netmon_dump_can_write_encap_1_x(int encap)
+{
+ /*
+ * Per-packet encapsulations are *not* supported in NetMon 1.x
+ * format.
+ */
+ if (encap < 0 || (unsigned) encap >= NUM_WTAP_ENCAPS || wtap_encap[encap] == -1)
+ return WTAP_ERR_UNWRITABLE_ENCAP;
+
+ return 0;
+}
+
+static int netmon_dump_can_write_encap_2_x(int encap)
+{
+ /*
+ * Per-packet encapsulations are supported in NetMon 2.1
+ * format.
+ */
+ if (encap == WTAP_ENCAP_PER_PACKET)
+ return 0;
+
+ if (encap < 0 || (unsigned) encap >= NUM_WTAP_ENCAPS || wtap_encap[encap] == -1)
+ return WTAP_ERR_UNWRITABLE_ENCAP;
+
+ return 0;
+}
+
+/* Returns TRUE on success, FALSE on failure; sets "*err" to an error code on
+ failure */
+static gboolean netmon_dump_open(wtap_dumper *wdh, gboolean is_v2,
+ int *err, gchar **err_info _U_)
+{
+ netmon_dump_t *netmon;
+
+ /* We can't fill in all the fields in the file header, as we
+ haven't yet written any packets. As we'll have to rewrite
+ the header when we've written out all the packets, we just
+ skip over the header for now. */
+ if (wtap_dump_file_seek(wdh, CAPTUREFILE_HEADER_SIZE, SEEK_SET, err) == -1)
+ return FALSE;
+
+ wdh->bytes_dumped = CAPTUREFILE_HEADER_SIZE;
+ wdh->subtype_write = netmon_dump;
+ wdh->subtype_finish = netmon_dump_finish;
+
+ netmon = g_new(netmon_dump_t, 1);
+ wdh->priv = (void *)netmon;
+ netmon->is_v2 = is_v2;
+ netmon->frame_table_offset = CAPTUREFILE_HEADER_SIZE;
+ netmon->got_first_record_time = FALSE;
+ netmon->frame_table = NULL;
+ netmon->frame_table_index = 0;
+ netmon->frame_table_size = 0;
+ netmon->no_more_room = FALSE;
+
+ return TRUE;
+}
+
+static gboolean netmon_dump_open_1_x(wtap_dumper *wdh, int *err, gchar **err_info _U_)
+{
+ return netmon_dump_open(wdh, FALSE, err, err_info);
+}
+
+static gboolean netmon_dump_open_2_x(wtap_dumper *wdh, int *err, gchar **err_info _U_)
+{
+ return netmon_dump_open(wdh, TRUE, err, err_info);
+}
+
+/* Write a record for a packet to a dump file.
+ Returns TRUE on success, FALSE on failure. */
+static gboolean netmon_dump(wtap_dumper *wdh, const wtap_rec *rec,
+ const guint8 *pd, int *err, gchar **err_info _U_)
+{
+ const union wtap_pseudo_header *pseudo_header = &rec->rec_header.packet_header.pseudo_header;
+ netmon_dump_t *netmon = (netmon_dump_t *)wdh->priv;
+ struct netmonrec_1_x_hdr rec_1_x_hdr;
+ struct netmonrec_2_x_hdr rec_2_x_hdr;
+ void *hdrp;
+ size_t rec_size;
+ struct netmonrec_2_1_trlr rec_2_x_trlr;
+ size_t hdr_size;
+ struct netmon_atm_hdr atm_hdr;
+ int atm_hdrsize;
+ gint64 secs;
+ gint32 nsecs;
+
+ /* We can only write packet records. */
+ if (rec->rec_type != REC_TYPE_PACKET) {
+ *err = WTAP_ERR_UNWRITABLE_REC_TYPE;
+ return FALSE;
+ }
+
+ if (netmon->is_v2) {
+ /* Don't write anything we're not willing to read. */
+ if (rec->rec_header.packet_header.caplen > WTAP_MAX_PACKET_SIZE_STANDARD) {
+ *err = WTAP_ERR_PACKET_TOO_LARGE;
+ return FALSE;
+ }
+ } else {
+ /*
+ * Make sure this packet doesn't have a link-layer type that
+ * differs from the one for the file.
+ */
+ if (wdh->file_encap != rec->rec_header.packet_header.pkt_encap) {
+ *err = WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
+ return FALSE;
+ }
+
+ /*
+ * The length fields are 16-bit, so there's a hard limit
+ * of 65535.
+ */
+ if (rec->rec_header.packet_header.caplen > 65535) {
+ *err = WTAP_ERR_PACKET_TOO_LARGE;
+ return FALSE;
+ }
+ }
+
+ if (wdh->file_encap == WTAP_ENCAP_PER_PACKET) {
+ /*
+ * Is this network type supported?
+ */
+ if (rec->rec_header.packet_header.pkt_encap < 0 ||
+ (unsigned) rec->rec_header.packet_header.pkt_encap >= NUM_WTAP_ENCAPS ||
+ wtap_encap[rec->rec_header.packet_header.pkt_encap] == -1) {
+ /*
+ * No. Fail.
+ */
+ *err = WTAP_ERR_UNWRITABLE_ENCAP;
+ return FALSE;
+ }
+
+ /*
+ * Fill in the trailer with the network type.
+ */
+ phtoles(rec_2_x_trlr.network, wtap_encap[rec->rec_header.packet_header.pkt_encap]);
+ }
+
+ /*
+ * Will the file offset of this frame fit in a 32-bit unsigned
+ * integer?
+ */
+ if (netmon->no_more_room) {
+ /*
+ * No, so the file is too big for NetMon format to
+ * handle.
+ */
+ *err = EFBIG;
+ return FALSE;
+ }
+
+ /*
+ * NetMon files have a capture start time in the file header,
+ * and have times relative to that in the packet headers;
+ * pick the time of the first packet as the capture start
+ * time.
+ *
+ * That time has millisecond resolution, so chop any
+ * sub-millisecond part of the time stamp off.
+ */
+ if (!netmon->got_first_record_time) {
+ netmon->first_record_time.secs = rec->ts.secs;
+ netmon->first_record_time.nsecs =
+ (rec->ts.nsecs/1000000)*1000000;
+ netmon->got_first_record_time = TRUE;
+ }
+
+ if (wdh->file_encap == WTAP_ENCAP_ATM_PDUS)
+ atm_hdrsize = sizeof (struct netmon_atm_hdr);
+ else
+ atm_hdrsize = 0;
+ secs = (gint64)(rec->ts.secs - netmon->first_record_time.secs);
+ nsecs = rec->ts.nsecs - netmon->first_record_time.nsecs;
+ while (nsecs < 0) {
+ /*
+ * Propagate a borrow into the seconds.
+ * The seconds is a time_t, and can be < 0
+ * (unlikely, as neither UN*X nor DOS
+ * nor the original Mac System existed
+ * before January 1, 1970, 00:00:00 UTC),
+ * while the nanoseconds should be positive,
+ * as in "nanoseconds since the instant of time
+ * represented by the seconds".
+ *
+ * We do not want t to be negative, as, according
+ * to the C90 standard, "if either operand [of /
+ * or %] is negative, whether the result of the
+ * / operator is the largest integer less than or
+ * equal to the algebraic quotient or the smallest
+ * greater than or equal to the algebraic quotient
+ * is implementation-defined, as is the sign of
+ * the result of the % operator", and we want
+ * the result of the division and remainder
+ * operations to be the same on all platforms.
+ */
+ nsecs += 1000000000;
+ secs--;
+ }
+ if (netmon->is_v2) {
+ rec_2_x_hdr.ts_delta = GUINT64_TO_LE(secs*1000000 + (nsecs + 500)/1000);
+ rec_2_x_hdr.orig_len = GUINT32_TO_LE(rec->rec_header.packet_header.len + atm_hdrsize);
+ rec_2_x_hdr.incl_len = GUINT32_TO_LE(rec->rec_header.packet_header.caplen + atm_hdrsize);
+ hdrp = &rec_2_x_hdr;
+ hdr_size = sizeof rec_2_x_hdr;
+ } else {
+ rec_1_x_hdr.ts_delta = GUINT32_TO_LE(secs*1000 + (nsecs + 500000)/1000000);
+ rec_1_x_hdr.orig_len = GUINT16_TO_LE(rec->rec_header.packet_header.len + atm_hdrsize);
+ rec_1_x_hdr.incl_len = GUINT16_TO_LE(rec->rec_header.packet_header.caplen + atm_hdrsize);
+ hdrp = &rec_1_x_hdr;
+ hdr_size = sizeof rec_1_x_hdr;
+ }
+
+ /*
+ * Keep track of the record size, as we need to update
+ * the current file offset.
+ */
+ rec_size = 0;
+
+ if (!wtap_dump_file_write(wdh, hdrp, hdr_size, err))
+ return FALSE;
+ rec_size += hdr_size;
+
+ if (wdh->file_encap == WTAP_ENCAP_ATM_PDUS) {
+ /*
+ * Write the ATM header.
+ * We supply all-zero destination and source addresses.
+ */
+ memset(&atm_hdr.dest, 0, sizeof atm_hdr.dest);
+ memset(&atm_hdr.src, 0, sizeof atm_hdr.src);
+ atm_hdr.vpi = g_htons(pseudo_header->atm.vpi);
+ atm_hdr.vci = g_htons(pseudo_header->atm.vci);
+ if (!wtap_dump_file_write(wdh, &atm_hdr, sizeof atm_hdr, err))
+ return FALSE;
+ rec_size += sizeof atm_hdr;
+ }
+
+ if (!wtap_dump_file_write(wdh, pd, rec->rec_header.packet_header.caplen, err))
+ return FALSE;
+ rec_size += rec->rec_header.packet_header.caplen;
+
+ if (wdh->file_encap == WTAP_ENCAP_PER_PACKET) {
+ /*
+ * Write out the trailer.
+ */
+ if (!wtap_dump_file_write(wdh, &rec_2_x_trlr,
+ sizeof rec_2_x_trlr, err))
+ return FALSE;
+ rec_size += sizeof rec_2_x_trlr;
+ }
+
+ /*
+ * Stash the file offset of this frame.
+ */
+ if (netmon->frame_table_size == 0) {
+ /*
+ * Haven't yet allocated the buffer for the frame table.
+ */
+ netmon->frame_table = (guint32 *)g_malloc(1024 * sizeof *netmon->frame_table);
+ netmon->frame_table_size = 1024;
+ } else {
+ /*
+ * We've allocated it; are we at the end?
+ */
+ if (netmon->frame_table_index >= netmon->frame_table_size) {
+ /*
+ * Yes - double the size of the frame table.
+ */
+ netmon->frame_table_size *= 2;
+ netmon->frame_table = (guint32 *)g_realloc(netmon->frame_table,
+ netmon->frame_table_size * sizeof *netmon->frame_table);
+ }
+ }
+
+ netmon->frame_table[netmon->frame_table_index] =
+ GUINT32_TO_LE(netmon->frame_table_offset);
+
+ /*
+ * Is this the last record we can write?
+ * I.e., will the frame table offset of the next record not fit
+ * in a 32-bit frame table offset entry?
+ *
+ * (We don't bother checking whether the number of frames
+ * will fit in a 32-bit value, as, even if each record were
+ * 1 byte, if there were more than 2^32-1 packets, the frame
+ * table offset of at least one of those packets will be >
+ * 2^32 - 1.)
+ *
+ * Note: this also catches the unlikely possibility that
+ * the record itself is > 2^32 - 1 bytes long.
+ */
+ if ((guint64)netmon->frame_table_offset + rec_size > G_MAXUINT32) {
+ /*
+ * Yup, too big.
+ */
+ netmon->no_more_room = TRUE;
+ }
+ netmon->frame_table_index++;
+ netmon->frame_table_offset += (guint32) rec_size;
+
+ return TRUE;
+}
+
+/* Finish writing to a dump file.
+ Returns TRUE on success, FALSE on failure. */
+static gboolean netmon_dump_finish(wtap_dumper *wdh, int *err,
+ gchar **err_info _U_)
+{
+ netmon_dump_t *netmon = (netmon_dump_t *)wdh->priv;
+ size_t n_to_write;
+ struct netmon_hdr file_hdr;
+ const char *magicp;
+ size_t magic_size;
+ struct tm *tm;
+ gint64 saved_bytes_dumped;
+
+ /* Write out the frame table. "netmon->frame_table_index" is
+ the number of entries we've put into it. */
+ n_to_write = netmon->frame_table_index * sizeof *netmon->frame_table;
+ if (!wtap_dump_file_write(wdh, netmon->frame_table, n_to_write, err))
+ return FALSE;
+
+ /* Now go fix up the file header. */
+ if (wtap_dump_file_seek(wdh, 0, SEEK_SET, err) == -1)
+ return FALSE;
+ /* Save bytes_dumped since following calls to wtap_dump_file_write()
+ * will still (mistakenly) increase it.
+ */
+ saved_bytes_dumped = wdh->bytes_dumped;
+ memset(&file_hdr, '\0', sizeof file_hdr);
+ if (netmon->is_v2) {
+ magicp = netmon_2_x_magic;
+ magic_size = sizeof netmon_2_x_magic;
+ /*
+ * NetMon file version, for 2.x, is 2.0;
+ * for 3.0, it's 2.1.
+ *
+ * If the file encapsulation is WTAP_ENCAP_PER_PACKET,
+ * we need version 2.1.
+ *
+ * XXX - version 2.3 supports UTC time stamps; when
+ * should we use it? According to the file format
+ * documentation, NetMon 3.3 "cannot properly
+ * interpret" the UTC timestamp information; does
+ * that mean it ignores it and uses the local-time
+ * start time and time deltas, or mishandles them?
+ * Also, NetMon 3.1 and earlier can't read version
+ * 2.2, much less version 2.3.
+ */
+ file_hdr.ver_major = 2;
+ file_hdr.ver_minor =
+ (wdh->file_encap == WTAP_ENCAP_PER_PACKET) ? 1 : 0;
+ } else {
+ magicp = netmon_1_x_magic;
+ magic_size = sizeof netmon_1_x_magic;
+ /* NetMon file version, for 1.x, is 1.1 */
+ file_hdr.ver_major = 1;
+ file_hdr.ver_minor = 1;
+ }
+ if (!wtap_dump_file_write(wdh, magicp, magic_size, err))
+ return FALSE;
+
+ if (wdh->file_encap == WTAP_ENCAP_PER_PACKET) {
+ /*
+ * We're writing NetMon 2.1 format, so the media
+ * type in the file header is irrelevant. Set it
+ * to 1, just as Network Monitor does.
+ */
+ file_hdr.network = GUINT16_TO_LE(1);
+ } else
+ file_hdr.network = GUINT16_TO_LE(wtap_encap[wdh->file_encap]);
+ tm = localtime(&netmon->first_record_time.secs);
+ if (tm != NULL) {
+ file_hdr.ts_year = GUINT16_TO_LE(1900 + tm->tm_year);
+ file_hdr.ts_month = GUINT16_TO_LE(tm->tm_mon + 1);
+ file_hdr.ts_dow = GUINT16_TO_LE(tm->tm_wday);
+ file_hdr.ts_day = GUINT16_TO_LE(tm->tm_mday);
+ file_hdr.ts_hour = GUINT16_TO_LE(tm->tm_hour);
+ file_hdr.ts_min = GUINT16_TO_LE(tm->tm_min);
+ file_hdr.ts_sec = GUINT16_TO_LE(tm->tm_sec);
+ } else {
+ file_hdr.ts_year = GUINT16_TO_LE(1900 + 0);
+ file_hdr.ts_month = GUINT16_TO_LE(0 + 1);
+ file_hdr.ts_dow = GUINT16_TO_LE(0);
+ file_hdr.ts_day = GUINT16_TO_LE(0);
+ file_hdr.ts_hour = GUINT16_TO_LE(0);
+ file_hdr.ts_min = GUINT16_TO_LE(0);
+ file_hdr.ts_sec = GUINT16_TO_LE(0);
+ }
+ file_hdr.ts_msec = GUINT16_TO_LE(netmon->first_record_time.nsecs/1000000);
+ file_hdr.frametableoffset = GUINT32_TO_LE(netmon->frame_table_offset);
+ file_hdr.frametablelength =
+ GUINT32_TO_LE(netmon->frame_table_index * sizeof *netmon->frame_table);
+ if (!wtap_dump_file_write(wdh, &file_hdr, sizeof file_hdr, err))
+ return FALSE;
+
+ wdh->bytes_dumped = saved_bytes_dumped;
+ return TRUE;
+}
+
+static const struct supported_block_type netmon_1_x_blocks_supported[] = {
+ /*
+ * We support packet blocks, with no comments or other options.
+ */
+ { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
+};
+
+static const struct file_type_subtype_info netmon_1_x_info = {
+ "Microsoft NetMon 1.x", "netmon1", "cap", NULL,
+ TRUE, BLOCKS_SUPPORTED(netmon_1_x_blocks_supported),
+ netmon_dump_can_write_encap_1_x, netmon_dump_open_1_x, NULL
+};
+
+static const struct supported_block_type netmon_2_x_blocks_supported[] = {
+ /*
+ * We support packet blocks, with no comments or other options.
+ */
+ { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
+};
+
+static const struct file_type_subtype_info netmon_2_x_info = {
+ "Microsoft NetMon 2.x", "netmon2", "cap", NULL,
+ TRUE, BLOCKS_SUPPORTED(netmon_2_x_blocks_supported),
+ netmon_dump_can_write_encap_2_x, netmon_dump_open_2_x, NULL
+};
+
+void register_netmon(void)
+{
+ netmon_1_x_file_type_subtype = wtap_register_file_type_subtype(&netmon_1_x_info);
+ netmon_2_x_file_type_subtype = wtap_register_file_type_subtype(&netmon_2_x_info);
+
+ /*
+ * Register names for backwards compatibility with the
+ * wtap_filetypes table in Lua.
+ */
+ wtap_register_backwards_compatibility_lua_name("NETMON_1_x",
+ netmon_1_x_file_type_subtype);
+ wtap_register_backwards_compatibility_lua_name("NETMON_2_x",
+ netmon_2_x_file_type_subtype);
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/wiretap/netmon.h b/wiretap/netmon.h
new file mode 100644
index 00000000..2146b35e
--- /dev/null
+++ b/wiretap/netmon.h
@@ -0,0 +1,17 @@
+/** @file
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __NETMON_H__
+#define __NETMON_H__
+
+#include <glib.h>
+#include "wtap.h"
+
+wtap_open_return_val netmon_open(wtap *wth, int *err, gchar **err_info);
+
+#endif
diff --git a/wiretap/netscaler.c b/wiretap/netscaler.c
new file mode 100644
index 00000000..c1b229e0
--- /dev/null
+++ b/wiretap/netscaler.c
@@ -0,0 +1,2555 @@
+/* netscaler.c
+ *
+ * Wiretap Library
+ * Copyright (c) 2006 by Ravi Kondamuru <Ravi.Kondamuru@citrix.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "config.h"
+#include <string.h>
+#include "wtap-int.h"
+#include "file_wrappers.h"
+#include "netscaler.h"
+#include <wsutil/ws_assert.h>
+
+/* Defines imported from netscaler code: nsperfrc.h */
+
+#define NSPR_SIGSTR_V10 "NetScaler Performance Data"
+#define NSPR_SIGSTR_V20 "NetScaler V20 Performance Data"
+#define NSPR_SIGSTR NSPR_SIGSTR_V20
+#define NSPR_SIGSTR_V30 "Netscaler V30 Performance Data"
+#define NSPR_SIGSTR_V35 "Netscaler V35 Performance Data"
+/* Defined but not used */
+#define NSPR_SIGSTR_V21 "NetScaler V21 Performance Data"
+#define NSPR_SIGSTR_V22 "NetScaler V22 Performance Data"
+
+/*
+ * NetScaler trace files are divided into 8K pages, with each page
+ * containing one or more records. The last page of the file
+ * might be less than 8K bytes.
+ *
+ * Records are not split across page boundaries; if a record doesn't
+ * fit in what remains in a page, the page is padded with null bytes
+ * and the next record is put at the beginning of the next page.
+ * A record type value of 0 means "unused space", so if there are
+ * enough null bytes to constitute a record type value, it will
+ * look as if there's an "unused space" record (which has no fields
+ * other than the type and zero or more additional padding bytes).
+ */
+#define NSPR_PAGESIZE 8192
+#define NSPR_PAGESIZE_TRACE (2*NSPR_PAGESIZE)
+
+/* The different record types
+** NOTE: The Record Type is two byte fields and unused space is recognized by
+** either bytes being zero, therefore no record should any byte value as
+** zero.
+**
+** New Performance Record Type is only one byte.
+*/
+#define NSPR_UNUSEDSPACE_V10 0x0000 /* rest of the page is unused */
+#define NSPR_UNUSEDSPACE_V20 0x00 /* rest of the page is unused */
+#define NSPR_SIGNATURE_V10 0x0101 /* signature */
+#define NSPR_SIGNATURE_V20 0x01 /* signature */
+#define NSPR_SIGNATURE_V30 NSPR_SIGNATURE_V20
+#define NSPR_SIGNATURE_V35 NSPR_SIGNATURE_V20
+#define NSPR_ABSTIME_V10 0x0107 /* data capture time in secs from 1970*/
+#define NSPR_ABSTIME_V20 0x07 /* data capture time in secs from 1970*/
+#define NSPR_RELTIME_V10 0x0108 /* relative time in ms from last time */
+#define NSPR_RELTIME_V20 0x08 /* relative time in ms from last time */
+#define NSPR_RELTIMEHR_V10 0x0109 /* high resolution relative time */
+#define NSPR_RELTIMEHR_V20 0x09 /* high resolution relative time */
+#define NSPR_SYSTARTIME_V10 0x010A /* system start time */
+#define NSPR_SYSTARTIME_V20 0x0A /* system start time */
+#define NSPR_RELTIME2B_V10 0x010B /* relative time in ms from last time */
+#define NSPR_RELTIME2B_V20 0x0B /* relative time in ms from last time */
+
+
+/* The high resolution relative time format.
+** The MS 2 bits of the high resolution time is defined as follows:
+** 00 : time value is in seconds
+** 01 : time value is in milliseconds
+** 10 : time value is in microseconds
+** 11 : time value is in nanoseconds
+*/
+#define NSPR_HRTIME_MASKTM 0x3FFFFFFF /* mask to get time value */
+#define NSPR_HRTIME_MASKFMT 0xC0000000 /* time value format mask */
+#define NSPR_HRTIME_SEC 0x00000000 /* time value in second */
+#define NSPR_HRTIME_MSEC 0x40000000 /* time value in mili second */
+#define NSPR_HRTIME_USEC 0x80000000 /* time value in micro second */
+#define NSPR_HRTIME_NSEC 0xC0000000 /* time value in nano second */
+
+
+typedef struct nspr_header_v10
+{
+ guint8 ph_RecordType[2]; /* Record Type */
+ guint8 ph_RecordSize[2]; /* Record Size including header */
+} nspr_header_v10_t;
+#define nspr_header_v10_s ((guint32)sizeof(nspr_header_v10_t))
+
+/* This is V20 short header (2 bytes long) to be included where needed */
+#define NSPR_HEADER_V20(prefix) \
+ guint8 prefix##_RecordType; /* Record Type */ \
+ guint8 prefix##_RecordSize /* Record Size including header */ \
+ /* end of declaration */
+
+/* This is new long header (3 bytes long) to be included where needed */
+#define NSPR_HEADER3B_V20(prefix) \
+ guint8 prefix##_RecordType; /* Record Type */ \
+ guint8 prefix##_RecordSizeLow; /* Record Size including header */ \
+ guint8 prefix##_RecordSizeHigh /* Record Size including header */ \
+ /* end of declaration */
+#define NSPR_HEADER3B_V21 NSPR_HEADER3B_V20
+#define NSPR_HEADER3B_V22 NSPR_HEADER3B_V20
+#define NSPR_HEADER3B_V30 NSPR_HEADER3B_V20
+
+typedef struct nspr_hd_v20
+{
+ NSPR_HEADER3B_V20(phd); /* long performance header */
+
+} nspr_hd_v20_t;
+#define nspr_hd_v20_s ((guint32)sizeof(nspr_hd_v20_t))
+
+
+/*
+** How to know if header size is short or long?
+** The short header size can be 0-127 bytes long. If MS Bit of ph_RecordSize
+** is set then record size has 2 bytes
+*/
+#define NSPR_V20RECORDSIZE_2BYTES 0x80U
+
+/* Performance Data Header with device number */
+typedef struct nspr_headerdev_v10
+{
+ guint8 ph_RecordType[2]; /* Record Type */
+ guint8 ph_RecordSize[2]; /* Record Size including header */
+ guint8 ph_DevNo[4]; /* Network Device (NIC/CONN) number */
+} nspr_headerdev_v10_t;
+#define nspr_headerdev_v10_s ((guint32)sizeof(nspr_headerdev_v10_t))
+
+typedef struct nspr_hd_v10
+{
+ nspr_header_v10_t phd; /* performance header */
+} nspr_hd_v10_t;
+#define nspr_hd_v10_s ((guint32)sizeof(nspr_hd_v10_t))
+
+typedef struct nspr_hdev_v10
+{
+ nspr_headerdev_v10_t phd; /* performance header */
+} nspr_hdev_v10_t;
+#define nspr_hdev_v10_s ((guint32)sizeof(nspr_hdev_v10_t))
+
+/* if structure has defined phd as first field, it can use following names */
+#define nsprRecordType phd.ph_RecordType
+#define nsprRecordSize phd.ph_RecordSize
+#define nsprReserved phd.ph_Reserved
+#define nsprRecordTypeOrg phd.ph_Reserved
+#define nsprDevNo phd.ph_DevNo
+
+/* NSPR_SIGNATURE_V10 structure */
+#define NSPR_SIGSIZE_V10 56 /* signature value size in bytes */
+typedef struct nspr_signature_v10
+{
+ nspr_header_v10_t phd; /* performance header */
+ guint8 sig_EndianType; /* Endian Type for the data */
+ guint8 sig_Reserved0;
+ guint8 sig_Reserved1[2];
+ gchar sig_Signature[NSPR_SIGSIZE_V10]; /* Signature value */
+} nspr_signature_v10_t;
+#define nspr_signature_v10_s ((guint32)sizeof(nspr_signature_v10_t))
+
+/* NSPR_SIGNATURE_V20 structure */
+#define NSPR_SIGSIZE_V20 sizeof(NSPR_SIGSTR_V20) /* signature value size in bytes */
+typedef struct nspr_signature_v20
+{
+ NSPR_HEADER_V20(sig); /* short performance header */
+ guint8 sig_EndianType; /* Endian Type for the data */
+ gchar sig_Signature[NSPR_SIGSIZE_V20]; /* Signature value */
+} nspr_signature_v20_t;
+#define nspr_signature_v20_s ((guint32)sizeof(nspr_signature_v20_t))
+
+/* NSPR_SIGNATURE_V30 structure */
+#define NSPR_SIGSIZE_V30 sizeof(NSPR_SIGSTR_V30) /* signature value size in bytes */
+typedef struct nspr_signature_v30
+{
+ NSPR_HEADER_V20(sig); /* short performance header */
+ guint8 sig_EndianType; /* Endian Type for the data */
+ gchar sig_Signature[NSPR_SIGSIZE_V30]; /* Signature value */
+} nspr_signature_v30_t;
+#define nspr_signature_v30_s ((guint32)sizeof(nspr_signature_v30_t))
+
+#define NSPR_SIGSIZE_V35 sizeof(NSPR_SIGSTR_V35) /* signature value size in bytes */
+typedef struct nspr_signature_v35
+{
+ NSPR_HEADER_V20(sig); /* short performance header */
+ guint8 sig_EndianType; /* Endian Type for the data */
+ gchar sig_Signature[NSPR_SIGSIZE_V35]; /* Signature value */
+} nspr_signature_v35_t;
+#define nspr_signature_v35_s ((guint32)sizeof(nspr_signature_v35_t))
+
+/* NSPR_ABSTIME_V10 and NSPR_SYSTARTIME_V10 structure */
+typedef struct nspr_abstime_v10
+{
+ nspr_header_v10_t phd; /* performance header */
+ guint8 abs_RelTime[4]; /* relative time is ms from last time */
+ guint8 abs_Time[4]; /* absolute time in seconds from 1970 */
+} nspr_abstime_v10_t;
+#define nspr_abstime_v10_s ((guint32)sizeof(nspr_abstime_v10_t))
+
+
+/* NSPR_ABSTIME_V20 and NSPR_SYSTARTIME_V20 structure */
+typedef struct nspr_abstime_v20
+{
+ NSPR_HEADER_V20(abs); /* short performance header */
+ guint8 abs_RelTime[2]; /* relative time is ms from last time */
+ guint8 abs_Time[4]; /* absolute time in seconds from 1970 */
+} nspr_abstime_v20_t;
+#define nspr_abstime_v20_s ((guint32)sizeof(nspr_abstime_v20_t))
+
+
+
+/* full packet trace structure */
+typedef struct nspr_pktracefull_v10
+{
+ nspr_headerdev_v10_t phd; /* performance header */
+ guint8 fp_RelTimeHr[4]; /* High resolution relative time */
+} nspr_pktracefull_v10_t;
+#define nspr_pktracefull_v10_s ((guint32)(sizeof(nspr_pktracefull_v10_t)))
+
+/* new full packet trace structure v20 */
+typedef struct nspr_pktracefull_v20
+{
+ NSPR_HEADER3B_V20(fp); /* long performance header */
+ guint8 fp_DevNo; /* Network Device (NIC) number */
+ guint8 fp_RelTimeHr[4]; /* High resolution relative time */
+} nspr_pktracefull_v20_t;
+#define nspr_pktracefull_v20_s ((guint32)(sizeof(nspr_pktracefull_v20_t)))
+
+/* new full packet trace structure v21 */
+typedef struct nspr_pktracefull_v21
+{
+ NSPR_HEADER3B_V21(fp); /* long performance header */
+ guint8 fp_DevNo; /* Network Device (NIC) number */
+ guint8 fp_RelTimeHr[4]; /* High resolution relative time */
+ guint8 fp_PcbDevNo[4]; /* PCB devno */
+ guint8 fp_lPcbDevNo[4]; /* link PCB devno */
+} nspr_pktracefull_v21_t;
+#define nspr_pktracefull_v21_s ((guint32)(sizeof(nspr_pktracefull_v21_t)))
+
+/* new full packet trace structure v22 */
+typedef struct nspr_pktracefull_v22
+{
+ NSPR_HEADER3B_V22(fp); /* long performance header */
+ guint8 fp_DevNo; /* Network Device (NIC) number */
+ guint8 fp_RelTimeHr[4]; /* High resolution relative time */
+ guint8 fp_PcbDevNo[4]; /* PCB devno */
+ guint8 fp_lPcbDevNo[4]; /* link PCB devno */
+ guint8 fp_VlanTag[2]; /* vlan tag */
+} nspr_pktracefull_v22_t;
+#define nspr_pktracefull_v22_s ((guint32)(sizeof(nspr_pktracefull_v22_t)))
+
+typedef struct nspr_pktracefull_v23
+{
+ NSPR_HEADER3B_V22(fp); /* long performance header */
+ guint8 fp_DevNo; /* Network Device (NIC) number */
+ guint8 fp_AbsTimeHr[8]; /* High resolution absolute time */
+ guint8 fp_PcbDevNo[4]; /* PCB devno */
+ guint8 fp_lPcbDevNo[4]; /* link PCB devno */
+ guint8 fp_VlanTag[2]; /* vlan tag */
+ guint8 fp_Coreid[2]; /* coreid of the packet */
+} nspr_pktracefull_v23_t;
+#define nspr_pktracefull_v23_s ((guint32)(sizeof(nspr_pktracefull_v23_t)))
+
+/* New full packet trace structure v24 for cluster tracing */
+typedef struct nspr_pktracefull_v24
+{
+ NSPR_HEADER3B_V22(fp); /* long performance header */
+ guint8 fp_DevNo; /* Network Device (NIC) number */
+ guint8 fp_AbsTimeHr[8]; /* High resolution absolute time in nanosec */
+ guint8 fp_PcbDevNo[4]; /* PCB devno */
+ guint8 fp_lPcbDevNo[4]; /* link PCB devno */
+ guint8 fp_VlanTag[2]; /* vlan tag */
+ guint8 fp_Coreid[2]; /* coreid of the packet */
+ guint8 fp_srcNodeId[2]; /* source node # */
+ guint8 fp_destNodeId[2]; /* destination node # */
+ guint8 fp_clFlags; /* cluster flags */
+} nspr_pktracefull_v24_t;
+#define nspr_pktracefull_v24_s ((guint32)(sizeof(nspr_pktracefull_v24_t)))
+
+/* New full packet trace structure v25 for vm info tracing */
+typedef struct nspr_pktracefull_v25
+{
+ NSPR_HEADER3B_V22(fp); /* long performance header */
+ guint8 fp_DevNo; /* Network Device (NIC) number */
+ guint8 fp_AbsTimeHr[8]; /* High resolution absolute time in nanosec */
+ guint8 fp_PcbDevNo[4]; /* PCB devno */
+ guint8 fp_lPcbDevNo[4]; /* link PCB devno */
+ guint8 fp_VlanTag[2]; /* vlan tag */
+ guint8 fp_Coreid[2]; /* coreid of the packet */
+ guint8 fp_srcNodeId[2]; /* source node # */
+ guint8 fp_destNodeId[2]; /* destination node # */
+ guint8 fp_clFlags; /* cluster flags */
+ guint8 fp_src_vmname_len; /* vm src info */
+ guint8 fp_dst_vmname_len; /* vm src info */
+} nspr_pktracefull_v25_t;
+#define nspr_pktracefull_v25_s ((guint32)(sizeof(nspr_pktracefull_v25_t)))
+
+/* New full packet trace structure v26 for vm info tracing */
+typedef struct nspr_pktracefull_v26
+{
+ NSPR_HEADER3B_V22(fp); /* long performance header */
+ guint8 fp_DevNo; /* Network Device (NIC) number */
+ guint8 fp_AbsTimeHr[8]; /* High resolution absolute time in nanosec */
+ guint8 fp_PcbDevNo[4]; /* PCB devno */
+ guint8 fp_lPcbDevNo[4]; /* link PCB devno */
+ guint8 fp_VlanTag[2]; /* vlan tag */
+ guint8 fp_Coreid[2]; /* coreid of the packet */
+ guint8 fp_srcNodeId[2]; /* source node # */
+ guint8 fp_destNodeId[2]; /* destination node # */
+ guint8 fp_clFlags; /* cluster flags */
+ guint8 fp_src_vmname_len; /* vm src info */
+ guint8 fp_dst_vmname_len; /* vm src info */
+ guint8 fp_reserved;
+ guint8 fp_ns_activity[4];
+ guint8 fp_reserved_32[12]; /* Adding more field to reduce wireshark changes every time */
+} nspr_pktracefull_v26_t;
+#define nspr_pktracefull_v26_s ((guint32)(sizeof(nspr_pktracefull_v26_t)))
+
+/* partial packet trace structure */
+typedef struct nspr_pktracepart_v10
+{
+ nspr_headerdev_v10_t phd; /* performance header */
+ guint8 pp_RelTimeHr[4]; /* High resolution relative time */
+ guint8 pp_PktSizeOrg[2]; /* Original packet size */
+ guint8 pp_PktOffset[2]; /* starting offset in packet */
+} nspr_pktracepart_v10_t;
+#define nspr_pktracepart_v10_s ((guint32)(sizeof(nspr_pktracepart_v10_t)))
+
+/* new partial packet trace structure */
+typedef struct nspr_pktracepart_v20
+{
+ NSPR_HEADER3B_V20(pp); /* long performance header */
+ guint8 pp_DevNo; /* Network Device (NIC) number */
+ guint8 pp_RelTimeHr[4]; /* High resolution relative time */
+ guint8 pp_PktSizeOrg[2]; /* Original packet size */
+ guint8 pp_PktOffset[2]; /* starting offset in packet */
+} nspr_pktracepart_v20_t;
+#define nspr_pktracepart_v20_s ((guint32)(sizeof(nspr_pktracepart_v20_t)))
+
+/* new partial packet trace structure */
+typedef struct nspr_pktracepart_v21
+{
+ NSPR_HEADER3B_V21(pp); /* long performance header */
+ guint8 pp_DevNo; /* Network Device (NIC) number */
+ guint8 pp_RelTimeHr[4]; /* High resolution relative time */
+ guint8 pp_PktSizeOrg[2]; /* Original packet size */
+ guint8 pp_PktOffset[2]; /* starting offset in packet */
+ guint8 pp_PcbDevNo[4]; /* PCB devno */
+ guint8 pp_lPcbDevNo[4]; /* link PCB devno */
+} nspr_pktracepart_v21_t;
+#define nspr_pktracepart_v21_s ((guint32)(sizeof(nspr_pktracepart_v21_t)))
+
+/* new partial packet trace structure v22 */
+typedef struct nspr_pktracepart_v22
+{
+ NSPR_HEADER3B_V22(pp); /* long performance header */
+ guint8 pp_DevNo; /* Network Device (NIC) number */
+ guint8 pp_RelTimeHr[4]; /* High resolution relative time */
+ guint8 pp_PktSizeOrg[2]; /* Original packet size */
+ guint8 pp_PktOffset[2]; /* starting offset in packet */
+ guint8 pp_PcbDevNo[4]; /* PCB devno */
+ guint8 pp_lPcbDevNo[4]; /* link PCB devno */
+ guint8 pp_VlanTag[2]; /* Vlan Tag */
+} nspr_pktracepart_v22_t;
+#define nspr_pktracepart_v22_s ((guint32)(sizeof(nspr_pktracepart_v22_t)))
+
+typedef struct nspr_pktracepart_v23
+{
+ NSPR_HEADER3B_V22(pp); /* long performance header */
+ guint8 pp_DevNo; /* Network Device (NIC) number */
+ guint8 pp_AbsTimeHr[8]; /* High resolution absolute time */
+ guint8 pp_PktSizeOrg[2]; /* Original packet size */
+ guint8 pp_PktOffset[2]; /* starting offset in packet */
+ guint8 pp_PcbDevNo[4]; /* PCB devno */
+ guint8 pp_lPcbDevNo[4]; /* link PCB devno */
+ guint8 pp_VlanTag[2]; /* vlan tag */
+ guint8 pp_Coreid[2]; /* Coreid of the packet */
+} nspr_pktracepart_v23_t;
+#define nspr_pktracepart_v23_s ((guint32)(sizeof(nspr_pktracepart_v23_t)))
+
+/* New partial packet trace structure v24 for cluster tracing */
+typedef struct nspr_pktracepart_v24
+{
+ NSPR_HEADER3B_V22(pp); /* long performance header */
+ guint8 pp_DevNo; /* Network Device (NIC) number */
+ guint8 pp_AbsTimeHr[8]; /*High resolution absolute time in nanosec*/
+ guint8 pp_PktSizeOrg[2]; /* Original packet size */
+ guint8 pp_PktOffset[2]; /* starting offset in packet */
+ guint8 pp_PcbDevNo[4]; /* PCB devno */
+ guint8 pp_lPcbDevNo[4]; /* link PCB devno */
+ guint8 pp_VlanTag[2]; /* vlan tag */
+ guint8 pp_Coreid[2]; /* Coreid of the packet */
+ guint8 pp_srcNodeId[2]; /* source node # */
+ guint8 pp_destNodeId[2]; /* destination node # */
+ guint8 pp_clFlags; /* cluster flags */
+} nspr_pktracepart_v24_t;
+#define nspr_pktracepart_v24_s ((guint32)(sizeof(nspr_pktracepart_v24_t)))
+
+/* New partial packet trace structure v25 for vm info tracing */
+typedef struct nspr_pktracepart_v25
+{
+ NSPR_HEADER3B_V22(pp); /* long performance header */
+ guint8 pp_DevNo; /* Network Device (NIC) number */
+ guint8 pp_AbsTimeHr[8]; /*High resolution absolute time in nanosec*/
+ guint8 pp_PktSizeOrg[2]; /* Original packet size */
+ guint8 pp_PktOffset[2]; /* starting offset in packet */
+ guint8 pp_PcbDevNo[4]; /* PCB devno */
+ guint8 pp_lPcbDevNo[4]; /* link PCB devno */
+ guint8 pp_VlanTag[2]; /* vlan tag */
+ guint8 pp_Coreid[2]; /* Coreid of the packet */
+ guint8 pp_srcNodeId[2]; /* source node # */
+ guint8 pp_destNodeId[2]; /* destination node # */
+ guint8 pp_clFlags; /* cluster flags */
+ guint8 pp_src_vmname_len; /* vm info */
+ guint8 pp_dst_vmname_len; /* vm info */
+} nspr_pktracepart_v25_t;
+#define nspr_pktracepart_v25_s ((guint32)(sizeof(nspr_pktracepart_v25_t)))
+
+/* New full packet trace structure v30 for multipage spanning data */
+typedef struct nspr_pktracefull_v30
+{
+ NSPR_HEADER3B_V30(fp); /* long performance header */
+ guint8 fp_DevNo; /* Network Device (NIC) number */
+ guint8 fp_AbsTimeHr[8]; /*High resolution absolute time in nanosec*/
+ guint8 fp_PcbDevNo[4]; /* PCB devno */
+ guint8 fp_lPcbDevNo[4]; /* link PCB devno */
+ guint8 fp_PktSizeOrg[2]; /* Original packet size */
+ guint8 fp_VlanTag[2]; /* vlan tag */
+ guint8 fp_Coreid[2]; /* coreid of the packet */
+ guint8 fp_srcNodeId[2]; /* cluster nodeid of the packet */
+ guint8 fp_destNodeId[2];
+ guint8 fp_clFlags;
+ guint8 fp_src_vmname_len;
+ guint8 fp_dst_vmname_len;
+ guint8 fp_reserved[3];
+ guint8 fp_ns_activity[4];
+ guint8 fp_reserved_32[12];
+} nspr_pktracefull_v30_t;
+#define nspr_pktracefull_v30_s ((guint32)(sizeof(nspr_pktracefull_v30_t)))
+
+/* New full packet trace structure v35 for multipage spanning data */
+typedef struct nspr_pktracefull_v35
+{
+ NSPR_HEADER3B_V30(fp); /* long performance header */
+ guint8 fp_DevNo; /* Network Device (NIC) number */
+ guint8 fp_AbsTimeHr[8]; /*High resolution absolute time in nanosec*/
+ guint8 fp_PcbDevNo[4]; /* PCB devno */
+ guint8 fp_lPcbDevNo[4]; /* link PCB devno */
+ guint8 fp_PktSizeOrg[2]; /* Original packet size */
+ guint8 fp_VlanTag[2]; /* vlan tag */
+ guint8 fp_Coreid[2]; /* coreid of the packet */
+ guint8 fp_headerlen[2];
+ guint8 fp_errorcode;
+ guint8 fp_app;
+ guint8 fp_ns_activity[4];
+ guint8 fp_nextrectype;
+} nspr_pktracefull_v35_t;
+#define nspr_pktracefull_v35_s ((guint32)(sizeof(nspr_pktracefull_v35_t)))
+
+/* New partial packet trace structure v26 for vm info tracing */
+typedef struct nspr_pktracepart_v26
+{
+ NSPR_HEADER3B_V22(pp); /* long performance header */
+ guint8 pp_DevNo; /* Network Device (NIC) number */
+ guint8 pp_AbsTimeHr[8]; /*High resolution absolute time in nanosec*/
+ guint8 pp_PktSizeOrg[2]; /* Original packet size */
+ guint8 pp_PktOffset[2]; /* starting offset in packet */
+ guint8 pp_PcbDevNo[4]; /* PCB devno */
+ guint8 pp_lPcbDevNo[4]; /* link PCB devno */
+ guint8 pp_VlanTag[2]; /* vlan tag */
+ guint8 pp_Coreid[2]; /* Coreid of the packet */
+ guint8 pp_srcNodeId[2]; /* source node # */
+ guint8 pp_destNodeId[2]; /* destination node # */
+ guint8 pp_clFlags; /* cluster flags */
+ guint8 pp_src_vmname_len; /* vm info */
+ guint8 pp_dst_vmname_len; /* vm info */
+ guint8 pp_reserved;
+ guint8 pp_ns_activity[4];
+ guint8 pp_reserved_32[12]; /* Adding more field to reduce wireshark changes every time */
+} nspr_pktracepart_v26_t;
+#define nspr_pktracepart_v26_s ((guint32)(sizeof(nspr_pktracepart_v26_t)))
+
+#define __TNDO(rec,enumprefix,structname,hdrname)\
+ static const guint8 enumprefix##_##hdrname##_offset = (guint8)sizeof(nspr_##structname##_t);
+
+#define __TNO(rec,enumprefix,structprefix,structname,hdrname,structfieldname) \
+ static const guint8 enumprefix##_##hdrname##_offset = (guint8)GPOINTER_TO_INT(offsetof(nspr_##structname##_t,structprefix##_##structfieldname));
+
+#define __TNL(rec,enumprefix,structprefix,structname,hdrname,structfieldname) \
+ static const guint8 enumprefix##_##hdrname##_len = (guint8)sizeof(((nspr_##structname##_t*)0)->structprefix##_##structfieldname);
+
+#define __TNV1O(rec,enumprefix,structprefix,structname,hdrname,structfieldname) \
+ static const guint8 enumprefix##_##hdrname##_offset = (guint8)GPOINTER_TO_INT(offsetof(nspr_##structname##_t,structfieldname));
+
+#define __TNV1L(rec,enumprefix,structprefix,structname,hdrname,structfieldname) \
+ static const guint8 enumprefix##_##hdrname##_len = (guint8)sizeof(((nspr_##structname##_t*)0)->structfieldname);
+
+#define TRACE_V10_REC_LEN_OFF(rec,enumprefix,structprefix,structname) \
+ __TNV1O(rec,enumprefix,structprefix,structname,dir,phd.ph_RecordType)\
+ __TNV1L(rec,enumprefix,structprefix,structname,dir,phd.ph_RecordType)\
+ __TNV1O(rec,enumprefix,structprefix,structname,nicno,phd.ph_DevNo)\
+ __TNV1L(rec,enumprefix,structprefix,structname,nicno,phd.ph_DevNo)\
+ __TNDO(rec,enumprefix,structname,eth)
+
+#define TRACE_V20_REC_LEN_OFF(rec,enumprefix,structprefix,structname) \
+ __TNO(rec,enumprefix,structprefix,structname,dir,RecordType)\
+ __TNL(rec,enumprefix,structprefix,structname,dir,RecordType)\
+ __TNO(rec,enumprefix,structprefix,structname,nicno,DevNo)\
+ __TNL(rec,enumprefix,structprefix,structname,nicno,DevNo)\
+ __TNDO(rec,enumprefix,structname,eth)
+
+#define TRACE_V21_REC_LEN_OFF(rec,enumprefix,structprefix,structname) \
+ TRACE_V20_REC_LEN_OFF(rec,enumprefix,structprefix,structname)\
+ __TNO(rec,enumprefix,structprefix,structname,pcb,PcbDevNo)\
+ __TNO(rec,enumprefix,structprefix,structname,l_pcb,lPcbDevNo)
+
+#define TRACE_V22_REC_LEN_OFF(rec,enumprefix,structprefix,structname) \
+ TRACE_V21_REC_LEN_OFF(rec,enumprefix,structprefix,structname)\
+ __TNO(rec,enumprefix,structprefix,structname,vlantag,VlanTag)
+
+#define TRACE_V23_REC_LEN_OFF(rec,enumprefix,structprefix,structname) \
+ TRACE_V22_REC_LEN_OFF(rec,enumprefix,structprefix,structname)\
+ __TNO(rec,enumprefix,structprefix,structname,coreid,Coreid)
+
+#define TRACE_V24_REC_LEN_OFF(rec,enumprefix,structprefix,structname) \
+ TRACE_V23_REC_LEN_OFF(rec,enumprefix,structprefix,structname)\
+ __TNO(rec,enumprefix,structprefix,structname,srcnodeid,srcNodeId)\
+ __TNO(rec,enumprefix,structprefix,structname,destnodeid,destNodeId)\
+ __TNO(rec,enumprefix,structprefix,structname,clflags,clFlags)
+
+#define TRACE_V25_REC_LEN_OFF(rec,enumprefix,structprefix,structname) \
+ TRACE_V24_REC_LEN_OFF(rec,enumprefix,structprefix,structname)\
+ __TNO(rec,enumprefix,structprefix,structname,src_vmname_len,src_vmname_len)\
+ __TNO(rec,enumprefix,structprefix,structname,dst_vmname_len,dst_vmname_len)\
+ __TNDO(rec,enumprefix,structname,data)
+
+#define TRACE_V26_REC_LEN_OFF(rec,enumprefix,structprefix,structname) \
+ TRACE_V25_REC_LEN_OFF(rec,enumprefix,structprefix,structname)\
+ __TNO(rec,enumprefix,structprefix,structname,ns_activity,ns_activity)\
+
+#define TRACE_V30_REC_LEN_OFF(rec, enumprefix, structprefix, structname) \
+ TRACE_V26_REC_LEN_OFF(rec,enumprefix,structprefix,structname)\
+
+#define TRACE_V35_REC_LEN_OFF(rec, enumprefix, structprefix, structname) \
+ TRACE_V23_REC_LEN_OFF(rec,enumprefix,structprefix,structname)\
+ __TNDO(rec,enumprefix,structname,data)\
+ __TNO(rec,enumprefix,structprefix,structname,ns_activity,ns_activity)
+
+ TRACE_V10_REC_LEN_OFF(NULL,v10_part,pp,pktracepart_v10)
+ TRACE_V10_REC_LEN_OFF(NULL,v10_full,fp,pktracefull_v10)
+ TRACE_V20_REC_LEN_OFF(NULL,v20_part,pp,pktracepart_v20)
+ TRACE_V20_REC_LEN_OFF(NULL,v20_full,fp,pktracefull_v20)
+ TRACE_V21_REC_LEN_OFF(NULL,v21_part,pp,pktracepart_v21)
+ TRACE_V21_REC_LEN_OFF(NULL,v21_full,fp,pktracefull_v21)
+ TRACE_V22_REC_LEN_OFF(NULL,v22_part,pp,pktracepart_v22)
+ TRACE_V22_REC_LEN_OFF(NULL,v22_full,fp,pktracefull_v22)
+ TRACE_V23_REC_LEN_OFF(NULL,v23_part,pp,pktracepart_v23)
+ TRACE_V23_REC_LEN_OFF(NULL,v23_full,fp,pktracefull_v23)
+ TRACE_V24_REC_LEN_OFF(NULL,v24_part,pp,pktracepart_v24)
+ TRACE_V24_REC_LEN_OFF(NULL,v24_full,fp,pktracefull_v24)
+ TRACE_V25_REC_LEN_OFF(NULL,v25_part,pp,pktracepart_v25)
+ TRACE_V25_REC_LEN_OFF(NULL,v25_full,fp,pktracefull_v25)
+ TRACE_V26_REC_LEN_OFF(NULL,v26_part,pp,pktracepart_v26)
+ TRACE_V26_REC_LEN_OFF(NULL,v26_full,fp,pktracefull_v26)
+ TRACE_V30_REC_LEN_OFF(NULL,v30_full,fp,pktracefull_v30)
+ TRACE_V35_REC_LEN_OFF(NULL,v35_full,fp,pktracefull_v35)
+
+#undef __TNV1O
+#undef __TNV1L
+#undef __TNO
+#undef __TNDO
+#undef __TNL
+
+
+#define ns_setabstime(nstrace, AbsoluteTime, RelativeTimems) \
+ do { \
+ (nstrace)->nspm_curtime = AbsoluteTime; \
+ (nstrace)->nspm_curtimemsec += RelativeTimems; \
+ (nstrace)->nspm_curtimelastmsec = nstrace->nspm_curtimemsec; \
+ } while(0)
+
+
+#define ns_setrelativetime(nstrace, RelativeTimems) \
+ do { \
+ guint32 rsec; \
+ (nstrace)->nspm_curtimemsec += RelativeTimems; \
+ rsec = (guint32)((nstrace)->nspm_curtimemsec - (nstrace)->nspm_curtimelastmsec)/1000; \
+ (nstrace)->nspm_curtime += rsec; \
+ (nstrace)->nspm_curtimelastmsec += rsec * 1000; \
+ } while (0)
+
+
+typedef struct {
+ gchar *pnstrace_buf;
+ guint32 page_size;
+ gint64 xxx_offset;
+ guint32 nstrace_buf_offset;
+ guint32 nstrace_buflen;
+ /* Performance Monitor Time variables */
+ guint32 nspm_curtime; /* current time since 1970 */
+ guint64 nspm_curtimemsec; /* current time in milliseconds */
+ guint64 nspm_curtimelastmsec; /* nspm_curtime last update time in milliseconds */
+ guint64 nsg_creltime;
+ guint64 file_size;
+} nstrace_t;
+
+/*
+ * File versions.
+ */
+#define NSPM_SIGNATURE_1_0 0
+#define NSPM_SIGNATURE_2_0 1
+#define NSPM_SIGNATURE_3_0 2
+#define NSPM_SIGNATURE_3_5 3
+#define NSPM_SIGNATURE_NOMATCH -1
+
+static int nspm_signature_version(gchar*, guint);
+static gboolean nstrace_read_v10(wtap *wth, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info,
+ gint64 *data_offset);
+static gboolean nstrace_read_v20(wtap *wth, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info,
+ gint64 *data_offset);
+static gboolean nstrace_read_v30(wtap *wth, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info,
+ gint64 *data_offset);
+static gboolean nstrace_seek_read_v10(wtap *wth, gint64 seek_off,
+ wtap_rec *rec,
+ Buffer *buf,
+ int *err, gchar **err_info);
+static gboolean nstrace_seek_read_v20(wtap *wth, gint64 seek_off,
+ wtap_rec *rec,
+ Buffer *buf,
+ int *err, gchar **err_info);
+static gboolean nstrace_seek_read_v30(wtap *wth, gint64 seek_off,
+ wtap_rec *rec,
+ Buffer *buf,
+ int *err, gchar **err_info);
+static void nstrace_close(wtap *wth);
+
+static gboolean nstrace_set_start_time_v10(wtap *wth, int *err,
+ gchar **err_info);
+static gboolean nstrace_set_start_time_v20(wtap *wth, int *err,
+ gchar **err_info);
+static gboolean nstrace_set_start_time(wtap *wth, int version, int *err,
+ gchar **err_info);
+static guint64 ns_hrtime2nsec(guint32 tm);
+
+static gboolean nstrace_dump(wtap_dumper *wdh, const wtap_rec *rec,
+ const guint8 *pd, int *err, gchar **err_info);
+
+
+static int nstrace_1_0_file_type_subtype = -1;
+static int nstrace_2_0_file_type_subtype = -1;
+static int nstrace_3_0_file_type_subtype = -1;
+static int nstrace_3_5_file_type_subtype = -1;
+
+void register_nstrace(void);
+
+/*
+ * Minimum of the page size and the amount of data left in the file;
+ * the last page of a file can be short.
+ */
+#define GET_READ_PAGE_SIZE(remaining_file_size) ((gint32)((remaining_file_size>NSPR_PAGESIZE)?NSPR_PAGESIZE:remaining_file_size))
+#define GET_READ_PAGE_SIZEV3(remaining_file_size) ((gint32)((remaining_file_size>NSPR_PAGESIZE_TRACE)?NSPR_PAGESIZE_TRACE:remaining_file_size))
+
+/*
+ * Check whether we have enough room to retrieve the data in the caller.
+ * If not, we have a malformed file.
+ */
+static gboolean nstrace_ensure_buflen(nstrace_t* nstrace, guint offset, guint len, int *err, gchar** err_info)
+{
+ if (offset > nstrace->nstrace_buflen || nstrace->nstrace_buflen - offset < len) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("nstrace: malformed file");
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static guint64 ns_hrtime2nsec(guint32 tm)
+{
+ guint32 val = tm & NSPR_HRTIME_MASKTM;
+ switch(tm & NSPR_HRTIME_MASKFMT)
+ {
+ case NSPR_HRTIME_SEC: return (guint64)val*1000000000;
+ case NSPR_HRTIME_MSEC: return (guint64)val*1000000;
+ case NSPR_HRTIME_USEC: return (guint64)val*1000;
+ case NSPR_HRTIME_NSEC: return val;
+ }
+ return tm;
+}
+
+static gboolean
+nstrace_read_page(wtap *wth, int *err, gchar **err_info)
+{
+ nstrace_t *nstrace = (nstrace_t *)wth->priv;
+ int bytes_read;
+
+ bytes_read = file_read(nstrace->pnstrace_buf, nstrace->page_size, wth->fh);
+ if (bytes_read < 0) {
+ *err = file_error(wth->fh, err_info);
+ return FALSE;
+ }
+ if (bytes_read == 0) {
+ /*
+ * EOF.
+ */
+ *err = 0;
+ return FALSE;
+ }
+ nstrace->nstrace_buflen = (guint32)bytes_read;
+ return TRUE;
+}
+
+/*
+** Netscaler trace format open routines
+*/
+wtap_open_return_val nstrace_open(wtap *wth, int *err, gchar **err_info)
+{
+ int file_version;
+ gchar *nstrace_buf;
+ gint64 file_size;
+ gint32 page_size;
+ int bytes_read;
+ nstrace_t *nstrace;
+
+ if ((file_size = wtap_file_size(wth, err)) == -1)
+ return WTAP_OPEN_ERROR;
+ if (file_size == 0)
+ return WTAP_OPEN_NOT_MINE;
+ /* The size is 64 bits; we assume it fits in 63 bits, so it's positive */
+
+ nstrace_buf = (gchar *)g_malloc(NSPR_PAGESIZE);
+ page_size = NSPR_PAGESIZE;
+
+ /*
+ * Read the first page, so we can look for a signature.
+ * A short read is OK, as a file may have fewer records
+ * than required to fill up a page.
+ */
+ bytes_read = file_read(nstrace_buf, NSPR_PAGESIZE, wth->fh);
+ if (bytes_read < 0) {
+ *err = file_error(wth->fh, err_info);
+ g_free(nstrace_buf);
+ return WTAP_OPEN_ERROR;
+ }
+ if (bytes_read == 0) {
+ /* An empty file. */
+ g_free(nstrace_buf);
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ /*
+ * Scan it for a signature block.
+ */
+ file_version = nspm_signature_version(nstrace_buf, (guint)bytes_read);
+ switch (file_version) {
+
+ case NSPM_SIGNATURE_1_0:
+ wth->file_type_subtype = nstrace_1_0_file_type_subtype;
+ wth->file_encap = WTAP_ENCAP_NSTRACE_1_0;
+ break;
+
+ case NSPM_SIGNATURE_2_0:
+ wth->file_type_subtype = nstrace_2_0_file_type_subtype;
+ wth->file_encap = WTAP_ENCAP_NSTRACE_2_0;
+ break;
+
+ case NSPM_SIGNATURE_3_0:
+ wth->file_type_subtype = nstrace_3_0_file_type_subtype;
+ wth->file_encap = WTAP_ENCAP_NSTRACE_3_0;
+ /*
+ * File pages are larger in version 3.0; grow the buffer.
+ * (XXX - use g_realloc()?)
+ */
+ g_free(nstrace_buf);
+ nstrace_buf = (gchar *)g_malloc(NSPR_PAGESIZE_TRACE);
+ page_size = NSPR_PAGESIZE_TRACE;
+ break;
+
+ case NSPM_SIGNATURE_3_5:
+ wth->file_type_subtype = nstrace_3_5_file_type_subtype;
+ wth->file_encap = WTAP_ENCAP_NSTRACE_3_5;
+ /*
+ * File pages are larger in version 3.5; grow the buffer.
+ * (XXX - use g_realloc()?)
+ */
+ g_free(nstrace_buf);
+ nstrace_buf = (gchar *)g_malloc(NSPR_PAGESIZE_TRACE);
+ page_size = NSPR_PAGESIZE_TRACE;
+ break;
+
+ default:
+ /* No known signature found, assume it's not NetScaler */
+ g_free(nstrace_buf);
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ switch (file_version)
+ {
+ case NSPM_SIGNATURE_1_0:
+ wth->subtype_read = nstrace_read_v10;
+ wth->subtype_seek_read = nstrace_seek_read_v10;
+ break;
+
+ case NSPM_SIGNATURE_2_0:
+ wth->subtype_read = nstrace_read_v20;
+ wth->subtype_seek_read = nstrace_seek_read_v20;
+ break;
+
+ case NSPM_SIGNATURE_3_0:
+ wth->subtype_read = nstrace_read_v30;
+ wth->subtype_seek_read = nstrace_seek_read_v30;
+ break;
+
+ case NSPM_SIGNATURE_3_5:
+ wth->subtype_read = nstrace_read_v30;
+ wth->subtype_seek_read = nstrace_seek_read_v30;
+ break;
+ }
+ wth->subtype_close = nstrace_close;
+
+ nstrace = g_new(nstrace_t, 1);
+ wth->priv = (void *)nstrace;
+ nstrace->pnstrace_buf = nstrace_buf;
+ nstrace->page_size = page_size;
+ nstrace->xxx_offset = 0;
+ nstrace->nstrace_buf_offset = 0;
+ nstrace->nspm_curtime = 0;
+ nstrace->nspm_curtimemsec = 0;
+ nstrace->nspm_curtimelastmsec = 0;
+ nstrace->nsg_creltime = 0;
+ nstrace->file_size = file_size;
+
+ /*
+ * Seek back to the beginning of the file and read the first page,
+ * now that we know the page size.
+ */
+ if ((file_seek(wth->fh, 0, SEEK_SET, err)) == -1)
+ {
+ g_free(nstrace_buf);
+ return WTAP_OPEN_ERROR;
+ }
+ if (!nstrace_read_page(wth, err, err_info)) {
+ if (*err == 0) {
+ /* EOF, so an empty file. */
+ g_free(nstrace_buf);
+ return WTAP_OPEN_NOT_MINE;
+ }
+ /* Read error. */
+ return WTAP_OPEN_ERROR;
+ }
+
+ /* Set the start time by looking for the abstime record */
+ if ((nstrace_set_start_time(wth, file_version, err, err_info)) == FALSE)
+ {
+ /*
+ * No absolute time record seen, so we just reset the read
+ * pointer to the start of the file, so we start reading
+ * at the first record, rather than skipping records up
+ * to and including an absolute time record.
+ */
+ if (*err != 0)
+ {
+ /* We got an error reading the records. */
+ return WTAP_OPEN_ERROR;
+ }
+ if ((file_seek(wth->fh, 0, SEEK_SET, err)) == -1)
+ {
+ return WTAP_OPEN_ERROR;
+ }
+
+ /* Read the first page of data */
+ if (!nstrace_read_page(wth, err, err_info)) {
+ if (*err == 0) {
+ /* EOF, so an empty file. */
+ g_free(nstrace_buf);
+ return WTAP_OPEN_NOT_MINE;
+ }
+ /* Read error. */
+ return WTAP_OPEN_ERROR;
+ }
+
+ /* reset the buffer offset */
+ nstrace->nstrace_buf_offset = 0;
+ }
+
+ wth->file_tsprec = WTAP_TSPREC_NSEC;
+
+ *err = 0;
+
+ /*
+ * Add an IDB; we don't know how many interfaces were
+ * involved, so we just say one interface, about which
+ * we only know the link-layer type, snapshot length,
+ * and time stamp resolution.
+ */
+ wtap_add_generated_idb(wth);
+
+ return WTAP_OPEN_MINE;
+}
+
+/*
+** Generates a function that checks whether the specified signature
+** field, with the specified size, matches the signature string for
+** the version specified as an argument to the macro.
+**
+** The function does so by checking whether the signature string for
+** the version in question is a prefix of the signature field. The
+** signature field appears to be a blob of text, with one or more
+** lines, with lines separated by '\n', and the last line terminated
+** with '\0'. The first lign is the signature field; it may end with
+** '\n', meaning there's another line following it, or it may end
+** with '\0', meaning it's the last line.
+**
+** For that to be true, the field must have a size >= to the size (not
+** counting the terminating'\0') of the version's signature string,
+** and the first N bytes of the field, where N is the length of the
+** version string of the version (again, not counting the terminating
+** '\0'), are equal to the version's signature string.
+**
+** XXX - should this do an exact match rather than a prefix match,
+** checking whether either a '\n' or '\0' follows the first line?
+*/
+#define nspm_signature_func(ver) \
+ static guint32 nspm_signature_isv##ver(gchar *sigp, size_t sigsize) {\
+ size_t versiglen = sizeof(NSPR_SIGSTR_V##ver)-1;\
+ return sigsize >= versiglen && strncmp(sigp,NSPR_SIGSTR_V##ver,versiglen) == 0;\
+ }
+
+nspm_signature_func(10)
+nspm_signature_func(20)
+nspm_signature_func(30)
+nspm_signature_func(35)
+
+/*
+** Scan a page for something that looks like a signature record and,
+** if we find one, check the signature against the ones we support.
+** If we find one we support, return the file type/subtype for that
+** file version. If we don't find a signature record with a signature
+** we support, return NSPM_SIGNATURE_NOMATCH.
+**
+** We don't know what version the file is, so we can't make
+** assumptions about the format of the records.
+**
+** XXX - can we assume the signature block is the first block?
+*/
+static int
+nspm_signature_version(gchar *nstrace_buf, guint len)
+{
+ gchar *dp = nstrace_buf;
+
+ for ( ; len > MIN(nspr_signature_v10_s, nspr_signature_v20_s); dp++, len--)
+ {
+#define sigv10p ((nspr_signature_v10_t*)dp)
+ /*
+ * If this is a V10 signature record, then:
+ *
+ * 1) we have a full signature record's worth of data in what
+ * remains of the first page;
+ *
+ * 2) it appears to have a record type of NSPR_SIGNATURE_V10;
+ *
+ * 3) the length field specifies a length that fits in what
+ * remains of the first page;
+ *
+ * 4) it also specifies something as large as, or larger than,
+ * the declared size of a V10 signature record.
+ *
+ * (XXX - are all V10 signature records that size, or might they
+ * be smaller, with a shorter signature field?)
+ */
+ if (len >= nspr_signature_v10_s &&
+ (pletoh16(&sigv10p->nsprRecordType) == NSPR_SIGNATURE_V10) &&
+ (pletoh16(&sigv10p->nsprRecordSize) <= len) &&
+ (pletoh16(&sigv10p->nsprRecordSize) >= nspr_signature_v10_s))
+ {
+ if ((nspm_signature_isv10(sigv10p->sig_Signature, sizeof sigv10p->sig_Signature)))
+ return NSPM_SIGNATURE_1_0;
+ }
+#undef sigv10p
+
+#define sigv20p ((nspr_signature_v20_t*)dp)
+ /*
+ * If this is a V20-or-later signature record, then:
+ *
+ * 1) we have a full signature record's worth of data in what
+ * remains of the first page;
+ *
+ * 2) it appears to have a record type of NSPR_SIGNATURE_V20;
+ *
+ * 3) the length field specifies a length that fits in what
+ * remains of the first page;
+ *
+ * 4) it also specifies something as large as, or larger than,
+ * the declared size of a V20 signature record.
+ */
+ if (len >= nspr_signature_v20_s &&
+ (sigv20p->sig_RecordType == NSPR_SIGNATURE_V20) &&
+ (sigv20p->sig_RecordSize <= len) &&
+ (sigv20p->sig_RecordSize >= nspr_signature_v20_s))
+ {
+ if (nspm_signature_isv20(sigv20p->sig_Signature, sizeof sigv20p->sig_Signature)){
+ return NSPM_SIGNATURE_2_0;
+ } else if (nspm_signature_isv30(sigv20p->sig_Signature, sizeof sigv20p->sig_Signature)){
+ return NSPM_SIGNATURE_3_0;
+ } else if (nspm_signature_isv35(sigv20p->sig_Signature, sizeof sigv20p->sig_Signature)){
+ return NSPM_SIGNATURE_3_5;
+ }
+ }
+#undef sigv20p
+ }
+
+ return NSPM_SIGNATURE_NOMATCH; /* no version found */
+}
+
+#define nspr_getv10recordtype(hdp) (pletoh16(&(hdp)->nsprRecordType))
+#define nspr_getv10recordsize(hdp) (pletoh16(&(hdp)->nsprRecordSize))
+#define nspr_getv20recordtype(hdp) ((hdp)->phd_RecordType)
+#define nspr_getv20recordsize(hdp) \
+ (guint32)(((hdp)->phd_RecordSizeLow & NSPR_V20RECORDSIZE_2BYTES)? \
+ (((hdp)->phd_RecordSizeHigh * NSPR_V20RECORDSIZE_2BYTES)+ \
+ ((hdp)->phd_RecordSizeLow & ~NSPR_V20RECORDSIZE_2BYTES)) : \
+ (hdp)->phd_RecordSizeLow)
+
+
+/*
+ * For a given file version, this defines a routine to find an absolute
+ * time record in a file of that version and set the start time based on
+ * that.
+ *
+ * The routine called from the open routine after a file has been recognized
+ * as a NetScaler trace.
+ */
+#define nstrace_set_start_time_ver(ver) \
+ gboolean nstrace_set_start_time_v##ver(wtap *wth, int *err, gchar **err_info) \
+ {\
+ nstrace_t *nstrace = (nstrace_t *)wth->priv;\
+ gchar* nstrace_buf = nstrace->pnstrace_buf;\
+ guint32 nstrace_buf_offset = nstrace->nstrace_buf_offset;\
+ guint32 nstrace_buflen = nstrace->nstrace_buflen;\
+ guint32 record_size;\
+ do\
+ {\
+ while (nstrace_buf_offset < nstrace_buflen)\
+ {\
+ if (!nstrace_ensure_buflen(nstrace, nstrace_buf_offset, sizeof(nspr_hd_v##ver##_t), err, err_info))\
+ return FALSE;\
+ nspr_hd_v##ver##_t *fp = (nspr_hd_v##ver##_t *) &nstrace_buf[nstrace_buf_offset];\
+ switch (nspr_getv##ver##recordtype(fp))\
+ {\
+ case NSPR_ABSTIME_V##ver:\
+ if (!nstrace_ensure_buflen(nstrace, nstrace_buf_offset, sizeof(nspr_abstime_v##ver##_t), err, err_info))\
+ return FALSE;\
+ ns_setabstime(nstrace, pletoh32(&((nspr_abstime_v##ver##_t *) fp)->abs_Time), pletoh16(&((nspr_abstime_v##ver##_t *) fp)->abs_RelTime));\
+ nstrace->nstrace_buf_offset = nstrace_buf_offset + nspr_getv##ver##recordsize(fp);\
+ nstrace->nstrace_buflen = nstrace_buflen;\
+ return TRUE;\
+ case NSPR_UNUSEDSPACE_V10:\
+ nstrace_buf_offset = nstrace_buflen;\
+ break;\
+ default:\
+ record_size = nspr_getv##ver##recordsize(fp);\
+ if (record_size == 0) {\
+ *err = WTAP_ERR_BAD_FILE;\
+ *err_info = g_strdup("nstrace: zero size record found");\
+ return FALSE;\
+ }\
+ nstrace_buf_offset += record_size;\
+ }\
+ }\
+ nstrace_buf_offset = 0;\
+ nstrace->xxx_offset += nstrace_buflen;\
+ nstrace_buflen = GET_READ_PAGE_SIZE((nstrace->file_size - nstrace->xxx_offset));\
+ }while((nstrace_buflen > 0) && (nstrace_read_page(wth, err, err_info)));\
+ return FALSE;\
+ }
+
+nstrace_set_start_time_ver(10)
+nstrace_set_start_time_ver(20)
+
+#undef nspr_getv10recordtype
+#undef nspr_getv20recordtype
+
+/*
+** Set the start time of the trace file. We look for the first ABSTIME record. We use that
+** to set the start time. Apart from that we also make sure that we remember the position of
+** the next record after the ABSTIME record. Inorder to report correct time values, all trace
+** records before the ABSTIME record are ignored.
+*/
+static gboolean nstrace_set_start_time(wtap *wth, int file_version, int *err,
+ gchar **err_info)
+{
+ if (file_version == NSPM_SIGNATURE_1_0)
+ return nstrace_set_start_time_v10(wth, err, err_info);
+ else if (file_version == NSPM_SIGNATURE_2_0)
+ return nstrace_set_start_time_v20(wth, err, err_info);
+ else if (file_version == NSPM_SIGNATURE_3_0)
+ return nstrace_set_start_time_v20(wth, err, err_info);
+ return FALSE;
+}
+
+#define __TNDO(rec,enumprefix,structname,hdrname)\
+ (rec)->rec_header.packet_header.pseudo_header.nstr.hdrname##_offset = enumprefix##_##hdrname##_offset;
+
+#define __TNO(rec,enumprefix,structprefix,structname,hdrname,structfieldname) \
+ (rec)->rec_header.packet_header.pseudo_header.nstr.hdrname##_offset = enumprefix##_##hdrname##_offset;
+
+#define __TNL(rec,enumprefix,structprefix,structname,hdrname,structfieldname) \
+ (rec)->rec_header.packet_header.pseudo_header.nstr.hdrname##_len = enumprefix##_##hdrname##_len;
+
+#define __TNV1O(rec,enumprefix,structprefix,structname,hdrname,structfieldname) \
+ __TNO(rec,enumprefix,structprefix,structname,hdrname,structfieldname)
+
+#define __TNV1L(rec,enumprefix,structprefix,structname,hdrname,structfieldname) \
+ __TNL(rec,enumprefix,structprefix,structname,hdrname,structfieldname)
+
+
+
+/*
+** Netscaler trace format read routines.
+**
+** The maximum value of the record data size is 65535, which is less than
+** WTAP_MAX_PACKET_SIZE_STANDARD will ever be, so we don't need to check it.
+*/
+#define TIMEDEFV10(rec,fp,type) \
+ do {\
+ (rec)->presence_flags = WTAP_HAS_TS;\
+ nsg_creltime += ns_hrtime2nsec(pletoh32(&type->type##_RelTimeHr));\
+ (rec)->ts.secs = nstrace->nspm_curtime + (guint32) (nsg_creltime / 1000000000);\
+ (rec)->ts.nsecs = (guint32) (nsg_creltime % 1000000000);\
+ }while(0)
+
+#define PARTSIZEDEFV10(rec,pp,ver) \
+ do {\
+ (rec)->presence_flags |= WTAP_HAS_CAP_LEN;\
+ (rec)->rec_header.packet_header.len = pletoh16(&pp->pp_PktSizeOrg) + nspr_pktracepart_v##ver##_s;\
+ (rec)->rec_header.packet_header.caplen = pletoh16(&pp->nsprRecordSize);\
+ }while(0)
+
+#define FULLSIZEDEFV10(rec,fp,ver) \
+ do {\
+ (rec)->rec_header.packet_header.len = pletoh16(&(fp)->nsprRecordSize);\
+ (rec)->rec_header.packet_header.caplen = (rec)->rec_header.packet_header.len;\
+ }while(0)
+
+#define PACKET_DESCRIBE(rec,buf,FULLPART,fullpart,ver,type,HEADERVER) \
+ do {\
+ /* Make sure the record header is entirely contained in the page */\
+ if ((nstrace_buflen - nstrace_buf_offset) < sizeof(nspr_pktrace##fullpart##_v##ver##_t)) {\
+ *err = WTAP_ERR_BAD_FILE;\
+ *err_info = g_strdup("nstrace: record header crosses page boundary");\
+ return FALSE;\
+ }\
+ nspr_pktrace##fullpart##_v##ver##_t *type = (nspr_pktrace##fullpart##_v##ver##_t *) &nstrace_buf[nstrace_buf_offset];\
+ /* Check sanity of record size */\
+ if (pletoh16(&type->nsprRecordSize) < sizeof *type) {\
+ *err = WTAP_ERR_BAD_FILE;\
+ *err_info = g_strdup("nstrace: record size is less than record header size");\
+ return FALSE;\
+ }\
+ (rec)->rec_type = REC_TYPE_PACKET;\
+ (rec)->block = wtap_block_create(WTAP_BLOCK_PACKET);\
+ TIMEDEFV##ver((rec),fp,type);\
+ FULLPART##SIZEDEFV##ver((rec),type,ver);\
+ TRACE_V##ver##_REC_LEN_OFF((rec),v##ver##_##fullpart,type,pktrace##fullpart##_v##ver);\
+ /* Make sure the record is entirely contained in the page */\
+ if ((nstrace_buflen - nstrace_buf_offset) < (rec)->rec_header.packet_header.caplen) {\
+ *err = WTAP_ERR_BAD_FILE;\
+ *err_info = g_strdup("nstrace: record crosses page boundary");\
+ return FALSE;\
+ }\
+ ws_buffer_assure_space((buf), (rec)->rec_header.packet_header.caplen);\
+ memcpy(ws_buffer_start_ptr((buf)), type, (rec)->rec_header.packet_header.caplen);\
+ *data_offset = nstrace->xxx_offset + nstrace_buf_offset;\
+ nstrace->nstrace_buf_offset = nstrace_buf_offset + (rec)->rec_header.packet_header.caplen;\
+ nstrace->nstrace_buflen = nstrace_buflen;\
+ nstrace->nsg_creltime = nsg_creltime;\
+ return TRUE;\
+ }while(0)
+
+static gboolean nstrace_read_v10(wtap *wth, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info, gint64 *data_offset)
+{
+ nstrace_t *nstrace = (nstrace_t *)wth->priv;
+ guint64 nsg_creltime = nstrace->nsg_creltime;
+ gchar *nstrace_buf = nstrace->pnstrace_buf;
+ guint32 nstrace_buf_offset = nstrace->nstrace_buf_offset;
+ guint32 nstrace_buflen = nstrace->nstrace_buflen;
+
+ *err = 0;
+ *err_info = NULL;
+ do
+ {
+ while ((nstrace_buf_offset < nstrace_buflen) &&
+ ((nstrace_buflen - nstrace_buf_offset) >= ((gint32)sizeof((( nspr_header_v10_t*)&nstrace_buf[nstrace_buf_offset])->ph_RecordType))))
+ {
+
+#define GENERATE_CASE_FULL(rec,buf,ver,HEADERVER) \
+ case NSPR_PDPKTRACEFULLTX_V##ver:\
+ case NSPR_PDPKTRACEFULLTXB_V##ver:\
+ case NSPR_PDPKTRACEFULLRX_V##ver:\
+ PACKET_DESCRIBE(rec,buf,FULL,full,ver,fp,HEADERVER);
+
+#define GENERATE_CASE_PART(rec,buf,ver,HEADERVER) \
+ case NSPR_PDPKTRACEPARTTX_V##ver:\
+ case NSPR_PDPKTRACEPARTTXB_V##ver:\
+ case NSPR_PDPKTRACEPARTRX_V##ver:\
+ PACKET_DESCRIBE(rec,buf,PART,part,ver,pp,HEADERVER);
+
+ switch (pletoh16(&(( nspr_header_v10_t*)&nstrace_buf[nstrace_buf_offset])->ph_RecordType))
+ {
+ GENERATE_CASE_FULL(rec,buf,10,100)
+ GENERATE_CASE_PART(rec,buf,10,100)
+
+#undef GENERATE_CASE_FULL
+#undef GENERATE_CASE_PART
+
+ case NSPR_ABSTIME_V10:
+ {
+ if (!nstrace_ensure_buflen(nstrace, nstrace_buf_offset, sizeof(nspr_pktracefull_v10_t), err, err_info))
+ return FALSE;
+ nspr_pktracefull_v10_t *fp = (nspr_pktracefull_v10_t *) &nstrace_buf[nstrace_buf_offset];
+ if (pletoh16(&fp->nsprRecordSize) == 0) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("nstrace: zero size record found");
+ return FALSE;
+ }
+ ns_setabstime(nstrace, pletoh32(((nspr_abstime_v10_t *) fp)->abs_Time), pletoh32(&((nspr_abstime_v10_t *) fp)->abs_RelTime));
+ nstrace_buf_offset += pletoh16(&fp->nsprRecordSize);
+ break;
+ }
+
+ case NSPR_RELTIME_V10:
+ {
+ if (!nstrace_ensure_buflen(nstrace, nstrace_buf_offset, sizeof(nspr_pktracefull_v10_t), err, err_info))
+ return FALSE;
+ nspr_pktracefull_v10_t *fp = (nspr_pktracefull_v10_t *) &nstrace_buf[nstrace_buf_offset];
+ if (pletoh16(&fp->nsprRecordSize) == 0) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("nstrace: zero size record found");
+ return FALSE;
+ }
+ ns_setrelativetime(nstrace, pletoh32(((nspr_abstime_v10_t *) fp)->abs_RelTime));
+ nstrace_buf_offset += pletoh16(&fp->nsprRecordSize);
+ break;
+ }
+
+ case NSPR_UNUSEDSPACE_V10:
+ nstrace_buf_offset = nstrace_buflen;
+ break;
+
+ default:
+ {
+ if (!nstrace_ensure_buflen(nstrace, nstrace_buf_offset, sizeof(nspr_pktracefull_v10_t), err, err_info))
+ return FALSE;
+ nspr_pktracefull_v10_t *fp = (nspr_pktracefull_v10_t *) &nstrace_buf[nstrace_buf_offset];
+ if (pletoh16(&fp->nsprRecordSize) == 0) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("nstrace: zero size record found");
+ return FALSE;
+ }
+ nstrace_buf_offset += pletoh16(&fp->nsprRecordSize);
+ break;
+ }
+ }
+ }
+
+ nstrace_buf_offset = 0;
+ nstrace->xxx_offset += nstrace_buflen;
+ nstrace_buflen = GET_READ_PAGE_SIZE((nstrace->file_size - nstrace->xxx_offset));
+ }while((nstrace_buflen > 0) && (nstrace_read_page(wth, err, err_info)));
+
+ return FALSE;
+}
+
+#undef PACKET_DESCRIBE
+
+#define TIMEDEFV20(rec,fp,type) \
+ do {\
+ (rec)->presence_flags = WTAP_HAS_TS;\
+ nsg_creltime += ns_hrtime2nsec(pletoh32(fp->type##_RelTimeHr));\
+ (rec)->ts.secs = nstrace->nspm_curtime + (guint32) (nsg_creltime / 1000000000);\
+ (rec)->ts.nsecs = (guint32) (nsg_creltime % 1000000000);\
+ }while(0)
+
+#define TIMEDEFV23(rec,fp,type) \
+ do {\
+ (rec)->presence_flags = WTAP_HAS_TS;\
+ /* access _AbsTimeHr as a 64bit value */\
+ nsg_creltime = pletoh64(fp->type##_AbsTimeHr);\
+ (rec)->ts.secs = (guint32) (nsg_creltime / 1000000000);\
+ (rec)->ts.nsecs = (guint32) (nsg_creltime % 1000000000);\
+ }while(0)
+
+#define TIMEDEFV21(rec,fp,type) TIMEDEFV20(rec,fp,type)
+#define TIMEDEFV22(rec,fp,type) TIMEDEFV20(rec,fp,type)
+#define TIMEDEFV24(rec,fp,type) TIMEDEFV23(rec,fp,type)
+#define TIMEDEFV25(rec,fp,type) TIMEDEFV24(rec,fp,type)
+#define TIMEDEFV26(rec,fp,type) TIMEDEFV24(rec,fp,type)
+
+/*
+** The maximum value of the record data size is 65535, which is less than
+** WTAP_MAX_PACKET_SIZE_STANDARD will ever be, so we don't need to check it.
+*/
+#define PARTSIZEDEFV20(rec,pp,ver) \
+ do {\
+ (rec)->presence_flags |= WTAP_HAS_CAP_LEN;\
+ (rec)->rec_header.packet_header.len = pletoh16(&pp->pp_PktSizeOrg) + nspr_pktracepart_v##ver##_s;\
+ (rec)->rec_header.packet_header.caplen = nspr_getv20recordsize((nspr_hd_v20_t *)pp);\
+ }while(0)
+
+#define PARTSIZEDEFV21(rec,pp,ver) PARTSIZEDEFV20(rec,pp,ver)
+#define PARTSIZEDEFV22(rec,pp,ver) PARTSIZEDEFV20(rec,pp,ver)
+#define PARTSIZEDEFV23(rec,pp,ver) PARTSIZEDEFV20(rec,pp,ver)
+#define PARTSIZEDEFV24(rec,pp,ver) PARTSIZEDEFV20(rec,pp,ver)
+#define PARTSIZEDEFV25(rec,pp,ver) PARTSIZEDEFV20(rec,pp,ver)
+#define PARTSIZEDEFV26(rec,pp,ver) PARTSIZEDEFV20(rec,pp,ver)
+
+#define FULLSIZEDEFV20(rec,fp,ver)\
+ do {\
+ (rec)->rec_header.packet_header.len = nspr_getv20recordsize((nspr_hd_v20_t *)fp);\
+ (rec)->rec_header.packet_header.caplen = (rec)->rec_header.packet_header.len;\
+ }while(0)
+
+#define FULLSIZEDEFV21(rec,fp,ver) FULLSIZEDEFV20(rec,fp,ver)
+#define FULLSIZEDEFV22(rec,fp,ver) FULLSIZEDEFV20(rec,fp,ver)
+#define FULLSIZEDEFV23(rec,fp,ver) FULLSIZEDEFV20(rec,fp,ver)
+#define FULLSIZEDEFV24(rec,fp,ver) FULLSIZEDEFV20(rec,fp,ver)
+#define FULLSIZEDEFV25(rec,fp,ver) FULLSIZEDEFV20(rec,fp,ver)
+#define FULLSIZEDEFV26(rec,fp,ver) FULLSIZEDEFV20(rec,fp,ver)
+
+#define PACKET_DESCRIBE(rec,buf,FULLPART,ver,enumprefix,type,structname,HEADERVER)\
+ do {\
+ nspr_##structname##_t *fp= (nspr_##structname##_t*)&nstrace_buf[nstrace_buf_offset];\
+ /* Make sure the record header is entirely contained in the page */\
+ if ((nstrace_buflen - nstrace_buf_offset) < sizeof *fp) {\
+ *err = WTAP_ERR_BAD_FILE;\
+ *err_info = g_strdup("nstrace: record header crosses page boundary");\
+ return FALSE;\
+ }\
+ /* Check sanity of record size */\
+ if (nspr_getv20recordsize((nspr_hd_v20_t *)fp) < sizeof *fp) {\
+ *err = WTAP_ERR_BAD_FILE;\
+ *err_info = g_strdup("nstrace: record size is less than record header size");\
+ return FALSE;\
+ }\
+ (rec)->rec_type = REC_TYPE_PACKET;\
+ (rec)->block = wtap_block_create(WTAP_BLOCK_PACKET);\
+ TIMEDEFV##ver((rec),fp,type);\
+ FULLPART##SIZEDEFV##ver((rec),fp,ver);\
+ TRACE_V##ver##_REC_LEN_OFF((rec),enumprefix,type,structname);\
+ (rec)->rec_header.packet_header.pseudo_header.nstr.rec_type = NSPR_HEADER_VERSION##HEADERVER;\
+ /* Make sure the record is entirely contained in the page */\
+ if ((nstrace_buflen - nstrace_buf_offset) < (rec)->rec_header.packet_header.caplen) {\
+ *err = WTAP_ERR_BAD_FILE;\
+ *err_info = g_strdup("nstrace: record crosses page boundary");\
+ return FALSE;\
+ }\
+ ws_buffer_assure_space((buf), (rec)->rec_header.packet_header.caplen);\
+ memcpy(ws_buffer_start_ptr((buf)), fp, (rec)->rec_header.packet_header.caplen);\
+ *data_offset = nstrace->xxx_offset + nstrace_buf_offset;\
+ nstrace->nstrace_buf_offset = nstrace_buf_offset + nspr_getv20recordsize((nspr_hd_v20_t *)fp);\
+ nstrace->nstrace_buflen = nstrace_buflen;\
+ nstrace->nsg_creltime = nsg_creltime;\
+ return TRUE;\
+ }while(0)
+
+static gboolean nstrace_read_v20(wtap *wth, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info, gint64 *data_offset)
+{
+ nstrace_t *nstrace = (nstrace_t *)wth->priv;
+ guint64 nsg_creltime = nstrace->nsg_creltime;
+ gchar *nstrace_buf = nstrace->pnstrace_buf;
+ guint32 nstrace_buf_offset = nstrace->nstrace_buf_offset;
+ guint32 nstrace_buflen = nstrace->nstrace_buflen;
+
+ *err = 0;
+ *err_info = NULL;
+ do
+ {
+ while ((nstrace_buf_offset < nstrace_buflen) &&
+ ((nstrace_buflen - nstrace_buf_offset) >= ((gint32)sizeof((( nspr_hd_v20_t*)&nstrace_buf[nstrace_buf_offset])->phd_RecordType))))
+ {
+ switch ((( nspr_hd_v20_t*)&nstrace_buf[nstrace_buf_offset])->phd_RecordType)
+ {
+
+#define GENERATE_CASE_FULL(rec,buf,ver,HEADERVER) \
+ case NSPR_PDPKTRACEFULLTX_V##ver:\
+ case NSPR_PDPKTRACEFULLTXB_V##ver:\
+ case NSPR_PDPKTRACEFULLRX_V##ver:\
+ PACKET_DESCRIBE(rec,buf,FULL,ver,v##ver##_full,fp,pktracefull_v##ver,HEADERVER);
+
+#define GENERATE_CASE_FULL_V25(rec,buf,ver,HEADERVER) \
+ case NSPR_PDPKTRACEFULLTX_V##ver:\
+ case NSPR_PDPKTRACEFULLTXB_V##ver:\
+ case NSPR_PDPKTRACEFULLRX_V##ver:\
+ case NSPR_PDPKTRACEFULLNEWRX_V##ver:\
+ PACKET_DESCRIBE(rec,buf,FULL,ver,v##ver##_full,fp,pktracefull_v##ver,HEADERVER);
+
+#define GENERATE_CASE_PART(rec,buf,ver,HEADERVER) \
+ case NSPR_PDPKTRACEPARTTX_V##ver:\
+ case NSPR_PDPKTRACEPARTTXB_V##ver:\
+ case NSPR_PDPKTRACEPARTRX_V##ver:\
+ PACKET_DESCRIBE(rec,buf,PART,ver,v##ver##_part,pp,pktracepart_v##ver,HEADERVER);
+
+#define GENERATE_CASE_PART_V25(rec,buf,ver,HEADERVER) \
+ case NSPR_PDPKTRACEPARTTX_V##ver:\
+ case NSPR_PDPKTRACEPARTTXB_V##ver:\
+ case NSPR_PDPKTRACEPARTRX_V##ver:\
+ case NSPR_PDPKTRACEPARTNEWRX_V##ver:\
+ PACKET_DESCRIBE(rec,buf,PART,ver,v##ver##_part,pp,pktracepart_v##ver,HEADERVER);
+
+ GENERATE_CASE_FULL(rec,buf,20,200);
+ GENERATE_CASE_PART(rec,buf,20,200);
+ GENERATE_CASE_FULL(rec,buf,21,201);
+ GENERATE_CASE_PART(rec,buf,21,201);
+ GENERATE_CASE_FULL(rec,buf,22,202);
+ GENERATE_CASE_PART(rec,buf,22,202);
+ GENERATE_CASE_FULL(rec,buf,23,203);
+ GENERATE_CASE_PART(rec,buf,23,203);
+ GENERATE_CASE_FULL_V25(rec,buf,24,204);
+ GENERATE_CASE_PART_V25(rec,buf,24,204);
+ GENERATE_CASE_FULL_V25(rec,buf,25,205);
+ GENERATE_CASE_PART_V25(rec,buf,25,205);
+ GENERATE_CASE_FULL_V25(rec,buf,26,206);
+ GENERATE_CASE_PART_V25(rec,buf,26,206);
+
+#undef GENERATE_CASE_FULL
+#undef GENERATE_CASE_FULL_V25
+#undef GENERATE_CASE_PART
+#undef GENERATE_CASE_PART_V25
+
+ case NSPR_ABSTIME_V20:
+ {
+ if (!nstrace_ensure_buflen(nstrace, nstrace_buf_offset, sizeof(nspr_pktracefull_v20_t), err, err_info))
+ return FALSE;
+ nspr_pktracefull_v20_t *fp20 = (nspr_pktracefull_v20_t *) &nstrace_buf[nstrace_buf_offset];
+ if (nspr_getv20recordsize((nspr_hd_v20_t *)fp20) == 0) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("nstrace: zero size record found");
+ return FALSE;
+ }
+ if (!nstrace_ensure_buflen(nstrace, nstrace_buf_offset, sizeof(nspr_hd_v20_t), err, err_info))
+ return FALSE;
+ nstrace_buf_offset += nspr_getv20recordsize((nspr_hd_v20_t *)fp20);
+ if (!nstrace_ensure_buflen(nstrace, nstrace_buf_offset, sizeof(nspr_abstime_v20_t), err, err_info))
+ return FALSE;
+ ns_setabstime(nstrace, pletoh32(&((nspr_abstime_v20_t *) fp20)->abs_Time), pletoh16(&((nspr_abstime_v20_t *) fp20)->abs_RelTime));
+ break;
+ }
+
+ case NSPR_RELTIME_V20:
+ {
+ if (!nstrace_ensure_buflen(nstrace, nstrace_buf_offset, sizeof(nspr_pktracefull_v20_t), err, err_info))
+ return FALSE;
+ nspr_pktracefull_v20_t *fp20 = (nspr_pktracefull_v20_t *) &nstrace_buf[nstrace_buf_offset];
+ if (nspr_getv20recordsize((nspr_hd_v20_t *)fp20) == 0) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("nstrace: zero size record found");
+ return FALSE;
+ }
+ if (!nstrace_ensure_buflen(nstrace, nstrace_buf_offset, sizeof(nspr_abstime_v20_t), err, err_info))
+ return FALSE;
+ ns_setrelativetime(nstrace, pletoh16(&((nspr_abstime_v20_t *) fp20)->abs_RelTime));
+ nstrace_buf_offset += nspr_getv20recordsize((nspr_hd_v20_t *)fp20);
+ break;
+ }
+
+ case NSPR_UNUSEDSPACE_V20:
+ {
+ if (nstrace_buf_offset >= NSPR_PAGESIZE/2)
+ nstrace_buf_offset = nstrace_buflen;
+ else
+ nstrace_buf_offset = NSPR_PAGESIZE/2;
+ break;
+ }
+
+ default:
+ {
+ if (!nstrace_ensure_buflen(nstrace, nstrace_buf_offset, sizeof(nspr_pktracefull_v20_t), err, err_info))
+ return FALSE;
+ nspr_pktracefull_v20_t *fp20 = (nspr_pktracefull_v20_t *) &nstrace_buf[nstrace_buf_offset];
+ if (nspr_getv20recordsize((nspr_hd_v20_t *)fp20) == 0) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("nstrace: zero size record found");
+ return FALSE;
+ }
+ nstrace_buf_offset += nspr_getv20recordsize((nspr_hd_v20_t *)fp20);
+ break;
+ }
+ }
+ }
+
+ nstrace_buf_offset = 0;
+ nstrace->xxx_offset += nstrace_buflen;
+ nstrace_buflen = GET_READ_PAGE_SIZE((nstrace->file_size - nstrace->xxx_offset));
+ }while((nstrace_buflen > 0) && (nstrace_read_page(wth, err, err_info)));
+
+ return FALSE;
+}
+
+#undef PACKET_DESCRIBE
+
+#define SETETHOFFSET_35(rec)\
+ (rec)->rec_header.packet_header.pseudo_header.nstr.eth_offset = pletoh16(&fp->fp_headerlen);\
+
+#define SETETHOFFSET_30(rec) ;\
+
+#define TIMEDEFV30(rec,fp,type) \
+ do {\
+ (rec)->presence_flags = WTAP_HAS_TS;\
+ /* access _AbsTimeHr as a 64bit value */\
+ nsg_creltime = pletoh64(fp->type##_AbsTimeHr);\
+ (rec)->ts.secs = (guint32) (nsg_creltime / 1000000000);\
+ (rec)->ts.nsecs = (guint32) (nsg_creltime % 1000000000);\
+ }while(0)
+
+#define TIMEDEFV35 TIMEDEFV30
+
+/*
+** The maximum value of the record data size is 65535, which is less than
+** WTAP_MAX_PACKET_SIZE_STANDARD will ever be, so we don't need to check it.
+*/
+#define FULLSIZEDEFV30(rec,fp,ver)\
+ do {\
+ (rec)->presence_flags |= WTAP_HAS_CAP_LEN;\
+ (rec)->rec_header.packet_header.len = pletoh16(&fp->fp_PktSizeOrg) + nspr_pktracefull_v##ver##_s + fp->fp_src_vmname_len + fp->fp_dst_vmname_len;\
+ (rec)->rec_header.packet_header.caplen = nspr_getv20recordsize((nspr_hd_v20_t *)fp);\
+ }while(0)
+
+#define FULLSIZEDEFV35(rec,fp,ver)\
+ do {\
+ (rec)->presence_flags |= WTAP_HAS_CAP_LEN;\
+ (rec)->rec_header.packet_header.len = pletoh16(&fp->fp_PktSizeOrg) + pletoh16(&fp->fp_headerlen);\
+ (rec)->rec_header.packet_header.caplen = nspr_getv20recordsize((nspr_hd_v20_t *)fp);\
+ }while(0)
+
+#define PACKET_DESCRIBE(rec,buf,FULLPART,ver,enumprefix,type,structname,HEADERVER)\
+ do {\
+ /* Make sure the record header is entirely contained in the page */\
+ if ((nstrace->nstrace_buflen - nstrace_buf_offset) < sizeof(nspr_##structname##_t)) {\
+ *err = WTAP_ERR_BAD_FILE;\
+ *err_info = g_strdup("nstrace: record header crosses page boundary");\
+ g_free(nstrace_tmpbuff);\
+ return FALSE;\
+ }\
+ nspr_##structname##_t *fp = (nspr_##structname##_t *) &nstrace_buf[nstrace_buf_offset];\
+ (rec)->rec_type = REC_TYPE_PACKET;\
+ (rec)->block = wtap_block_create(WTAP_BLOCK_PACKET);\
+ TIMEDEFV##ver((rec),fp,type);\
+ FULLPART##SIZEDEFV##ver((rec),fp,ver);\
+ TRACE_V##ver##_REC_LEN_OFF((rec),enumprefix,type,structname);\
+ SETETHOFFSET_##ver(rec)\
+ (rec)->rec_header.packet_header.pseudo_header.nstr.rec_type = NSPR_HEADER_VERSION##HEADERVER;\
+ /* Check sanity of record size */\
+ if ((rec)->rec_header.packet_header.caplen < sizeof *fp) {\
+ *err = WTAP_ERR_BAD_FILE;\
+ *err_info = g_strdup("nstrace: record size is less than record header size");\
+ g_free(nstrace_tmpbuff);\
+ return FALSE;\
+ }\
+ ws_buffer_assure_space((buf), (rec)->rec_header.packet_header.caplen);\
+ *data_offset = nstrace->xxx_offset + nstrace_buf_offset;\
+ /* Copy record header */\
+ while (nstrace_tmpbuff_off < nspr_##structname##_s) {\
+ if (nstrace_buf_offset >= nstrace_buflen) {\
+ *err = WTAP_ERR_BAD_FILE;\
+ *err_info = g_strdup("nstrace: malformed file");\
+ g_free(nstrace_tmpbuff);\
+ return FALSE;\
+ }\
+ nstrace_tmpbuff[nstrace_tmpbuff_off++] = nstrace_buf[nstrace_buf_offset++];\
+ }\
+ nst_dataSize = nspr_getv20recordsize(hdp);\
+ rec_size = nst_dataSize - nstrace_tmpbuff_off;\
+ nsg_nextPageOffset = ((nstrace_buf_offset + rec_size) >= (guint)nstrace->nstrace_buflen) ?\
+ ((nstrace_buf_offset + rec_size) - (NSPR_PAGESIZE_TRACE - 1)) : 0;\
+ /* Copy record data */\
+ while (nsg_nextPageOffset) {\
+ /* Copy everything from this page */\
+ while (nstrace_buf_offset < nstrace->nstrace_buflen) {\
+ nstrace_tmpbuff[nstrace_tmpbuff_off++] = nstrace_buf[nstrace_buf_offset++];\
+ }\
+ nstrace->xxx_offset += nstrace_buflen;\
+ nstrace_buflen = NSPR_PAGESIZE_TRACE;\
+ /* Read the next page */\
+ bytes_read = file_read(nstrace_buf, NSPR_PAGESIZE_TRACE, wth->fh);\
+ if ( !file_eof(wth->fh) && bytes_read != NSPR_PAGESIZE_TRACE) {\
+ g_free(nstrace_tmpbuff);\
+ return FALSE;\
+ } else {\
+ nstrace_buf_offset = 0;\
+ }\
+ nstrace_buflen = bytes_read;\
+ rec_size = nst_dataSize - nstrace_tmpbuff_off;\
+ nsg_nextPageOffset = ((nstrace_buf_offset + rec_size) >= (guint)nstrace->nstrace_buflen) ?\
+ ((nstrace_buf_offset + rec_size) - (NSPR_PAGESIZE_TRACE- 1)): 0;\
+ } \
+ /* Copy the rest of the record */\
+ while (nstrace_tmpbuff_off < nst_dataSize) {\
+ nstrace_tmpbuff[nstrace_tmpbuff_off++] = nstrace_buf[nstrace_buf_offset++];\
+ }\
+ memcpy(ws_buffer_start_ptr((buf)), nstrace_tmpbuff, (rec)->rec_header.packet_header.caplen);\
+ nstrace->nstrace_buf_offset = nstrace_buf_offset;\
+ nstrace->nstrace_buflen = nstrace_buflen;\
+ nstrace->nsg_creltime = nsg_creltime;\
+ g_free(nstrace_tmpbuff);\
+ return TRUE;\
+ } while(0)
+
+static gboolean nstrace_read_v30(wtap *wth, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info, gint64 *data_offset)
+{
+ nstrace_t *nstrace = (nstrace_t *)wth->priv;
+ guint64 nsg_creltime;
+ gchar *nstrace_buf = nstrace->pnstrace_buf;
+ guint32 nstrace_buf_offset = nstrace->nstrace_buf_offset;
+ guint32 nstrace_buflen = nstrace->nstrace_buflen;
+ guint8* nstrace_tmpbuff;
+ guint32 nstrace_tmpbuff_off=0,nst_dataSize=0,rec_size=0,nsg_nextPageOffset=0;
+ nspr_hd_v20_t *hdp;
+ int bytes_read = 0;
+
+ *err = 0;
+ *err_info = NULL;
+ if(nstrace_buflen == 0){
+ return FALSE; /* Reached End Of File */
+ }
+
+ nstrace_tmpbuff = (guint8*)g_malloc(65536);
+
+ do
+ {
+
+ if (nstrace_buf_offset >= nstrace_buflen) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("nstrace: malformed file");
+ g_free(nstrace_tmpbuff);
+ return FALSE;
+ }
+
+ if (!nstrace_buf[nstrace_buf_offset] && nstrace_buf_offset <= NSPR_PAGESIZE_TRACE){
+ nstrace_buf_offset = NSPR_PAGESIZE_TRACE;
+ }
+ if (file_eof(wth->fh) && bytes_read > 0 && bytes_read < NSPR_PAGESIZE_TRACE){
+ memset(&nstrace_buf[bytes_read], 0, NSPR_PAGESIZE_TRACE-bytes_read);
+ }
+ while ((nstrace_buf_offset < NSPR_PAGESIZE_TRACE) &&
+ nstrace_buf[nstrace_buf_offset])
+ {
+ if (!nstrace_ensure_buflen(nstrace, nstrace_buf_offset, sizeof(nspr_hd_v20_t), err, err_info)) {
+ g_free(nstrace_tmpbuff);
+ return FALSE;
+ }
+ hdp = (nspr_hd_v20_t *) &nstrace_buf[nstrace_buf_offset];
+ if (nspr_getv20recordsize(hdp) == 0) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("nstrace: zero size record found");
+ g_free(nstrace_tmpbuff);
+ return FALSE;
+ }
+ switch (hdp->phd_RecordType)
+ {
+
+#define GENERATE_CASE_FULL_V30(rec,buf,ver,HEADERVER) \
+ case NSPR_PDPKTRACEFULLTX_V##ver:\
+ case NSPR_PDPKTRACEFULLTXB_V##ver:\
+ case NSPR_PDPKTRACEFULLRX_V##ver:\
+ case NSPR_PDPKTRACEFULLNEWRX_V##ver:\
+ PACKET_DESCRIBE(rec,buf,FULL,ver,v##ver##_full,fp,pktracefull_v##ver,HEADERVER);
+
+ GENERATE_CASE_FULL_V30(rec,buf,30,300);
+
+#undef GENERATE_CASE_FULL_V30
+
+#define GENERATE_CASE_FULL_V35(rec,buf,ver,HEADERVER) \
+ case NSPR_PDPKTRACEFULLTX_V##ver:\
+ case NSPR_PDPKTRACEFULLTXB_V##ver:\
+ case NSPR_PDPKTRACEFULLRX_V##ver:\
+ case NSPR_PDPKTRACEFULLNEWRX_V##ver:\
+ PACKET_DESCRIBE(rec,buf,FULL,ver,v##ver##_full,fp,pktracefull_v##ver,HEADERVER);
+ GENERATE_CASE_FULL_V35(rec,buf,35,350);
+
+#undef GENERATE_CASE_FULL_V35
+
+ case NSPR_ABSTIME_V20:
+ {
+ nstrace_buf_offset += nspr_getv20recordsize(hdp);
+ if (!nstrace_ensure_buflen(nstrace, nstrace_buf_offset, sizeof(nspr_abstime_v20_t), err, err_info)) {
+ g_free(nstrace_tmpbuff);
+ return FALSE;
+ }
+ ns_setabstime(nstrace, pletoh32(&((nspr_abstime_v20_t *) &nstrace_buf[nstrace_buf_offset])->abs_Time), pletoh16(&((nspr_abstime_v20_t *) &nstrace_buf[nstrace_buf_offset])->abs_RelTime));
+ break;
+ }
+
+ case NSPR_RELTIME_V20:
+ {
+ if (!nstrace_ensure_buflen(nstrace, nstrace_buf_offset, sizeof(nspr_abstime_v20_t), err, err_info)) {
+ g_free(nstrace_tmpbuff);
+ return FALSE;
+ }
+ ns_setrelativetime(nstrace, pletoh16(&((nspr_abstime_v20_t *) &nstrace_buf[nstrace_buf_offset])->abs_RelTime));
+ nstrace_buf_offset += nspr_getv20recordsize(hdp);
+ break;
+ }
+
+ default:
+ {
+ if (!nstrace_ensure_buflen(nstrace, nstrace_buf_offset, sizeof(nspr_hd_v20_t), err, err_info)) {
+ g_free(nstrace_tmpbuff);
+ return FALSE;
+ }
+ nstrace_buf_offset += nspr_getv20recordsize(hdp);
+ break;
+ }
+ }
+ }
+ nstrace_buf_offset = 0;
+ nstrace->xxx_offset += nstrace_buflen;
+ nstrace_buflen = NSPR_PAGESIZE_TRACE;
+ } while((nstrace_buflen > 0) && (bytes_read = file_read(nstrace_buf, nstrace_buflen, wth->fh)) > 0 && (file_eof(wth->fh) || (guint32)bytes_read == nstrace_buflen));
+
+ if (bytes_read < 0)
+ *err = file_error(wth->fh, err_info);
+ else
+ *err = 0;
+ g_free(nstrace_tmpbuff);
+ return FALSE;
+}
+
+#undef PACKET_DESCRIBE
+
+/*
+ * XXX - for these, we can't set the time stamp in the seek-read
+ * routine, because the time stamps are relative.
+ */
+#undef TIMEDEFV10
+#define TIMEDEFV10(rec,fp,type) \
+ do {\
+ (rec)->presence_flags = 0;\
+ }while(0)
+
+#define PACKET_DESCRIBE(rec,FULLPART,fullpart,ver,type,HEADERVER) \
+ do {\
+ nspr_pktrace##fullpart##_v##ver##_t *type = (nspr_pktrace##fullpart##_v##ver##_t *) pd;\
+ (rec)->rec_type = REC_TYPE_PACKET;\
+ (rec)->block = wtap_block_create(WTAP_BLOCK_PACKET);\
+ TIMEDEFV##ver((rec),fp,type);\
+ FULLPART##SIZEDEFV##ver((rec),type,ver);\
+ TRACE_V##ver##_REC_LEN_OFF(rec,v##ver##_##fullpart,type,pktrace##fullpart##_v##ver);\
+ (rec)->rec_header.packet_header.pseudo_header.nstr.rec_type = NSPR_HEADER_VERSION##HEADERVER;\
+ }while(0)
+
+static gboolean nstrace_seek_read_v10(wtap *wth, gint64 seek_off,
+ wtap_rec *rec, Buffer *buf, int *err, gchar **err_info)
+{
+ nspr_hd_v10_t hdr;
+ guint record_length;
+ guint8 *pd;
+ unsigned int bytes_to_read;
+
+ *err = 0;
+
+ if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
+ return FALSE;
+
+ /*
+ ** Read the record header.
+ */
+ if (!wtap_read_bytes(wth->random_fh, (void *)&hdr, sizeof hdr,
+ err, err_info))
+ return FALSE;
+
+ /*
+ ** Get the record length.
+ */
+ record_length = nspr_getv10recordsize(&hdr);
+
+ /*
+ ** Copy the header to the buffer and read the rest of the record..
+ */
+ ws_buffer_assure_space(buf, record_length);
+ pd = ws_buffer_start_ptr(buf);
+ memcpy(pd, (void *)&hdr, sizeof hdr);
+ if (record_length > sizeof hdr) {
+ bytes_to_read = (unsigned int)(record_length - sizeof hdr);
+ if (!wtap_read_bytes(wth->random_fh, pd + sizeof hdr, bytes_to_read,
+ err, err_info))
+ return FALSE;
+ }
+
+ /*
+ ** Fill in what part of the struct wtap_rec we can.
+ */
+#define GENERATE_CASE_FULL(rec,type,HEADERVER) \
+ case NSPR_PDPKTRACEFULLTX_V##type:\
+ case NSPR_PDPKTRACEFULLTXB_V##type:\
+ case NSPR_PDPKTRACEFULLRX_V##type:\
+ PACKET_DESCRIBE(rec,FULL,full,type,fp,HEADERVER);\
+ break;
+
+#define GENERATE_CASE_PART(rec,type,HEADERVER) \
+ case NSPR_PDPKTRACEPARTTX_V##type:\
+ case NSPR_PDPKTRACEPARTTXB_V##type:\
+ case NSPR_PDPKTRACEPARTRX_V##type:\
+ PACKET_DESCRIBE(rec,PART,part,type,pp,HEADERVER);\
+ break;
+
+ switch (pletoh16(&(( nspr_header_v10_t*)pd)->ph_RecordType))
+ {
+ GENERATE_CASE_FULL(rec,10,100)
+ GENERATE_CASE_PART(rec,10,100)
+ }
+
+#undef GENERATE_CASE_FULL
+#undef GENERATE_CASE_PART
+
+ return TRUE;
+}
+
+#undef PACKET_DESCRIBE
+
+/*
+ * XXX - for these, we can't set the time stamp in the seek-read
+ * routine, because the time stamps are relative.
+ */
+#undef TIMEDEFV20
+#define TIMEDEFV20(rec,fp,type) \
+ do {\
+ (rec)->presence_flags = 0;\
+ }while(0)
+
+#undef TIMEDEFV21
+#undef TIMEDEFV22
+#define TIMEDEFV21(rec,fp,type) TIMEDEFV20(rec,fp,type)
+#define TIMEDEFV22(rec,fp,type) TIMEDEFV20(rec,fp,type)
+
+#define PACKET_DESCRIBE(rec,FULLPART,ver,enumprefix,type,structname,HEADERVER)\
+ do {\
+ nspr_##structname##_t *fp= (nspr_##structname##_t*)pd;\
+ (rec)->rec_type = REC_TYPE_PACKET;\
+ (rec)->block = wtap_block_create(WTAP_BLOCK_PACKET);\
+ TIMEDEFV##ver((rec),fp,type);\
+ FULLPART##SIZEDEFV##ver((rec),fp,ver);\
+ TRACE_V##ver##_REC_LEN_OFF((rec),enumprefix,type,structname);\
+ (rec)->rec_header.packet_header.pseudo_header.nstr.rec_type = NSPR_HEADER_VERSION##HEADERVER;\
+ return TRUE;\
+ }while(0)
+
+static gboolean nstrace_seek_read_v20(wtap *wth, gint64 seek_off,
+ wtap_rec *rec, Buffer *buf, int *err, gchar **err_info)
+{
+ nspr_hd_v20_t hdr;
+ guint record_length;
+ guint hdrlen;
+ guint8 *pd;
+ unsigned int bytes_to_read;
+ guint64 nsg_creltime;
+
+ *err = 0;
+
+ if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
+ return FALSE;
+
+ /*
+ ** Read the first 2 bytes of the record header.
+ */
+ if (!wtap_read_bytes(wth->random_fh, (void *)&hdr, 2, err, err_info))
+ return FALSE;
+ hdrlen = 2;
+
+ /*
+ ** Is there a third byte? If so, read it.
+ */
+ if (hdr.phd_RecordSizeLow & NSPR_V20RECORDSIZE_2BYTES) {
+ if (!wtap_read_bytes(wth->random_fh, (void *)&hdr.phd_RecordSizeHigh, 1,
+ err, err_info))
+ return FALSE;
+ hdrlen = 3;
+ }
+
+ /*
+ ** Get the record length.
+ */
+ record_length = nspr_getv20recordsize(&hdr);
+
+ /*
+ ** Copy the header to the buffer and read the rest of the record..
+ */
+ ws_buffer_assure_space(buf, record_length);
+ pd = ws_buffer_start_ptr(buf);
+ memcpy(pd, (void *)&hdr, hdrlen);
+ if (record_length > hdrlen) {
+ bytes_to_read = (unsigned int)(record_length - hdrlen);
+ if (!wtap_read_bytes(wth->random_fh, pd + hdrlen, bytes_to_read,
+ err, err_info))
+ return FALSE;
+ }
+
+#define GENERATE_CASE_FULL(rec,ver,HEADERVER) \
+ case NSPR_PDPKTRACEFULLTX_V##ver:\
+ case NSPR_PDPKTRACEFULLTXB_V##ver:\
+ case NSPR_PDPKTRACEFULLRX_V##ver:\
+ PACKET_DESCRIBE(rec,FULL,ver,v##ver##_full,fp,pktracefull_v##ver,HEADERVER);
+
+#define GENERATE_CASE_FULL_V25(rec,ver,HEADERVER) \
+ case NSPR_PDPKTRACEFULLTX_V##ver:\
+ case NSPR_PDPKTRACEFULLTXB_V##ver:\
+ case NSPR_PDPKTRACEFULLRX_V##ver:\
+ case NSPR_PDPKTRACEFULLNEWRX_V##ver:\
+ PACKET_DESCRIBE(rec,FULL,ver,v##ver##_full,fp,pktracefull_v##ver,HEADERVER);
+
+#define GENERATE_CASE_PART(rec,ver,HEADERVER) \
+ case NSPR_PDPKTRACEPARTTX_V##ver:\
+ case NSPR_PDPKTRACEPARTTXB_V##ver:\
+ case NSPR_PDPKTRACEPARTRX_V##ver:\
+ PACKET_DESCRIBE(rec,PART,ver,v##ver##_part,pp,pktracepart_v##ver,HEADERVER);
+
+#define GENERATE_CASE_PART_V25(rec,ver,HEADERVER) \
+ case NSPR_PDPKTRACEPARTTX_V##ver:\
+ case NSPR_PDPKTRACEPARTTXB_V##ver:\
+ case NSPR_PDPKTRACEPARTRX_V##ver:\
+ case NSPR_PDPKTRACEPARTNEWRX_V##ver:\
+ PACKET_DESCRIBE(rec,PART,ver,v##ver##_part,pp,pktracepart_v##ver,HEADERVER);
+
+ switch ((( nspr_hd_v20_t*)pd)->phd_RecordType)
+ {
+ GENERATE_CASE_FULL(rec,20,200)
+ GENERATE_CASE_PART(rec,20,200)
+ GENERATE_CASE_FULL(rec,21,201)
+ GENERATE_CASE_PART(rec,21,201)
+ GENERATE_CASE_FULL(rec,22,202)
+ GENERATE_CASE_PART(rec,22,202)
+ GENERATE_CASE_FULL(rec,23,203)
+ GENERATE_CASE_PART(rec,23,203)
+ GENERATE_CASE_FULL_V25(rec,24,204)
+ GENERATE_CASE_PART_V25(rec,24,204)
+ GENERATE_CASE_FULL_V25(rec,25,205)
+ GENERATE_CASE_PART_V25(rec,25,205)
+ GENERATE_CASE_FULL_V25(rec,26,206)
+ GENERATE_CASE_PART_V25(rec,26,206)
+ }
+
+#undef GENERATE_CASE_FULL
+#undef GENERATE_CASE_FULL_V25
+#undef GENERATE_CASE_PART
+#undef GENERATE_CASE_PART_V25
+
+ return TRUE;
+}
+
+#undef PACKET_DESCRIBE
+#undef SETETHOFFSET_35
+#undef SETETHOFFSET_30
+
+#define SETETHOFFSET_35(rec)\
+ {\
+ (rec)->rec_header.packet_header.pseudo_header.nstr.eth_offset = pletoh16(&fp->fp_headerlen);\
+ }
+
+#define SETETHOFFSET_30(rec) ;\
+
+#define PACKET_DESCRIBE(rec,FULLPART,ver,enumprefix,type,structname,HEADERVER)\
+ do {\
+ nspr_##structname##_t *fp= (nspr_##structname##_t*)pd;\
+ (rec)->rec_type = REC_TYPE_PACKET;\
+ (rec)->block = wtap_block_create(WTAP_BLOCK_PACKET);\
+ TIMEDEFV##ver((rec),fp,type);\
+ SETETHOFFSET_##ver(rec);\
+ FULLPART##SIZEDEFV##ver((rec),fp,ver);\
+ TRACE_V##ver##_REC_LEN_OFF((rec),enumprefix,type,structname);\
+ (rec)->rec_header.packet_header.pseudo_header.nstr.rec_type = NSPR_HEADER_VERSION##HEADERVER;\
+ return TRUE;\
+ }while(0)
+
+static gboolean nstrace_seek_read_v30(wtap *wth, gint64 seek_off,
+ wtap_rec *rec, Buffer *buf, int *err, gchar **err_info)
+{
+ nspr_hd_v20_t hdr;
+ guint record_length;
+ guint hdrlen;
+ guint8 *pd;
+ unsigned int bytes_to_read;
+ guint64 nsg_creltime;
+
+ *err = 0;
+
+ if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
+ return FALSE;
+ /*
+ ** Read the first 2 bytes of the record header.
+ */
+ if (!wtap_read_bytes(wth->random_fh, (void *)&hdr, 2, err, err_info))
+ return FALSE;
+ hdrlen = 2;
+
+ /*
+ ** Is there a third byte? If so, read it.
+ */
+ if (hdr.phd_RecordSizeLow & NSPR_V20RECORDSIZE_2BYTES) {
+ if (!wtap_read_bytes(wth->random_fh, (void *)&hdr.phd_RecordSizeHigh, 1,
+ err, err_info))
+ return FALSE;
+ hdrlen = 3;
+ }
+
+ /*
+ ** Get the record length.
+ ** The maximum value of the record data size is 65535, which is less
+ ** than WTAP_MAX_PACKET_SIZE_STANDARD will ever be, so we don't need to check it.
+ */
+ record_length = nspr_getv20recordsize(&hdr);
+
+ /*
+ ** Copy the header to the buffer and read the rest of the record..
+ */
+ ws_buffer_assure_space(buf, record_length);
+ pd = ws_buffer_start_ptr(buf);
+ memcpy(pd, (void *)&hdr, hdrlen);
+ if (record_length > hdrlen) {
+ bytes_to_read = (unsigned int)(record_length - hdrlen);
+ if (!wtap_read_bytes(wth->random_fh, pd + hdrlen, bytes_to_read,
+ err, err_info))
+ return FALSE;
+ }
+
+ (rec)->rec_header.packet_header.caplen = (rec)->rec_header.packet_header.len = record_length;
+
+#define GENERATE_CASE_V30(rec,ver,HEADERVER) \
+ case NSPR_PDPKTRACEFULLTX_V##ver:\
+ case NSPR_PDPKTRACEFULLTXB_V##ver:\
+ case NSPR_PDPKTRACEFULLRX_V##ver:\
+ case NSPR_PDPKTRACEFULLNEWRX_V##ver:\
+ PACKET_DESCRIBE(rec,FULL,ver,v##ver##_full,fp,pktracefull_v##ver,HEADERVER);
+
+ switch ((( nspr_hd_v20_t*)pd)->phd_RecordType)
+ {
+ GENERATE_CASE_V30(rec,30, 300);
+ GENERATE_CASE_V30(rec,35, 350);
+ }
+
+ return TRUE;
+}
+
+
+/*
+** Netscaler trace format close routines.
+*/
+static void nstrace_close(wtap *wth)
+{
+ nstrace_t *nstrace = (nstrace_t *)wth->priv;
+
+ g_free(nstrace->pnstrace_buf);
+}
+
+
+#define NSTRACE_1_0 0
+#define NSTRACE_2_0 1
+#define NSTRACE_3_0 2
+#define NSTRACE_3_5 3
+
+typedef struct {
+ guint version;
+ guint16 page_offset;
+ guint16 page_len;
+ guint32 absrec_time;
+ gboolean newfile;
+} nstrace_dump_t;
+
+/* Returns 0 if we could write the specified encapsulation type,
+** an error indication otherwise. */
+static int nstrace_10_dump_can_write_encap(int encap)
+{
+ if (encap == WTAP_ENCAP_NSTRACE_1_0)
+ return 0;
+
+ return WTAP_ERR_UNWRITABLE_ENCAP;
+}
+
+
+/* Returns 0 if we could write the specified encapsulation type,
+** an error indication otherwise. */
+static int nstrace_20_dump_can_write_encap(int encap)
+{
+ if (encap == WTAP_ENCAP_NSTRACE_2_0)
+ return 0;
+
+ return WTAP_ERR_UNWRITABLE_ENCAP;
+}
+
+/* Returns 0 if we could write the specified encapsulation type,
+** an error indication otherwise. */
+static int nstrace_30_dump_can_write_encap(int encap)
+{
+ if (encap == WTAP_ENCAP_NSTRACE_3_0)
+ return 0;
+
+ return WTAP_ERR_UNWRITABLE_ENCAP;
+}
+
+/* Returns 0 if we could write the specified encapsulation type,
+** an error indication otherwise. */
+static int nstrace_35_dump_can_write_encap(int encap)
+{
+ if (encap == WTAP_ENCAP_NSTRACE_3_5)
+ return 0;
+
+ return WTAP_ERR_UNWRITABLE_ENCAP;
+}
+
+/* Returns TRUE on success, FALSE on failure; sets "*err" to an error code on
+** failure */
+static gboolean nstrace_dump_open(wtap_dumper *wdh, guint version, int *err _U_,
+ gchar **err_info _U_)
+{
+ nstrace_dump_t *nstrace;
+
+ wdh->subtype_write = nstrace_dump;
+
+ nstrace = g_new(nstrace_dump_t, 1);
+ wdh->priv = (void *)nstrace;
+ nstrace->version = version;
+ nstrace->page_offset = 0;
+ if ((nstrace->version == NSTRACE_3_0) ||
+ (nstrace->version == NSTRACE_3_5))
+ nstrace->page_len = NSPR_PAGESIZE_TRACE;
+ else
+ nstrace->page_len = NSPR_PAGESIZE;
+
+ nstrace->absrec_time = 0;
+ nstrace->newfile = TRUE;
+
+ return TRUE;
+}
+
+static gboolean nstrace_10_dump_open(wtap_dumper *wdh, int *err,
+ gchar **err_info)
+{
+ return nstrace_dump_open(wdh, NSTRACE_1_0, err, err_info);
+}
+
+static gboolean nstrace_20_dump_open(wtap_dumper *wdh, int *err,
+ gchar **err_info)
+{
+ return nstrace_dump_open(wdh, NSTRACE_2_0, err, err_info);
+}
+
+static gboolean nstrace_30_dump_open(wtap_dumper *wdh, int *err,
+ gchar **err_info)
+{
+ return nstrace_dump_open(wdh, NSTRACE_3_0, err, err_info);
+}
+
+static gboolean nstrace_35_dump_open(wtap_dumper *wdh, int *err,
+ gchar **err_info)
+{
+ return nstrace_dump_open(wdh, NSTRACE_3_5, err, err_info);
+}
+
+static gboolean nstrace_add_signature(wtap_dumper *wdh, int *err)
+{
+ nstrace_dump_t *nstrace = (nstrace_dump_t *)wdh->priv;
+
+ if (nstrace->version == NSTRACE_1_0)
+ {
+ guint16 val16b;
+ nspr_signature_v10_t sig10;
+
+ /* populate the record */
+ val16b = GUINT16_TO_LE(NSPR_SIGNATURE_V10);
+ memcpy(sig10.phd.ph_RecordType, &val16b, sizeof sig10.phd.ph_RecordType);
+ val16b = GUINT16_TO_LE(nspr_signature_v10_s);
+ memcpy(sig10.phd.ph_RecordSize, &val16b, sizeof sig10.phd.ph_RecordSize);
+ memset(sig10.sig_Signature, 0, NSPR_SIGSIZE_V10);
+ (void) g_strlcpy(sig10.sig_Signature, NSPR_SIGSTR_V10, NSPR_SIGSIZE_V10);
+
+ /* Write the record into the file */
+ if (!wtap_dump_file_write(wdh, &sig10, nspr_signature_v10_s,
+ err))
+ return FALSE;
+
+ /* Move forward the page offset */
+ nstrace->page_offset += (guint16) nspr_signature_v10_s;
+
+ } else if (nstrace->version == NSTRACE_2_0)
+ {
+ nspr_signature_v20_t sig20;
+
+ sig20.sig_RecordType = NSPR_SIGNATURE_V20;
+ sig20.sig_RecordSize = nspr_signature_v20_s;
+ memcpy(sig20.sig_Signature, NSPR_SIGSTR_V20, sizeof(NSPR_SIGSTR_V20));
+
+ /* Write the record into the file */
+ if (!wtap_dump_file_write(wdh, &sig20, sig20.sig_RecordSize,
+ err))
+ return FALSE;
+
+ /* Move forward the page offset */
+ nstrace->page_offset += (guint16) sig20.sig_RecordSize;
+
+ } else if (nstrace->version == NSTRACE_3_0)
+ {
+ nspr_signature_v30_t sig30;
+
+ sig30.sig_RecordType = NSPR_SIGNATURE_V30;
+ sig30.sig_RecordSize = nspr_signature_v30_s;
+ memcpy(sig30.sig_Signature, NSPR_SIGSTR_V30, sizeof(NSPR_SIGSTR_V30));
+
+ /* Write the record into the file */
+ if (!wtap_dump_file_write(wdh, &sig30, sig30.sig_RecordSize,
+ err))
+ return FALSE;
+
+ /* Move forward the page offset */
+ nstrace->page_offset += (guint16) sig30.sig_RecordSize;
+ } else if (nstrace->version == NSTRACE_3_5)
+ {
+ nspr_signature_v35_t sig35;
+
+ sig35.sig_RecordType = NSPR_SIGNATURE_V35;
+ sig35.sig_RecordSize = nspr_signature_v35_s;
+ memcpy(sig35.sig_Signature, NSPR_SIGSTR_V35, sizeof(NSPR_SIGSTR_V35));
+
+ /* Write the record into the file */
+ if (!wtap_dump_file_write(wdh, &sig35, sig35.sig_RecordSize,
+ err))
+ return FALSE;
+
+ /* Move forward the page offset */
+ nstrace->page_offset += (guint16) sig35.sig_RecordSize;
+ } else
+ {
+ ws_assert_not_reached();
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+static gboolean
+nstrace_add_abstime(wtap_dumper *wdh, const wtap_rec *rec,
+ const guint8 *pd, int *err)
+{
+ nstrace_dump_t *nstrace = (nstrace_dump_t *)wdh->priv;
+ guint64 nsg_creltime;
+
+ if (nstrace->version == NSTRACE_1_0)
+ {
+ guint16 val16;
+ guint32 reltime;
+ guint64 abstime;
+ nspr_abstime_v10_t abs10;
+
+ /* populate the record */
+ val16 = GUINT16_TO_LE(NSPR_ABSTIME_V10);
+ memcpy(abs10.phd.ph_RecordType, &val16, sizeof abs10.phd.ph_RecordType);
+ val16 = GUINT16_TO_LE(nspr_abstime_v10_s);
+ memcpy(abs10.phd.ph_RecordSize, &val16, sizeof abs10.phd.ph_RecordSize);
+
+ memcpy(&reltime, ((const nspr_pktracefull_v10_t *)pd)->fp_RelTimeHr, sizeof reltime);
+ nsg_creltime = ns_hrtime2nsec(reltime);
+
+ memset(abs10.abs_RelTime, 0, sizeof abs10.abs_RelTime);
+ abstime = GUINT32_TO_LE((guint32)rec->ts.secs - (guint32)(nsg_creltime/1000000000));
+ memcpy(abs10.abs_Time, &abstime, sizeof abs10.abs_Time);
+
+ /* Write the record into the file */
+ if (!wtap_dump_file_write(wdh, &abs10, nspr_abstime_v10_s, err))
+ return FALSE;
+
+ /* Move forward the page offset */
+ nstrace->page_offset += nspr_abstime_v10_s;
+
+ } else if ((nstrace->version == NSTRACE_2_0) ||
+ (nstrace->version == NSTRACE_3_0) ||
+ (nstrace->version == NSTRACE_3_5)) {
+ guint32 reltime;
+ guint64 abstime;
+ nspr_abstime_v20_t abs20;
+
+ abs20.abs_RecordType = NSPR_ABSTIME_V20;
+ abs20.abs_RecordSize = nspr_abstime_v20_s;
+
+ memcpy(&reltime, ((const nspr_pktracefull_v20_t *)pd)->fp_RelTimeHr, sizeof reltime);
+ nsg_creltime = ns_hrtime2nsec(reltime);
+
+ memset(abs20.abs_RelTime, 0, sizeof abs20.abs_RelTime);
+ abstime = GUINT32_TO_LE((guint32)rec->ts.secs - (guint32)(nsg_creltime/1000000000));
+ memcpy(abs20.abs_RelTime, &abstime, sizeof abs20.abs_RelTime);
+
+ /* Write the record into the file */
+ if (!wtap_dump_file_write(wdh, &abs20, nspr_abstime_v20_s, err))
+ return FALSE;
+
+ /* Move forward the page offset */
+ nstrace->page_offset += nspr_abstime_v20_s;
+
+ } else
+ {
+ ws_assert_not_reached();
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+/* Write a record for a packet to a dump file.
+ Returns TRUE on success, FALSE on failure. */
+static gboolean nstrace_dump(wtap_dumper *wdh, const wtap_rec *rec,
+ const guint8 *pd, int *err, gchar **err_info _U_)
+{
+ nstrace_dump_t *nstrace = (nstrace_dump_t *)wdh->priv;
+
+ /* We can only write packet records. */
+ if (rec->rec_type != REC_TYPE_PACKET) {
+ *err = WTAP_ERR_UNWRITABLE_REC_TYPE;
+ return FALSE;
+ }
+
+ /*
+ * Make sure this packet doesn't have a link-layer type that
+ * differs from the one for the file.
+ */
+ if (wdh->file_encap != rec->rec_header.packet_header.pkt_encap) {
+ *err = WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
+ return FALSE;
+ }
+
+ if (nstrace->newfile == TRUE)
+ {
+ nstrace->newfile = FALSE;
+ /* Add the signature record and abs time record */
+ if (nstrace->version == NSTRACE_1_0)
+ {
+ if (!nstrace_add_signature(wdh, err) ||
+ !nstrace_add_abstime(wdh, rec, pd, err))
+ return FALSE;
+ } else if (nstrace->version == NSTRACE_2_0)
+ {
+ if (!nstrace_add_signature(wdh, err) ||
+ !nstrace_add_abstime(wdh, rec, pd, err))
+ return FALSE;
+ } else if (nstrace->version == NSTRACE_3_0 ||
+ nstrace->version == NSTRACE_3_5 )
+ {
+ if (!nstrace_add_signature(wdh, err) ||
+ !nstrace_add_abstime(wdh, rec, pd, err))
+ return FALSE;
+ } else
+ {
+ ws_assert_not_reached();
+ return FALSE;
+ }
+ }
+
+ switch (rec->rec_header.packet_header.pseudo_header.nstr.rec_type)
+ {
+ case NSPR_HEADER_VERSION100:
+
+ if (nstrace->version == NSTRACE_1_0)
+ {
+ if (nstrace->page_offset + rec->rec_header.packet_header.caplen >= nstrace->page_len)
+ {
+ /* Start on the next page */
+ if (wtap_dump_file_seek(wdh, (nstrace->page_len - nstrace->page_offset), SEEK_CUR, err) == -1)
+ return FALSE;
+
+ nstrace->page_offset = 0;
+
+ /* Possibly add signature and abstime records and increment offset */
+ if (!nstrace_add_signature(wdh, err))
+ return FALSE;
+ }
+
+ /* Write the actual record as is */
+ if (!wtap_dump_file_write(wdh, pd, rec->rec_header.packet_header.caplen, err))
+ return FALSE;
+
+ nstrace->page_offset += (guint16) rec->rec_header.packet_header.caplen;
+ } else if (nstrace->version == NSTRACE_2_0)
+ {
+ *err = WTAP_ERR_UNWRITABLE_FILE_TYPE;
+ return FALSE;
+ }
+
+ break;
+
+ case NSPR_HEADER_VERSION200:
+ case NSPR_HEADER_VERSION201:
+ case NSPR_HEADER_VERSION202:
+ case NSPR_HEADER_VERSION203:
+ case NSPR_HEADER_VERSION204:
+ case NSPR_HEADER_VERSION205:
+ case NSPR_HEADER_VERSION206:
+ if (nstrace->version == NSTRACE_1_0)
+ {
+ *err = WTAP_ERR_UNWRITABLE_FILE_TYPE;
+ return FALSE;
+ } else if (nstrace->version == NSTRACE_2_0)
+ {
+ if (nstrace->page_offset + rec->rec_header.packet_header.caplen >= nstrace->page_len)
+ {
+ /* Start on the next page */
+ if (wtap_dump_file_seek(wdh, (nstrace->page_len - nstrace->page_offset), SEEK_CUR, err) == -1)
+ return FALSE;
+
+ nstrace->page_offset = 0;
+
+ /* Possibly add signature and abstime records and increment offset */
+ if (!nstrace_add_signature(wdh, err))
+ return FALSE;
+ }
+
+ /* Write the actual record as is */
+ if (!wtap_dump_file_write(wdh, pd, rec->rec_header.packet_header.caplen, err))
+ return FALSE;
+
+ nstrace->page_offset += (guint16) rec->rec_header.packet_header.caplen;
+ }
+
+ break;
+
+ case NSPR_HEADER_VERSION300:
+ case NSPR_HEADER_VERSION350:
+ if (nstrace->version == NSTRACE_1_0)
+ {
+ *err = WTAP_ERR_UNWRITABLE_FILE_TYPE;
+ return FALSE;
+ } else if (nstrace->version == NSTRACE_2_0)
+ {
+ *err = WTAP_ERR_UNWRITABLE_FILE_TYPE;
+ return FALSE;
+ } else if (nstrace->version == NSTRACE_3_0 || nstrace->version == NSTRACE_3_5)
+ {
+ if (nstrace->page_offset + rec->rec_header.packet_header.caplen >= nstrace->page_len)
+ {
+ /* Start on the next page */
+ if (wtap_dump_file_seek(wdh, (nstrace->page_len - nstrace->page_offset), SEEK_CUR, err) == -1)
+ return FALSE;
+
+ nstrace->page_offset = 0;
+
+ /* Possibly add signature and abstime records and increment offset */
+ if (!nstrace_add_signature(wdh, err))
+ return FALSE;
+ }
+
+ /* Write the actual record as is */
+ if (!wtap_dump_file_write(wdh, pd, rec->rec_header.packet_header.caplen, err))
+ return FALSE;
+
+ nstrace->page_offset += (guint16) rec->rec_header.packet_header.caplen;
+ } else
+ {
+ ws_assert_not_reached();
+ return FALSE;
+ }
+ break;
+
+ default:
+ ws_assert_not_reached();
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static const struct supported_block_type nstrace_1_0_blocks_supported[] = {
+ /*
+ * We support packet blocks, with no comments or other options.
+ */
+ { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
+};
+
+static const struct file_type_subtype_info nstrace_1_0_info = {
+ "NetScaler Trace (Version 1.0)", "nstrace10", NULL, NULL,
+ TRUE, BLOCKS_SUPPORTED(nstrace_1_0_blocks_supported),
+ nstrace_10_dump_can_write_encap, nstrace_10_dump_open, NULL
+};
+
+static const struct supported_block_type nstrace_2_0_blocks_supported[] = {
+ /*
+ * We support packet blocks, with no comments or other options.
+ */
+ { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
+};
+
+static const struct file_type_subtype_info nstrace_2_0_info = {
+ "NetScaler Trace (Version 2.0)", "nstrace20", "cap", NULL,
+ TRUE, BLOCKS_SUPPORTED(nstrace_2_0_blocks_supported),
+ nstrace_20_dump_can_write_encap, nstrace_20_dump_open, NULL
+};
+
+static const struct supported_block_type nstrace_3_0_blocks_supported[] = {
+ /*
+ * We support packet blocks, with no comments or other options.
+ */
+ { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
+};
+
+static const struct file_type_subtype_info nstrace_3_0_info = {
+ "NetScaler Trace (Version 3.0)", "nstrace30", "cap", NULL,
+ TRUE, BLOCKS_SUPPORTED(nstrace_3_0_blocks_supported),
+ nstrace_30_dump_can_write_encap, nstrace_30_dump_open, NULL
+};
+
+static const struct supported_block_type nstrace_3_5_blocks_supported[] = {
+ /*
+ * We support packet blocks, with no comments or other options.
+ */
+ { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
+};
+
+static const struct file_type_subtype_info nstrace_3_5_info = {
+ "NetScaler Trace (Version 3.5)", "nstrace35", "cap", NULL,
+ TRUE, BLOCKS_SUPPORTED(nstrace_3_5_blocks_supported),
+ nstrace_35_dump_can_write_encap, nstrace_35_dump_open, NULL
+};
+
+void register_nstrace(void)
+{
+ nstrace_1_0_file_type_subtype = wtap_register_file_type_subtype(&nstrace_1_0_info);
+ nstrace_2_0_file_type_subtype = wtap_register_file_type_subtype(&nstrace_2_0_info);
+ nstrace_3_0_file_type_subtype = wtap_register_file_type_subtype(&nstrace_3_0_info);
+ nstrace_3_5_file_type_subtype = wtap_register_file_type_subtype(&nstrace_3_5_info);
+
+ /*
+ * Register names for backwards compatibility with the
+ * wtap_filetypes table in Lua.
+ */
+ wtap_register_backwards_compatibility_lua_name("NETSCALER_1_0",
+ nstrace_1_0_file_type_subtype);
+ wtap_register_backwards_compatibility_lua_name("NETSCALER_2_0",
+ nstrace_2_0_file_type_subtype);
+ wtap_register_backwards_compatibility_lua_name("NETSCALER_3_0",
+ nstrace_3_0_file_type_subtype);
+ wtap_register_backwards_compatibility_lua_name("NETSCALER_3_5",
+ nstrace_3_5_file_type_subtype);
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * vi: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */
diff --git a/wiretap/netscaler.h b/wiretap/netscaler.h
new file mode 100644
index 00000000..a992bf8a
--- /dev/null
+++ b/wiretap/netscaler.h
@@ -0,0 +1,112 @@
+/** @file
+ *
+ * Wiretap Library
+ * Copyright (c) 2006 by Ravi Kondamuru <Ravi.Kondamuru@citrix.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef _NETSCALER_H
+#define _NETSCALER_H
+
+#include <glib.h>
+#include <wiretap/wtap.h>
+
+/* Physical Device full packet trace */
+#define NSPR_PDPKTRACEFULLTX_V10 0x0310 /* Transmitted */
+#define NSPR_PDPKTRACEFULLTX_V20 0xC0 /* Transmitted */
+#define NSPR_PDPKTRACEFULLTXB_V10 0x0311 /* In transmit buffer */
+#define NSPR_PDPKTRACEFULLTXB_V20 0xC1 /* In transmit buffer */
+#define NSPR_PDPKTRACEFULLRX_V10 0x0312 /* Received */
+#define NSPR_PDPKTRACEFULLRX_V20 0xC2 /* Received */
+
+/* Physical Device partial packet trace */
+
+#define NSPR_PDPKTRACEPARTTX_V10 0x0314 /* Transmitted */
+#define NSPR_PDPKTRACEPARTTX_V20 0xC4 /* Transmitted */
+#define NSPR_PDPKTRACEPARTTXB_V10 0x0315 /* In transmit buffer */
+#define NSPR_PDPKTRACEPARTTXB_V20 0xC5 /* In transmit buffer */
+#define NSPR_PDPKTRACEPARTRX_V10 0x0316 /* Received */
+#define NSPR_PDPKTRACEPARTRX_V20 0xC6 /* Received */
+
+/* pcb devno support (c.f. REQ16549) */
+#define NSPR_PDPKTRACEFULLTX_V21 0xD0 /* Transmitted */
+#define NSPR_PDPKTRACEFULLTXB_V21 0xD1 /* In transmit buffer */
+#define NSPR_PDPKTRACEFULLRX_V21 0xD2 /* Received */
+#define NSPR_PDPKTRACEPARTTX_V21 0xD4 /* Transmitted */
+#define NSPR_PDPKTRACEPARTTXB_V21 0xD5 /* In transmit buffer */
+#define NSPR_PDPKTRACEPARTRX_V21 0xD6 /* Received */
+
+/* vlan tag support (c.f. REQ24791) */
+#define NSPR_PDPKTRACEFULLTX_V22 0xE0 /* Transmitted */
+#define NSPR_PDPKTRACEFULLTXB_V22 0xE1 /* In transmit buffer */
+#define NSPR_PDPKTRACEFULLRX_V22 0xE2 /* Received */
+#define NSPR_PDPKTRACEPARTTX_V22 0xE4 /* Transmitted */
+#define NSPR_PDPKTRACEPARTTXB_V22 0xE5 /* In transmit buffer */
+#define NSPR_PDPKTRACEPARTRX_V22 0xE6 /* Received */
+
+/* Per core tracing */
+#define NSPR_PDPKTRACEFULLTX_V23 0xF0 /* Transmitted */
+#define NSPR_PDPKTRACEFULLTXB_V23 0xF1 /* In transmit buffer */
+#define NSPR_PDPKTRACEFULLRX_V23 0xF2 /* Received */
+#define NSPR_PDPKTRACEPARTTX_V23 0xF4 /* Transmitted */
+#define NSPR_PDPKTRACEPARTTXB_V23 0xF5 /* In transmit buffer */
+#define NSPR_PDPKTRACEPARTRX_V23 0xF6 /* Received */
+
+/* cluster tracing*/
+#define NSPR_PDPKTRACEFULLTX_V24 0xF8 /* Transmitted */
+#define NSPR_PDPKTRACEFULLTXB_V24 0xF9 /* In transmit buffer */
+#define NSPR_PDPKTRACEFULLRX_V24 0xFA /* Received packets before NIC pipelining */
+#define NSPR_PDPKTRACEFULLNEWRX_V24 0xfB /* Received packets after NIC pipelining */
+#define NSPR_PDPKTRACEPARTTX_V24 0xFC /* Transmitted */
+#define NSPR_PDPKTRACEPARTTXB_V24 0xFD /* In transmit buffer */
+#define NSPR_PDPKTRACEPARTRX_V24 0xFE /* Received packets before NIC pipelining */
+#define NSPR_PDPKTRACEPARTNEWRX_V24 0xFF /* Received packets after NIC pipelining */
+
+/* vm info tracing*/
+#define NSPR_PDPKTRACEFULLTX_V25 0xB0 /* Transmitted */
+#define NSPR_PDPKTRACEFULLTXB_V25 0xB1 /* In transmit buffer */
+#define NSPR_PDPKTRACEFULLRX_V25 0xB2 /* Received packets before NIC pipelining */
+#define NSPR_PDPKTRACEFULLNEWRX_V25 0xB3 /* Received packets after NIC pipelining */
+#define NSPR_PDPKTRACEPARTTX_V25 0xB4 /* Transmitted */
+#define NSPR_PDPKTRACEPARTTXB_V25 0xB5 /* In transmit buffer */
+#define NSPR_PDPKTRACEPARTRX_V25 0xB6 /* Received packets before NIC pipelining */
+#define NSPR_PDPKTRACEPARTNEWRX_V25 0xB7 /* Received packets after NIC pipelining */
+
+/* NS DEBUG INFO PER PACKET */
+#define NSPR_PDPKTRACEFULLTX_V26 0xA0 /* Transmitted */
+#define NSPR_PDPKTRACEFULLTXB_V26 0xA1 /* In transmit buffer */
+#define NSPR_PDPKTRACEFULLRX_V26 0xA2 /* Received packets before NIC pipelining */
+#define NSPR_PDPKTRACEFULLNEWRX_V26 0xA3 /* Received packets after NIC pipelining */
+#define NSPR_PDPKTRACEPARTTX_V26 0xA4 /* Transmitted */
+#define NSPR_PDPKTRACEPARTTXB_V26 0xA5 /* In transmit buffer */
+#define NSPR_PDPKTRACEPARTRX_V26 0xA6 /* Received packets before NIC pipelining */
+#define NSPR_PDPKTRACEPARTNEWRX_V26 0xA7 /* Received packets after NIC pipelining */
+
+/* Jumbo Frame Support */
+#define NSPR_PDPKTRACEFULLTX_V30 0xA8 /* Transmitted */
+#define NSPR_PDPKTRACEFULLTXB_V30 0xA9 /* In transmit buffer */
+#define NSPR_PDPKTRACEFULLRX_V30 0xAA /* Received packets before NIC pipelining */
+#define NSPR_PDPKTRACEFULLNEWRX_V30 0xAB /* Received packets after NIC pipelining */
+
+#define NSPR_PDPKTRACEFULLTX_V35 0xAC /* Transmitted */
+#define NSPR_PDPKTRACEFULLTXB_V35 0xAD /* In transmit buffer */
+#define NSPR_PDPKTRACEFULLRX_V35 0xAE /* Received packets before NIC pipelining */
+#define NSPR_PDPKTRACEFULLNEWRX_V35 0xAF /* Received packets after NIC pipelining */
+
+
+/* Record types */
+#define NSPR_HEADER_VERSION100 0x10
+#define NSPR_HEADER_VERSION200 0x20
+#define NSPR_HEADER_VERSION201 0x21
+#define NSPR_HEADER_VERSION202 0x22
+#define NSPR_HEADER_VERSION203 0x23
+#define NSPR_HEADER_VERSION204 0x24
+#define NSPR_HEADER_VERSION205 0x25
+#define NSPR_HEADER_VERSION206 0x26
+#define NSPR_HEADER_VERSION300 0x30
+#define NSPR_HEADER_VERSION350 0x35
+
+wtap_open_return_val nstrace_open(wtap *wth, int *err, gchar **err_info);
+
+#endif /* _NETSCALER_H */
diff --git a/wiretap/netscreen.c b/wiretap/netscreen.c
new file mode 100644
index 00000000..dc8f964b
--- /dev/null
+++ b/wiretap/netscreen.c
@@ -0,0 +1,553 @@
+/* netscreen.c
+ *
+ * Juniper NetScreen snoop output parser
+ * Created by re-using a lot of code from cosine.c
+ * Copyright (c) 2007 by Sake Blok <sake@euronet.nl>
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "config.h"
+#include "wtap-int.h"
+#include "netscreen.h"
+#include "file_wrappers.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+/* XXX TODO:
+ *
+ * o Construct a list of interfaces, with interface names, give
+ * them link-layer types based on the interface name and packet
+ * data, and supply interface IDs with each packet (i.e., make
+ * this supply a pcapng-style set of interfaces and associate
+ * packets with interfaces). This is probably the right way
+ * to "Pass the interface names and the traffic direction to either
+ * the frame-structure, a pseudo-header or use PPI." See the
+ * message at
+ *
+ * https://www.wireshark.org/lists/wireshark-dev/200708/msg00029.html
+ *
+ * to see whether any further discussion is still needed. I suspect
+ * it doesn't; pcapng existed at the time, as per the final
+ * message in that thread:
+ *
+ * https://www.wireshark.org/lists/wireshark-dev/200708/msg00039.html
+ *
+ * but I don't think we fully *supported* it at that point. Now
+ * that we do, we have the infrastructure to support this, except
+ * that we currently have no way to translate interface IDs to
+ * interface names in the "frame" dissector or to supply interface
+ * information as part of the packet metadata from Wiretap modules.
+ * That should be fixed so that we can show interface information,
+ * such as the interface name, in packet dissections from, for example,
+ * pcapng captures.
+ */
+
+static gboolean info_line(const gchar *line);
+static gint64 netscreen_seek_next_packet(wtap *wth, int *err, gchar **err_info,
+ char *hdr);
+static gboolean netscreen_check_file_type(wtap *wth, int *err,
+ gchar **err_info);
+static gboolean netscreen_read(wtap *wth, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info, gint64 *data_offset);
+static gboolean netscreen_seek_read(wtap *wth, gint64 seek_off,
+ wtap_rec *rec, Buffer *buf, int *err, gchar **err_info);
+static gboolean parse_netscreen_packet(FILE_T fh, wtap_rec *rec,
+ Buffer* buf, char *line, int *err, gchar **err_info);
+static int parse_single_hex_dump_line(char* rec, guint8 *buf,
+ guint byte_offset, guint pkt_len);
+
+/* Error returns from parse_single_hex_dump_line() */
+#define PARSE_LINE_INVALID_CHARACTER -1
+#define PARSE_LINE_NO_BYTES_SEEN -2
+#define PARSE_LINE_TOO_MANY_BYTES_SEEN -3
+
+static int netscreen_file_type_subtype = -1;
+
+void register_netscreen(void);
+
+/* Returns TRUE if the line appears to be a line with protocol info.
+ Otherwise it returns FALSE. */
+static gboolean info_line(const gchar *line)
+{
+ int i=NETSCREEN_SPACES_ON_INFO_LINE;
+
+ while (i-- > 0) {
+ if (g_ascii_isspace(*line)) {
+ line++;
+ continue;
+ } else {
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+/* Seeks to the beginning of the next packet, and returns the
+ byte offset. Copy the header line to hdr. Returns -1 on failure,
+ and sets "*err" to the error and sets "*err_info" to null or an
+ additional error string. */
+static gint64 netscreen_seek_next_packet(wtap *wth, int *err, gchar **err_info,
+ char *hdr)
+{
+ gint64 cur_off;
+ char buf[NETSCREEN_LINE_LENGTH];
+
+ while (1) {
+ cur_off = file_tell(wth->fh);
+ if (cur_off == -1) {
+ /* Error */
+ *err = file_error(wth->fh, err_info);
+ return -1;
+ }
+ if (file_gets(buf, sizeof(buf), wth->fh) == NULL) {
+ /* EOF or error. */
+ *err = file_error(wth->fh, err_info);
+ break;
+ }
+ if (strstr(buf, NETSCREEN_REC_MAGIC_STR1) ||
+ strstr(buf, NETSCREEN_REC_MAGIC_STR2)) {
+ (void) g_strlcpy(hdr, buf, NETSCREEN_LINE_LENGTH);
+ return cur_off;
+ }
+ }
+ return -1;
+}
+
+/* Look through the first part of a file to see if this is
+ * NetScreen snoop output.
+ *
+ * Returns TRUE if it is, FALSE if it isn't or if we get an I/O error;
+ * if we get an I/O error, "*err" will be set to a non-zero value and
+ * "*err_info" is set to null or an additional error string.
+ */
+static gboolean netscreen_check_file_type(wtap *wth, int *err, gchar **err_info)
+{
+ char buf[NETSCREEN_LINE_LENGTH];
+ guint reclen, line;
+
+ buf[NETSCREEN_LINE_LENGTH-1] = '\0';
+
+ for (line = 0; line < NETSCREEN_HEADER_LINES_TO_CHECK; line++) {
+ if (file_gets(buf, NETSCREEN_LINE_LENGTH, wth->fh) == NULL) {
+ /* EOF or error. */
+ *err = file_error(wth->fh, err_info);
+ return FALSE;
+ }
+
+ reclen = (guint) strlen(buf);
+ if (reclen < MIN(strlen(NETSCREEN_HDR_MAGIC_STR1), strlen(NETSCREEN_HDR_MAGIC_STR2))) {
+ continue;
+ }
+
+ if (strstr(buf, NETSCREEN_HDR_MAGIC_STR1) ||
+ strstr(buf, NETSCREEN_HDR_MAGIC_STR2)) {
+ return TRUE;
+ }
+ }
+ *err = 0;
+ return FALSE;
+}
+
+
+wtap_open_return_val netscreen_open(wtap *wth, int *err, gchar **err_info)
+{
+
+ /* Look for a NetScreen snoop header line */
+ if (!netscreen_check_file_type(wth, err, err_info)) {
+ if (*err != 0 && *err != WTAP_ERR_SHORT_READ)
+ return WTAP_OPEN_ERROR;
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ if (file_seek(wth->fh, 0L, SEEK_SET, err) == -1) /* rewind */
+ return WTAP_OPEN_ERROR;
+
+ wth->file_encap = WTAP_ENCAP_UNKNOWN;
+ wth->file_type_subtype = netscreen_file_type_subtype;
+ wth->snapshot_length = 0; /* not known */
+ wth->subtype_read = netscreen_read;
+ wth->subtype_seek_read = netscreen_seek_read;
+ wth->file_tsprec = WTAP_TSPREC_100_MSEC;
+
+ return WTAP_OPEN_MINE;
+}
+
+/* Find the next packet and parse it; called from wtap_read(). */
+static gboolean netscreen_read(wtap *wth, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info, gint64 *data_offset)
+{
+ gint64 offset;
+ char line[NETSCREEN_LINE_LENGTH];
+
+ /* Find the next packet */
+ offset = netscreen_seek_next_packet(wth, err, err_info, line);
+ if (offset < 0)
+ return FALSE;
+
+ /* Parse the header and convert the ASCII hex dump to binary data */
+ if (!parse_netscreen_packet(wth->fh, rec, buf, line, err, err_info))
+ return FALSE;
+
+ /*
+ * If the per-file encapsulation isn't known, set it to this
+ * packet's encapsulation.
+ *
+ * If it *is* known, and it isn't this packet's encapsulation,
+ * set it to WTAP_ENCAP_PER_PACKET, as this file doesn't
+ * have a single encapsulation for all packets in the file.
+ */
+ if (wth->file_encap == WTAP_ENCAP_UNKNOWN)
+ wth->file_encap = rec->rec_header.packet_header.pkt_encap;
+ else {
+ if (wth->file_encap != rec->rec_header.packet_header.pkt_encap)
+ wth->file_encap = WTAP_ENCAP_PER_PACKET;
+ }
+
+ *data_offset = offset;
+ return TRUE;
+}
+
+/* Used to read packets in random-access fashion */
+static gboolean
+netscreen_seek_read(wtap *wth, gint64 seek_off, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info)
+{
+ char line[NETSCREEN_LINE_LENGTH];
+
+ if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1) {
+ return FALSE;
+ }
+
+ if (file_gets(line, NETSCREEN_LINE_LENGTH, wth->random_fh) == NULL) {
+ *err = file_error(wth->random_fh, err_info);
+ if (*err == 0) {
+ *err = WTAP_ERR_SHORT_READ;
+ }
+ return FALSE;
+ }
+
+ return parse_netscreen_packet(wth->random_fh, rec, buf, line,
+ err, err_info);
+}
+
+/* Parses a packet record header. There are a few possible formats:
+ *
+ * XXX list extra formats here!
+6843828.0: trust(o) len=98:00121ebbd132->00600868d659/0800
+ 192.168.1.1 -> 192.168.1.10/6
+ vhl=45, tos=00, id=37739, frag=0000, ttl=64 tlen=84
+ tcp:ports 2222->2333, seq=3452113890, ack=1540618280, flag=5018/ACK
+ 00 60 08 68 d6 59 00 12 1e bb d1 32 08 00 45 00 .`.h.Y.....2..E.
+ 00 54 93 6b 00 00 40 06 63 dd c0 a8 01 01 c0 a8 .T.k..@.c.......
+ 01 0a 08 ae 09 1d cd c3 13 e2 5b d3 f8 28 50 18 ..........[..(P.
+ 1f d4 79 21 00 00 e7 76 89 64 16 e2 19 0a 80 09 ..y!...v.d......
+ 31 e7 04 28 04 58 f3 d9 b1 9f 3d 65 1a db d8 61 1..(.X....=e...a
+ 2c 21 b6 d3 20 60 0c 8c 35 98 88 cf 20 91 0e a9 ,!...`..5.......
+ 1d 0b ..
+
+ * The first line of a packet is in the form
+
+<secs>.<dsecs>: <iface>({i,o}) len=<length>:<llinfo>>
+
+ * where:
+ *
+ * <secs> and <dsecs> are a time stamp in seconds and deciseconds,
+ * giving the time since the firewall was booted;
+ *
+ * <iface> is the name of the interface on which the packet was
+ * received or on which it was transmitted;
+ *
+ * {i,o} is i for a received packet and o for a transmitted packet;
+ *
+ * <length> is the length of the packet on the network;
+ *
+ * <llinfo>, at least for Ethernet, appears to be a source MAC
+ * address, folowed by "->", folowed by a destination MAC
+ * address, followed by a sequence of Ethertypes, each
+ * preceded by a "/" (multiple Ethertypes if there are VLAN
+ * tags and the like), possibly followed by ", tag <tag>".
+ *
+ * Following that may be some "info lines", each of which is indented
+ * by 14 spaces, giving a dissection of the payload after the
+ * link-layer header.
+ *
+ * Following that is a hex/ASCII dump of the contents of the
+ * packet, with 16 octets per line.
+ */
+static gboolean
+parse_netscreen_packet(FILE_T fh, wtap_rec *rec, Buffer* buf,
+ char *line, int *err, gchar **err_info)
+{
+ guint pkt_len;
+ int sec;
+ int dsec;
+ char cap_int[NETSCREEN_MAX_INT_NAME_LENGTH];
+ char direction[2];
+ char cap_src[13];
+ char cap_dst[13];
+ guint8 *pd;
+ gchar *p;
+ int n, i = 0;
+ int offset = 0;
+ gchar dststr[13];
+
+ rec->rec_type = REC_TYPE_PACKET;
+ rec->block = wtap_block_create(WTAP_BLOCK_PACKET);
+ rec->presence_flags = WTAP_HAS_TS|WTAP_HAS_CAP_LEN;
+ /* Suppress compiler warnings */
+ memset(cap_int, 0, sizeof(cap_int));
+ memset(cap_dst, 0, sizeof(cap_dst));
+
+ if (sscanf(line, "%9d.%9d: %15[a-z0-9/:.-](%1[io]) len=%9u:%12s->%12s/",
+ &sec, &dsec, cap_int, direction, &pkt_len, cap_src, cap_dst) < 5) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("netscreen: Can't parse packet-header");
+ return -1;
+ }
+ if (pkt_len > WTAP_MAX_PACKET_SIZE_STANDARD) {
+ /*
+ * Probably a corrupt capture file; don't blow up trying
+ * to allocate space for an immensely-large packet.
+ */
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("netscreen: File has %u-byte packet, bigger than maximum of %u",
+ pkt_len, WTAP_MAX_PACKET_SIZE_STANDARD);
+ return FALSE;
+ }
+
+ /*
+ * If direction[0] is 'o', the direction is NETSCREEN_EGRESS,
+ * otherwise it's NETSCREEN_INGRESS.
+ */
+
+ rec->ts.secs = sec;
+ rec->ts.nsecs = dsec * 100000000;
+ rec->rec_header.packet_header.len = pkt_len;
+
+ /* Make sure we have enough room for the packet */
+ ws_buffer_assure_space(buf, pkt_len);
+ pd = ws_buffer_start_ptr(buf);
+
+ while(1) {
+
+ /* The last packet is not delimited by an empty line, but by EOF
+ * So accept EOF as a valid delimiter too
+ */
+ if (file_gets(line, NETSCREEN_LINE_LENGTH, fh) == NULL) {
+ break;
+ }
+
+ /*
+ * Skip blanks.
+ * The number of blanks is not fixed - for wireless
+ * interfaces, there may be 14 extra spaces before
+ * the hex data.
+ */
+ for (p = &line[0]; g_ascii_isspace(*p); p++)
+ ;
+ /* packets are delimited with empty lines */
+ if (*p == '\0') {
+ break;
+ }
+
+ n = parse_single_hex_dump_line(p, pd, offset, pkt_len);
+
+ /*
+ * The smallest packet has a length of 6 bytes.
+ * If the first line either gets an error when
+ * parsed as hex data, or has fewer than 6
+ * bytes of hex data, check whether it's an
+ * info line by see if it has at least
+ * NETSCREEN_SPACES_ON_INFO_LINE spaces at the
+ * beginning.
+ *
+ * If it does, count this line and, if we have,
+ * so far, skipped no more than NETSCREEN_MAX_INFOLINES
+ * lines, skip this line.
+ */
+ if (offset == 0 && n < 6) {
+ if (info_line(line)) {
+ /* Info line */
+ if (++i <= NETSCREEN_MAX_INFOLINES) {
+ /* Skip this line */
+ continue;
+ }
+ } else {
+ if (n >= 0) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("netscreen: first line of packet data has only %d hex bytes, < 6");
+ return FALSE;
+ }
+ /* Otherwise, fall through to report error */
+ }
+ }
+
+ /* If there is no more data and the line was not empty,
+ * then there must be an error in the file
+ */
+ if (n < 0) {
+ switch (n) {
+
+ case PARSE_LINE_INVALID_CHARACTER:
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("netscreen: invalid character in hex data");
+ break;
+
+ case PARSE_LINE_NO_BYTES_SEEN:
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("netscreen: no hex bytes seen in hex data");
+ break;
+
+ case PARSE_LINE_TOO_MANY_BYTES_SEEN:
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("netscreen: number of hex bytes seen in hex data is greater than the packet length");
+ break;
+
+ default:
+ *err = WTAP_ERR_INTERNAL;
+ *err_info = g_strdup_printf("netscreen: unknown error %d from parse_single_hex_dump_line()", n);
+ break;
+ }
+
+ return FALSE;
+ }
+
+ /* Adjust the offset to the data that was just added to the buffer */
+ offset += n;
+
+ }
+
+ /*
+ * Determine the encapsulation type, based on the
+ * first 4 characters of the interface name
+ *
+ * XXX convert this to a 'case' structure when adding more
+ * (non-ethernet) interfacetypes
+ */
+ if (strncmp(cap_int, "adsl", 4) == 0) {
+ /* The ADSL interface can be bridged with or without
+ * PPP encapsulation. Check whether the first six bytes
+ * of the hex data are the same as the destination mac
+ * address in the header. If they are, assume ethernet
+ * LinkLayer or else PPP
+ */
+ snprintf(dststr, 13, "%02x%02x%02x%02x%02x%02x",
+ pd[0], pd[1], pd[2], pd[3], pd[4], pd[5]);
+ if (strncmp(dststr, cap_dst, 12) == 0)
+ rec->rec_header.packet_header.pkt_encap = WTAP_ENCAP_ETHERNET;
+ else
+ rec->rec_header.packet_header.pkt_encap = WTAP_ENCAP_PPP;
+ }
+ else if (strncmp(cap_int, "seri", 4) == 0)
+ rec->rec_header.packet_header.pkt_encap = WTAP_ENCAP_PPP;
+ else
+ rec->rec_header.packet_header.pkt_encap = WTAP_ENCAP_ETHERNET;
+
+ rec->rec_header.packet_header.caplen = offset;
+
+ return TRUE;
+}
+
+/* Take a string representing one line from a hex dump, with leading white
+ * space removed, and converts the text to binary data. We place the bytes
+ * in the buffer at the specified offset.
+ *
+ * Returns number of bytes successfully read, -1 if bad. */
+static int
+parse_single_hex_dump_line(char* rec, guint8 *buf, guint byte_offset, guint pkt_len)
+{
+ int num_items_scanned;
+ guint8 character;
+ guint8 byte;
+
+
+ for (num_items_scanned = 0; num_items_scanned < 16; num_items_scanned++) {
+ character = *rec++;
+ if (character >= '0' && character <= '9')
+ byte = character - '0' + 0;
+ else if (character >= 'A' && character <= 'F')
+ byte = character - 'A' + 0xA;
+ else if (character >= 'a' && character <= 'f')
+ byte = character - 'a' + 0xa;
+ else if (character == ' ' || character == '\r' || character == '\n' || character == '\0') {
+ /* Nothing more to parse */
+ break;
+ } else
+ return PARSE_LINE_INVALID_CHARACTER; /* not a hex digit, space before ASCII dump, or EOL */
+ byte <<= 4;
+ character = *rec++ & 0xFF;
+ if (character >= '0' && character <= '9')
+ byte += character - '0' + 0;
+ else if (character >= 'A' && character <= 'F')
+ byte += character - 'A' + 0xA;
+ else if (character >= 'a' && character <= 'f')
+ byte += character - 'a' + 0xa;
+ else
+ return PARSE_LINE_INVALID_CHARACTER; /* not a hex digit */
+
+ /* If there was more hex-data than was announced in the len=x
+ * header, then there must be an error in the file; quit
+ * now, as adding this byte will overflow the buffer.
+ */
+ if (byte_offset + num_items_scanned >= pkt_len) {
+ return PARSE_LINE_TOO_MANY_BYTES_SEEN;
+ }
+
+ buf[byte_offset + num_items_scanned] = byte;
+ character = *rec++ & 0xFF;
+ if (character == '\0' || character == '\r' || character == '\n') {
+ /* Nothing more to parse */
+ break;
+ } else if (character != ' ') {
+ /* not space before ASCII dump */
+ return PARSE_LINE_INVALID_CHARACTER;
+ }
+ }
+ if (num_items_scanned == 0)
+ return PARSE_LINE_NO_BYTES_SEEN;
+
+ return num_items_scanned;
+}
+
+static const struct supported_block_type netscreen_blocks_supported[] = {
+ /*
+ * We support packet blocks, with no comments or other options.
+ */
+ { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
+};
+
+static const struct file_type_subtype_info netscreen_info = {
+ "NetScreen snoop text file", "netscreen", "txt", NULL,
+ FALSE, BLOCKS_SUPPORTED(netscreen_blocks_supported),
+ NULL, NULL, NULL
+};
+
+void register_netscreen(void)
+{
+ netscreen_file_type_subtype = wtap_register_file_type_subtype(&netscreen_info);
+
+ /*
+ * Register name for backwards compatibility with the
+ * wtap_filetypes table in Lua.
+ */
+ wtap_register_backwards_compatibility_lua_name("NETSCREEN",
+ netscreen_file_type_subtype);
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/wiretap/netscreen.h b/wiretap/netscreen.h
new file mode 100644
index 00000000..6ea391c8
--- /dev/null
+++ b/wiretap/netscreen.h
@@ -0,0 +1,39 @@
+/** @file
+ *
+ * Juniper NetScreen snoop output parser
+ * Created by re-using a lot of code from cosine.c
+ * Copyright (c) 2007 by Sake Blok <sake@euronet.nl>
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ */
+
+#ifndef __W_NETSCREEN_H__
+#define __W_NETSCREEN_H__
+
+#include <glib.h>
+#include "wtap.h"
+
+/* Magic text to check for NetScreen snoop output */
+#define NETSCREEN_HDR_MAGIC_STR1 "(i) len="
+#define NETSCREEN_HDR_MAGIC_STR2 "(o) len="
+
+/* Magic text for start of packet */
+#define NETSCREEN_REC_MAGIC_STR1 NETSCREEN_HDR_MAGIC_STR1
+#define NETSCREEN_REC_MAGIC_STR2 NETSCREEN_HDR_MAGIC_STR2
+
+#define NETSCREEN_LINE_LENGTH 128
+#define NETSCREEN_HEADER_LINES_TO_CHECK 32
+#define NETSCREEN_MAX_INFOLINES 8
+#define NETSCREEN_SPACES_ON_INFO_LINE 14
+#define NETSCREEN_MAX_INT_NAME_LENGTH 16
+
+#define NETSCREEN_INGRESS FALSE
+#define NETSCREEN_EGRESS TRUE
+
+wtap_open_return_val netscreen_open(wtap *wth, int *err, gchar **err_info);
+
+#endif
diff --git a/wiretap/nettl.c b/wiretap/nettl.c
new file mode 100644
index 00000000..2a462710
--- /dev/null
+++ b/wiretap/nettl.c
@@ -0,0 +1,840 @@
+/* nettl.c
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * Enhancements by Mark C. Brown <mbrown@hp.com>
+ * Copyright (C) 2003, 2005 Hewlett-Packard Development Company, L.P.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include "wtap-int.h"
+#include "file_wrappers.h"
+#include "nettl.h"
+
+/* HP nettl file header */
+
+/* Magic number size */
+#define MAGIC_SIZE 12
+
+/* HP-UX 9.x */
+static const guint8 nettl_magic_hpux9[MAGIC_SIZE] = {
+ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xD0, 0x00
+};
+/* HP-UX 10.x and 11.x */
+static const guint8 nettl_magic_hpux10[MAGIC_SIZE] = {
+ 0x54, 0x52, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80
+};
+
+#define FILE_HDR_SIZE 128
+#define NETTL_FILENAME_SIZE 56
+
+struct nettl_file_hdr {
+ guint8 magic[MAGIC_SIZE];
+ gchar file_name[NETTL_FILENAME_SIZE];
+ gchar tz[20];
+ gchar host_name[9];
+ gchar os_vers[9];
+ guint8 os_v;
+ guint8 xxa[8];
+ gchar model[11];
+ guint16 unknown; /* just padding to 128 bytes? */
+};
+
+/* HP nettl record header */
+/* see /usr/include/sys/netdiag1.h for hints */
+struct nettlrec_hdr {
+ guint16 hdr_len;
+ guint16 subsys;
+ guint32 devid;
+ guint8 xxa[4];
+ guint32 kind;
+ guint8 xxb[16];
+ guint32 caplen;
+ guint32 length;
+ guint32 sec;
+ guint32 usec;
+ guint32 pid;
+ guint8 xxc[8];
+ guint32 uid;
+ /* Other stuff might be here, but isn't always here */
+};
+
+/*
+ * This is what we treat as the minimum size of a record header.
+ * It is *not* necessarily the same as sizeof(struct nettlrec_hdr),
+ * because it doesn't include any padding added to the structure.
+ */
+#define NETTL_REC_HDR_LEN 64
+
+/* HP nettl record header for the SX25L2 subsystem - The FCS is not included
+ in the file. */
+struct nettlrec_sx25l2_hdr {
+ guint8 xxa[8];
+ guint8 from_dce;
+ guint8 xxb[55];
+ guint8 caplen[2];
+ guint8 length[2];
+ guint8 xxc[4];
+ guint8 sec[4];
+ guint8 usec[4];
+ guint8 xxd[4];
+};
+
+/* NL_LS_DRIVER :
+The following shows what the header and subheader looks like for NS_LS_DRIVER
+The capture was taken on HPUX11 and for a 100baseT interface.
+
+000080 00 44 00 0b 00 00 00 02 00 00 00 00 20 00 00 00
+000090 00 00 00 00 00 00 04 06 00 00 00 00 00 00 00 00
+0000a0 00 00 00 74 00 00 00 74 3c e3 76 19 00 06 34 63
+0000b0 ff ff ff ff 00 00 00 00 00 00 00 00 ff ff ff ff
+0000c0 00 00 00 00 00 00 01 02 00 5c 00 5c ff ff ff ff
+0000d0 3c e3 76 19 00 06 34 5a 00 0b 00 14 <here starts the MAC header>
+
+Each entry starts with 0x0044000b
+
+The values 0x005c at position 0x0000c8 and 0x0000ca matches the number of
+bytes in the packet up to the next entry, which starts with 0x00440b again.
+These are the captured and real and captured length of the packet.
+
+The values 0x00000074 at positions 0x0000a0 and 0x0000a4 seems to indicate
+the same number as positions 0x0000c8 and 0x0000ca but added with 24.
+Perhaps we have here two layers of headers.
+The first layer is fixed and consists of all the bytes from 0x000084 up to and
+including 0x0000c3 which is a generic header for all packets captured from any
+device. This header might be of fixed size 64 bytes (although the first two
+bytes appear to be the length of that header, in big-endian format) and there
+might be something in it which indicates the type of the next header which is
+link type specific. Following this header there is another header for the
+100baseT interface which in this case is 24 bytes long spanning positions
+0x0000c4 to 0x0000db.
+
+In another capture, claimed to be taken on an HP-UX 8 box, but with a
+file header suggesting it was taken on HP-UX 10.20, the header for
+NS_LS_DRIVER looks like:
+
+000080 00 40 00 0b ff ff ff ff 00 00 00 00 00 00 00 00
+000090 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+0000a0 00 00 00 51 00 00 00 51 42 02 5e bf 00 0e ab 7c
+0000b0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+0000c0 00 02 01 00 00 3b 00 3b ff ff ff ff 42 02 5e bf
+0000d0 00 0e 8e 44 00 0b <here starts the MAC header>
+
+When someone reports that the loading of the captures breaks, we can
+compare this header above with what he/she got to learn how to
+distinguish between different types of link specific headers.
+
+
+For now, the subheader for 100baseT seems to be
+ 4-5 captured length
+ 6-7 actual length
+ 8-11 unknown
+ 12-15 secs
+ 16-19 usecs
+ 20-21 unknown
+*/
+struct nettlrec_ns_ls_drv_eth_hdr {
+ guint8 xxa[4];
+ guint8 caplen[2];
+ guint8 length[2];
+ guint8 xxb[4];
+ guint8 sec[4];
+ guint8 usec[4];
+ guint8 xxc[2];
+};
+
+/*
+ * This is the size of an NS_LS_DRV_ETH header; it is *not* necessarily
+ * the same as sizeof(struct nettlrec_ns_ls_drv_eth_hdr), because it
+ * doesn't include any padding added to the structure.
+ */
+#define NS_LS_DRV_ETH_HDR_LEN 22
+
+/* header is followed by data and once again the total length (2 bytes) ! */
+
+typedef struct {
+ gboolean is_hpux_11;
+} nettl_t;
+
+static gboolean nettl_read(wtap *wth, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info, gint64 *data_offset);
+static gboolean nettl_seek_read(wtap *wth, gint64 seek_off,
+ wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info);
+static gboolean nettl_read_rec(wtap *wth, FILE_T fh, wtap_rec *rec,
+ Buffer *buf, int *err, gchar **err_info);
+static gboolean nettl_dump(wtap_dumper *wdh, const wtap_rec *rec,
+ const guint8 *pd, int *err, gchar **err_info);
+
+static int nettl_file_type_subtype = -1;
+
+void register_nettl(void);
+
+wtap_open_return_val nettl_open(wtap *wth, int *err, gchar **err_info)
+{
+ struct nettl_file_hdr file_hdr;
+ guint16 dummy[2];
+ int subsys;
+ nettl_t *nettl;
+
+ memset(&file_hdr, 0, sizeof(file_hdr));
+
+ /* Read in the string that should be at the start of a HP file */
+ if (!wtap_read_bytes(wth->fh, file_hdr.magic, MAGIC_SIZE, err, err_info)) {
+ if (*err != WTAP_ERR_SHORT_READ)
+ return WTAP_OPEN_ERROR;
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ if (memcmp(file_hdr.magic, nettl_magic_hpux9, MAGIC_SIZE) &&
+ memcmp(file_hdr.magic, nettl_magic_hpux10, MAGIC_SIZE)) {
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ /* Read the rest of the file header */
+ if (!wtap_read_bytes(wth->fh, file_hdr.file_name, FILE_HDR_SIZE - MAGIC_SIZE,
+ err, err_info))
+ return WTAP_OPEN_ERROR;
+
+ /* This is an nettl file */
+ wth->file_type_subtype = nettl_file_type_subtype;
+ nettl = g_new(nettl_t,1);
+ wth->priv = (void *)nettl;
+ if (file_hdr.os_vers[2] == '1' && file_hdr.os_vers[3] == '1')
+ nettl->is_hpux_11 = TRUE;
+ else
+ nettl->is_hpux_11 = FALSE;
+ wth->subtype_read = nettl_read;
+ wth->subtype_seek_read = nettl_seek_read;
+ wth->snapshot_length = 0; /* not available */
+
+ /* read the first header to take a guess at the file encap */
+ if (!wtap_read_bytes_or_eof(wth->fh, dummy, 4, err, err_info)) {
+ if (*err == 0) {
+ /* EOF, so no records */
+ return WTAP_OPEN_NOT_MINE;
+ }
+ return WTAP_OPEN_ERROR;
+ }
+
+ subsys = g_ntohs(dummy[1]);
+ switch (subsys) {
+ case NETTL_SUBSYS_HPPB_FDDI :
+ case NETTL_SUBSYS_EISA_FDDI :
+ case NETTL_SUBSYS_PCI_FDDI :
+ case NETTL_SUBSYS_HSC_FDDI :
+ wth->file_encap = WTAP_ENCAP_NETTL_FDDI;
+ break;
+ case NETTL_SUBSYS_TOKEN :
+ case NETTL_SUBSYS_PCI_TR :
+ wth->file_encap = WTAP_ENCAP_NETTL_TOKEN_RING;
+ break;
+ case NETTL_SUBSYS_NS_LS_IP :
+ case NETTL_SUBSYS_NS_LS_LOOPBACK :
+ case NETTL_SUBSYS_NS_LS_TCP :
+ case NETTL_SUBSYS_NS_LS_UDP :
+ case NETTL_SUBSYS_NS_LS_IPV6 :
+ wth->file_encap = WTAP_ENCAP_NETTL_RAW_IP;
+ break;
+ case NETTL_SUBSYS_NS_LS_ICMP :
+ wth->file_encap = WTAP_ENCAP_NETTL_RAW_ICMP;
+ break;
+ case NETTL_SUBSYS_NS_LS_ICMPV6 :
+ wth->file_encap = WTAP_ENCAP_NETTL_RAW_ICMPV6;
+ break;
+ case NETTL_SUBSYS_NS_LS_TELNET :
+ wth->file_encap = WTAP_ENCAP_NETTL_RAW_TELNET;
+ break;
+ default:
+ /* If this assumption is bad, the read will catch it */
+ wth->file_encap = WTAP_ENCAP_NETTL_ETHERNET;
+ }
+
+ if (file_seek(wth->fh, FILE_HDR_SIZE, SEEK_SET, err) == -1) {
+ return WTAP_OPEN_ERROR;
+ }
+ wth->file_tsprec = WTAP_TSPREC_USEC;
+
+ return WTAP_OPEN_MINE;
+}
+
+/* Read the next packet */
+static gboolean nettl_read(wtap *wth, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info, gint64 *data_offset)
+{
+ /* Read record. */
+ *data_offset = file_tell(wth->fh);
+ if (!nettl_read_rec(wth, wth->fh, rec, buf, err, err_info)) {
+ /* Read error or EOF */
+ return FALSE;
+ }
+
+ /*
+ * If the per-file encapsulation isn't known, set it to this
+ * packet's encapsulation.
+ *
+ * If it *is* known, and it isn't this packet's encapsulation,
+ * set it to WTAP_ENCAP_PER_PACKET, as this file doesn't
+ * have a single encapsulation for all packets in the file.
+ */
+ if (wth->file_encap == WTAP_ENCAP_UNKNOWN)
+ wth->file_encap = rec->rec_header.packet_header.pkt_encap;
+ else {
+ if (wth->file_encap != rec->rec_header.packet_header.pkt_encap)
+ wth->file_encap = WTAP_ENCAP_PER_PACKET;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+nettl_seek_read(wtap *wth, gint64 seek_off, wtap_rec *rec,
+ Buffer *buf, int *err, gchar **err_info)
+{
+ if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
+ return FALSE;
+
+ /* Read record. */
+ if (!nettl_read_rec(wth, wth->random_fh, rec, buf, err, err_info)) {
+ /* Read error or EOF */
+ if (*err == 0) {
+ /* EOF means "short read" in random-access mode */
+ *err = WTAP_ERR_SHORT_READ;
+ }
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static gboolean
+nettl_read_rec(wtap *wth, FILE_T fh, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info)
+{
+ union wtap_pseudo_header *pseudo_header = &rec->rec_header.packet_header.pseudo_header;
+ nettl_t *nettl = (nettl_t *)wth->priv;
+ gboolean fddihack = FALSE;
+ struct nettlrec_hdr rec_hdr;
+ guint16 hdr_len;
+ struct nettlrec_ns_ls_drv_eth_hdr drv_eth_hdr;
+ guint32 length, caplen;
+ int subsys;
+ guint padlen;
+ int datalen;
+ guint8 dummyc[16];
+ int bytes_to_read;
+ guint8 *pd;
+
+ if (!wtap_read_bytes_or_eof(fh, &rec_hdr.hdr_len, sizeof rec_hdr.hdr_len,
+ err, err_info))
+ return FALSE;
+ hdr_len = g_ntohs(rec_hdr.hdr_len);
+ if (hdr_len < NETTL_REC_HDR_LEN) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("nettl: record header length %u too short",
+ hdr_len);
+ return FALSE;
+ }
+ if (!wtap_read_bytes(fh, &rec_hdr.subsys, NETTL_REC_HDR_LEN - 2,
+ err, err_info))
+ return FALSE;
+ subsys = g_ntohs(rec_hdr.subsys);
+ hdr_len -= NETTL_REC_HDR_LEN;
+ /* Skip the rest of the header. */
+ if (!wtap_read_bytes(fh, NULL, hdr_len, err, err_info))
+ return FALSE;
+
+ if ( (pntoh32(&rec_hdr.kind) & NETTL_HDR_PDU_MASK) == 0 ) {
+ /* not actually a data packet (PDU) trace record */
+ rec->rec_header.packet_header.pkt_encap = WTAP_ENCAP_NETTL_RAW_IP;
+ length = pntoh32(&rec_hdr.length);
+ caplen = pntoh32(&rec_hdr.caplen);
+ padlen = 0;
+ } else switch (subsys) {
+ case NETTL_SUBSYS_LAN100 :
+ case NETTL_SUBSYS_EISA100BT :
+ case NETTL_SUBSYS_BASE100 :
+ case NETTL_SUBSYS_GSC100BT :
+ case NETTL_SUBSYS_PCI100BT :
+ case NETTL_SUBSYS_SPP100BT :
+ case NETTL_SUBSYS_100VG :
+ case NETTL_SUBSYS_GELAN :
+ case NETTL_SUBSYS_BTLAN :
+ case NETTL_SUBSYS_INTL100 :
+ case NETTL_SUBSYS_IGELAN :
+ case NETTL_SUBSYS_IETHER :
+ case NETTL_SUBSYS_IXGBE :
+ case NETTL_SUBSYS_HSSN :
+ case NETTL_SUBSYS_IGSSN :
+ case NETTL_SUBSYS_ICXGBE :
+ case NETTL_SUBSYS_IEXGBE :
+ case NETTL_SUBSYS_IOCXGBE :
+ case NETTL_SUBSYS_IQXGBE :
+ case NETTL_SUBSYS_HPPB_FDDI :
+ case NETTL_SUBSYS_EISA_FDDI :
+ case NETTL_SUBSYS_PCI_FDDI :
+ case NETTL_SUBSYS_HSC_FDDI :
+ case NETTL_SUBSYS_TOKEN :
+ case NETTL_SUBSYS_PCI_TR :
+ case NETTL_SUBSYS_NS_LS_IP :
+ case NETTL_SUBSYS_NS_LS_LOOPBACK :
+ case NETTL_SUBSYS_NS_LS_TCP :
+ case NETTL_SUBSYS_NS_LS_UDP :
+ case NETTL_SUBSYS_HP_APAPORT :
+ case NETTL_SUBSYS_HP_APALACP :
+ case NETTL_SUBSYS_NS_LS_IPV6 :
+ case NETTL_SUBSYS_NS_LS_ICMPV6 :
+ case NETTL_SUBSYS_NS_LS_ICMP :
+ case NETTL_SUBSYS_NS_LS_TELNET :
+ case NETTL_SUBSYS_NS_LS_SCTP :
+ if( (subsys == NETTL_SUBSYS_NS_LS_IP)
+ || (subsys == NETTL_SUBSYS_NS_LS_LOOPBACK)
+ || (subsys == NETTL_SUBSYS_NS_LS_UDP)
+ || (subsys == NETTL_SUBSYS_NS_LS_TCP)
+ || (subsys == NETTL_SUBSYS_NS_LS_SCTP)
+ || (subsys == NETTL_SUBSYS_NS_LS_IPV6)) {
+ rec->rec_header.packet_header.pkt_encap = WTAP_ENCAP_NETTL_RAW_IP;
+ } else if (subsys == NETTL_SUBSYS_NS_LS_ICMP) {
+ rec->rec_header.packet_header.pkt_encap = WTAP_ENCAP_NETTL_RAW_ICMP;
+ } else if (subsys == NETTL_SUBSYS_NS_LS_ICMPV6) {
+ rec->rec_header.packet_header.pkt_encap = WTAP_ENCAP_NETTL_RAW_ICMPV6;
+ } else if (subsys == NETTL_SUBSYS_NS_LS_TELNET) {
+ rec->rec_header.packet_header.pkt_encap = WTAP_ENCAP_NETTL_RAW_TELNET;
+ } else if( (subsys == NETTL_SUBSYS_HPPB_FDDI)
+ || (subsys == NETTL_SUBSYS_EISA_FDDI)
+ || (subsys == NETTL_SUBSYS_PCI_FDDI)
+ || (subsys == NETTL_SUBSYS_HSC_FDDI) ) {
+ rec->rec_header.packet_header.pkt_encap = WTAP_ENCAP_NETTL_FDDI;
+ } else if( (subsys == NETTL_SUBSYS_PCI_TR)
+ || (subsys == NETTL_SUBSYS_TOKEN) ) {
+ rec->rec_header.packet_header.pkt_encap = WTAP_ENCAP_NETTL_TOKEN_RING;
+ } else {
+ rec->rec_header.packet_header.pkt_encap = WTAP_ENCAP_NETTL_ETHERNET;
+ }
+
+ length = pntoh32(&rec_hdr.length);
+ caplen = pntoh32(&rec_hdr.caplen);
+
+ /* HPPB FDDI has different inbound vs outbound trace records */
+ if (subsys == NETTL_SUBSYS_HPPB_FDDI) {
+ if (pntoh32(&rec_hdr.kind) == NETTL_HDR_PDUIN) {
+ /* inbound is very strange...
+ there are an extra 3 bytes after the DSAP and SSAP
+ for SNAP frames ???
+ */
+ fddihack=TRUE;
+ padlen = 0;
+ } else {
+ /* outbound appears to have variable padding */
+ if (!wtap_read_bytes(fh, dummyc, 9, err, err_info))
+ return FALSE;
+ /* padding is usually either a total 11 or 16 bytes??? */
+ padlen = (int)dummyc[8];
+ if (!wtap_read_bytes(fh, NULL, padlen, err, err_info))
+ return FALSE;
+ padlen += 9;
+ }
+ } else if ( (subsys == NETTL_SUBSYS_PCI_FDDI)
+ || (subsys == NETTL_SUBSYS_EISA_FDDI)
+ || (subsys == NETTL_SUBSYS_HSC_FDDI) ) {
+ /* other flavor FDDI cards have an extra 3 bytes of padding */
+ if (!wtap_read_bytes(fh, NULL, 3, err, err_info))
+ return FALSE;
+ padlen = 3;
+ } else if (subsys == NETTL_SUBSYS_NS_LS_LOOPBACK) {
+ /* LOOPBACK has an extra 26 bytes of padding */
+ if (!wtap_read_bytes(fh, NULL, 26, err, err_info))
+ return FALSE;
+ padlen = 26;
+ } else if (subsys == NETTL_SUBSYS_NS_LS_SCTP) {
+ /*
+ * SCTP 8 byte header that we will ignore...
+ * 32 bit integer defines format
+ * 1 = Log
+ * 2 = ASCII
+ * 3 = Binary (PDUs should be Binary format)
+ * 32 bit integer defines type
+ * 1 = Inbound
+ * 2 = Outbound
+ */
+ if (!wtap_read_bytes(fh, NULL, 8, err, err_info))
+ return FALSE;
+ padlen = 8;
+ } else {
+ padlen = 0;
+ }
+ break;
+
+ case NETTL_SUBSYS_NS_LS_DRIVER :
+ /* XXX we don't know how to identify this as ethernet frames, so
+ we assume everything is. We will crash and burn for anything else */
+ /* for encapsulated 100baseT we do this */
+ rec->rec_header.packet_header.pkt_encap = WTAP_ENCAP_NETTL_ETHERNET;
+ if (!wtap_read_bytes(fh, &drv_eth_hdr, NS_LS_DRV_ETH_HDR_LEN,
+ err, err_info))
+ return FALSE;
+
+ length = pntoh16(&drv_eth_hdr.length);
+ caplen = pntoh16(&drv_eth_hdr.caplen);
+ /*
+ * XXX - is there a length field that would give the length
+ * of this header, so that we don't have to check for
+ * nettl files from HP-UX 11?
+ *
+ * And what are the extra two bytes?
+ */
+ if (nettl->is_hpux_11) {
+ if (!wtap_read_bytes(fh, NULL, 2, err, err_info))
+ return FALSE;
+ }
+ padlen = 0;
+ break;
+
+ case NETTL_SUBSYS_SX25L2:
+ case NETTL_SUBSYS_SX25L3:
+ /*
+ * XXX - is the 24-byte padding actually a header with
+ * packet lengths, time stamps, etc., just as is the case
+ * for NETTL_SUBSYS_NS_LS_DRIVER? It might be
+ *
+ * guint8 caplen[2];
+ * guint8 length[2];
+ * guint8 xxc[4];
+ * guint8 sec[4];
+ * guint8 usec[4];
+ * guint8 xxd[4];
+ *
+ * or something such as that - if it has 4 bytes before that
+ * (making it 24 bytes), it'd be like struct
+ * nettlrec_ns_ls_drv_eth_hdr but with 2 more bytes at the end.
+ *
+ * And is "from_dce" at xxa[0] in the nettlrec_hdr structure?
+ */
+ rec->rec_header.packet_header.pkt_encap = WTAP_ENCAP_NETTL_X25;
+ length = pntoh32(&rec_hdr.length);
+ caplen = pntoh32(&rec_hdr.caplen);
+ padlen = 24; /* sizeof (struct nettlrec_sx25l2_hdr) - NETTL_REC_HDR_LEN + 4 */
+ if (!wtap_read_bytes(fh, NULL, padlen, err, err_info))
+ return FALSE;
+ break;
+
+ default:
+ /* We're going to assume it's ethernet if we don't recognize the
+ subsystem -- We'll probably spew junks and core if it isn't... */
+ wth->file_encap = WTAP_ENCAP_PER_PACKET;
+ rec->rec_header.packet_header.pkt_encap = WTAP_ENCAP_NETTL_ETHERNET;
+ length = pntoh32(&rec_hdr.length);
+ caplen = pntoh32(&rec_hdr.caplen);
+ padlen = 0;
+ break;
+ }
+
+ if (length < padlen) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("nettl: packet length %u in record header too short, less than %u",
+ length, padlen);
+ return FALSE;
+ }
+ rec->rec_type = REC_TYPE_PACKET;
+ rec->block = wtap_block_create(WTAP_BLOCK_PACKET);
+ rec->presence_flags = WTAP_HAS_TS|WTAP_HAS_CAP_LEN;
+ rec->rec_header.packet_header.len = length - padlen;
+ if (caplen < padlen) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("nettl: captured length %u in record header too short, less than %u",
+ caplen, padlen);
+ return FALSE;
+ }
+ datalen = caplen - padlen;
+ rec->rec_header.packet_header.caplen = datalen;
+ rec->ts.secs = pntoh32(&rec_hdr.sec);
+ rec->ts.nsecs = pntoh32(&rec_hdr.usec) * 1000;
+
+ pseudo_header->nettl.subsys = subsys;
+ pseudo_header->nettl.devid = pntoh32(&rec_hdr.devid);
+ pseudo_header->nettl.kind = pntoh32(&rec_hdr.kind);
+ pseudo_header->nettl.pid = pntoh32(&rec_hdr.pid);
+ pseudo_header->nettl.uid = pntoh32(&rec_hdr.uid);
+
+ if (rec->rec_header.packet_header.caplen > WTAP_MAX_PACKET_SIZE_STANDARD) {
+ /*
+ * Probably a corrupt capture file; don't blow up trying
+ * to allocate space for an immensely-large packet.
+ */
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("nettl: File has %u-byte packet, bigger than maximum of %u",
+ rec->rec_header.packet_header.caplen, WTAP_MAX_PACKET_SIZE_STANDARD);
+ return FALSE;
+ }
+
+ /*
+ * Read the packet data.
+ */
+ ws_buffer_assure_space(buf, datalen);
+ pd = ws_buffer_start_ptr(buf);
+ if (fddihack) {
+ /* read in FC, dest, src, DSAP and SSAP */
+ bytes_to_read = 15;
+ if (bytes_to_read > datalen)
+ bytes_to_read = datalen;
+ if (!wtap_read_bytes(fh, pd, bytes_to_read, err, err_info))
+ return FALSE;
+ datalen -= bytes_to_read;
+ if (datalen == 0) {
+ /* There's nothing past the FC, dest, src, DSAP and SSAP */
+ return TRUE;
+ }
+ if (pd[13] == 0xAA) {
+ /* it's SNAP, have to eat 3 bytes??? */
+ bytes_to_read = 3;
+ if (bytes_to_read > datalen)
+ bytes_to_read = datalen;
+ if (!wtap_read_bytes(fh, NULL, bytes_to_read, err, err_info))
+ return FALSE;
+ datalen -= bytes_to_read;
+ if (datalen == 0) {
+ /* There's nothing past the FC, dest, src, DSAP, SSAP, and 3 bytes to eat */
+ return TRUE;
+ }
+ }
+ if (!wtap_read_bytes(fh, pd + 15, datalen, err, err_info))
+ return FALSE;
+ } else {
+ if (!wtap_read_bytes(fh, pd, datalen, err, err_info))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/* Returns 0 if we could write the specified encapsulation type,
+ an error indication otherwise. nettl files are WTAP_ENCAP_UNKNOWN
+ when they are first opened, so we allow that for tshark read/write.
+ */
+
+static int nettl_dump_can_write_encap(int encap)
+{
+
+ switch (encap) {
+ case WTAP_ENCAP_ETHERNET:
+ case WTAP_ENCAP_FDDI_BITSWAPPED:
+ case WTAP_ENCAP_TOKEN_RING:
+ case WTAP_ENCAP_NETTL_ETHERNET:
+ case WTAP_ENCAP_NETTL_FDDI:
+ case WTAP_ENCAP_NETTL_TOKEN_RING:
+ case WTAP_ENCAP_NETTL_RAW_IP:
+ case WTAP_ENCAP_NETTL_RAW_ICMP:
+ case WTAP_ENCAP_NETTL_RAW_ICMPV6:
+ case WTAP_ENCAP_NETTL_RAW_TELNET:
+/*
+ case WTAP_ENCAP_NETTL_X25:
+*/
+ case WTAP_ENCAP_PER_PACKET:
+ case WTAP_ENCAP_UNKNOWN:
+ case WTAP_ENCAP_NETTL_UNKNOWN:
+ return 0;
+ default:
+ return WTAP_ERR_UNWRITABLE_ENCAP;
+ }
+}
+
+
+/* Returns TRUE on success, FALSE on failure;
+ sets "*err" to an error code on failure */
+static gboolean nettl_dump_open(wtap_dumper *wdh, int *err, gchar **err_info _U_)
+{
+ struct nettl_file_hdr file_hdr;
+
+ /* This is a nettl file */
+ wdh->subtype_write = nettl_dump;
+
+ /* Write the file header. */
+ memset(&file_hdr,0,sizeof(file_hdr));
+ memcpy(file_hdr.magic,nettl_magic_hpux10,sizeof(file_hdr.magic));
+ (void) g_strlcpy(file_hdr.file_name,"/tmp/wireshark.TRC000",NETTL_FILENAME_SIZE);
+ (void) g_strlcpy(file_hdr.tz,"UTC",20);
+ (void) g_strlcpy(file_hdr.host_name,"",9);
+ (void) g_strlcpy(file_hdr.os_vers,"B.11.11",9);
+ file_hdr.os_v=0x55;
+ (void) g_strlcpy(file_hdr.model,"9000/800",11);
+ file_hdr.unknown=g_htons(0x406);
+ if (!wtap_dump_file_write(wdh, &file_hdr, sizeof file_hdr, err))
+ return FALSE;
+
+ return TRUE;
+}
+
+/* Write a record for a packet to a dump file.
+ Returns TRUE on success, FALSE on failure. */
+static gboolean nettl_dump(wtap_dumper *wdh,
+ const wtap_rec *rec,
+ const guint8 *pd, int *err, gchar **err_info _U_)
+{
+ const union wtap_pseudo_header *pseudo_header = &rec->rec_header.packet_header.pseudo_header;
+ struct nettlrec_hdr rec_hdr;
+ guint8 dummyc[24];
+
+ /* We can only write packet records. */
+ if (rec->rec_type != REC_TYPE_PACKET) {
+ *err = WTAP_ERR_UNWRITABLE_REC_TYPE;
+ return FALSE;
+ }
+
+ /* Don't write anything we're not willing to read. */
+ if (rec->rec_header.packet_header.caplen > WTAP_MAX_PACKET_SIZE_STANDARD) {
+ *err = WTAP_ERR_PACKET_TOO_LARGE;
+ return FALSE;
+ }
+
+ memset(&rec_hdr,0,sizeof(rec_hdr));
+ /* HP-UX 11.X header should be 68 bytes */
+ rec_hdr.hdr_len = g_htons(sizeof(rec_hdr) + 4);
+ rec_hdr.kind = g_htonl(NETTL_HDR_PDUIN);
+ /*
+ * Probably interpreted as signed in other programs that read it.
+ * Maybe HPE will decide to make it unsigned, which could probably
+ * be made to work once the last 32-bit UN*X is gone and time_t
+ * is universally 64-bit.
+ */
+ if (rec->ts.secs < 0 || rec->ts.secs > G_MAXINT32) {
+ *err = WTAP_ERR_TIME_STAMP_NOT_SUPPORTED;
+ return FALSE;
+ }
+ rec_hdr.sec = g_htonl((guint32)rec->ts.secs);
+ rec_hdr.usec = g_htonl(rec->ts.nsecs/1000);
+ rec_hdr.caplen = g_htonl(rec->rec_header.packet_header.caplen);
+ rec_hdr.length = g_htonl(rec->rec_header.packet_header.len);
+ rec_hdr.devid = -1;
+ rec_hdr.pid = -1;
+ rec_hdr.uid = -1;
+
+ switch (rec->rec_header.packet_header.pkt_encap) {
+
+ case WTAP_ENCAP_NETTL_FDDI:
+ /* account for pad bytes */
+ rec_hdr.caplen = g_htonl(rec->rec_header.packet_header.caplen + 3);
+ rec_hdr.length = g_htonl(rec->rec_header.packet_header.len + 3);
+ /* fall through and fill the rest of the fields */
+ /* FALL THROUGH */
+ case WTAP_ENCAP_NETTL_ETHERNET:
+ case WTAP_ENCAP_NETTL_TOKEN_RING:
+ case WTAP_ENCAP_NETTL_RAW_IP:
+ case WTAP_ENCAP_NETTL_RAW_ICMP:
+ case WTAP_ENCAP_NETTL_RAW_ICMPV6:
+ case WTAP_ENCAP_NETTL_RAW_TELNET:
+ case WTAP_ENCAP_NETTL_UNKNOWN:
+ rec_hdr.subsys = g_htons(pseudo_header->nettl.subsys);
+ rec_hdr.devid = g_htonl(pseudo_header->nettl.devid);
+ rec_hdr.kind = g_htonl(pseudo_header->nettl.kind);
+ rec_hdr.pid = g_htonl(pseudo_header->nettl.pid);
+ rec_hdr.uid = g_htons(pseudo_header->nettl.uid);
+ break;
+
+ case WTAP_ENCAP_RAW_IP:
+ rec_hdr.subsys = g_htons(NETTL_SUBSYS_NS_LS_IP);
+ break;
+
+ case WTAP_ENCAP_ETHERNET:
+ rec_hdr.subsys = g_htons(NETTL_SUBSYS_BTLAN);
+ break;
+
+ case WTAP_ENCAP_FDDI_BITSWAPPED:
+ rec_hdr.subsys = g_htons(NETTL_SUBSYS_PCI_FDDI);
+ /* account for pad bytes */
+ rec_hdr.caplen = g_htonl(rec->rec_header.packet_header.caplen + 3);
+ rec_hdr.length = g_htonl(rec->rec_header.packet_header.len + 3);
+ break;
+
+ case WTAP_ENCAP_TOKEN_RING:
+ rec_hdr.subsys = g_htons(NETTL_SUBSYS_PCI_TR);
+ break;
+#if 0
+ case WTAP_ENCAP_NETTL_X25:
+ rec_hdr.caplen = g_htonl(rec->rec_header.packet_header.caplen + 24);
+ rec_hdr.length = g_htonl(rec->rec_header.packet_header.len + 24);
+ rec_hdr.subsys = g_htons(pseudo_header->nettl.subsys);
+ rec_hdr.devid = g_htonl(pseudo_header->nettl.devid);
+ rec_hdr.kind = g_htonl(pseudo_header->nettl.kind);
+ rec_hdr.pid = g_htonl(pseudo_header->nettl.pid);
+ rec_hdr.uid = g_htons(pseudo_header->nettl.uid);
+ break;
+#endif
+ default:
+ /* found one we don't support */
+ *err = WTAP_ERR_UNWRITABLE_ENCAP;
+ return FALSE;
+ }
+
+ if (!wtap_dump_file_write(wdh, &rec_hdr, sizeof(rec_hdr), err))
+ return FALSE;
+
+ /* Write out 4 extra bytes of unknown stuff for HP-UX11
+ * header format.
+ */
+ memset(dummyc, 0, sizeof dummyc);
+ if (!wtap_dump_file_write(wdh, dummyc, 4, err))
+ return FALSE;
+
+ if ((rec->rec_header.packet_header.pkt_encap == WTAP_ENCAP_FDDI_BITSWAPPED) ||
+ (rec->rec_header.packet_header.pkt_encap == WTAP_ENCAP_NETTL_FDDI)) {
+ /* add those weird 3 bytes of padding */
+ if (!wtap_dump_file_write(wdh, dummyc, 3, err))
+ return FALSE;
+ }
+/*
+ } else if (rec->rec_header.packet_header.pkt_encap == WTAP_ENCAP_NETTL_X25) {
+ if (!wtap_dump_file_write(wdh, dummyc, 24, err))
+ return FALSE;
+ }
+*/
+
+ /* write actual PDU data */
+
+ if (!wtap_dump_file_write(wdh, pd, rec->rec_header.packet_header.caplen, err))
+ return FALSE;
+
+ return TRUE;
+}
+
+static const struct supported_block_type nettl_blocks_supported[] = {
+ /*
+ * We support packet blocks, with no comments or other options.
+ */
+ { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
+};
+
+static const struct file_type_subtype_info nettl_info = {
+ "HP-UX nettl trace", "nettl", "trc0", "trc1",
+ FALSE, BLOCKS_SUPPORTED(nettl_blocks_supported),
+ nettl_dump_can_write_encap, nettl_dump_open, NULL
+};
+
+void register_nettl(void)
+{
+ nettl_file_type_subtype = wtap_register_file_type_subtype(&nettl_info);
+
+ /*
+ * Register name for backwards compatibility with the
+ * wtap_filetypes table in Lua.
+ */
+ wtap_register_backwards_compatibility_lua_name("NETTL",
+ nettl_file_type_subtype);
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * vi: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */
diff --git a/wiretap/nettl.h b/wiretap/nettl.h
new file mode 100644
index 00000000..3bb41fde
--- /dev/null
+++ b/wiretap/nettl.h
@@ -0,0 +1,121 @@
+/** @file
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * Enhancements by Mark C. Brown <mbrown@hp.com>
+ * Copyright (C) 2003, 2005 Hewlett-Packard Development Company, L.P.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __NETTL_H__
+#define __NETTL_H__
+
+#include <glib.h>
+#include <wiretap/wtap.h>
+
+/* nettl subsystems are defined in /etc/nettlgen.conf */
+
+#define NETTL_SUBSYS_NS_LS_LOGGING 0
+#define NETTL_SUBSYS_NS_LS_NFT 1
+#define NETTL_SUBSYS_NS_LS_LOOPBACK 2
+#define NETTL_SUBSYS_NS_LS_NI 3
+#define NETTL_SUBSYS_NS_LS_IPC 4
+#define NETTL_SUBSYS_NS_LS_SOCKREGD 5
+#define NETTL_SUBSYS_NS_LS_TCP 6
+#define NETTL_SUBSYS_NS_LS_PXP 7
+#define NETTL_SUBSYS_NS_LS_UDP 8
+#define NETTL_SUBSYS_NS_LS_IP 9
+#define NETTL_SUBSYS_NS_LS_PROBE 10
+#define NETTL_SUBSYS_NS_LS_DRIVER 11
+#define NETTL_SUBSYS_NS_LS_RLBD 12
+#define NETTL_SUBSYS_NS_LS_BUFS 13
+#define NETTL_SUBSYS_NS_LS_CASE21 14
+#define NETTL_SUBSYS_NS_LS_ROUTER21 15
+#define NETTL_SUBSYS_NS_LS_NFS 16
+#define NETTL_SUBSYS_NS_LS_NETISR 17
+#define NETTL_SUBSYS_NS_LS_NSE 18
+#define NETTL_SUBSYS_NS_LS_STRLOG 19
+#define NETTL_SUBSYS_NS_LS_TIRDWR 21
+#define NETTL_SUBSYS_NS_LS_TIMOD 22
+#define NETTL_SUBSYS_NS_LS_ICMP 23
+#define NETTL_SUBSYS_FILTER 26
+#define NETTL_SUBSYS_NAME 27
+#define NETTL_SUBSYS_IGMP 29
+#define NETTL_SUBSYS_SX25L2 34
+#define NETTL_SUBSYS_SX25L3 35
+#define NETTL_SUBSYS_FTAM_INIT 64
+#define NETTL_SUBSYS_FTAM_RESP 65
+#define NETTL_SUBSYS_FTAM_VFS 70
+#define NETTL_SUBSYS_FTAM_USER 72
+#define NETTL_SUBSYS_OTS 90
+#define NETTL_SUBSYS_NETWORK 91
+#define NETTL_SUBSYS_TRANSPORT 92
+#define NETTL_SUBSYS_SESSION 93
+#define NETTL_SUBSYS_ACSE_PRES 94
+#define NETTL_SUBSYS_SHM 116
+#define NETTL_SUBSYS_ACSE_US 119
+#define NETTL_SUBSYS_HPS 121
+#define NETTL_SUBSYS_CM 122
+#define NETTL_SUBSYS_ULA_UTILS 123
+#define NETTL_SUBSYS_EM 124
+#define NETTL_SUBSYS_HP_APAPORT 189
+#define NETTL_SUBSYS_HP_APALACP 190
+#define NETTL_SUBSYS_NS_LS_IPV6 244
+#define NETTL_SUBSYS_NS_LS_ICMPV6 245
+#define NETTL_SUBSYS_NS_LS_TELNET 267
+#define NETTL_SUBSYS_NS_LS_SCTP 268
+
+/* Ethernet cards */
+#define NETTL_SUBSYS_100VG 37
+#define NETTL_SUBSYS_LAN100 164
+#define NETTL_SUBSYS_EISA100BT 172
+#define NETTL_SUBSYS_BASE100 173
+#define NETTL_SUBSYS_GSC100BT 178
+#define NETTL_SUBSYS_PCI100BT 179
+#define NETTL_SUBSYS_SPP100BT 180
+#define NETTL_SUBSYS_GELAN 185
+#define NETTL_SUBSYS_BTLAN 210
+#define NETTL_SUBSYS_INTL100 233
+#define NETTL_SUBSYS_IGELAN 252
+#define NETTL_SUBSYS_IETHER 253
+#define NETTL_SUBSYS_IXGBE 265
+#define NETTL_SUBSYS_ICXGBE 271
+#define NETTL_SUBSYS_IEXGBE 275
+#define NETTL_SUBSYS_IOCXGBE 277
+#define NETTL_SUBSYS_IQXGBE 278
+
+/* FDDI cards */
+#define NETTL_SUBSYS_HPPB_FDDI 95
+#define NETTL_SUBSYS_EISA_FDDI 174
+#define NETTL_SUBSYS_PCI_FDDI 176
+#define NETTL_SUBSYS_HSC_FDDI 177
+
+/* Token Ring cards */
+#define NETTL_SUBSYS_TOKEN 31
+#define NETTL_SUBSYS_PCI_TR 187
+
+/* Accelerated Virtual I/O (AVIO) drivers */
+#define NETTL_SUBSYS_HSSN 269
+#define NETTL_SUBSYS_IGSSN 270
+
+/* from /usr/include/sys/subsys_id.h */
+
+#define NETTL_HDR_HDRIN 0x80000000
+#define NETTL_HDR_HDROUT 0x40000000
+#define NETTL_HDR_PDUIN 0x20000000
+#define NETTL_HDR_PDUOUT 0x10000000
+#define NETTL_HDR_PROCEDURE_TRACE 0x08000000
+#define NETTL_HDR_STATE_TRACE 0x04000000
+#define NETTL_HDR_ERROR_TRACE 0x02000000
+#define NETTL_HDR_LOG_TRACE 0x01000000
+#define NETTL_HDR_LOOPBACK 0x00800000
+#define NETTL_HDR_PTOP 0x00400000
+#define NETTL_HDR_SUBSYSTEM_BITS_MASK 0x000fffff
+
+#define NETTL_HDR_PDU_MASK 0x30000000
+
+wtap_open_return_val nettl_open(wtap *wth, int *err, gchar **err_info);
+
+#endif
diff --git a/wiretap/nettrace_3gpp_32_423.c b/wiretap/nettrace_3gpp_32_423.c
new file mode 100644
index 00000000..7de46946
--- /dev/null
+++ b/wiretap/nettrace_3gpp_32_423.c
@@ -0,0 +1,789 @@
+/* nettrace_3gpp_32_423.c
+ *
+ * Decoder for 3GPP TS 32.423 file format for the Wiretap library.
+ * The main purpose is to have Wireshark decode raw message content (<rawMsg> tag).
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * Ref: https://portal.3gpp.org/desktopmodules/Specifications/SpecificationDetails.aspx?specificationId=2010
+ */
+
+#include "config.h"
+
+#include <sys/types.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include "wtap-int.h"
+#include "file_wrappers.h"
+
+#include <wsutil/exported_pdu_tlvs.h>
+#include <wsutil/buffer.h>
+#include "wsutil/tempfile.h"
+#include "wsutil/os_version_info.h"
+#include "wsutil/str_util.h"
+#include <wsutil/inet_addr.h>
+#include <wsutil/ws_assert.h>
+
+
+#include "nettrace_3gpp_32_423.h"
+
+/* String constants sought in the XML data.
+ * Written as strings instead of lists of chars for readability.
+ * Use the CLEN() macro to get the length of the constant without counting
+ * the null byte at the end.
+ */
+#define CLEN(x) (sizeof(x)-1)
+static const guchar c_xml_magic[] = "<?xml";
+static const guchar c_file_header[] = "<fileHeader";
+static const guchar c_file_format_version[] = "fileFormatVersion=\"";
+static const guchar c_threegpp_doc_no[] = "32.423";
+static const guchar c_begin_time[] = "<traceCollec beginTime=\"";
+static const guchar c_s_msg[] = "<msg";
+static const guchar c_e_msg[] = "</msg>";
+static const guchar c_s_rawmsg[] = "<rawMsg";
+static const guchar c_change_time[] = "changeTime=\"";
+static const guchar c_function[] = "function=\"";
+static const guchar c_proto_name[] = "name=\"";
+static const guchar c_address[] = "ddress"; /* omit the 'a' to cater for "Address" */
+static const guchar c_s_initiator[] = "<initiator";
+static const guchar c_e_initiator[] = "</initiator>";
+static const guchar c_s_target[] = "<target";
+static const guchar c_e_target[] = "</target>";
+static const guchar c_protocol[] = "protocol=\"";
+
+/* These are protocol names we may put in the exported-pdu data based on
+ * what's in the XML. They're defined here as constants so we can use
+ * sizeof()/CLEN() on them and slightly reduce our use of magic constants
+ * for their size. (Modern compilers should make this no slower than that.)
+ */
+static const guchar c_sai_req[] = "gsm_map.v3.arg.opcode";
+static const guchar c_sai_rsp[] = "gsm_map.v3.res.opcode";
+static const guchar c_nas_eps[] = "nas-eps_plain";
+static const guchar c_nas_5gs[] = "nas-5gs";
+
+#define RINGBUFFER_START_SIZE G_MAXINT
+#define RINGBUFFER_CHUNK_SIZE 1024
+
+#define MAX_FUNCTION_LEN 64
+#define MAX_NAME_LEN 64
+#define MAX_PROTO_LEN 16
+#define MAX_DTBL_LEN 32
+
+/* We expect to find all the info we need to tell if this file is ours
+ * within this many bytes. Must include the beginTime attribute.
+ */
+#define MAGIC_BUF_SIZE 1024
+
+typedef struct nettrace_3gpp_32_423_file_info {
+ GByteArray *buffer; // holds current chunk of file
+ gint64 start_offset; // where in the file the start of the buffer points
+ nstime_t start_time; // from <traceCollec beginTime=""> attribute
+} nettrace_3gpp_32_423_file_info_t;
+
+
+typedef struct exported_pdu_info {
+ guint32 presence_flags;
+ guint8 src_ip[16];
+ guint32 ptype; /* Based on epan/address.h port_type valid for both src and dst*/
+ guint32 src_port;
+ guint8 dst_ip[16];
+ guint32 dst_port;
+ char* proto_col_str;
+} exported_pdu_info_t;
+
+/* flags for exported_pdu_info.presence_flags */
+#define EXP_PDU_TAG_IP_SRC_BIT 0x001
+#define EXP_PDU_TAG_IP_DST_BIT 0x002
+#define EXP_PDU_TAG_SRC_PORT_BIT 0x004
+#define EXP_PDU_TAG_DST_PORT_BIT 0x008
+#define EXP_PDU_TAG_ORIG_FNO_BIT 0x010
+#define EXP_PDU_TAG_SS7_OPC_BIT 0x020
+#define EXP_PDU_TAG_SS7_DPC_BIT 0x040
+#define EXP_PDU_TAG_IP6_SRC_BIT 0x080
+#define EXP_PDU_TAG_IP6_DST_BIT 0x100
+#define EXP_PDU_TAG_DVBCI_EVT_BIT 0x0100
+#define EXP_PDU_TAG_COL_PROT_BIT 0x0200
+
+
+static int nettrace_3gpp_32_423_file_type_subtype = -1;
+
+void register_nettrace_3gpp_32_423(void);
+
+/* Parse a string IPv4 or IPv6 address into bytes for exported_pdu_info.
+ * Also parses the port pairs and transport layer type.
+ */
+static char*
+nettrace_parse_address(char* curr_pos, char* next_pos, gboolean is_src_addr, exported_pdu_info_t *exported_pdu_info)
+{
+ guint port;
+ char transp_str[5];
+ int scan_found;
+ char str[3];
+ char* end_pos, *skip_pos;
+ char ip_addr_str[WS_INET6_ADDRSTRLEN];
+ int str_len;
+ ws_in6_addr ip6_addr;
+ guint32 ip4_addr;
+ gchar tempchar;
+
+ /* curr_pos pointing to first char after address */
+
+ /* Excample from one trace, unsure if it's generic...
+ * {address == 192.168.73.1, port == 5062, transport == Udp}
+ * {address == [2001:1b70:8294:210a::78], port...
+ * {address == 2001:1B70:8294:210A::90, port...
+ * Address=198.142.204.199,Port=2123
+ */
+ /* Skip whitespace and equalsigns) */
+ for (skip_pos = curr_pos; skip_pos < next_pos &&
+ ((tempchar = *skip_pos) == ' ' ||
+ tempchar == '\t' || tempchar == '\r' || tempchar == '\n' || tempchar == '=');
+ skip_pos++);
+
+ curr_pos = skip_pos;
+
+ (void) g_strlcpy(str, curr_pos, 3);
+ /* If we find "" here we have no IP address */
+ if (strcmp(str, "\"\"") == 0) {
+ return next_pos;
+ }
+ str[1] = 0;
+ if (strcmp(str, "[") == 0) {
+ /* Should we check for a digit here?*/
+ end_pos = strstr(curr_pos, "]");
+
+ }else {
+ /* Should we check for a digit here?*/
+ end_pos = strstr(curr_pos, ",");
+ }
+ if (!end_pos) {
+ return next_pos;
+ }
+
+ str_len = (int)(end_pos - curr_pos)+1;
+ if (str_len > WS_INET6_ADDRSTRLEN) {
+ return next_pos;
+ }
+ (void) g_strlcpy(ip_addr_str, curr_pos, str_len);
+ curr_pos = end_pos;
+ if (ws_inet_pton6(ip_addr_str, &ip6_addr)) {
+ if (is_src_addr) {
+ exported_pdu_info->presence_flags |= EXP_PDU_TAG_IP6_SRC_BIT;
+ memcpy(exported_pdu_info->src_ip, ip6_addr.bytes, EXP_PDU_TAG_IPV6_LEN);
+ }
+ else {
+ exported_pdu_info->presence_flags |= EXP_PDU_TAG_IP6_DST_BIT;
+ memcpy(exported_pdu_info->dst_ip, ip6_addr.bytes, EXP_PDU_TAG_IPV6_LEN);
+ }
+ }
+ else if (ws_inet_pton4(ip_addr_str, &ip4_addr)) {
+ if (is_src_addr) {
+ exported_pdu_info->presence_flags |= EXP_PDU_TAG_IP_SRC_BIT;
+ memcpy(exported_pdu_info->src_ip, &ip4_addr, EXP_PDU_TAG_IPV4_LEN);
+ }
+ else {
+ exported_pdu_info->presence_flags |= EXP_PDU_TAG_IP_DST_BIT;
+ memcpy(exported_pdu_info->dst_ip, &ip4_addr, EXP_PDU_TAG_IPV4_LEN);
+ }
+ }
+
+ curr_pos++;
+ scan_found = sscanf(curr_pos, ", %*s %*s %5u, %*s %*s %4s", &port, transp_str);
+ if (scan_found == 2) {
+ /* Only add port_type once */
+ if (exported_pdu_info->ptype == EXP_PDU_PT_NONE) {
+ if (g_ascii_strncasecmp(transp_str, "udp", 3) == 0) {
+ exported_pdu_info->ptype = EXP_PDU_PT_UDP;
+ }
+ else if (g_ascii_strncasecmp(transp_str, "tcp", 3) == 0) {
+ exported_pdu_info->ptype = EXP_PDU_PT_TCP;
+ }
+ else if (g_ascii_strncasecmp(transp_str, "sctp", 4) == 0) {
+ exported_pdu_info->ptype = EXP_PDU_PT_SCTP;
+ }
+ }
+ if (is_src_addr) {
+ exported_pdu_info->presence_flags |= EXP_PDU_TAG_SRC_PORT_BIT;
+ exported_pdu_info->src_port = port;
+ }
+ else {
+ exported_pdu_info->presence_flags |= EXP_PDU_TAG_DST_PORT_BIT;
+ exported_pdu_info->dst_port = port;
+ }
+ }
+ return next_pos;
+}
+
+
+/* Parse a <msg ...><rawMsg ...>XXXX</rawMsg></msg> into packet data. */
+static gboolean
+nettrace_msg_to_packet(nettrace_3gpp_32_423_file_info_t *file_info, wtap_rec *rec, Buffer *buf, guint8 *input, gsize len, int *err, gchar **err_info)
+{
+/* Convenience macro. haystack must be >= input! */
+#define STRNSTR(haystack, needle) g_strstr_len(haystack, (len - ((guint8*)(haystack) - (guint8*)input)), needle)
+
+ gboolean status = TRUE;
+ char *curr_pos, *next_msg_pos, *next_pos, *prev_pos;
+ exported_pdu_info_t exported_pdu_info = {0};
+
+ char* raw_msg_pos;
+ char* start_msg_tag_cont;
+ char function_str[MAX_FUNCTION_LEN+1];
+ char name_str[MAX_NAME_LEN+1];
+ char proto_name_str[MAX_PROTO_LEN+1];
+ char dissector_table_str[MAX_DTBL_LEN+1];
+ int dissector_table_val = 0;
+
+ int function_str_len = 0;
+ int name_str_len = 0;
+ int proto_str_len, dissector_table_str_len, raw_data_len, pkt_data_len, exp_pdu_tags_len, i;
+ guint8 *packet_buf;
+ gint val1, val2;
+ gboolean use_proto_table = FALSE;
+
+ /* We should always and only be called with a <msg....</msg> payload */
+ if (0 != strncmp(input, c_s_msg, CLEN(c_s_msg))) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("nettrace_3gpp_32_423: Did not start with \"%s\"", c_s_msg);
+ return FALSE;
+ }
+
+ curr_pos = input + CLEN(c_s_msg);
+
+ rec->rec_type = REC_TYPE_PACKET;
+ rec->block = wtap_block_create(WTAP_BLOCK_PACKET);
+ rec->presence_flags = 0; /* start out assuming no special features */
+ rec->ts.secs = 0;
+ rec->ts.nsecs = 0;
+
+ /* Clear for each iteration */
+ exported_pdu_info.presence_flags = 0;
+ exported_pdu_info.ptype = EXP_PDU_PT_NONE;
+
+ prev_pos = curr_pos;
+ /* Look for the end of the tag first */
+ next_msg_pos = STRNSTR(curr_pos, ">");
+ if (!next_msg_pos) {
+ /* Something's wrong, bail out */
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("Did not find end of tag \">\"");
+ status = FALSE;
+ goto end;
+ }
+ /* Check if its a tag close "/>" */
+ if (*(next_msg_pos - 1) == '/') {
+ /* There is no rawmsg here. Should have been caught before we got called */
+ *err = WTAP_ERR_INTERNAL;
+ *err_info = g_strdup("Had \"<msg />\" with no \"<rawMsg>\"");
+ status = FALSE;
+ goto end;
+ }
+ start_msg_tag_cont = curr_pos = prev_pos;
+ next_msg_pos = STRNSTR(curr_pos, c_e_msg);
+ if (!next_msg_pos) {
+ /* Something's wrong, bail out */
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("nettrace_3gpp_32_423: Did not find \"%s\"", c_e_msg);
+ status = FALSE;
+ goto end;
+ }
+
+ /* Check if we have a time stamp "changeTime"
+ * expressed in number of seconds and milliseconds (nbsec.ms).
+ * Only needed if we have a "beginTime" for this file.
+ */
+ if (!nstime_is_unset(&(file_info->start_time))) {
+ int scan_found;
+ guint second = 0, ms = 0;
+
+ curr_pos = STRNSTR(start_msg_tag_cont, c_change_time);
+ /* Check if we have the tag or if we passed the end of the current message */
+ if (curr_pos != NULL) {
+ curr_pos += CLEN(c_change_time);
+ scan_found = sscanf(curr_pos, "%u.%u", &second, &ms);
+
+ if (scan_found == 2) {
+ guint start_ms = file_info->start_time.nsecs / 1000000;
+ guint elapsed_ms = start_ms + ms;
+ if (elapsed_ms > 1000) {
+ elapsed_ms -= 1000;
+ second++;
+ }
+ rec->presence_flags |= WTAP_HAS_TS;
+ rec->ts.secs = file_info->start_time.secs + second;
+ rec->ts.nsecs = (elapsed_ms * 1000000);
+ }
+ }
+ }
+
+ /* See if we have a "function" */
+ function_str[0] = '\0'; /* if we don't have a function */
+ curr_pos = STRNSTR(start_msg_tag_cont, c_function);
+ if (curr_pos != NULL) {
+ /* extract the function */
+ curr_pos += CLEN(c_function);
+ next_pos = STRNSTR(curr_pos, "\"");
+ function_str_len = (int)(next_pos - curr_pos);
+ if (function_str_len > MAX_FUNCTION_LEN) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("nettrace_3gpp_32_423: function_str_len > %d", MAX_FUNCTION_LEN);
+ goto end;
+ }
+
+ (void) g_strlcpy(function_str, curr_pos, (gsize)function_str_len + 1);
+ ascii_strdown_inplace(function_str);
+
+ }
+
+ /* See if we have a "name" */
+ name_str[0] = '\0'; /* if we don't have a name */
+ curr_pos = STRNSTR(start_msg_tag_cont, c_proto_name);
+ if (curr_pos != NULL) {
+ /* extract the name */
+ curr_pos += CLEN(c_proto_name);
+ next_pos = STRNSTR(curr_pos, "\"");
+ name_str_len = (int)(next_pos - curr_pos);
+ if (name_str_len > MAX_NAME_LEN) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("nettrace_3gpp_32_423: name_str_len > %d", MAX_NAME_LEN);
+ goto end;
+ }
+
+ (void) g_strlcpy(name_str, curr_pos, (gsize)name_str_len + 1);
+ ascii_strdown_inplace(name_str);
+
+ }
+ /* Check if we have "<initiator>"
+ * It might contain an address
+ */
+ curr_pos = STRNSTR(start_msg_tag_cont, c_s_initiator);
+ /* Check if we have the tag or if we passed the end of the current message */
+ if (curr_pos != NULL) {
+ curr_pos += CLEN(c_s_initiator);
+ next_pos = STRNSTR(curr_pos, c_e_initiator);
+ /* Find address */
+ curr_pos = STRNSTR(curr_pos, c_address);
+ if (curr_pos != NULL) {
+ curr_pos += CLEN(c_address);
+ nettrace_parse_address(curr_pos, next_pos, TRUE/* SRC */, &exported_pdu_info);
+ }
+ }
+
+ /* Check if we have "<target>"
+ * It might contain an address
+ */
+ curr_pos = STRNSTR(start_msg_tag_cont, c_s_target);
+ /* Check if we have the tag or if we passed the end of the current message */
+ if (curr_pos != NULL) {
+ curr_pos += CLEN(c_s_target);
+ curr_pos = curr_pos + 7;
+ next_pos = STRNSTR(curr_pos, c_e_target);
+ /* Find address */
+ curr_pos = STRNSTR(curr_pos, c_address);
+ if (curr_pos != NULL) {
+ curr_pos += CLEN(c_address);
+ /* curr_pos set below */
+ nettrace_parse_address(curr_pos, next_pos, FALSE/* DST */, &exported_pdu_info);
+ }
+ }
+
+ /* Do we have a raw message in the <msg> </msg> section? */
+ raw_msg_pos = STRNSTR(start_msg_tag_cont, c_s_rawmsg);
+ if (raw_msg_pos == NULL) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("nettrace_3gpp_32_423: Did not find \"%s\"", c_s_rawmsg);
+ status = FALSE;
+ goto end;
+ }
+ curr_pos = STRNSTR(raw_msg_pos, c_protocol);
+ if (curr_pos == NULL) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("nettrace_3gpp_32_423: Did not find \"%s\"", c_protocol);
+ status = FALSE;
+ goto end;
+ }
+ curr_pos += CLEN(c_protocol);
+ next_pos = STRNSTR(curr_pos, "\"");
+ proto_str_len = (int)(next_pos - curr_pos);
+ if (proto_str_len > MAX_PROTO_LEN){
+ status = FALSE;
+ goto end;
+ }
+ (void) g_strlcpy(proto_name_str, curr_pos, (gsize)proto_str_len+1);
+ ascii_strdown_inplace(proto_name_str);
+
+ /* Do string matching and replace with Wiresharks protocol name */
+ if (strcmp(proto_name_str, "gtpv2-c") == 0) {
+ /* Change to gtpv2 */
+ proto_name_str[5] = '\0';
+ proto_str_len = 5;
+ }
+ if (strcmp(proto_name_str, "nas") == 0) {
+ if (strcmp(function_str, "s1") == 0) {
+ /* Change to nas-eps_plain */
+ (void) g_strlcpy(proto_name_str, c_nas_eps, sizeof(c_nas_eps));
+ proto_str_len = CLEN(c_nas_eps);
+ } else if (strcmp(function_str, "n1") == 0) {
+ /* Change to nas-5gs */
+ (void) g_strlcpy(proto_name_str, c_nas_5gs, sizeof(c_nas_5gs));
+ proto_str_len = CLEN(c_nas_5gs);
+ } else {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("nettrace_3gpp_32_423: No handle of message \"%s\" on function \"%s\" ", proto_name_str, function_str);
+ status = FALSE;
+ goto end;
+ }
+ }
+
+ if (strcmp(proto_name_str, "map") == 0) {
+ /* For GSM map, it looks like the message data is stored like SendAuthenticationInfoArg
+ * use the GSM MAP dissector table to dissect the content.
+ */
+ exported_pdu_info.proto_col_str = g_strdup("GSM MAP");
+
+ if (strcmp(name_str, "sai_request") == 0) {
+ use_proto_table = TRUE;
+ (void) g_strlcpy(dissector_table_str, c_sai_req, sizeof(c_sai_req));
+ dissector_table_str_len = CLEN(c_sai_req);
+ dissector_table_val = 56;
+ exported_pdu_info.presence_flags |= EXP_PDU_TAG_COL_PROT_BIT;
+ }
+ else if (strcmp(name_str, "sai_response") == 0) {
+ use_proto_table = TRUE;
+ (void) g_strlcpy(dissector_table_str, c_sai_rsp, sizeof(c_sai_rsp));
+ dissector_table_str_len = CLEN(c_sai_rsp);
+ dissector_table_val = 56;
+ exported_pdu_info.presence_flags |= EXP_PDU_TAG_COL_PROT_BIT;
+ } else {
+ g_free(exported_pdu_info.proto_col_str);
+ exported_pdu_info.proto_col_str = NULL;
+ }
+ }
+ /* Find the start of the raw data */
+ curr_pos = STRNSTR(next_pos, ">") + 1;
+ next_pos = STRNSTR(curr_pos, "<");
+ raw_data_len = (int)(next_pos - curr_pos);
+
+ /* Fill packet buff */
+ ws_buffer_clean(buf);
+ if (use_proto_table == FALSE) {
+ wtap_buffer_append_epdu_tag(buf, EXP_PDU_TAG_DISSECTOR_NAME, proto_name_str, proto_str_len);
+ }
+ else {
+ wtap_buffer_append_epdu_tag(buf, EXP_PDU_TAG_DISSECTOR_TABLE_NAME, dissector_table_str, dissector_table_str_len);
+ wtap_buffer_append_epdu_uint(buf, EXP_PDU_TAG_DISSECTOR_TABLE_NAME_NUM_VAL, dissector_table_val);
+ }
+
+ if (exported_pdu_info.presence_flags & EXP_PDU_TAG_COL_PROT_BIT) {
+ wtap_buffer_append_epdu_string(buf, EXP_PDU_TAG_COL_PROT_TEXT, exported_pdu_info.proto_col_str);
+ g_free(exported_pdu_info.proto_col_str);
+ exported_pdu_info.proto_col_str = NULL;
+ }
+
+ if (exported_pdu_info.presence_flags & EXP_PDU_TAG_IP_SRC_BIT) {
+ wtap_buffer_append_epdu_tag(buf, EXP_PDU_TAG_IPV4_SRC, exported_pdu_info.src_ip, EXP_PDU_TAG_IPV4_LEN);
+ }
+ if (exported_pdu_info.presence_flags & EXP_PDU_TAG_IP_DST_BIT) {
+ wtap_buffer_append_epdu_tag(buf, EXP_PDU_TAG_IPV4_DST, exported_pdu_info.dst_ip, EXP_PDU_TAG_IPV4_LEN);
+ }
+
+ if (exported_pdu_info.presence_flags & EXP_PDU_TAG_IP6_SRC_BIT) {
+ wtap_buffer_append_epdu_tag(buf, EXP_PDU_TAG_IPV6_SRC, exported_pdu_info.src_ip, EXP_PDU_TAG_IPV6_LEN);
+ }
+ if (exported_pdu_info.presence_flags & EXP_PDU_TAG_IP6_DST_BIT) {
+ wtap_buffer_append_epdu_tag(buf, EXP_PDU_TAG_IPV6_DST, exported_pdu_info.dst_ip, EXP_PDU_TAG_IPV6_LEN);
+ }
+
+ if (exported_pdu_info.presence_flags & (EXP_PDU_TAG_SRC_PORT_BIT | EXP_PDU_TAG_DST_PORT_BIT)) {
+ wtap_buffer_append_epdu_uint(buf, EXP_PDU_TAG_PORT_TYPE, exported_pdu_info.ptype);
+ }
+ if (exported_pdu_info.presence_flags & EXP_PDU_TAG_SRC_PORT_BIT) {
+ wtap_buffer_append_epdu_uint(buf, EXP_PDU_TAG_SRC_PORT, exported_pdu_info.src_port);
+ }
+ if (exported_pdu_info.presence_flags & EXP_PDU_TAG_DST_PORT_BIT) {
+ wtap_buffer_append_epdu_uint(buf, EXP_PDU_TAG_DST_PORT, exported_pdu_info.dst_port);
+ }
+
+ /* Add end of options */
+ exp_pdu_tags_len = wtap_buffer_append_epdu_end(buf);
+
+ /* Convert the hex raw msg data to binary and write to the packet buf*/
+ pkt_data_len = raw_data_len / 2;
+ ws_buffer_assure_space(buf, pkt_data_len);
+ packet_buf = ws_buffer_end_ptr(buf);
+
+ for (i = 0; i < pkt_data_len; i++) {
+ gchar chr1, chr2;
+
+ chr1 = *curr_pos++;
+ chr2 = *curr_pos++;
+ val1 = g_ascii_xdigit_value(chr1);
+ val2 = g_ascii_xdigit_value(chr2);
+ if ((val1 != -1) && (val2 != -1)) {
+ *packet_buf++ = ((guint8)val1 * 16) + val2;
+ }
+ else {
+ /* Something wrong, bail out */
+ *err_info = ws_strdup_printf("nettrace_3gpp_32_423: Could not parse hex data, bufsize %u index %u %c%c",
+ (pkt_data_len + exp_pdu_tags_len),
+ i,
+ chr1,
+ chr2);
+ *err = WTAP_ERR_BAD_FILE;
+ status = FALSE;
+ goto end;
+ }
+ }
+ ws_buffer_increase_length(buf, pkt_data_len);
+
+ rec->rec_header.packet_header.caplen = (guint32)ws_buffer_length(buf);
+ rec->rec_header.packet_header.len = (guint32)ws_buffer_length(buf);
+
+end:
+ return status;
+#undef STRNSTR
+}
+
+/* Read from fh and store into buffer, until buffer contains needle.
+ * Returns location of needle once found, or NULL if it's never found
+ * (due to either EOF or read error).
+ */
+static guint8 *
+read_until(GByteArray *buffer, const guchar *needle, FILE_T fh, int *err, gchar **err_info)
+{
+ guint8 read_buffer[RINGBUFFER_CHUNK_SIZE];
+ guint8 *found_it;
+ gint bytes_read = 0;
+
+ while (NULL == (found_it = g_strstr_len(buffer->data, buffer->len, needle))) {
+ bytes_read = file_read(read_buffer, RINGBUFFER_CHUNK_SIZE, fh);
+ if (bytes_read < 0) {
+ *err = file_error(fh, err_info);
+ break;
+ }
+ if (bytes_read == 0) {
+ break;
+ }
+ g_byte_array_append(buffer, read_buffer, bytes_read);
+ }
+ return found_it;
+}
+
+/* Find a complete packet, parse and return it to wiretap.
+ * Set as the subtype_read function in the file_open function below.
+ */
+static gboolean
+nettrace_read(wtap *wth, wtap_rec *rec, Buffer *buf, int *err, gchar **err_info, gint64 *data_offset)
+{
+ nettrace_3gpp_32_423_file_info_t *file_info = (nettrace_3gpp_32_423_file_info_t *)wth->priv;
+ guint8 *buf_start;
+ guint8 *msg_start, *msg_end;
+ guint msg_offset = 0;
+ gsize msg_len = 0;
+ gboolean status = FALSE;
+
+ /* Make sure we have a start and end of message in our buffer -- end first */
+ msg_end = read_until(file_info->buffer, c_e_msg, wth->fh, err, err_info);
+ if (msg_end == NULL) {
+ goto end;
+ }
+
+ buf_start = file_info->buffer->data;
+ /* Now search backwards for the message start
+ * (doing it this way should skip over any empty "<msg ... />" tags we have)
+ */
+ msg_start = g_strrstr_len(buf_start, (guint)(msg_end - buf_start), c_s_msg);
+ if (msg_start == NULL || msg_start > msg_end) {
+ *err_info = ws_strdup_printf("nettrace_3gpp_32_423: Found \"%s\" without matching \"%s\"", c_e_msg, c_s_msg);
+ *err = WTAP_ERR_BAD_FILE;
+ goto end;
+ }
+
+ /* We know we have a message, what's its offset from the buffer start? */
+ msg_offset = (guint)(msg_start - buf_start);
+ msg_end += CLEN(c_e_msg);
+ msg_len = (guint)(msg_end - msg_start);
+
+ /* Tell Wireshark to put us at the start of the "<msg" for seek_read later */
+ *data_offset = file_info->start_offset + msg_offset;
+
+ /* pass all of <msg....</msg> to nettrace_msg_to_packet() */
+ status = nettrace_msg_to_packet(file_info, rec, buf, msg_start, msg_len, err, err_info);
+
+ /* Finally, shift our buffer to the end of this message to get ready for the next one.
+ * Re-use msg_len to get the length of the data we're done with.
+ */
+ msg_len = msg_end - file_info->buffer->data;
+ while (G_UNLIKELY(msg_len > G_MAXUINT)) {
+ g_byte_array_remove_range(file_info->buffer, 0, G_MAXUINT);
+ msg_len -= G_MAXUINT;
+ }
+ g_byte_array_remove_range(file_info->buffer, 0, (guint)msg_len);
+ file_info->start_offset += msg_len;
+
+end:
+ if (status == FALSE) {
+ /* There's no more to read. Empty out the buffer */
+ g_byte_array_set_size(file_info->buffer, 0);
+ }
+
+ return status;
+}
+
+/* Seek to the complete packet at the offset, parse and return it to wiretap.
+ * Set as the subtype_seek_read function in the file_open function below.
+ */
+static gboolean
+nettrace_seek_read(wtap *wth, gint64 seek_off, wtap_rec *rec, Buffer *buf, int *err, gchar **err_info)
+{
+ nettrace_3gpp_32_423_file_info_t *file_info = (nettrace_3gpp_32_423_file_info_t *)wth->priv;
+ gboolean status = FALSE;
+ guint8 *msg_end;
+ guint msg_len = 0;
+
+ /* We stored the offset of the "<msg" for this packet */
+ if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
+ return FALSE;
+
+ msg_end = read_until(file_info->buffer, c_e_msg, wth->random_fh, err, err_info);
+ if (msg_end == NULL) {
+ return FALSE;
+ }
+ msg_end += CLEN(c_e_msg);
+ msg_len = (guint)(msg_end - file_info->buffer->data);
+
+ status = nettrace_msg_to_packet(file_info, rec, buf, file_info->buffer->data, msg_len, err, err_info);
+ g_byte_array_set_size(file_info->buffer, 0);
+ return status;
+}
+
+/* Clean up any memory we allocated for dealing with this file.
+ * Set as the subtype_close function in the file_open function below.
+ * (wiretap frees wth->priv itself)
+ */
+static void
+nettrace_close(wtap *wth)
+{
+ nettrace_3gpp_32_423_file_info_t *file_info = (nettrace_3gpp_32_423_file_info_t *)wth->priv;
+
+ if (file_info != NULL && file_info->buffer != NULL) {
+ g_byte_array_free(file_info->buffer, TRUE);
+ file_info->buffer = NULL;
+ }
+}
+
+/* Test the current file to see if it's one we can read.
+ * Set in file_access.c as the function to be called for this file type.
+ */
+wtap_open_return_val
+nettrace_3gpp_32_423_file_open(wtap *wth, int *err, gchar **err_info)
+{
+ char magic_buf[MAGIC_BUF_SIZE+1];
+ int bytes_read;
+ const char *curr_pos;
+ nstime_t start_time;
+ nettrace_3gpp_32_423_file_info_t *file_info;
+ gint64 start_offset;
+
+ start_offset = file_tell(wth->fh); // Most likely 0 but doesn't hurt to check
+ bytes_read = file_read(magic_buf, MAGIC_BUF_SIZE, wth->fh);
+
+ if (bytes_read < 0) {
+ *err = file_error(wth->fh, err_info);
+ return WTAP_OPEN_ERROR;
+ }
+ if (bytes_read == 0){
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ if (memcmp(magic_buf, c_xml_magic, CLEN(c_xml_magic)) != 0){
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ curr_pos = g_strstr_len(magic_buf, bytes_read, c_file_header);
+ if (!curr_pos) {
+ return WTAP_OPEN_NOT_MINE;
+ }
+ curr_pos = g_strstr_len(curr_pos, bytes_read-(curr_pos-magic_buf), c_file_format_version);
+ if (!curr_pos) {
+ return WTAP_OPEN_NOT_MINE;
+ }
+ curr_pos += CLEN(c_file_format_version);
+ if (memcmp(curr_pos, c_threegpp_doc_no, CLEN(c_threegpp_doc_no)) != 0){
+ return WTAP_OPEN_NOT_MINE;
+ }
+ /* Next we expect something like <traceCollec beginTime="..."/> */
+ curr_pos = g_strstr_len(curr_pos, bytes_read-(curr_pos-magic_buf), c_begin_time);
+ if (!curr_pos) {
+ return WTAP_OPEN_NOT_MINE;
+ }
+ curr_pos += CLEN(c_begin_time);
+ /* Next we expect an ISO 8601-format time */
+ curr_pos = iso8601_to_nstime(&start_time, curr_pos, ISO8601_DATETIME);
+ if (!curr_pos) {
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ /* Ok it's our file. From here we'll need to free memory */
+ file_info = g_new0(nettrace_3gpp_32_423_file_info_t, 1);
+ file_info->start_time = start_time;
+ file_info->start_offset = start_offset + (curr_pos - magic_buf);
+ file_info->buffer = g_byte_array_sized_new(RINGBUFFER_START_SIZE);
+ g_byte_array_append(file_info->buffer, curr_pos, (guint)(bytes_read - (curr_pos - magic_buf)));
+
+ wth->file_type_subtype = nettrace_3gpp_32_423_file_type_subtype;
+ wth->file_encap = WTAP_ENCAP_WIRESHARK_UPPER_PDU;
+ wth->file_tsprec = WTAP_TSPREC_MSEC;
+ wth->subtype_read = nettrace_read;
+ wth->subtype_seek_read = nettrace_seek_read;
+ wth->subtype_close = nettrace_close;
+ wth->snapshot_length = 0;
+ wth->priv = (void*)file_info;
+
+ return WTAP_OPEN_MINE;
+}
+
+static const struct supported_block_type nettrace_3gpp_32_423_blocks_supported[] = {
+ /*
+ * We support packet blocks, with no comments or other options.
+ */
+ { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
+};
+
+static const struct file_type_subtype_info nettrace_3gpp_32_423_info = {
+ "3GPP TS 32.423 Trace", "3gpp32423", NULL, NULL,
+ FALSE, BLOCKS_SUPPORTED(nettrace_3gpp_32_423_blocks_supported),
+ NULL, NULL, NULL
+};
+
+void register_nettrace_3gpp_32_423(void)
+{
+ nettrace_3gpp_32_423_file_type_subtype = wtap_register_file_type_subtype(&nettrace_3gpp_32_423_info);
+
+ /*
+ * Register name for backwards compatibility with the
+ * wtap_filetypes table in Lua.
+ */
+ wtap_register_backwards_compatibility_lua_name("NETTRACE_3GPP_32_423",
+ nettrace_3gpp_32_423_file_type_subtype);
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/wiretap/nettrace_3gpp_32_423.h b/wiretap/nettrace_3gpp_32_423.h
new file mode 100644
index 00000000..e34100ed
--- /dev/null
+++ b/wiretap/nettrace_3gpp_32_423.h
@@ -0,0 +1,17 @@
+/** @file
+ *
+ * MIME file format decoder for the Wiretap library.
+ *
+ * Wiretap Library
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __NETTRACE_3GPP_32_423__
+#define __NETTRACE_3GPP_32_423__
+
+#include <glib.h>
+#include "wtap.h"
+
+wtap_open_return_val nettrace_3gpp_32_423_file_open(wtap *wth, int *err, gchar **err_info);
+
+#endif
diff --git a/wiretap/netxray.c b/wiretap/netxray.c
new file mode 100644
index 00000000..6ba6c795
--- /dev/null
+++ b/wiretap/netxray.c
@@ -0,0 +1,2223 @@
+/* netxray.c
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include "wtap-int.h"
+#include "file_wrappers.h"
+#include "netxray.h"
+#include "atm.h"
+
+/* Capture file header, *including* magic number, is padded to 128 bytes. */
+#define CAPTUREFILE_HEADER_SIZE 128
+
+/* Magic number size, in both 1.x and later files. */
+#define MAGIC_SIZE 4
+
+/* Magic number in NetXRay 1.x files. */
+static const char old_netxray_magic[MAGIC_SIZE] = {
+ 'V', 'L', '\0', '\0'
+};
+
+/* Magic number in NetXRay 2.0 and later, and Windows Sniffer, files. */
+static const char netxray_magic[MAGIC_SIZE] = {
+ 'X', 'C', 'P', '\0'
+};
+
+/* NetXRay file header (minus magic number). */
+/* */
+/* As field usages are identified, please revise as needed */
+/* Please do *not* use netxray_hdr xxx... names in the code */
+/* (Placeholder names for all 'unknown' fields are */
+/* of form xxx_x<hex_hdr_offset> */
+/* where <hex_hdr_offset> *includes* the magic number) */
+
+struct netxray_hdr {
+ char version[8]; /* version number */
+ guint32 start_time; /* UNIX [UTC] time when capture started */
+
+ guint32 nframes; /* number of packets */
+ guint32 xxx_x14; /* unknown [some kind of file offset] */
+ guint32 start_offset; /* offset of first packet in capture */
+ guint32 end_offset; /* offset after last packet in capture */
+
+ guint32 xxx_x20; /* unknown [some kind of file offset] */
+ guint32 xxx_x24; /* unknown [unused ?] */
+ guint32 xxx_x28; /* unknown [some kind of file offset] */
+ guint8 network; /* datalink type */
+ guint8 network_plus; /* [See code] */
+ guint8 xxx_x2E[2]; /* unknown */
+
+ guint8 timeunit; /* encodes length of a tick */
+ guint8 xxx_x31[3]; /* XXX - upper 3 bytes of timeunit ? */
+ guint32 timelo; /* lower 32 bits of capture start time stamp */
+ guint32 timehi; /* upper 32 bits of capture start time stamp */
+ guint32 linespeed; /* speed of network, in bits/second */
+
+ guint8 xxx_x40[12]; /* unknown [other stuff] */
+ guint8 realtick[4]; /* (ticks/sec for Ethernet/Ndis/Timeunit=2 ?) */
+ /* (realtick[1], realtick[2] also currently */
+ /* used as flag for 'FCS presence') */
+
+ guint8 xxx_x50[4]; /* unknown [other stuff] */
+ guint8 captype; /* capture type */
+ guint8 xxx_x55[3]; /* unknown [other stuff] */
+ guint8 xxx_x58[4]; /* unknown [other stuff] */
+ guint8 wan_hdlc_subsub_captype; /* WAN HDLC subsub_captype */
+ guint8 xxx_x5D[3]; /* unknown [other stuff] */
+
+ guint8 xxx_x60[16]; /* unknown [other stuff] */
+
+ guint8 xxx_x70[14]; /* unknown [other stuff] */
+ gint16 timezone_hrs; /* timezone hours [at least for version 2.2..]; */
+ /* positive values = west of UTC: */
+ /* negative values = east of UTC: */
+ /* e.g. +5 is American Eastern */
+ /* [Does not appear to be adjusted for DST ] */
+};
+
+/*
+ * Capture type, in hdr.captype.
+ *
+ * Values other than 0 are dependent on the network type.
+ * For Ethernet captures, it indicates the type of capture pod.
+ * For WAN captures (all of which are done with a pod), it indicates
+ * the link-layer type.
+ */
+#define CAPTYPE_NDIS 0 /* Capture on network interface using NDIS */
+
+/*
+ * Ethernet capture types.
+ */
+#define ETH_CAPTYPE_GIGPOD 2 /* gigabit Ethernet captured with pod */
+#define ETH_CAPTYPE_OTHERPOD 3 /* non-gigabit Ethernet captured with pod */
+#define ETH_CAPTYPE_OTHERPOD2 5 /* gigabit Ethernet via pod ?? */
+ /* Captype 5 seen in capture from Distributed Sniffer with: */
+ /* Version 4.50.211 software */
+ /* SysKonnect SK-9843 Gigabit Ethernet Server Adapter */
+#define ETH_CAPTYPE_GIGPOD2 6 /* gigabit Ethernet, captured with blade on S6040-model Sniffer */
+
+/*
+ * WAN capture types.
+ */
+#define WAN_CAPTYPE_BROUTER 1 /* Bridge/router captured with pod */
+#define WAN_CAPTYPE_PPP 3 /* PPP captured with pod */
+#define WAN_CAPTYPE_FRELAY 4 /* Frame Relay captured with pod */
+#define WAN_CAPTYPE_BROUTER2 5 /* Bridge/router captured with pod */
+#define WAN_CAPTYPE_HDLC 6 /* HDLC (X.25, ISDN) captured with pod */
+#define WAN_CAPTYPE_SDLC 7 /* SDLC captured with pod */
+#define WAN_CAPTYPE_HDLC2 8 /* HDLC captured with pod */
+#define WAN_CAPTYPE_BROUTER3 9 /* Bridge/router captured with pod */
+#define WAN_CAPTYPE_SMDS 10 /* SMDS DXI */
+#define WAN_CAPTYPE_BROUTER4 11 /* Bridge/router captured with pod */
+#define WAN_CAPTYPE_BROUTER5 12 /* Bridge/router captured with pod */
+#define WAN_CAPTYPE_CHDLC 19 /* Cisco router (CHDLC) captured with pod */
+
+#define CAPTYPE_ATM 15 /* ATM captured with pod */
+
+/*
+ * # of ticks that equal 1 second, in version 002.xxx files other
+ * than Ethernet captures with a captype other than CAPTYPE_NDIS;
+ * the index into this array is hdr.timeunit.
+ *
+ * DO NOT SEND IN PATCHES THAT CHANGE ANY OF THE NON-ZERO VALUES IN
+ * ANY OF THE TpS TABLES. THOSE VALUES ARE CORRECT FOR AT LEAST ONE
+ * CAPTURE, SO CHANGING THEM WILL BREAK AT LEAST SOME CAPTURES. WE
+ * WILL NOT CHECK IN PATCHES THAT CHANGE THESE VALUES.
+ *
+ * Instead, if a value in a TpS table is wrong, check whether captype
+ * has a non-zero value; if so, perhaps we need a new TpS table for the
+ * corresponding network type and captype, or perhaps the 'realtick'
+ * field contains the correct ticks-per-second value.
+ *
+ * TpS...[] entries of 0.0 mean that no capture file for the
+ * corresponding captype/timeunit values has yet been seen, or that
+ * we're using the 'realtick' value.
+ *
+ * XXX - 05/29/07: For Ethernet captype = 0 (NDIS) and timeunit = 2:
+ * Perusal of a number of Sniffer captures
+ * (including those from Wireshark bug reports
+ * and those from the Wireshark 'menagerie')
+ * suggests that 'realtick' for this case
+ * contains the correct ticks/second to be used.
+ * So: we'll use realtick for Ethernet captype=0 and timeunit=2.
+ * (It might be that realtick should be used for Ethernet captype = 0
+ * and timeunit = 1 but I've not yet enough captures to be sure).
+ * Based upon the captures reviewed to date, realtick cannot be used for
+ * any of the other Ethernet captype/timeunit combinations for which there
+ * are non-zero values in the TpS tables.
+ *
+ * In at least one capture where "realtick" doesn't correspond
+ * to the value from the appropriate TpS table, the per-packet header's
+ * "xxx" field is all zero, so it's not as if a 2.x header includes
+ * a "compatibility" time stamp corresponding to the value from the
+ * TpS table and a "real" time stamp corresponding to "realtick".
+ *
+ * XXX - the item corresponding to timeunit = 2 is 1193180.0, presumably
+ * because somebody found it gave the right answer for some captures, but
+ * 3 times that, i.e. 3579540.0, appears to give the right answer for some
+ * other captures.
+ *
+ * Some captures have realtick of 1193182, some have 3579545, and some
+ * have 1193000. Most of those, in one set of captures somebody has,
+ * are wrong. (Did that mean "wrong for some capture files, but not
+ * for the files in which they occurred", or "wrong for the files in
+ * which they occurred? If it's "wrong for some capture files, but
+ * not for the files in which they occurred", perhaps those were Ethernet
+ * captures with a captype of 0 and timeunit = 2, so that we now use
+ * realtick, and perhaps that fixes the problems.)
+ *
+ * XXX - in at least one ATM capture, hdr.realtick is 1193180.0
+ * and hdr.timeunit is 0. Does that capture have a captype of
+ * CAPTYPE_ATM? If so, what should the table for ATM captures with
+ * that captype be?
+ */
+static const double TpS[] = { 1e6, 1193000.0, 1193182.0 };
+#define NUM_NETXRAY_TIMEUNITS (sizeof TpS / sizeof TpS[0])
+
+/*
+ * Table of time units for Ethernet captures with captype ETH_CAPTYPE_GIGPOD.
+ * 0.0 means "unknown".
+ *
+ * It appears that, at least for Ethernet captures, if captype is
+ * ETH_CAPTYPE_GIGPOD, that indicates that it's a gigabit Ethernet
+ * capture, possibly from a special whizzo gigabit pod, and also
+ * indicates that the time stamps have some higher resolution than
+ * in other captures, possibly thanks to a high-resolution timer
+ * on the pod.
+ *
+ * It also appears that the time units might differ for gigabit pod
+ * captures between version 002.001 and 002.002. For 002.001,
+ * the values below are correct; for 002.002, it's claimed that
+ * the right value for TpS_gigpod[2] is 1250000.0, but at least one
+ * 002.002 gigabit pod capture has 31250000.0 as the right value.
+ * XXX: Note that the TpS_otherpod[2] value is 1250000.0; It seems
+ * reasonable to suspect that the original claim might actually
+ * have been for a capture with a captype of 'otherpod'.
+ * (Based upon captures reviewed realtick does not contain the
+ * correct TpS values for the 'gigpod' captype).
+ */
+static const double TpS_gigpod[] = { 1e9, 0.0, 31250000.0 };
+#define NUM_NETXRAY_TIMEUNITS_GIGPOD (sizeof TpS_gigpod / sizeof TpS_gigpod[0])
+
+/*
+ * Table of time units for Ethernet captures with captype ETH_CAPTYPE_OTHERPOD.
+ * (Based upon captures reviewed realtick does not contain the
+ * correct TpS values for the 'otherpod' captype).
+ */
+static const double TpS_otherpod[] = { 1e6, 0.0, 1250000.0 };
+#define NUM_NETXRAY_TIMEUNITS_OTHERPOD (sizeof TpS_otherpod / sizeof TpS_otherpod[0])
+
+/*
+ * Table of time units for Ethernet captures with captype ETH_CAPTYPE_OTHERPOD2.
+ * (Based upon captures reviewed realtick does not contain the
+ * correct TpS values for the 'otherpod2' captype).
+ */
+static const double TpS_otherpod2[] = { 1e6, 0.0, 0.0 };
+#define NUM_NETXRAY_TIMEUNITS_OTHERPOD2 (sizeof TpS_otherpod2 / sizeof TpS_otherpod2[0])
+
+/*
+ * Table of time units for Ethernet captures with captype ETH_CAPTYPE_GIGPOD2.
+ * (Based upon captures reviewed realtick does not contain the
+ * correct TpS values for the 'gigpod2' captype).
+ */
+static const double TpS_gigpod2[] = { 1e9, 0.0, 20000000.0 };
+#define NUM_NETXRAY_TIMEUNITS_GIGPOD2 (sizeof TpS_gigpod2 / sizeof TpS_gigpod2[0])
+
+/* Version number strings. */
+static const char vers_1_0[] = {
+ '0', '0', '1', '.', '0', '0', '0', '\0'
+};
+
+static const char vers_1_1[] = {
+ '0', '0', '1', '.', '1', '0', '0', '\0'
+};
+
+static const char vers_2_000[] = {
+ '0', '0', '2', '.', '0', '0', '0', '\0'
+};
+
+static const char vers_2_001[] = {
+ '0', '0', '2', '.', '0', '0', '1', '\0'
+};
+
+static const char vers_2_002[] = {
+ '0', '0', '2', '.', '0', '0', '2', '\0'
+};
+
+static const char vers_2_003[] = {
+ '0', '0', '2', '.', '0', '0', '3', '\0'
+};
+
+/* Old NetXRay data record format - followed by frame data. */
+struct old_netxrayrec_hdr {
+ guint32 timelo; /* lower 32 bits of time stamp */
+ guint32 timehi; /* upper 32 bits of time stamp */
+ guint16 len; /* packet length */
+ guint8 xxx[6]; /* unknown */
+};
+
+/* NetXRay format version 1.x data record format - followed by frame data. */
+struct netxrayrec_1_x_hdr {
+ guint32 timelo; /* lower 32 bits of time stamp */
+ guint32 timehi; /* upper 32 bits of time stamp */
+ guint16 orig_len; /* packet length */
+ guint16 incl_len; /* capture length */
+ guint8 xxx[16]; /* unknown */
+};
+
+/*
+ * NetXRay format version 2.x data record format - followed by frame data.
+ *
+ * The xxx fields appear to be:
+ *
+ * xxx[0]: ATM traffic type and subtype in the low 3 bits of
+ * each nibble, and flags(?) in the upper bit of each nibble.
+ * Always 0 for 802.11?
+ *
+ * xxx[1]: Always 0 for 802.11?
+ *
+ * xxx[2], xxx[3]: for Ethernet, 802.11, ISDN LAPD, LAPB,
+ * Frame Relay, if both are 0xff, there are 4 bytes of stuff
+ * at the end of the packet data, which might be an FCS or
+ * which might be junk to discard.
+ *
+ * xxx[4-7]: Always 0 for 802.11?
+ *
+ * xxx[8], xxx[9]: 2 bytes of a flag word? If treated as
+ * a 2-byte little-endian flag word:
+ *
+ * 0x0001: Error of some sort, including bad CRC, although
+ * in one ISDN capture it's set in some B2 channel
+ * packets of unknown content (as opposed to the B1
+ * traffic in the capture, which is PPP)
+ * 0x0002: Seen in 802.11 - short preamble? Bad CRC?
+ * 0x0004: Some particular type of error?
+ * 0x0008: For (Gigabit?) Ethernet (with special probe?),
+ * 4 bytes at end are junk rather than CRC?
+ * 0x0100: CRC error on ATM? Protected and Not decrypted
+ * for 802.11? Bad CRC? Short preamble?
+ * 0x0200: Something for ATM? Something else for 802.11?
+ * 0x0400: raw ATM cell
+ * 0x0800: OAM cell?
+ * 0x2000: port on which the packet was captured?
+ *
+ * The Sniffer Portable 4.8 User's Guide lists a set of packet status
+ * flags including:
+ *
+ * packet is marked;
+ * packet was captured from Port A on the pod or adapter card;
+ * packet was captured from Port B on the pod or adapter card;
+ * packet has a symptom or diagnosis associated with it;
+ * packet is an event filter trigger;
+ * CRC error packet with normal packet size;
+ * CRC error packet with oversize error;
+ * packet size < 64 bytes (including CRC) but with valid CRC;
+ * packet size < 64 bytes (including CRC) with CRC error;
+ * packet size > 1518 bytes (including CRC) but with valid CRC;
+ * packet damaged by a collision;
+ * packet length not a multiple of 8 bits;
+ * address conflict in the ring on Token Ring;
+ * packet is not copied (received) by the destination host on
+ * Token Ring;
+ * AAL5 length error;
+ * AAL5 maximum segments error;
+ * ATM timeout error;
+ * ATM buffer error;
+ * ATM unknown error;
+ * and a ton of AAL2 errors.
+ *
+ * Not all those bits necessarily correspond to flag bits in the file,
+ * but some might.
+ *
+ * In one ATM capture, the 0x2000 bit was set for all frames; in another,
+ * it's unset for all frames. This, plus the ATMbook having two ports,
+ * suggests that it *might* be a "port A vs. port B" flag.
+ *
+ * The 0x0001 bit appears to be set for CRC errors on Ethernet and 802.11.
+ * It also appears to be set on ATM for AAL5 PDUs that appear to be
+ * completely reassembled and that have a CRC error and for frames that
+ * appear to be part of a full AAL5 PDU. In at least two files with
+ * frames of the former type, the 0x0100 and 0x0200 flags are set;
+ * in at least one file with frames of the latter type, neither of
+ * those flags are set.
+ *
+ * The field appears to be somewhat random in some captures,
+ * however.
+ *
+ * xxx[10]: for 802.11, always 0?
+ *
+ * xxx[11]: for 802.11, 0x05 if the packet is WEP-encrypted(?).
+ *
+ * xxx[12]: for 802.11, channel number.
+ *
+ * xxx[13]: for 802.11, data rate, in 500 Kb/s units.
+ *
+ * xxx[14]: for 802.11, signal strength.
+ *
+ * xxx[15]: for 802.11, noise level; 0xFF means none reported,
+ * 0x7F means 100%.
+ *
+ * xxx[16-19]: for 802.11, PHY header, at least for {HR/}DSSS,
+ * in at least one capture.
+ * In another capture, xxx[16] appears to be the
+ * data rate in 500 Kb/s units
+ * Chip-dependent stuff?
+ *
+ * xxx[20-25]: for 802.11, MAC address of sending machine(?).
+ *
+ * xxx[26]: for 802.11, one of 0x00, 0x01, 0x03, or 0x0b?
+ *
+ * xxx[27]: for 802.11, one of 0x00 or 0x30?
+ */
+struct netxrayrec_2_x_hdr {
+ guint32 timelo; /* lower 32 bits of time stamp */
+ guint32 timehi; /* upper 32 bits of time stamp */
+ guint16 orig_len; /* packet length */
+ guint16 incl_len; /* capture length */
+ guint8 xxx[28]; /* various data */
+};
+
+/*
+ * Union of the data record headers.
+ */
+union netxrayrec_hdr {
+ struct old_netxrayrec_hdr old_hdr;
+ struct netxrayrec_1_x_hdr hdr_1_x;
+ struct netxrayrec_2_x_hdr hdr_2_x;
+};
+
+typedef struct {
+ time_t start_time;
+ double ticks_per_sec;
+ double start_timestamp;
+ gboolean wrapped;
+ guint32 nframes;
+ gint64 start_offset;
+ gint64 end_offset;
+ int version_major;
+ gboolean fcs_valid; /* if packets have valid FCS at the end */
+ guint isdn_type; /* 1 = E1 PRI, 2 = T1 PRI, 3 = BRI */
+} netxray_t;
+
+static gboolean netxray_read(wtap *wth, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info, gint64 *data_offset);
+static gboolean netxray_seek_read(wtap *wth, gint64 seek_off,
+ wtap_rec *rec, Buffer *buf, int *err, gchar **err_info);
+static int netxray_process_rec_header(wtap *wth, FILE_T fh,
+ wtap_rec *rec, int *err, gchar **err_info);
+static void netxray_guess_atm_type(wtap *wth, wtap_rec *rec,
+ Buffer *buf);
+static gboolean netxray_dump_1_1(wtap_dumper *wdh,
+ const wtap_rec *rec,
+ const guint8 *pd, int *err, gchar **err_info);
+static gboolean netxray_dump_finish_1_1(wtap_dumper *wdh, int *err,
+ gchar **err_info);
+static gboolean netxray_dump_2_0(wtap_dumper *wdh,
+ const wtap_rec *rec,
+ const guint8 *pd, int *err, gchar **err_info);
+static gboolean netxray_dump_finish_2_0(wtap_dumper *wdh, int *err,
+ gchar **err_info);
+
+static int netxray_old_file_type_subtype = -1;
+static int netxray_1_0_file_type_subtype = -1;
+static int netxray_1_1_file_type_subtype = -1;
+static int netxray_2_00x_file_type_subtype = -1;
+
+void register_netxray(void);
+
+wtap_open_return_val
+netxray_open(wtap *wth, int *err, gchar **err_info)
+{
+ char magic[MAGIC_SIZE];
+ gboolean is_old;
+ struct netxray_hdr hdr;
+ guint network_type;
+ double ticks_per_sec;
+ int version_major, version_minor;
+ int file_type;
+ double start_timestamp;
+ static const int netxray_encap[] = {
+ WTAP_ENCAP_UNKNOWN,
+ WTAP_ENCAP_ETHERNET,
+ WTAP_ENCAP_TOKEN_RING,
+ WTAP_ENCAP_FDDI_BITSWAPPED,
+ /*
+ * XXX - some PPP captures may look like Ethernet,
+ * perhaps because they're using NDIS to capture on the
+ * same machine and it provides simulated-Ethernet
+ * packets, but captures taken with various serial
+ * pods use the same network type value but aren't
+ * shaped like Ethernet. We handle that below.
+ */
+ WTAP_ENCAP_ETHERNET, /* WAN(PPP), but shaped like Ethernet */
+ WTAP_ENCAP_UNKNOWN, /* LocalTalk */
+ WTAP_ENCAP_UNKNOWN, /* "DIX" - should not occur */
+ WTAP_ENCAP_UNKNOWN, /* ARCNET raw */
+ WTAP_ENCAP_UNKNOWN, /* ARCNET 878.2 */
+ WTAP_ENCAP_ATM_PDUS_UNTRUNCATED,/* ATM */
+ WTAP_ENCAP_IEEE_802_11_WITH_RADIO,
+ /* Wireless WAN with radio information */
+ WTAP_ENCAP_UNKNOWN /* IrDA */
+ };
+ #define NUM_NETXRAY_ENCAPS (sizeof netxray_encap / sizeof netxray_encap[0])
+ int file_encap;
+ guint isdn_type = 0;
+ netxray_t *netxray;
+
+ /* Read in the string that should be at the start of a NetXRay
+ * file */
+ if (!wtap_read_bytes(wth->fh, magic, MAGIC_SIZE, err, err_info)) {
+ if (*err != WTAP_ERR_SHORT_READ)
+ return WTAP_OPEN_ERROR;
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ if (memcmp(magic, netxray_magic, MAGIC_SIZE) == 0) {
+ is_old = FALSE;
+ } else if (memcmp(magic, old_netxray_magic, MAGIC_SIZE) == 0) {
+ is_old = TRUE;
+ } else {
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ /* Read the rest of the header. */
+ if (!wtap_read_bytes(wth->fh, &hdr, sizeof hdr, err, err_info))
+ return WTAP_OPEN_ERROR;
+
+ if (is_old) {
+ version_major = 0;
+ version_minor = 0;
+ file_type = netxray_old_file_type_subtype;
+ } else {
+ /* It appears that version 1.1 files (as produced by Windows
+ * Sniffer Pro 2.0.01) have the time stamp in microseconds,
+ * rather than the milliseconds version 1.0 files appear to
+ * have.
+ *
+ * It also appears that version 2.00x files have per-packet
+ * headers with some extra fields. */
+ if (memcmp(hdr.version, vers_1_0, sizeof vers_1_0) == 0) {
+ version_major = 1;
+ version_minor = 0;
+ file_type = netxray_1_0_file_type_subtype;
+ } else if (memcmp(hdr.version, vers_1_1, sizeof vers_1_1) == 0) {
+ version_major = 1;
+ version_minor = 1;
+ file_type = netxray_1_1_file_type_subtype;
+ } else if (memcmp(hdr.version, vers_2_000, sizeof vers_2_000) == 0) {
+ version_major = 2;
+ version_minor = 0;
+ file_type = netxray_2_00x_file_type_subtype;
+ } else if (memcmp(hdr.version, vers_2_001, sizeof vers_2_001) == 0) {
+ version_major = 2;
+ version_minor = 1;
+ file_type = netxray_2_00x_file_type_subtype;
+ } else if (memcmp(hdr.version, vers_2_002, sizeof vers_2_002) == 0) {
+ version_major = 2;
+ version_minor = 2;
+ file_type = netxray_2_00x_file_type_subtype;
+ } else if (memcmp(hdr.version, vers_2_003, sizeof vers_2_003) == 0) {
+ version_major = 2;
+ version_minor = 3;
+ file_type = netxray_2_00x_file_type_subtype;
+ } else {
+ *err = WTAP_ERR_UNSUPPORTED;
+ *err_info = ws_strdup_printf("netxray: version \"%.8s\" unsupported", hdr.version);
+ return WTAP_OPEN_ERROR;
+ }
+ }
+
+ switch (hdr.network_plus) {
+
+ case 0:
+ /*
+ * The byte after hdr.network is usually 0, in which case
+ * the hdr.network byte is an NDIS network type value - 1.
+ */
+ network_type = hdr.network + 1;
+ break;
+
+ case 2:
+ /*
+ * However, in some Ethernet captures, it's 2, and the
+ * hdr.network byte is 1 rather than 0. We assume
+ * that if there's a byte after hdr.network with the value
+ * 2, the hdr.network byte is an NDIS network type, rather
+ * than an NDIS network type - 1.
+ */
+ network_type = hdr.network;
+ break;
+
+ default:
+ *err = WTAP_ERR_UNSUPPORTED;
+ *err_info = ws_strdup_printf("netxray: the byte after the network type has the value %u, which I don't understand",
+ hdr.network_plus);
+ return WTAP_OPEN_ERROR;
+ }
+
+ if (network_type >= NUM_NETXRAY_ENCAPS
+ || netxray_encap[network_type] == WTAP_ENCAP_UNKNOWN) {
+ *err = WTAP_ERR_UNSUPPORTED;
+ *err_info = ws_strdup_printf("netxray: network type %u (%u) unknown or unsupported",
+ network_type, hdr.network_plus);
+ return WTAP_OPEN_ERROR;
+ }
+
+ /*
+ * Figure out the time stamp units and start time stamp.
+ */
+ start_timestamp = (double)pletoh32(&hdr.timelo)
+ + (double)pletoh32(&hdr.timehi)*4294967296.0;
+ if (is_old) {
+ ticks_per_sec = 1000.0;
+ wth->file_tsprec = WTAP_TSPREC_MSEC;
+ } else if (version_major == 1) {
+ switch (version_minor) {
+
+ case 0:
+ ticks_per_sec = 1000.0;
+ wth->file_tsprec = WTAP_TSPREC_MSEC;
+ break;
+
+ case 1:
+ /*
+ * In version 1.1 files (as produced by Windows
+ * Sniffer Pro 2.0.01), the time stamp is in
+ * microseconds, rather than the milliseconds
+ * time stamps in NetXRay and older versions
+ * of Windows Sniffer.
+ */
+ ticks_per_sec = 1000000.0;
+ wth->file_tsprec = WTAP_TSPREC_USEC;
+ break;
+
+ default:
+ /* "Can't happen" - we rejected that above */
+ *err = WTAP_ERR_INTERNAL;
+ *err_info = ws_strdup_printf("netxray: version %d.%d somehow didn't get rejected",
+ version_major, version_minor);
+ return WTAP_OPEN_ERROR;
+ }
+ } else if (version_major == 2) {
+ /*
+ * Get the time stamp units from the appropriate TpS
+ * table or from the file header.
+ */
+ switch (network_type) {
+
+ case 1:
+ /*
+ * Ethernet - the table to use depends on whether
+ * this is an NDIS or pod capture.
+ */
+ switch (hdr.captype) {
+
+ case CAPTYPE_NDIS:
+ if (hdr.timeunit >= NUM_NETXRAY_TIMEUNITS) {
+ *err = WTAP_ERR_UNSUPPORTED;
+ *err_info = ws_strdup_printf(
+ "netxray: Unknown timeunit %u for Ethernet/CAPTYPE_NDIS version %.8s capture",
+ hdr.timeunit, hdr.version);
+ return WTAP_OPEN_ERROR;
+ }
+ /*
+ XXX: 05/29/07: Use 'realtick' instead of TpS table if timeunit=2;
+ Using 'realtick' in this case results
+ in the correct 'ticks per second' for all the captures that
+ I have of this type (including captures from a number of Wireshark
+ bug reports).
+ */
+ if (hdr.timeunit == 2) {
+ ticks_per_sec = pletoh32(hdr.realtick);
+ }
+ else {
+ ticks_per_sec = TpS[hdr.timeunit];
+ }
+ break;
+
+ case ETH_CAPTYPE_GIGPOD:
+ if (hdr.timeunit >= NUM_NETXRAY_TIMEUNITS_GIGPOD
+ || TpS_gigpod[hdr.timeunit] == 0.0) {
+ *err = WTAP_ERR_UNSUPPORTED;
+ *err_info = ws_strdup_printf(
+ "netxray: Unknown timeunit %u for Ethernet/ETH_CAPTYPE_GIGPOD version %.8s capture",
+ hdr.timeunit, hdr.version);
+ return WTAP_OPEN_ERROR;
+ }
+ ticks_per_sec = TpS_gigpod[hdr.timeunit];
+
+ /*
+ * At least for 002.002 and 002.003
+ * captures, the start time stamp is 0,
+ * not the value in the file.
+ */
+ if (version_minor == 2 || version_minor == 3)
+ start_timestamp = 0.0;
+ break;
+
+ case ETH_CAPTYPE_OTHERPOD:
+ if (hdr.timeunit >= NUM_NETXRAY_TIMEUNITS_OTHERPOD
+ || TpS_otherpod[hdr.timeunit] == 0.0) {
+ *err = WTAP_ERR_UNSUPPORTED;
+ *err_info = ws_strdup_printf(
+ "netxray: Unknown timeunit %u for Ethernet/ETH_CAPTYPE_OTHERPOD version %.8s capture",
+ hdr.timeunit, hdr.version);
+ return WTAP_OPEN_ERROR;
+ }
+ ticks_per_sec = TpS_otherpod[hdr.timeunit];
+
+ /*
+ * At least for 002.002 and 002.003
+ * captures, the start time stamp is 0,
+ * not the value in the file.
+ */
+ if (version_minor == 2 || version_minor == 3)
+ start_timestamp = 0.0;
+ break;
+
+ case ETH_CAPTYPE_OTHERPOD2:
+ if (hdr.timeunit >= NUM_NETXRAY_TIMEUNITS_OTHERPOD2
+ || TpS_otherpod2[hdr.timeunit] == 0.0) {
+ *err = WTAP_ERR_UNSUPPORTED;
+ *err_info = ws_strdup_printf(
+ "netxray: Unknown timeunit %u for Ethernet/ETH_CAPTYPE_OTHERPOD2 version %.8s capture",
+ hdr.timeunit, hdr.version);
+ return WTAP_OPEN_ERROR;
+ }
+ ticks_per_sec = TpS_otherpod2[hdr.timeunit];
+ /*
+ * XXX: start time stamp in the one capture file examined of this type was 0;
+ * We'll assume the start time handling is the same as for other pods.
+ *
+ * At least for 002.002 and 002.003
+ * captures, the start time stamp is 0,
+ * not the value in the file.
+ */
+ if (version_minor == 2 || version_minor == 3)
+ start_timestamp = 0.0;
+ break;
+
+ case ETH_CAPTYPE_GIGPOD2:
+ if (hdr.timeunit >= NUM_NETXRAY_TIMEUNITS_GIGPOD2
+ || TpS_gigpod2[hdr.timeunit] == 0.0) {
+ *err = WTAP_ERR_UNSUPPORTED;
+ *err_info = ws_strdup_printf(
+ "netxray: Unknown timeunit %u for Ethernet/ETH_CAPTYPE_GIGPOD2 version %.8s capture",
+ hdr.timeunit, hdr.version);
+ return WTAP_OPEN_ERROR;
+ }
+ ticks_per_sec = TpS_gigpod2[hdr.timeunit];
+ /*
+ * XXX: start time stamp in the one capture file examined of this type was 0;
+ * We'll assume the start time handling is the same as for other pods.
+ *
+ * At least for 002.002 and 002.003
+ * captures, the start time stamp is 0,
+ * not the value in the file.
+ */
+ if (version_minor == 2 || version_minor == 3)
+ start_timestamp = 0.0;
+ break;
+
+ default:
+ *err = WTAP_ERR_UNSUPPORTED;
+ *err_info = ws_strdup_printf(
+ "netxray: Unknown capture type %u for Ethernet version %.8s capture",
+ hdr.captype, hdr.version);
+ return WTAP_OPEN_ERROR;
+ }
+ break;
+
+ default:
+ if (hdr.timeunit >= NUM_NETXRAY_TIMEUNITS) {
+ *err = WTAP_ERR_UNSUPPORTED;
+ *err_info = ws_strdup_printf(
+ "netxray: Unknown timeunit %u for %u/%u version %.8s capture",
+ hdr.timeunit, network_type, hdr.captype,
+ hdr.version);
+ return WTAP_OPEN_ERROR;
+ }
+ ticks_per_sec = TpS[hdr.timeunit];
+ break;
+ }
+
+ /*
+ * If the number of ticks per second is greater than
+ * 1 million, make the precision be nanoseconds rather
+ * than microseconds.
+ *
+ * XXX - do values only slightly greater than one million
+ * correspond to a resolution sufficiently better than
+ * 1 microsecond to display more digits of precision?
+ * XXX - Seems reasonable to use nanosecs only if TPS >= 10M
+ */
+ if (ticks_per_sec >= 1e7)
+ wth->file_tsprec = WTAP_TSPREC_NSEC;
+ else
+ wth->file_tsprec = WTAP_TSPREC_USEC;
+ } else {
+ /* "Can't happen" - we rejected that above */
+ *err = WTAP_ERR_INTERNAL;
+ *err_info = ws_strdup_printf("netxray: version %d.%d somehow didn't get rejected",
+ version_major, version_minor);
+ return WTAP_OPEN_ERROR;
+ }
+ start_timestamp = start_timestamp/ticks_per_sec;
+
+ if (network_type == 4) {
+ /*
+ * In version 0 and 1, we assume, for now, that all
+ * WAN captures have frames that look like Ethernet
+ * frames (as a result, presumably, of having passed
+ * through NDISWAN).
+ *
+ * In version 2, it looks as if there's stuff in the
+ * file header to specify what particular type of WAN
+ * capture we have.
+ */
+ if (version_major == 2) {
+ switch (hdr.captype) {
+
+ case WAN_CAPTYPE_PPP:
+ /*
+ * PPP.
+ */
+ file_encap = WTAP_ENCAP_PPP_WITH_PHDR;
+ break;
+
+ case WAN_CAPTYPE_FRELAY:
+ /*
+ * Frame Relay.
+ *
+ * XXX - in at least one capture, this
+ * is Cisco HDLC, not Frame Relay, but
+ * in another capture, it's Frame Relay.
+ *
+ * [Bytes in each capture:
+ * Cisco HDLC: hdr.xxx_x60[06:10]: 0x02 0x00 0x01 0x00 0x06
+ * Frame Relay: hdr.xxx_x60[06:10] 0x00 0x00 0x00 0x00 0x00
+
+ * Cisco HDLC: hdr.xxx_x60[14:15]: 0xff 0xff
+ * Frame Relay: hdr.xxx_x60[14:15]: 0x00 0x00
+ * ]
+ */
+ file_encap = WTAP_ENCAP_FRELAY_WITH_PHDR;
+ break;
+
+ case WAN_CAPTYPE_HDLC:
+ case WAN_CAPTYPE_HDLC2:
+ /*
+ * Various HDLC flavors?
+ */
+ switch (hdr.wan_hdlc_subsub_captype) {
+
+ case 0: /* LAPB/X.25 */
+ /*
+ * XXX - at least one capture of
+ * this type appears to be PPP.
+ */
+ file_encap = WTAP_ENCAP_LAPB;
+ break;
+
+ case 1: /* E1 PRI */
+ case 2: /* T1 PRI */
+ case 3: /* BRI */
+ file_encap = WTAP_ENCAP_ISDN;
+ isdn_type = hdr.wan_hdlc_subsub_captype;
+ break;
+
+ default:
+ *err = WTAP_ERR_UNSUPPORTED;
+ *err_info = ws_strdup_printf("netxray: WAN HDLC capture subsubtype 0x%02x unknown or unsupported",
+ hdr.wan_hdlc_subsub_captype);
+ return WTAP_OPEN_ERROR;
+ }
+ break;
+
+ case WAN_CAPTYPE_SDLC:
+ /*
+ * SDLC.
+ */
+ file_encap = WTAP_ENCAP_SDLC;
+ break;
+
+ case WAN_CAPTYPE_CHDLC:
+ /*
+ * Cisco router (CHDLC) captured with pod
+ */
+ file_encap = WTAP_ENCAP_CHDLC_WITH_PHDR;
+ break;
+
+ default:
+ *err = WTAP_ERR_UNSUPPORTED;
+ *err_info = ws_strdup_printf("netxray: WAN capture subtype 0x%02x unknown or unsupported",
+ hdr.captype);
+ return WTAP_OPEN_ERROR;
+ }
+ } else
+ file_encap = WTAP_ENCAP_ETHERNET;
+ } else
+ file_encap = netxray_encap[network_type];
+
+ /* This is a netxray file */
+ wth->file_type_subtype = file_type;
+ netxray = g_new(netxray_t, 1);
+ wth->priv = (void *)netxray;
+ wth->subtype_read = netxray_read;
+ wth->subtype_seek_read = netxray_seek_read;
+ wth->file_encap = file_encap;
+ wth->snapshot_length = 0; /* not available in header */
+ netxray->start_time = pletoh32(&hdr.start_time);
+ netxray->ticks_per_sec = ticks_per_sec;
+ netxray->start_timestamp = start_timestamp;
+ netxray->version_major = version_major;
+
+ /*
+ * If frames have an extra 4 bytes of stuff at the end, is
+ * it an FCS, or just junk?
+ */
+ netxray->fcs_valid = FALSE;
+ switch (file_encap) {
+
+ case WTAP_ENCAP_ETHERNET:
+ case WTAP_ENCAP_IEEE_802_11_WITH_RADIO:
+ case WTAP_ENCAP_ISDN:
+ case WTAP_ENCAP_LAPB:
+ /*
+ * It appears that, in at least some version 2 Ethernet
+ * captures, for frames that have 0xff in hdr_2_x.xxx[2]
+ * and hdr_2_x.xxx[3] in the per-packet header:
+ *
+ * if, in the file header, hdr.realtick[1] is 0x34
+ * and hdr.realtick[2] is 0x12, the frames have an
+ * FCS at the end;
+ *
+ * otherwise, they have 4 bytes of junk at the end.
+ *
+ * Yes, it's strange that you have to check the *middle*
+ * of the time stamp field; you can't check for any
+ * particular value of the time stamp field.
+ *
+ * For now, we assume that to be true for 802.11 captures
+ * as well; it appears to be the case for at least one
+ * such capture - the file doesn't have 0x34 and 0x12,
+ * and the 4 bytes at the end of the frames with 0xff
+ * are junk, not an FCS.
+ *
+ * For ISDN captures, it appears, at least in some
+ * captures, to be similar, although I haven't yet
+ * checked whether it's a valid FCS.
+ *
+ * XXX - should we do this for all encapsulation types?
+ *
+ * XXX - is there some other field that *really* indicates
+ * whether we have an FCS or not? The check of the time
+ * stamp is bizarre, as we're checking the middle.
+ * Perhaps hdr.realtick[0] is 0x00, in which case time
+ * stamp units in the range 1192960 through 1193215
+ * correspond to captures with an FCS, but that's still
+ * a bit bizarre.
+ *
+ * Note that there are captures with a network type of 0
+ * (Ethernet) and capture type of 0 (NDIS) that do, and
+ * that don't, have 0x34 0x12 in them, and at least one
+ * of the NDIS captures with 0x34 0x12 in it has FCSes,
+ * so it's not as if no NDIS captures have an FCS.
+ *
+ * There are also captures with a network type of 4 (WAN),
+ * capture type of 6 (HDLC), and subtype of 2 (T1 PRI) that
+ * do, and that don't, have 0x34 0x12, so there are at least
+ * some captures taken with a WAN pod that might lack an FCS.
+ * (We haven't yet tried dissecting the 4 bytes at the
+ * end of packets with hdr_2_x.xxx[2] and hdr_2_x.xxx[3]
+ * equal to 0xff as an FCS.)
+ *
+ * All captures I've seen that have 0x34 and 0x12 *and*
+ * have at least one frame with an FCS have a value of
+ * 0x01 in xxx_x40[4]. No captures I've seen with a network
+ * type of 0 (Ethernet) missing 0x34 0x12 have 0x01 there,
+ * however. However, there's at least one capture
+ * without 0x34 and 0x12, with a network type of 0,
+ * and with 0x01 in xxx_x40[4], *without* FCSes in the
+ * frames - the 4 bytes at the end are all zero - so it's
+ * not as simple as "xxx_x40[4] = 0x01 means the 4 bytes at
+ * the end are FCSes". Also, there's also at least one
+ * 802.11 capture with an xxx_x40[4] value of 0x01 with junk
+ * rather than an FCS at the end of the frame, so xxx_x40[4]
+ * isn't an obvious flag to determine whether the
+ * capture has FCSes.
+ *
+ * There don't seem to be any other values in any of the
+ * xxx_x5..., xxx_x6...., xxx_x7.... fields
+ * that obviously correspond to frames having an FCS.
+ *
+ * 05/29/07: Examination of numerous sniffer captures suggests
+ * that the apparent correlation of certain realtick
+ * bytes to 'FCS presence' may actually be
+ * a 'false positive'.
+ * ToDo: Review analysis and update code.
+ * It might be that the ticks-per-second value
+ * is hardware-dependent, and that hardware with
+ * a particular realtick value puts an FCS there
+ * and other hardware doesn't.
+ */
+ if (version_major == 2) {
+ if (hdr.realtick[1] == 0x34 && hdr.realtick[2] == 0x12)
+ netxray->fcs_valid = TRUE;
+ }
+ break;
+ }
+
+ /*
+ * Remember the ISDN type, as we need it to interpret the
+ * channel number in ISDN captures.
+ */
+ netxray->isdn_type = isdn_type;
+
+ /* Remember the offset after the last packet in the capture (which
+ * isn't necessarily the last packet in the file), as it appears
+ * there's sometimes crud after it.
+ * XXX: Remember 'start_offset' to help testing for 'short file' at EOF
+ */
+ netxray->wrapped = FALSE;
+ netxray->nframes = pletoh32(&hdr.nframes);
+ netxray->start_offset = pletoh32(&hdr.start_offset);
+ netxray->end_offset = pletoh32(&hdr.end_offset);
+
+ /* Seek to the beginning of the data records. */
+ if (file_seek(wth->fh, netxray->start_offset, SEEK_SET, err) == -1) {
+ return WTAP_OPEN_ERROR;
+ }
+
+ /*
+ * Add an IDB; we don't know how many interfaces were
+ * involved, so we just say one interface, about which
+ * we only know the link-layer type, snapshot length,
+ * and time stamp resolution.
+ */
+ wtap_add_generated_idb(wth);
+
+ return WTAP_OPEN_MINE;
+}
+
+/* Read the next packet */
+static gboolean
+netxray_read(wtap *wth, wtap_rec *rec, Buffer *buf, int *err,
+ gchar **err_info, gint64 *data_offset)
+{
+ netxray_t *netxray = (netxray_t *)wth->priv;
+ int padding;
+
+reread:
+ /*
+ * Return the offset of the record header, so we can reread it
+ * if we go back to this frame.
+ */
+ *data_offset = file_tell(wth->fh);
+
+ /* Have we reached the end of the packet data? */
+ if (*data_offset == netxray->end_offset) {
+ /* Yes. */
+ *err = 0; /* it's just an EOF, not an error */
+ return FALSE;
+ }
+
+ /* Read and process record header. */
+ padding = netxray_process_rec_header(wth, wth->fh, rec, err, err_info);
+ if (padding < 0) {
+ /*
+ * Error or EOF.
+ */
+ if (*err != 0) {
+ /*
+ * Error of some sort; give up.
+ */
+ return FALSE;
+ }
+
+ /* We're at EOF. Wrap?
+ * XXX: Need to handle 'short file' cases
+ * (Distributed Sniffer seems to have a
+ * certain small propensity to generate 'short' files
+ * i.e. [many] bytes are missing from the end of the file)
+ * case 1: start_offset < end_offset
+ * wrap will read already read packets again;
+ * so: error with "short file"
+ * case 2: start_offset > end_offset ("circular" file)
+ * wrap will mean there's a gap (missing packets).
+ * However, I don't see a good way to identify this
+ * case so we'll just have to allow the wrap.
+ * (Maybe there can be an error message after all
+ * packets are read since there'll be less packets than
+ * specified in the file header).
+ * Note that these cases occur *only* if a 'short' eof occurs exactly
+ * at the expected beginning of a frame header record; If there is a
+ * partial frame header (or partial frame data) record, then the
+ * netxray_read... functions will detect the short record.
+ */
+ if (netxray->start_offset < netxray->end_offset) {
+ *err = WTAP_ERR_SHORT_READ;
+ return FALSE;
+ }
+
+ if (!netxray->wrapped) {
+ /* Yes. Remember that we did. */
+ netxray->wrapped = TRUE;
+ if (file_seek(wth->fh, CAPTUREFILE_HEADER_SIZE,
+ SEEK_SET, err) == -1)
+ return FALSE;
+ goto reread;
+ }
+
+ /* We've already wrapped - don't wrap again. */
+ return FALSE;
+ }
+
+ /*
+ * Read the packet data.
+ */
+ if (!wtap_read_packet_bytes(wth->fh, buf,
+ rec->rec_header.packet_header.caplen, err, err_info))
+ return FALSE;
+
+ /*
+ * If there's extra stuff at the end of the record, skip it.
+ */
+ if (!wtap_read_bytes(wth->fh, NULL, padding, err, err_info))
+ return FALSE;
+
+ /*
+ * If it's an ATM packet, and we don't have enough information
+ * from the packet header to determine its type or subtype,
+ * attempt to guess them from the packet data.
+ */
+ netxray_guess_atm_type(wth, rec, buf);
+ return TRUE;
+}
+
+static gboolean
+netxray_seek_read(wtap *wth, gint64 seek_off,
+ wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info)
+{
+ if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
+ return FALSE;
+
+ if (netxray_process_rec_header(wth, wth->random_fh, rec, err,
+ err_info) == -1) {
+ if (*err == 0) {
+ /*
+ * EOF - we report that as a short read, as
+ * we've read this once and know that it
+ * should be there.
+ */
+ *err = WTAP_ERR_SHORT_READ;
+ }
+ return FALSE;
+ }
+
+ /*
+ * Read the packet data.
+ */
+ if (!wtap_read_packet_bytes(wth->random_fh, buf, rec->rec_header.packet_header.caplen, err,
+ err_info))
+ return FALSE;
+
+ /*
+ * If it's an ATM packet, and we don't have enough information
+ * from the packet header to determine its type or subtype,
+ * attempt to guess them from the packet data.
+ */
+ netxray_guess_atm_type(wth, rec, buf);
+ return TRUE;
+}
+
+static int
+netxray_process_rec_header(wtap *wth, FILE_T fh, wtap_rec *rec,
+ int *err, gchar **err_info)
+{
+ netxray_t *netxray = (netxray_t *)wth->priv;
+ union netxrayrec_hdr hdr;
+ int hdr_size = 0;
+ double t;
+ int packet_size;
+ int padding = 0;
+
+ /* Read record header. */
+ switch (netxray->version_major) {
+
+ case 0:
+ hdr_size = sizeof (struct old_netxrayrec_hdr);
+ break;
+
+ case 1:
+ hdr_size = sizeof (struct netxrayrec_1_x_hdr);
+ break;
+
+ case 2:
+ hdr_size = sizeof (struct netxrayrec_2_x_hdr);
+ break;
+ }
+ if (!wtap_read_bytes_or_eof(fh, (void *)&hdr, hdr_size, err, err_info)) {
+ /*
+ * If *err is 0, we're at EOF. *err being 0 and a return
+ * value of -1 tells our caller we're at EOF.
+ *
+ * Otherwise, we got an error, and *err *not* being 0
+ * and a return value tells our caller we have an error.
+ */
+ return -1;
+ }
+
+ /*
+ * If this is Ethernet, 802.11, ISDN, X.25, or ATM, set the
+ * pseudo-header.
+ */
+ switch (netxray->version_major) {
+
+ case 1:
+ switch (wth->file_encap) {
+
+ case WTAP_ENCAP_ETHERNET:
+ /*
+ * XXX - if hdr_1_x.xxx[15] is 1
+ * the frame appears not to have any extra
+ * stuff at the end, but if it's 0,
+ * there appears to be 4 bytes of stuff
+ * at the end, but it's not an FCS.
+ *
+ * Or is that just the low-order bit?
+ *
+ * For now, we just say "no FCS".
+ */
+ rec->rec_header.packet_header.pseudo_header.eth.fcs_len = 0;
+ break;
+ }
+ break;
+
+ case 2:
+ switch (wth->file_encap) {
+
+ case WTAP_ENCAP_ETHERNET:
+ /*
+ * It appears, at least with version 2 captures,
+ * that we have 4 bytes of stuff (which might be
+ * a valid FCS or might be junk) at the end of
+ * the packet if hdr_2_x.xxx[2] and
+ * hdr_2_x.xxx[3] are 0xff, and we don't if
+ * they don't.
+ *
+ * It also appears that if the low-order bit of
+ * hdr_2_x.xxx[8] is set, the packet has a
+ * bad FCS.
+ */
+ if (hdr.hdr_2_x.xxx[2] == 0xff &&
+ hdr.hdr_2_x.xxx[3] == 0xff) {
+ /*
+ * We have 4 bytes of stuff at the
+ * end of the frame - FCS, or junk?
+ */
+ if (netxray->fcs_valid) {
+ /*
+ * FCS.
+ */
+ rec->rec_header.packet_header.pseudo_header.eth.fcs_len = 4;
+ } else {
+ /*
+ * Junk.
+ */
+ padding = 4;
+ }
+ } else
+ rec->rec_header.packet_header.pseudo_header.eth.fcs_len = 0;
+ break;
+
+ case WTAP_ENCAP_IEEE_802_11_WITH_RADIO:
+ /*
+ * It appears, in one 802.11 capture, that
+ * we have 4 bytes of junk at the ends of
+ * frames in which hdr_2_x.xxx[2] and
+ * hdr_2_x.xxx[3] are 0xff; I haven't
+ * seen any frames where it's an FCS, but,
+ * for now, we still check the fcs_valid
+ * flag - I also haven't seen any capture
+ * where we'd set it based on the realtick
+ * value.
+ *
+ * It also appears that if the low-order bit of
+ * hdr_2_x.xxx[8] is set, the packet has a
+ * bad FCS. According to Ken Mann, the 0x4 bit
+ * is sometimes also set for errors.
+ *
+ * Ken also says that xxx[11] is 0x5 when the
+ * packet is WEP-encrypted.
+ */
+ memset(&rec->rec_header.packet_header.pseudo_header.ieee_802_11, 0, sizeof(rec->rec_header.packet_header.pseudo_header.ieee_802_11));
+ if (hdr.hdr_2_x.xxx[2] == 0xff &&
+ hdr.hdr_2_x.xxx[3] == 0xff) {
+ /*
+ * We have 4 bytes of stuff at the
+ * end of the frame - FCS, or junk?
+ */
+ if (netxray->fcs_valid) {
+ /*
+ * FCS.
+ */
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.fcs_len = 4;
+ } else {
+ /*
+ * Junk.
+ */
+ padding = 4;
+ }
+ } else
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.fcs_len = 0;
+
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.decrypted = FALSE;
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.datapad = FALSE;
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy = PHDR_802_11_PHY_UNKNOWN;
+
+ /*
+ * XXX - any other information, such as PHY
+ * type, frequency, 11n/11ac information,
+ * etc.?
+ */
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.has_channel = TRUE;
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.channel =
+ hdr.hdr_2_x.xxx[12];
+
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.has_data_rate = TRUE;
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.data_rate =
+ hdr.hdr_2_x.xxx[13];
+
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.has_signal_percent = TRUE;
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.signal_percent =
+ hdr.hdr_2_x.xxx[14];
+
+ /*
+ * According to Ken Mann, at least in the captures
+ * he's seen, xxx[15] is the noise level, which
+ * is either 0xFF meaning "none reported" or a value
+ * from 0x00 to 0x7F for 0 to 100%.
+ */
+ if (hdr.hdr_2_x.xxx[15] != 0xFF) {
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.has_noise_percent = TRUE;
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.noise_percent =
+ hdr.hdr_2_x.xxx[15]*100/127;
+ }
+ break;
+
+ case WTAP_ENCAP_ISDN:
+ /*
+ * ISDN.
+ *
+ * The bottommost bit of byte 12 of hdr_2_x.xxx
+ * is the direction flag.
+ *
+ * The bottom 5 bits of byte 13 of hdr_2_x.xxx
+ * are the channel number, but some mapping is
+ * required for PRI. (Is it really just the time
+ * slot?)
+ */
+ rec->rec_header.packet_header.pseudo_header.isdn.uton =
+ (hdr.hdr_2_x.xxx[12] & 0x01);
+ rec->rec_header.packet_header.pseudo_header.isdn.channel =
+ hdr.hdr_2_x.xxx[13] & 0x1F;
+ switch (netxray->isdn_type) {
+
+ case 1:
+ /*
+ * E1 PRI. Channel numbers 0 and 16
+ * are the D channel; channel numbers 1
+ * through 15 are B1 through B15; channel
+ * numbers 17 through 31 are B16 through
+ * B31.
+ */
+ if (rec->rec_header.packet_header.pseudo_header.isdn.channel == 16)
+ rec->rec_header.packet_header.pseudo_header.isdn.channel = 0;
+ else if (rec->rec_header.packet_header.pseudo_header.isdn.channel > 16)
+ rec->rec_header.packet_header.pseudo_header.isdn.channel -= 1;
+ break;
+
+ case 2:
+ /*
+ * T1 PRI. Channel numbers 0 and 24
+ * are the D channel; channel numbers 1
+ * through 23 are B1 through B23.
+ */
+ if (rec->rec_header.packet_header.pseudo_header.isdn.channel == 24)
+ rec->rec_header.packet_header.pseudo_header.isdn.channel = 0;
+ else if (rec->rec_header.packet_header.pseudo_header.isdn.channel > 24)
+ rec->rec_header.packet_header.pseudo_header.isdn.channel -= 1;
+ break;
+ }
+
+ /*
+ * It appears, at least with version 2 captures,
+ * that we have 4 bytes of stuff (which might be
+ * a valid FCS or might be junk) at the end of
+ * the packet if hdr_2_x.xxx[2] and
+ * hdr_2_x.xxx[3] are 0xff, and we don't if
+ * they don't.
+ *
+ * XXX - does the low-order bit of hdr_2_x.xxx[8]
+ * indicate a bad FCS, as is the case with
+ * Ethernet?
+ */
+ if (hdr.hdr_2_x.xxx[2] == 0xff &&
+ hdr.hdr_2_x.xxx[3] == 0xff) {
+ /*
+ * FCS, or junk, at the end.
+ * XXX - is it an FCS if "fcs_valid" is
+ * true?
+ */
+ padding = 4;
+ }
+ break;
+
+ case WTAP_ENCAP_LAPB:
+ case WTAP_ENCAP_FRELAY_WITH_PHDR:
+ /*
+ * LAPB/X.25 and Frame Relay.
+ *
+ * The bottommost bit of byte 12 of hdr_2_x.xxx
+ * is the direction flag. (Probably true for other
+ * HDLC encapsulations as well.)
+ */
+ rec->rec_header.packet_header.pseudo_header.dte_dce.flags =
+ (hdr.hdr_2_x.xxx[12] & 0x01) ? 0x00 : FROM_DCE;
+
+ /*
+ * It appears, at least with version 2 captures,
+ * that we have 4 bytes of stuff (which might be
+ * a valid FCS or might be junk) at the end of
+ * the packet if hdr_2_x.xxx[2] and
+ * hdr_2_x.xxx[3] are 0xff, and we don't if
+ * they don't.
+ *
+ * XXX - does the low-order bit of hdr_2_x.xxx[8]
+ * indicate a bad FCS, as is the case with
+ * Ethernet?
+ */
+ if (hdr.hdr_2_x.xxx[2] == 0xff &&
+ hdr.hdr_2_x.xxx[3] == 0xff) {
+ /*
+ * FCS, or junk, at the end.
+ * XXX - is it an FCS if "fcs_valid" is
+ * true?
+ */
+ padding = 4;
+ }
+ break;
+
+ case WTAP_ENCAP_PPP_WITH_PHDR:
+ case WTAP_ENCAP_SDLC:
+ case WTAP_ENCAP_CHDLC_WITH_PHDR:
+ rec->rec_header.packet_header.pseudo_header.p2p.sent =
+ (hdr.hdr_2_x.xxx[12] & 0x01) ? TRUE : FALSE;
+ break;
+
+ case WTAP_ENCAP_ATM_PDUS_UNTRUNCATED:
+ /*
+ * XXX - the low-order bit of hdr_2_x.xxx[8]
+ * seems to indicate some sort of error. In
+ * at least one capture, a number of packets
+ * have that flag set, and they appear either
+ * to be the beginning part of an incompletely
+ * reassembled AAL5 PDU, with either checksum
+ * errors at higher levels (possibly due to
+ * the packet being reported as shorter than
+ * it actually is, and checksumming failing
+ * because it doesn't include all the data)
+ * or "Malformed frame" errors from being
+ * too short, or appear to be later parts
+ * of an incompletely reassembled AAL5 PDU
+ * with the last one in a sequence of errors
+ * having what looks like an AAL5 trailer,
+ * with a length and checksum.
+ *
+ * Does it just mean "reassembly failed",
+ * as appears to be the case in those
+ * packets, or does it mean "CRC error"
+ * at the AAL5 layer (which would be the
+ * case if you were treating an incompletely
+ * reassembled PDU as a completely reassembled
+ * PDU, although you'd also expect a length
+ * error in that case), or does it mean
+ * "generic error", with some other flag
+ * or flags indicating what particular
+ * error occurred? The documentation
+ * for Sniffer Pro 4.7 indicates a bunch
+ * of different error types, both in general
+ * and for ATM in particular.
+ *
+ * No obvious bits in hdr_2_x.xxx appear
+ * to be additional flags of that sort.
+ *
+ * XXX - in that capture, I see several
+ * reassembly errors in a row; should those
+ * packets be reassembled in the ATM dissector?
+ * What happens if a reassembly fails because
+ * a cell is bad?
+ */
+ rec->rec_header.packet_header.pseudo_header.atm.flags = 0;
+ if (hdr.hdr_2_x.xxx[8] & 0x01)
+ rec->rec_header.packet_header.pseudo_header.atm.flags |= ATM_REASSEMBLY_ERROR;
+ /*
+ * XXX - is 0x08 an "OAM cell" flag?
+ * Are the 0x01 and 0x02 bits error indications?
+ * Some packets in one capture that have the
+ * 0x01 bit set in hdr_2_x.xxx[8] and that
+ * appear to have been reassembled completely
+ * but have a bad CRC have 0x03 in hdr_2_x.xxx[9]
+ * (and don't have the 0x20 bit set).
+ *
+ * In the capture with incomplete reassemblies,
+ * all packets have the 0x20 bit set. In at
+ * least some of the captures with complete
+ * reassemblies with CRC errors, no packets
+ * have the 0x20 bit set.
+ *
+ * Are hdr_2_x.xxx[8] and hdr_2_x.xxx[9] a 16-bit
+ * flag field?
+ */
+ if (hdr.hdr_2_x.xxx[9] & 0x04)
+ rec->rec_header.packet_header.pseudo_header.atm.flags |= ATM_RAW_CELL;
+ rec->rec_header.packet_header.pseudo_header.atm.vpi = hdr.hdr_2_x.xxx[11];
+ rec->rec_header.packet_header.pseudo_header.atm.vci = pletoh16(&hdr.hdr_2_x.xxx[12]);
+ rec->rec_header.packet_header.pseudo_header.atm.channel =
+ (hdr.hdr_2_x.xxx[15] & 0x10)? 1 : 0;
+ rec->rec_header.packet_header.pseudo_header.atm.cells = 0;
+
+ /*
+ * XXX - the uppermost bit of hdr_2_xxx[0]
+ * looks as if it might be a flag of some sort.
+ * The remaining 3 bits appear to be an AAL
+ * type - 5 is, surprise surprise, AAL5.
+ */
+ switch (hdr.hdr_2_x.xxx[0] & 0x70) {
+
+ case 0x00: /* Unknown */
+ rec->rec_header.packet_header.pseudo_header.atm.aal = AAL_UNKNOWN;
+ rec->rec_header.packet_header.pseudo_header.atm.type = TRAF_UNKNOWN;
+ rec->rec_header.packet_header.pseudo_header.atm.subtype = TRAF_ST_UNKNOWN;
+ break;
+
+ case 0x10: /* XXX - AAL1? */
+ rec->rec_header.packet_header.pseudo_header.atm.aal = AAL_UNKNOWN;
+ rec->rec_header.packet_header.pseudo_header.atm.type = TRAF_UNKNOWN;
+ rec->rec_header.packet_header.pseudo_header.atm.subtype = TRAF_ST_UNKNOWN;
+ break;
+
+ case 0x20: /* XXX - AAL2? */
+ rec->rec_header.packet_header.pseudo_header.atm.aal = AAL_UNKNOWN;
+ rec->rec_header.packet_header.pseudo_header.atm.type = TRAF_UNKNOWN;
+ rec->rec_header.packet_header.pseudo_header.atm.subtype = TRAF_ST_UNKNOWN;
+ break;
+
+ case 0x40: /* XXX - AAL3/4? */
+ rec->rec_header.packet_header.pseudo_header.atm.aal = AAL_UNKNOWN;
+ rec->rec_header.packet_header.pseudo_header.atm.type = TRAF_UNKNOWN;
+ rec->rec_header.packet_header.pseudo_header.atm.subtype = TRAF_ST_UNKNOWN;
+ break;
+
+ case 0x30: /* XXX - AAL5 cells seen with this */
+ case 0x50: /* AAL5 (including signalling) */
+ case 0x60: /* XXX - AAL5 cells seen with this */
+ case 0x70: /* XXX - AAL5 cells seen with this */
+ rec->rec_header.packet_header.pseudo_header.atm.aal = AAL_5;
+ /*
+ * XXX - is the 0x08 bit of hdr_2_x.xxx[0]
+ * a flag? I've not yet seen a case where
+ * it matters.
+ */
+ switch (hdr.hdr_2_x.xxx[0] & 0x07) {
+
+ case 0x01:
+ case 0x02: /* Signalling traffic */
+ rec->rec_header.packet_header.pseudo_header.atm.aal = AAL_SIGNALLING;
+ rec->rec_header.packet_header.pseudo_header.atm.type = TRAF_UNKNOWN;
+ rec->rec_header.packet_header.pseudo_header.atm.subtype = TRAF_ST_UNKNOWN;
+ break;
+
+ case 0x03: /* ILMI */
+ rec->rec_header.packet_header.pseudo_header.atm.type = TRAF_ILMI;
+ rec->rec_header.packet_header.pseudo_header.atm.subtype = TRAF_ST_UNKNOWN;
+ break;
+
+ case 0x00:
+ case 0x04:
+ case 0x05:
+ /*
+ * I've seen a frame with type
+ * 0x30 and subtype 0x08 that
+ * was LANE 802.3, a frame
+ * with type 0x30 and subtype
+ * 0x04 that was LANE 802.3,
+ * and another frame with type
+ * 0x30 and subtype 0x08 that
+ * was junk with a string in
+ * it that had also appeared
+ * in some CDP and LE Control
+ * frames, and that was preceded
+ * by a malformed LE Control
+ * frame - was that a reassembly
+ * failure?
+ *
+ * I've seen frames with type
+ * 0x50 and subtype 0x0c, some
+ * of which were LE Control
+ * frames, and at least one
+ * of which was neither an LE
+ * Control frame nor a LANE
+ * 802.3 frame, and contained
+ * the string "ForeThought_6.2.1
+ * Alpha" - does that imply
+ * FORE's own encapsulation,
+ * or was this a reassembly failure?
+ * The latter frame was preceded
+ * by a malformed LE Control
+ * frame.
+ *
+ * I've seen a couple of frames
+ * with type 0x60 and subtype 0x00,
+ * one of which was LANE 802.3 and
+ * one of which was LE Control.
+ * I've seen one frame with type
+ * 0x60 and subtype 0x0c, which
+ * was LANE 802.3.
+ *
+ * I've seen a couple of frames
+ * with type 0x70 and subtype 0x00,
+ * both of which were LANE 802.3.
+ */
+ rec->rec_header.packet_header.pseudo_header.atm.type = TRAF_LANE;
+ rec->rec_header.packet_header.pseudo_header.atm.subtype = TRAF_ST_UNKNOWN;
+ break;
+
+ case 0x06: /* XXX - not seen yet */
+ rec->rec_header.packet_header.pseudo_header.atm.type = TRAF_UNKNOWN;
+ rec->rec_header.packet_header.pseudo_header.atm.subtype = TRAF_ST_UNKNOWN;
+ break;
+
+ case 0x07: /* LLC multiplexed */
+ rec->rec_header.packet_header.pseudo_header.atm.type = TRAF_LLCMX; /* XXX */
+ rec->rec_header.packet_header.pseudo_header.atm.subtype = TRAF_ST_UNKNOWN; /* XXX */
+ break;
+ }
+ break;
+ }
+ break;
+ }
+ break;
+ }
+
+ rec->rec_type = REC_TYPE_PACKET;
+ rec->block = wtap_block_create(WTAP_BLOCK_PACKET);
+ if (netxray->version_major == 0) {
+ rec->presence_flags = WTAP_HAS_TS;
+ t = (double)pletoh32(&hdr.old_hdr.timelo)
+ + (double)pletoh32(&hdr.old_hdr.timehi)*4294967296.0;
+ t /= netxray->ticks_per_sec;
+ t -= netxray->start_timestamp;
+ rec->ts.secs = netxray->start_time + (long)t;
+ rec->ts.nsecs = (int)((t-(double)(unsigned long)(t))
+ *1.0e9);
+ /*
+ * We subtract the padding from the packet size, so our caller
+ * doesn't see it.
+ */
+ packet_size = pletoh16(&hdr.old_hdr.len);
+ rec->rec_header.packet_header.caplen = packet_size - padding;
+ rec->rec_header.packet_header.len = rec->rec_header.packet_header.caplen;
+ } else {
+ rec->presence_flags = WTAP_HAS_TS|WTAP_HAS_CAP_LEN;
+ t = (double)pletoh32(&hdr.hdr_1_x.timelo)
+ + (double)pletoh32(&hdr.hdr_1_x.timehi)*4294967296.0;
+ t /= netxray->ticks_per_sec;
+ t -= netxray->start_timestamp;
+ rec->ts.secs = netxray->start_time + (time_t)t;
+ rec->ts.nsecs = (int)((t-(double)(unsigned long)(t))
+ *1.0e9);
+ /*
+ * We subtract the padding from the packet size, so our caller
+ * doesn't see it.
+ */
+ packet_size = pletoh16(&hdr.hdr_1_x.incl_len);
+ rec->rec_header.packet_header.caplen = packet_size - padding;
+ rec->rec_header.packet_header.len = pletoh16(&hdr.hdr_1_x.orig_len) - padding;
+ }
+
+ return padding;
+}
+
+static void
+netxray_guess_atm_type(wtap *wth, wtap_rec *rec, Buffer *buf)
+{
+ const guint8 *pd;
+
+ if (wth->file_encap == WTAP_ENCAP_ATM_PDUS_UNTRUNCATED &&
+ !(rec->rec_header.packet_header.pseudo_header.atm.flags & ATM_REASSEMBLY_ERROR)) {
+ if (rec->rec_header.packet_header.pseudo_header.atm.aal == AAL_UNKNOWN) {
+ /*
+ * Try to guess the type and subtype based
+ * on the VPI/VCI and packet contents.
+ */
+ pd = ws_buffer_start_ptr(buf);
+ atm_guess_traffic_type(rec, pd);
+ } else if (rec->rec_header.packet_header.pseudo_header.atm.aal == AAL_5 &&
+ rec->rec_header.packet_header.pseudo_header.atm.type == TRAF_LANE) {
+ /*
+ * Try to guess the subtype based on the
+ * packet contents.
+ */
+ pd = ws_buffer_start_ptr(buf);
+ atm_guess_lane_type(rec, pd);
+ }
+ }
+}
+
+typedef struct {
+ gboolean first_frame;
+ guint32 start_secs;
+ guint32 nframes;
+} netxray_dump_t;
+
+static const struct {
+ int wtap_encap_value;
+ int ndis_value;
+} wtap_encap_1_1[] = {
+ { WTAP_ENCAP_ETHERNET, 0 }, /* -> NDIS Ethernet */
+ { WTAP_ENCAP_TOKEN_RING, 1 }, /* -> NDIS Token Ring */
+ { WTAP_ENCAP_FDDI, 2 }, /* -> NDIS FDDI */
+ { WTAP_ENCAP_FDDI_BITSWAPPED, 2 }, /* -> NDIS FDDI */
+};
+#define NUM_WTAP_ENCAPS_1_1 (sizeof wtap_encap_1_1 / sizeof wtap_encap_1_1[0])
+
+static int
+wtap_encap_to_netxray_1_1_encap(int encap)
+{
+ unsigned int i;
+
+ for (i = 0; i < NUM_WTAP_ENCAPS_1_1; i++) {
+ if (encap == wtap_encap_1_1[i].wtap_encap_value)
+ return wtap_encap_1_1[i].ndis_value;
+ }
+
+ return -1;
+}
+
+/* Returns 0 if we could write the specified encapsulation type,
+ an error indication otherwise. */
+static int
+netxray_dump_can_write_encap_1_1(int encap)
+{
+ /* Per-packet encapsulations aren't supported. */
+ if (encap == WTAP_ENCAP_PER_PACKET)
+ return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
+
+ if (wtap_encap_to_netxray_1_1_encap(encap) == -1)
+ return WTAP_ERR_UNWRITABLE_ENCAP;
+
+ return 0;
+}
+
+/* Returns TRUE on success, FALSE on failure; sets "*err" to an error code on
+ failure */
+static gboolean
+netxray_dump_open_1_1(wtap_dumper *wdh, int *err, gchar **err_info _U_)
+{
+ netxray_dump_t *netxray;
+
+ wdh->subtype_write = netxray_dump_1_1;
+ wdh->subtype_finish = netxray_dump_finish_1_1;
+
+ /* We can't fill in all the fields in the file header, as we
+ haven't yet written any packets. As we'll have to rewrite
+ the header when we've written out all the packets, we just
+ skip over the header for now. */
+ if (wtap_dump_file_seek(wdh, CAPTUREFILE_HEADER_SIZE, SEEK_SET, err) == -1)
+ return FALSE;
+ wdh->bytes_dumped += CAPTUREFILE_HEADER_SIZE;
+
+ netxray = g_new(netxray_dump_t, 1);
+ wdh->priv = (void *)netxray;
+ netxray->first_frame = TRUE;
+ netxray->start_secs = 0;
+ netxray->nframes = 0;
+
+ return TRUE;
+}
+
+/* Write a record for a packet to a dump file.
+ Returns TRUE on success, FALSE on failure. */
+static gboolean
+netxray_dump_1_1(wtap_dumper *wdh,
+ const wtap_rec *rec,
+ const guint8 *pd, int *err, gchar **err_info _U_)
+{
+ netxray_dump_t *netxray = (netxray_dump_t *)wdh->priv;
+ guint64 timestamp;
+ guint32 t32;
+ struct netxrayrec_1_x_hdr rec_hdr;
+
+ /* We can only write packet records. */
+ if (rec->rec_type != REC_TYPE_PACKET) {
+ *err = WTAP_ERR_UNWRITABLE_REC_TYPE;
+ return FALSE;
+ }
+
+ /*
+ * Make sure this packet doesn't have a link-layer type that
+ * differs from the one for the file.
+ */
+ if (wdh->file_encap != rec->rec_header.packet_header.pkt_encap) {
+ *err = WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
+ return FALSE;
+ }
+
+ /* The captured length field is 16 bits, so there's a hard
+ limit of 65535. */
+ if (rec->rec_header.packet_header.caplen > 65535) {
+ *err = WTAP_ERR_PACKET_TOO_LARGE;
+ return FALSE;
+ }
+
+ /* NetXRay/Windows Sniffer files have a capture start date/time
+ in the header, in a UNIX-style format, with one-second resolution,
+ and a start time stamp with microsecond resolution that's just
+ an arbitrary time stamp relative to some unknown time (boot
+ time?), and have times relative to the start time stamp in
+ the packet headers; pick the seconds value of the time stamp
+ of the first packet as the UNIX-style start date/time, and make
+ the high-resolution start time stamp 0, with the time stamp of
+ packets being the delta between the stamp of the packet and
+ the stamp of the first packet with the microseconds part 0. */
+ if (netxray->first_frame) {
+ netxray->first_frame = FALSE;
+ /*
+ * XXX - NetXRay ran on Windows, where MSVC's localtime()
+ * can't handle time_t < 0, so *maybe* it makes sense
+ * to allow time stamps up to 2^32-1 "seconds since the
+ * Epoch", but maybe the start time in those files is
+ * signed, in which case we should check against
+ * G_MININT32 and G_MAXINT32 and make start_secs a
+ * gint32.
+ */
+ if (rec->ts.secs < 0 || rec->ts.secs > WTAP_NSTIME_32BIT_SECS_MAX) {
+ *err = WTAP_ERR_TIME_STAMP_NOT_SUPPORTED;
+ return FALSE;
+ }
+ netxray->start_secs = (guint32)rec->ts.secs;
+ }
+
+ /* build the header for each packet */
+ memset(&rec_hdr, '\0', sizeof(rec_hdr));
+ timestamp = ((guint64)rec->ts.secs - (guint64)netxray->start_secs)*1000000
+ + ((guint64)rec->ts.nsecs)/1000;
+ t32 = (guint32)(timestamp%G_GINT64_CONSTANT(4294967296));
+ rec_hdr.timelo = GUINT32_TO_LE(t32);
+ t32 = (guint32)(timestamp/G_GINT64_CONSTANT(4294967296));
+ rec_hdr.timehi = GUINT32_TO_LE(t32);
+ rec_hdr.orig_len = GUINT16_TO_LE(rec->rec_header.packet_header.len);
+ rec_hdr.incl_len = GUINT16_TO_LE(rec->rec_header.packet_header.caplen);
+
+ if (!wtap_dump_file_write(wdh, &rec_hdr, sizeof(rec_hdr), err))
+ return FALSE;
+
+ /* write the packet data */
+ if (!wtap_dump_file_write(wdh, pd, rec->rec_header.packet_header.caplen, err))
+ return FALSE;
+
+ netxray->nframes++;
+
+ return TRUE;
+}
+
+/* Finish writing to a dump file.
+ Returns TRUE on success, FALSE on failure. */
+static gboolean
+netxray_dump_finish_1_1(wtap_dumper *wdh, int *err, gchar **err_info _U_)
+{
+ char hdr_buf[CAPTUREFILE_HEADER_SIZE - sizeof(netxray_magic)];
+ netxray_dump_t *netxray = (netxray_dump_t *)wdh->priv;
+ gint64 filelen;
+ struct netxray_hdr file_hdr;
+
+ if (-1 == (filelen = wtap_dump_file_tell(wdh, err)))
+ return FALSE;
+
+ /* Go back to beginning */
+ if (wtap_dump_file_seek(wdh, 0, SEEK_SET, err) == -1)
+ return FALSE;
+
+ /* Rewrite the file header. */
+ if (!wtap_dump_file_write(wdh, netxray_magic, sizeof netxray_magic, err))
+ return FALSE;
+
+ /* "sniffer" version ? */
+ memset(&file_hdr, '\0', sizeof file_hdr);
+ memcpy(file_hdr.version, vers_1_1, sizeof vers_1_1);
+ file_hdr.start_time = GUINT32_TO_LE(netxray->start_secs);
+ file_hdr.nframes = GUINT32_TO_LE(netxray->nframes);
+ file_hdr.start_offset = GUINT32_TO_LE(CAPTUREFILE_HEADER_SIZE);
+ /* XXX - large files? */
+ file_hdr.end_offset = GUINT32_TO_LE((guint32)filelen);
+ file_hdr.network = wtap_encap_to_netxray_1_1_encap(wdh->file_encap);
+ file_hdr.timelo = GUINT32_TO_LE(0);
+ file_hdr.timehi = GUINT32_TO_LE(0);
+
+ memset(hdr_buf, '\0', sizeof hdr_buf);
+ memcpy(hdr_buf, &file_hdr, sizeof(file_hdr));
+ if (!wtap_dump_file_write(wdh, hdr_buf, sizeof hdr_buf, err))
+ return FALSE;
+
+ /* Don't double-count the size of the file header */
+ wdh->bytes_dumped = filelen;
+ return TRUE;
+}
+
+static const struct {
+ int wtap_encap_value;
+ int ndis_value;
+} wtap_encap_2_0[] = {
+ { WTAP_ENCAP_ETHERNET, 0 }, /* -> NDIS Ethernet */
+ { WTAP_ENCAP_TOKEN_RING, 1 }, /* -> NDIS Token Ring */
+ { WTAP_ENCAP_FDDI, 2 }, /* -> NDIS FDDI */
+ { WTAP_ENCAP_FDDI_BITSWAPPED, 2 }, /* -> NDIS FDDI */
+ { WTAP_ENCAP_PPP_WITH_PHDR, 3 }, /* -> NDIS WAN */
+ { WTAP_ENCAP_FRELAY_WITH_PHDR, 3 }, /* -> NDIS WAN */
+ { WTAP_ENCAP_LAPB, 3 }, /* -> NDIS WAN */
+ { WTAP_ENCAP_SDLC, 3 }, /* -> NDIS WAN */
+};
+#define NUM_WTAP_ENCAPS_2_0 (sizeof wtap_encap_2_0 / sizeof wtap_encap_2_0[0])
+
+static int
+wtap_encap_to_netxray_2_0_encap(int encap)
+{
+ unsigned int i;
+
+ for (i = 0; i < NUM_WTAP_ENCAPS_2_0; i++) {
+ if (encap == wtap_encap_2_0[i].wtap_encap_value)
+ return wtap_encap_2_0[i].ndis_value;
+ }
+
+ return -1;
+}
+
+/* Returns 0 if we could write the specified encapsulation type,
+ an error indication otherwise. */
+static int
+netxray_dump_can_write_encap_2_0(int encap)
+{
+ /* Per-packet encapsulations aren't supported. */
+ if (encap == WTAP_ENCAP_PER_PACKET)
+ return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
+
+ if (wtap_encap_to_netxray_2_0_encap(encap) == -1)
+ return WTAP_ERR_UNWRITABLE_ENCAP;
+
+ return 0;
+}
+
+/* Returns TRUE on success, FALSE on failure; sets "*err" to an error code on
+ failure */
+static gboolean
+netxray_dump_open_2_0(wtap_dumper *wdh, int *err, gchar **err_info _U_)
+{
+ netxray_dump_t *netxray;
+
+ wdh->subtype_write = netxray_dump_2_0;
+ wdh->subtype_finish = netxray_dump_finish_2_0;
+
+ /* We can't fill in all the fields in the file header, as we
+ haven't yet written any packets. As we'll have to rewrite
+ the header when we've written out all the packets, we just
+ skip over the header for now. */
+ if (wtap_dump_file_seek(wdh, CAPTUREFILE_HEADER_SIZE, SEEK_SET, err) == -1)
+ return FALSE;
+ wdh->bytes_dumped += CAPTUREFILE_HEADER_SIZE;
+
+ netxray = g_new(netxray_dump_t, 1);
+ wdh->priv = (void *)netxray;
+ netxray->first_frame = TRUE;
+ netxray->start_secs = 0;
+ netxray->nframes = 0;
+
+ return TRUE;
+}
+
+/* Write a record for a packet to a dump file.
+ Returns TRUE on success, FALSE on failure. */
+static gboolean
+netxray_dump_2_0(wtap_dumper *wdh,
+ const wtap_rec *rec,
+ const guint8 *pd, int *err, gchar **err_info _U_)
+{
+ const union wtap_pseudo_header *pseudo_header = &rec->rec_header.packet_header.pseudo_header;
+ netxray_dump_t *netxray = (netxray_dump_t *)wdh->priv;
+ guint64 timestamp;
+ guint32 t32;
+ struct netxrayrec_2_x_hdr rec_hdr;
+
+ /* We can only write packet records. */
+ if (rec->rec_type != REC_TYPE_PACKET) {
+ *err = WTAP_ERR_UNWRITABLE_REC_TYPE;
+ return FALSE;
+ }
+
+ /*
+ * Make sure this packet doesn't have a link-layer type that
+ * differs from the one for the file.
+ */
+ if (wdh->file_encap != rec->rec_header.packet_header.pkt_encap) {
+ *err = WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
+ return FALSE;
+ }
+
+ /* Don't write anything we're not willing to read. */
+ if (rec->rec_header.packet_header.caplen > WTAP_MAX_PACKET_SIZE_STANDARD) {
+ *err = WTAP_ERR_PACKET_TOO_LARGE;
+ return FALSE;
+ }
+
+ /* NetXRay/Windows Sniffer files have a capture start date/time
+ in the header, in a UNIX-style format, with one-second resolution,
+ and a start time stamp with microsecond resolution that's just
+ an arbitrary time stamp relative to some unknown time (boot
+ time?), and have times relative to the start time stamp in
+ the packet headers; pick the seconds value of the time stamp
+ of the first packet as the UNIX-style start date/time, and make
+ the high-resolution start time stamp 0, with the time stamp of
+ packets being the delta between the stamp of the packet and
+ the stamp of the first packet with the microseconds part 0. */
+ if (netxray->first_frame) {
+ netxray->first_frame = FALSE;
+ /*
+ * XXX - NetXRay ran on Windows, where MSVC's localtime()
+ * can't handle time_t < 0, so *maybe* it makes sense
+ * to allow time stamps up to 2^32-1 "seconds since the
+ * Epoch", but maybe the start time in those files is
+ * signed, in which case we should check against
+ * G_MININT32 and G_MAXINT32 and make start_secs a
+ * gint32.
+ */
+ if (rec->ts.secs < 0 || rec->ts.secs > WTAP_NSTIME_32BIT_SECS_MAX) {
+ *err = WTAP_ERR_TIME_STAMP_NOT_SUPPORTED;
+ return FALSE;
+ }
+ netxray->start_secs = (guint32)rec->ts.secs;
+ }
+
+ /* build the header for each packet */
+ memset(&rec_hdr, '\0', sizeof(rec_hdr));
+ timestamp = ((guint64)rec->ts.secs - (guint64)netxray->start_secs)*1000000
+ + ((guint64)rec->ts.nsecs)/1000;
+ t32 = (guint32)(timestamp%G_GINT64_CONSTANT(4294967296));
+ rec_hdr.timelo = GUINT32_TO_LE(t32);
+ t32 = (guint32)(timestamp/G_GINT64_CONSTANT(4294967296));
+ rec_hdr.timehi = GUINT32_TO_LE(t32);
+ rec_hdr.orig_len = GUINT16_TO_LE(rec->rec_header.packet_header.len);
+ rec_hdr.incl_len = GUINT16_TO_LE(rec->rec_header.packet_header.caplen);
+
+ switch (rec->rec_header.packet_header.pkt_encap) {
+
+ case WTAP_ENCAP_IEEE_802_11_WITH_RADIO:
+ rec_hdr.xxx[12] =
+ pseudo_header->ieee_802_11.has_channel ?
+ pseudo_header->ieee_802_11.channel :
+ 0;
+ rec_hdr.xxx[13] =
+ pseudo_header->ieee_802_11.has_data_rate ?
+ (guint8)pseudo_header->ieee_802_11.data_rate :
+ 0;
+ rec_hdr.xxx[14] =
+ pseudo_header->ieee_802_11.has_signal_percent ?
+ pseudo_header->ieee_802_11.signal_percent :
+ 0;
+ rec_hdr.xxx[15] =
+ pseudo_header->ieee_802_11.has_noise_percent ?
+ pseudo_header->ieee_802_11.noise_percent*127/100 :
+ 0xFF;
+ break;
+
+ case WTAP_ENCAP_PPP_WITH_PHDR:
+ case WTAP_ENCAP_SDLC:
+ rec_hdr.xxx[12] |= pseudo_header->p2p.sent ? 0x01 : 0x00;
+ break;
+
+ case WTAP_ENCAP_FRELAY_WITH_PHDR:
+ rec_hdr.xxx[12] |= (pseudo_header->dte_dce.flags & FROM_DCE) ? 0x00 : 0x01;
+ break;
+ }
+
+ if (!wtap_dump_file_write(wdh, &rec_hdr, sizeof(rec_hdr), err))
+ return FALSE;
+
+ /* write the packet data */
+ if (!wtap_dump_file_write(wdh, pd, rec->rec_header.packet_header.caplen, err))
+ return FALSE;
+
+ netxray->nframes++;
+
+ return TRUE;
+}
+
+/* Finish writing to a dump file.
+ Returns TRUE on success, FALSE on failure. */
+static gboolean
+netxray_dump_finish_2_0(wtap_dumper *wdh, int *err, gchar **err_info _U_)
+{
+ char hdr_buf[CAPTUREFILE_HEADER_SIZE - sizeof(netxray_magic)];
+ netxray_dump_t *netxray = (netxray_dump_t *)wdh->priv;
+ gint64 filelen;
+ struct netxray_hdr file_hdr;
+
+ if (-1 == (filelen = wtap_dump_file_tell(wdh, err)))
+ return FALSE;
+
+ /* Go back to beginning */
+ if (wtap_dump_file_seek(wdh, 0, SEEK_SET, err) == -1)
+ return FALSE;
+
+ /* Rewrite the file header. */
+ if (!wtap_dump_file_write(wdh, netxray_magic, sizeof netxray_magic, err))
+ return FALSE;
+
+ /* "sniffer" version ? */
+ memset(&file_hdr, '\0', sizeof file_hdr);
+ memcpy(file_hdr.version, vers_2_001, sizeof vers_2_001);
+ file_hdr.start_time = GUINT32_TO_LE(netxray->start_secs);
+ file_hdr.nframes = GUINT32_TO_LE(netxray->nframes);
+ file_hdr.start_offset = GUINT32_TO_LE(CAPTUREFILE_HEADER_SIZE);
+ /* XXX - large files? */
+ file_hdr.end_offset = GUINT32_TO_LE((guint32)filelen);
+ file_hdr.network = wtap_encap_to_netxray_2_0_encap(wdh->file_encap);
+ file_hdr.timelo = GUINT32_TO_LE(0);
+ file_hdr.timehi = GUINT32_TO_LE(0);
+ switch (wdh->file_encap) {
+
+ case WTAP_ENCAP_PPP_WITH_PHDR:
+ file_hdr.captype = WAN_CAPTYPE_PPP;
+ break;
+
+ case WTAP_ENCAP_FRELAY_WITH_PHDR:
+ file_hdr.captype = WAN_CAPTYPE_FRELAY;
+ break;
+
+ case WTAP_ENCAP_LAPB:
+ file_hdr.captype = WAN_CAPTYPE_HDLC;
+ file_hdr.wan_hdlc_subsub_captype = 0;
+ break;
+
+ case WTAP_ENCAP_SDLC:
+ file_hdr.captype = WAN_CAPTYPE_SDLC;
+ break;
+
+ default:
+ file_hdr.captype = CAPTYPE_NDIS;
+ break;
+ }
+
+ memset(hdr_buf, '\0', sizeof hdr_buf);
+ memcpy(hdr_buf, &file_hdr, sizeof(file_hdr));
+ if (!wtap_dump_file_write(wdh, hdr_buf, sizeof hdr_buf, err))
+ return FALSE;
+
+ /* Don't double-count the size of the file header */
+ wdh->bytes_dumped = filelen;
+ return TRUE;
+}
+
+static const struct supported_block_type netxray_old_blocks_supported[] = {
+ /*
+ * We support packet blocks, with no comments or other options.
+ */
+ { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
+};
+
+static const struct file_type_subtype_info netxray_old_info = {
+ "Cinco Networks NetXRay 1.x", "netxray1", "cap", NULL,
+ TRUE, BLOCKS_SUPPORTED(netxray_old_blocks_supported),
+ NULL, NULL, NULL
+};
+
+static const struct supported_block_type netxray_1_0_blocks_supported[] = {
+ /*
+ * We support packet blocks, with no comments or other options.
+ */
+ { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
+};
+
+static const struct file_type_subtype_info netxray_1_0_info = {
+ "Cinco Networks NetXRay 2.0 or later", "netxray2", "cap", NULL,
+ TRUE, BLOCKS_SUPPORTED(netxray_1_0_blocks_supported),
+ NULL, NULL, NULL
+};
+
+static const struct supported_block_type netxray_1_1_blocks_supported[] = {
+ /*
+ * We support packet blocks, with no comments or other options.
+ */
+ { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
+};
+
+static const struct file_type_subtype_info netxray_1_1_info = {
+ "NetXray, Sniffer (Windows) 1.1", "ngwsniffer_1_1", "cap", NULL,
+ TRUE, BLOCKS_SUPPORTED(netxray_1_1_blocks_supported),
+ netxray_dump_can_write_encap_1_1, netxray_dump_open_1_1, NULL
+};
+
+static const struct supported_block_type netxray_2_00x_blocks_supported[] = {
+ /*
+ * We support packet blocks, with no comments or other options.
+ */
+ { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
+};
+
+static const struct file_type_subtype_info netxray_2_00x_info = {
+ "Sniffer (Windows) 2.00x", "ngwsniffer_2_0", "cap", "caz",
+ TRUE, BLOCKS_SUPPORTED(netxray_2_00x_blocks_supported),
+ netxray_dump_can_write_encap_2_0, netxray_dump_open_2_0, NULL
+};
+
+void register_netxray(void)
+{
+ netxray_old_file_type_subtype = wtap_register_file_type_subtype(&netxray_old_info);
+ netxray_1_0_file_type_subtype = wtap_register_file_type_subtype(&netxray_1_0_info);
+ netxray_1_1_file_type_subtype = wtap_register_file_type_subtype(&netxray_1_1_info);
+ netxray_2_00x_file_type_subtype = wtap_register_file_type_subtype(&netxray_2_00x_info);
+
+ /*
+ * Register names for backwards compatibility with the
+ * wtap_filetypes table in Lua.
+ */
+ wtap_register_backwards_compatibility_lua_name("NETXRAY_OLD",
+ netxray_old_file_type_subtype);
+ wtap_register_backwards_compatibility_lua_name("NETXRAY_1_0",
+ netxray_1_0_file_type_subtype);
+ wtap_register_backwards_compatibility_lua_name("NETXRAY_1_1",
+ netxray_1_1_file_type_subtype);
+ wtap_register_backwards_compatibility_lua_name("NETXRAY_2_00x",
+ netxray_2_00x_file_type_subtype);
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/wiretap/netxray.h b/wiretap/netxray.h
new file mode 100644
index 00000000..68b24167
--- /dev/null
+++ b/wiretap/netxray.h
@@ -0,0 +1,17 @@
+/** @file
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __NETXRAY_H__
+#define __NETXRAY_H__
+
+#include <glib.h>
+#include "wtap.h"
+
+wtap_open_return_val netxray_open(wtap *wth, int *err, gchar **err_info);
+
+#endif
diff --git a/wiretap/ngsniffer.c b/wiretap/ngsniffer.c
new file mode 100644
index 00000000..4ee4eda1
--- /dev/null
+++ b/wiretap/ngsniffer.c
@@ -0,0 +1,2935 @@
+/* ngsniffer.c
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+/* The code in ngsniffer.c that decodes the time fields for each packet in the
+ * Sniffer trace originally came from code from TCPVIEW:
+ *
+ * TCPVIEW
+ *
+ * Author: Martin Hunt
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: martinh@cac.washington.edu
+ *
+ *
+ * Copyright 1992 by the University of Washington
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose and without fee is hereby granted, provided
+ * that the above copyright notice appears in all copies and that both the
+ * above copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of the University of Washington not be
+ * used in advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission. This software is made
+ * available "as is", and
+ * THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
+ * WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN
+ * NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT
+ * (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION
+ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+#include "config.h"
+
+#include <string.h>
+#include "wtap-int.h"
+#include "file_wrappers.h"
+#include "ngsniffer.h"
+#include <wsutil/ws_assert.h>
+
+/* Magic number in Sniffer files. */
+static const char ngsniffer_magic[] = {
+ 'T', 'R', 'S', 'N', 'I', 'F', 'F', ' ', 'd', 'a', 't', 'a',
+ ' ', ' ', ' ', ' ', 0x1a
+};
+
+/*
+ * Sniffer record types.
+ */
+#define REC_VERS 1 /* Version record (f_vers) */
+#define REC_FRAME2 4 /* Frame data (f_frame2) */
+#define REC_FRAME4 8 /* Frame data (f_frame4) */
+#define REC_FRAME6 12 /* Frame data (f_frame6) (see below) */
+#define REC_EOF 3 /* End-of-file record (no data follows) */
+/*
+ * and now for some unknown header types
+ */
+#define REC_HEADER1 6 /* Header containing various information,
+ * not yet reverse engineered - some binary,
+ * some strings (Serial numbers? Names
+ * under which the software is registered?
+ * Software version numbers? Mysterious
+ * strings such as "PA-55X" and "PA-30X"
+ * and "PA-57X" and "PA-11X"?), some strings
+ * that are partially overwritten
+ * ("UNSERIALIZED", "Network General
+ * Corporation"), differing from major
+ * version to major version */
+#define REC_HEADER2 7 /* Header containing ??? */
+#define REC_V2DESC 8 /* In version 2 sniffer traces contains
+ * info about this capturing session,
+ * in the form of a multi-line string
+ * with NL as the line separator.
+ * Collides with REC_FRAME4 */
+#define REC_HEADER3 13 /* Retransmission counts? */
+#define REC_HEADER4 14 /* ? */
+#define REC_HEADER5 15 /* ? */
+#define REC_HEADER6 16 /* More broadcast/retransmission counts? */
+#define REC_HEADER7 17 /* ? */
+
+/*
+ * Sniffer record header structure.
+ */
+struct rec_header {
+ guint16 type; /* record type */
+ guint16 length; /* record length */
+};
+
+/*
+ * Sniffer version record format.
+ */
+struct vers_rec {
+ gint16 maj_vers; /* major version number */
+ gint16 min_vers; /* minor version number */
+ gint16 time_dos; /* DOS-format time */
+ gint16 date; /* DOS-format date */
+ gint8 type; /* what type of records follow */
+ guint8 network; /* network type */
+ gint8 format; /* format version */
+ guint8 timeunit; /* timestamp units */
+ gint8 cmprs_vers; /* compression version */
+ gint8 cmprs_level; /* compression level */
+ gint16 rsvd[2]; /* reserved */
+};
+
+/*
+ * Network types.
+ */
+#define NETWORK_TRING 0 /* Token ring */
+#define NETWORK_ENET 1 /* Ethernet */
+#define NETWORK_ARCNET 2 /* ARCNET */
+#define NETWORK_STARLAN 3 /* StarLAN */
+#define NETWORK_PCNW 4 /* PC Network broadband (Sytek?) */
+#define NETWORK_LOCALTALK 5 /* LocalTalk */
+#define NETWORK_SYNCHRO 7 /* Internetwork analyzer (synchronous) */
+#define NETWORK_ASYNC 8 /* Internetwork analyzer (asynchronous) */
+#define NETWORK_FDDI 9 /* FDDI */
+#define NETWORK_ATM 10 /* ATM */
+
+/*
+ * Sniffer type 2 data record format - followed by frame data.
+ *
+ * The Expert Sniffer Network Analyzer Operations manual, Release 5.50,
+ * documents some of the values used in "fs" and "flags". "flags" don't
+ * look as if they'd be of much interest to us, as those are internal
+ * flags for state used by the Sniffer, but "fs" gives various status
+ * bits including error indications *and*:
+ *
+ * ISDN channel information for ISDN;
+ *
+ * PPP vs. SLIP information for Async.
+ *
+ * In that section it also refers to "FDDI analyzers using the NPI PCI
+ * FDDI adapter" and "FDDI analyzers using the NPI ISA FDDI adapter",
+ * referring to the first as "F1SNIFF" and the second as "FDSNIFF";
+ * those sound as if they *could* be replacements for "TRSNIFF" in
+ * the file header, but that manual says, earlier, that the header
+ * starts with "TRSNIFF data, no matter where the frames were
+ * collected".
+ *
+ * It also says that a type 2 record has an 8-bit "time_high"
+ * and an 8-bit "time_day" field; the code here used to have a
+ * 16-bit "time_high" value, but that gave wrong time stamps on at
+ * least some captures. Did some older manual have it as a 16-bit
+ * "tstamp_high", so that perhaps it depends on the version number
+ * in the file, or is it "tstamp_high" plus "tstamp_day" in all
+ * versions? (I forget whether this came purely from tcpview, or if
+ * I saw any of it in an NAI document.)
+ *
+ * We interpret them as unsigned, as interpreting them as signed
+ * would appear to allow time stamps that precede the start of the
+ * capture. The description of the record format shows them as
+ * "char", but the section "How the Analyzer Stores Time" shows a
+ * time stamp structure with those fields being "unsigned char".
+ *
+ * In addition, the description of the record format has the comment
+ * for the "time_day" field saying it's the time in days since the
+ * start of the capture, but the "How the Analyzer Stores Time"
+ * section says it's increased by 1 if the capture continues past
+ * midnight - and also says that the time stamp structure has a time
+ * relative to midnight when the capture started, not since the
+ * actual capture start, so that might be a difference between
+ * the internal time stamp in the Sniffer software and the time
+ * stamp in capture files (i.e., the latter might be relative to
+ * the time when the capture starts).
+ */
+struct frame2_rec {
+ guint16 time_low; /* low part of time stamp */
+ guint16 time_med; /* middle part of time stamp */
+ guint8 time_high; /* high part of the time stamp */
+ guint8 time_day; /* time in days since start of capture */
+ gint16 size; /* number of bytes of data */
+ guint8 fs; /* frame error status bits */
+ guint8 flags; /* buffer flags */
+ gint16 true_size; /* size of original frame, in bytes */
+ gint16 rsvd; /* reserved */
+};
+
+/*
+ * Bits in "fs".
+ *
+ * The bits differ for different link-layer types.
+ */
+
+/*
+ * Ethernet.
+ */
+#define FS_ETH_CRC 0x80 /* CRC error */
+#define FS_ETH_ALIGN 0x40 /* bad alignment */
+#define FS_ETH_RU 0x20 /* "RU out of resources" */
+#define FS_ETH_OVERRUN 0x10 /* DMA overrun */
+#define FS_ETH_RUNT 0x08 /* frame too small */
+#define FS_ETH_COLLISION 0x02 /* collision fragment */
+
+/*
+ * FDDI.
+ */
+#define FS_FDDI_INVALID 0x10 /* frame indicators are invalid */
+#define FS_FDDI_ERROR 0x20 /* "frame error bit 1" */
+#define FS_FDDI_PCI_VDL 0x01 /* VDL (Valid Data Length?) error on frame on PCI adapter */
+#define FS_FDDI_PCI_CRC 0x02 /* CRC error on frame on PCI adapter */
+#define FS_FDDI_ISA_CRC 0x20 /* CRC error on frame on ISA adapter */
+
+/*
+ * Internetwork analyzer (synchronous and asynchronous).
+ */
+#define FS_WAN_DTE 0x80 /* DTE->DCE frame */
+
+/*
+ * Internetwork analyzer (synchronous).
+ */
+#define FS_SYNC_LOST 0x01 /* some frames were lost */
+#define FS_SYNC_CRC 0x02 /* CRC error */
+#define FS_SYNC_ABORT 0x04 /* aborted frame */
+#define FS_ISDN_CHAN_MASK 0x18 /* ISDN channel */
+#define FS_ISDN_CHAN_D 0x18 /* ISDN channel D */
+#define FS_ISDN_CHAN_B1 0x08 /* ISDN channel B1 */
+#define FS_ISDN_CHAN_B2 0x10 /* ISDN channel B2 */
+
+/*
+ * Internetwork analyzer (asynchronous).
+ * XXX - are some of these synchronous flags? They're listed with the
+ * asynchronous flags in the Sniffer 5.50 Network Analyzer Operations
+ * manual. Is one of the "overrun" errors a synchronous overrun error?
+ */
+#define FS_ASYNC_LOST 0x01 /* some frames were lost */
+#define FS_ASYNC_OVERRUN 0x02 /* UART overrun, lost bytes */
+#define FS_ASYNC_FRAMING 0x04 /* bad character (framing error?) */
+#define FS_ASYNC_PPP 0x08 /* PPP frame */
+#define FS_ASYNC_SLIP 0x10 /* SLIP frame */
+#define FS_ASYNC_ALIGN 0x20 /* alignment or DLPP(?) error */
+#define FS_ASYNC_OVERRUN2 0x40 /* overrun or bad frame length */
+
+/*
+ * Sniffer type 4 data record format - followed by frame data.
+ *
+ * The ATM Sniffer manual says that the "flags" field holds "buffer flags;
+ * BF_xxxx", but doesn't say what the BF_xxxx flags are. They may
+ * be the same as they are in a type 2 record, in which case they're
+ * probably not of much interest to us.
+ *
+ * XXX - the manual also says there's an 8-byte "ATMTimeStamp" driver
+ * time stamp at the end of "ATMSaveInfo", but, from an ATM Sniffer capture
+ * file I've looked at, that appears not to be the case.
+ */
+
+/*
+ * Fields from the AAL5 trailer for the frame, if it's an AAL5 frame
+ * rather than a cell.
+ */
+typedef struct _ATM_AAL5Trailer {
+ guint16 aal5t_u2u; /* user-to-user indicator */
+ guint16 aal5t_len; /* length of the packet */
+ guint32 aal5t_chksum; /* checksum for AAL5 packet */
+} ATM_AAL5Trailer;
+
+typedef struct _ATMTimeStamp {
+ guint32 msw; /* most significant word */
+ guint32 lsw; /* least significant word */
+} ATMTimeStamp;
+
+typedef struct _ATMSaveInfo {
+ guint32 StatusWord; /* status word from driver */
+ ATM_AAL5Trailer Trailer; /* AAL5 trailer */
+ guint8 AppTrafType; /* traffic type */
+ guint8 AppHLType; /* protocol type */
+ guint16 AppReserved; /* reserved */
+ guint16 Vpi; /* virtual path identifier */
+ guint16 Vci; /* virtual circuit identifier */
+ guint16 channel; /* link: 0 for DCE, 1 for DTE */
+ guint16 cells; /* number of cells */
+ guint32 AppVal1; /* type-dependent */
+ guint32 AppVal2; /* type-dependent */
+} ATMSaveInfo;
+
+/*
+ * Bits in StatusWord.
+ */
+#define SW_ERRMASK 0x0F /* Error mask: */
+#define SW_RX_FIFO_UNDERRUN 0x01 /* Receive FIFO underrun */
+#define SW_RX_FIFO_OVERRUN 0x02 /* Receive FIFO overrun */
+#define SW_RX_PKT_TOO_LONG 0x03 /* Received packet > max size */
+#define SW_CRC_ERROR 0x04 /* CRC error */
+#define SW_USER_ABORTED_RX 0x05 /* User aborted receive */
+#define SW_BUF_LEN_TOO_LONG 0x06 /* buffer len > max buf */
+#define SW_INTERNAL_T1_ERROR 0x07 /* Internal T1 error */
+#define SW_RX_CHANNEL_DEACTIV8 0x08 /* Rx channel deactivate */
+
+#define SW_ERROR 0x80 /* Error indicator */
+#define SW_CONGESTION 0x40 /* Congestion indicator */
+#define SW_CLP 0x20 /* Cell loss priority indicator */
+#define SW_RAW_CELL 0x100 /* RAW cell indicator */
+#define SW_OAM_CELL 0x200 /* OAM cell indicator */
+
+/*
+ * Bits in AppTrafType.
+ *
+ * For AAL types other than AAL5, the packet data is presumably for a
+ * single cell, not a reassembled frame, as the ATM Sniffer manual says
+ * it doesn't reassemble cells other than AAL5 cells.
+ */
+#define ATT_AALTYPE 0x0F /* AAL type: */
+#define ATT_AAL_UNKNOWN 0x00 /* Unknown AAL */
+#define ATT_AAL1 0x01 /* AAL1 */
+#define ATT_AAL3_4 0x02 /* AAL3/4 */
+#define ATT_AAL5 0x03 /* AAL5 */
+#define ATT_AAL_USER 0x04 /* User AAL */
+#define ATT_AAL_SIGNALLING 0x05 /* Signaling AAL */
+#define ATT_OAMCELL 0x06 /* OAM cell */
+
+#define ATT_HLTYPE 0xF0 /* Higher-layer type: */
+#define ATT_HL_UNKNOWN 0x00 /* unknown */
+#define ATT_HL_LLCMX 0x10 /* LLC multiplexed (probably RFC 1483) */
+#define ATT_HL_VCMX 0x20 /* VC multiplexed (probably RFC 1483) */
+#define ATT_HL_LANE 0x30 /* LAN Emulation */
+#define ATT_HL_ILMI 0x40 /* ILMI */
+#define ATT_HL_FRMR 0x50 /* Frame Relay */
+#define ATT_HL_SPANS 0x60 /* FORE SPANS */
+#define ATT_HL_IPSILON 0x70 /* Ipsilon */
+
+/*
+ * Values for AppHLType; the interpretation depends on the ATT_HLTYPE
+ * bits in AppTrafType.
+ */
+#define AHLT_UNKNOWN 0x0
+#define AHLT_VCMX_802_3_FCS 0x1 /* VCMX: 802.3 FCS */
+#define AHLT_LANE_LE_CTRL 0x1 /* LANE: LE Ctrl */
+#define AHLT_IPSILON_FT0 0x1 /* Ipsilon: Flow Type 0 */
+#define AHLT_VCMX_802_4_FCS 0x2 /* VCMX: 802.4 FCS */
+#define AHLT_LANE_802_3 0x2 /* LANE: 802.3 */
+#define AHLT_IPSILON_FT1 0x2 /* Ipsilon: Flow Type 1 */
+#define AHLT_VCMX_802_5_FCS 0x3 /* VCMX: 802.5 FCS */
+#define AHLT_LANE_802_5 0x3 /* LANE: 802.5 */
+#define AHLT_IPSILON_FT2 0x3 /* Ipsilon: Flow Type 2 */
+#define AHLT_VCMX_FDDI_FCS 0x4 /* VCMX: FDDI FCS */
+#define AHLT_LANE_802_3_MC 0x4 /* LANE: 802.3 multicast */
+#define AHLT_VCMX_802_6_FCS 0x5 /* VCMX: 802.6 FCS */
+#define AHLT_LANE_802_5_MC 0x5 /* LANE: 802.5 multicast */
+#define AHLT_VCMX_802_3 0x7 /* VCMX: 802.3 */
+#define AHLT_VCMX_802_4 0x8 /* VCMX: 802.4 */
+#define AHLT_VCMX_802_5 0x9 /* VCMX: 802.5 */
+#define AHLT_VCMX_FDDI 0xa /* VCMX: FDDI */
+#define AHLT_VCMX_802_6 0xb /* VCMX: 802.6 */
+#define AHLT_VCMX_FRAGMENTS 0xc /* VCMX: Fragments */
+#define AHLT_VCMX_BPDU 0xe /* VCMX: BPDU */
+
+struct frame4_rec {
+ guint16 time_low; /* low part of time stamp */
+ guint16 time_med; /* middle part of time stamp */
+ guint8 time_high; /* high part of time stamp */
+ guint8 time_day; /* time in days since start of capture */
+ gint16 size; /* number of bytes of data */
+ gint8 fs; /* frame error status bits */
+ gint8 flags; /* buffer flags */
+ gint16 true_size; /* size of original frame, in bytes */
+ gint16 rsvd3; /* reserved */
+ gint16 atm_pad; /* pad to 4-byte boundary */
+ ATMSaveInfo atm_info; /* ATM-specific stuff */
+};
+
+/*
+ * XXX - I have a version 5.50 file with a bunch of token ring
+ * records listed as type "12". The record format below was
+ * derived from frame4_rec and a bit of experimentation.
+ * - Gerald
+ */
+struct frame6_rec {
+ guint16 time_low; /* low part of time stamp */
+ guint16 time_med; /* middle part of time stamp */
+ guint8 time_high; /* high part of time stamp */
+ guint8 time_day; /* time in days since start of capture */
+ gint16 size; /* number of bytes of data */
+ guint8 fs; /* frame error status bits */
+ guint8 flags; /* buffer flags */
+ gint16 true_size; /* size of original frame, in bytes */
+ guint8 chemical_x[22]; /* ? */
+};
+
+/*
+ * Network type values in some type 7 records.
+ *
+ * Captures with a major version number of 2 appear to have type 7
+ * records with text in them (at least one I have does).
+ *
+ * Captures with a major version of 4, and at least some captures with
+ * a major version of 5, have type 7 records with those values in the
+ * 5th byte.
+ *
+ * However, some captures with a major version number of 5 appear not to
+ * have type 7 records at all (at least one I have doesn't), but do appear
+ * to put non-zero values in the "rsvd" field of the version header (at
+ * least one I have does) - at least some other captures with smaller version
+ * numbers appear to put 0 there, so *maybe* that's where the network
+ * (sub)type is hidden in those captures. The version 5 captures I've seen
+ * that *do* have type 7 records put 0 there, so it's not as if *all* V5
+ * captures have something in the "rsvd" field, however.
+ *
+ * The semantics of these network types is inferred from the Sniffer
+ * documentation, as they correspond to types described in the UI;
+ * in particular, see
+ *
+ * http://www.mcafee.com/common/media/sniffer/support/sdos/operation.pdf
+ *
+ * starting at page 3-10 (56 of 496).
+ *
+ * XXX - I've seen X.25 captures with NET_ROUTER, and I've seen bridge/
+ * router captures with NET_HDLC. Sigh.... Are those just captures for
+ * which the user set the wrong network type when capturing?
+ */
+#define NET_SDLC 0 /* Probably "SDLC then SNA" */
+#define NET_HDLC 1 /* Used for X.25; is it used for other
+ things as well, or is it "HDLC then
+ X.25", as referred to by the document
+ cited above, and only used for X.25? */
+#define NET_FRAME_RELAY 2
+#define NET_ROUTER 3 /* Probably "Router/Bridge", for various
+ point-to-point protocols for use between
+ bridges and routers, including PPP as well
+ as various proprietary protocols; also
+ used for ISDN, for reasons not obvious
+ to me, given that a Sniffer knows
+ whether it's using a WAN or an ISDN pod */
+#define NET_PPP 4 /* "Asynchronous", which includes SLIP too */
+#define NET_SMDS 5 /* Not mentioned in the document, but
+ that's a document for version 5.50 of
+ the Sniffer, and that version might use
+ version 5 in the file format and thus
+ might not be using type 7 records */
+
+/*
+ * Values for V.timeunit, in picoseconds, so that they can be represented
+ * as integers. These values must be < 2^(64-40); see below.
+ *
+ * XXX - at least some captures with a V.timeunit value of 2 show
+ * packets with time stamps in 2011 if the time stamp is interpreted
+ * to be in units of 15 microseconds. The capture predates 2008,
+ * so that interpretation is probably wrong. Perhaps the interpretation
+ * of V.timeunit depends on the version number of the file?
+ */
+static const guint32 Psec[] = {
+ 15000000, /* 15.0 usecs = 15000000 psecs */
+ 838096, /* .838096 usecs = 838096 psecs */
+ 15000000, /* 15.0 usecs = 15000000 psecs */
+ 500000, /* 0.5 usecs = 500000 psecs */
+ 2000000, /* 2.0 usecs = 2000000 psecs */
+ 1000000, /* 1.0 usecs = 1000000 psecs */
+ /* XXX - Sniffer doc says 0.08 usecs = 80000 psecs */
+ 100000 /* 0.1 usecs = 100000 psecs */
+};
+#define NUM_NGSNIFF_TIMEUNITS (sizeof Psec / sizeof Psec[0])
+
+/* Information for a compressed Sniffer data stream. */
+typedef struct {
+ unsigned char *buf; /* buffer into which we uncompress data */
+ unsigned int nbytes; /* number of bytes of data in that buffer */
+ int nextout; /* offset in that buffer of stream's current position */
+ gint64 comp_offset; /* current offset in compressed data stream */
+ gint64 uncomp_offset; /* current offset in uncompressed data stream */
+} ngsniffer_comp_stream_t;
+
+typedef struct {
+ guint maj_vers;
+ guint min_vers;
+ gboolean is_compressed;
+ guint32 timeunit;
+ time_t start;
+ guint network; /* network type */
+ ngsniffer_comp_stream_t seq; /* sequential access */
+ ngsniffer_comp_stream_t rand; /* random access */
+ GList *first_blob; /* list element for first blob */
+ GList *last_blob; /* list element for last blob */
+ GList *current_blob; /* list element for current blob */
+} ngsniffer_t;
+
+/*
+ * DOS date to "struct tm" conversion values.
+ */
+/* DOS year = upper 7 bits */
+#define DOS_YEAR_OFFSET (1980-1900) /* tm_year = year+1900, DOS date year year+1980 */
+#define DOS_YEAR_SHIFT 9
+#define DOS_YEAR_MASK (0x7F<<DOS_YEAR_SHIFT)
+/* DOS month = next 4 bits */
+#define DOS_MONTH_OFFSET (-1) /* tm_mon = month #-1, DOS date month = month # */
+#define DOS_MONTH_SHIFT 5
+#define DOS_MONTH_MASK (0x0F<<DOS_MONTH_SHIFT)
+/* DOS day = next 5 bits */
+#define DOS_DAY_SHIFT 0
+#define DOS_DAY_MASK (0x1F<<DOS_DAY_SHIFT)
+
+static int process_header_records(wtap *wth, int *err, gchar **err_info,
+ gint16 maj_vers, guint8 network);
+static int process_rec_header2_v2(wtap *wth, unsigned char *buffer,
+ guint16 length, int *err, gchar **err_info);
+static int process_rec_header2_v145(wtap *wth, unsigned char *buffer,
+ guint16 length, gint16 maj_vers, int *err, gchar **err_info);
+static gboolean ngsniffer_read(wtap *wth, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info, gint64 *data_offset);
+static gboolean ngsniffer_seek_read(wtap *wth, gint64 seek_off,
+ wtap_rec *rec, Buffer *buf, int *err, gchar **err_info);
+static gboolean read_rec_header(wtap *wth, gboolean is_random,
+ struct rec_header *hdr, int *err, gchar **err_info);
+static gboolean process_frame_record(wtap *wth, gboolean is_random,
+ guint *padding, struct rec_header *hdr, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info);
+static void set_metadata_frame2(wtap *wth, wtap_rec *rec,
+ struct frame2_rec *frame2);
+static void set_pseudo_header_frame4(union wtap_pseudo_header *pseudo_header,
+ struct frame4_rec *frame4);
+static void set_pseudo_header_frame6(wtap *wth,
+ union wtap_pseudo_header *pseudo_header, struct frame6_rec *frame6);
+static int infer_pkt_encap(const guint8 *pd, int len);
+static int fix_pseudo_header(int encap, Buffer *buf, int len,
+ union wtap_pseudo_header *pseudo_header);
+static void ngsniffer_sequential_close(wtap *wth);
+static void ngsniffer_close(wtap *wth);
+static gboolean ngsniffer_dump(wtap_dumper *wdh, const wtap_rec *rec,
+ const guint8 *pd, int *err, gchar **err_info);
+static gboolean ngsniffer_dump_finish(wtap_dumper *wdh, int *err,
+ gchar **err_info);
+static int SnifferDecompress( unsigned char * inbuf, size_t inlen,
+ unsigned char * outbuf, size_t outlen, int *err, gchar **err_info );
+static gboolean ng_read_bytes_or_eof(wtap *wth, void *buffer,
+ unsigned int nbytes, gboolean is_random, int *err, gchar **err_info);
+static gboolean ng_read_bytes(wtap *wth, void *buffer, unsigned int nbytes,
+ gboolean is_random, int *err, gchar **err_info);
+static gboolean read_blob(FILE_T infile, ngsniffer_comp_stream_t *comp_stream,
+ int *err, gchar **err_info);
+static gboolean ng_skip_bytes_seq(wtap *wth, unsigned int count, int *err,
+ gchar **err_info);
+static gboolean ng_file_seek_rand(wtap *wth, gint64 offset, int *err,
+ gchar **err_info);
+
+static int ngsniffer_uncompressed_file_type_subtype = -1;
+static int ngsniffer_compressed_file_type_subtype = -1;
+
+void register_ngsniffer(void);
+
+wtap_open_return_val
+ngsniffer_open(wtap *wth, int *err, gchar **err_info)
+{
+ char magic[sizeof ngsniffer_magic];
+ char record_type[2];
+ char record_length[4]; /* only the first 2 bytes are length,
+ the last 2 are "reserved" and are thrown away */
+ guint16 type;
+ struct vers_rec version;
+ guint16 maj_vers;
+ guint16 start_date;
+#if 0
+ guint16 start_time;
+#endif
+ static const int sniffer_encap[] = {
+ WTAP_ENCAP_TOKEN_RING,
+ WTAP_ENCAP_ETHERNET,
+ WTAP_ENCAP_ARCNET,
+ WTAP_ENCAP_UNKNOWN, /* StarLAN */
+ WTAP_ENCAP_UNKNOWN, /* PC Network broadband */
+ WTAP_ENCAP_UNKNOWN, /* LocalTalk */
+ WTAP_ENCAP_UNKNOWN, /* Znet */
+ WTAP_ENCAP_PER_PACKET, /* Internetwork analyzer (synchronous) */
+ WTAP_ENCAP_PER_PACKET, /* Internetwork analyzer (asynchronous) */
+ WTAP_ENCAP_FDDI_BITSWAPPED,
+ WTAP_ENCAP_ATM_PDUS
+ };
+ #define NUM_NGSNIFF_ENCAPS (sizeof sniffer_encap / sizeof sniffer_encap[0])
+ struct tm tm;
+ gint64 current_offset;
+ ngsniffer_t *ngsniffer;
+
+ /* Read in the string that should be at the start of a Sniffer file */
+ if (!wtap_read_bytes(wth->fh, magic, sizeof magic, err, err_info)) {
+ if (*err != WTAP_ERR_SHORT_READ)
+ return WTAP_OPEN_ERROR;
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ if (memcmp(magic, ngsniffer_magic, sizeof ngsniffer_magic)) {
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ /*
+ * Read the first record, which the manual says is a version
+ * record.
+ */
+ if (!wtap_read_bytes(wth->fh, record_type, 2, err, err_info))
+ return WTAP_OPEN_ERROR;
+ if (!wtap_read_bytes(wth->fh, record_length, 4, err, err_info))
+ return WTAP_OPEN_ERROR;
+
+ type = pletoh16(record_type);
+
+ if (type != REC_VERS) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("ngsniffer: Sniffer file doesn't start with a version record");
+ return WTAP_OPEN_ERROR;
+ }
+
+ if (!wtap_read_bytes(wth->fh, &version, sizeof version, err, err_info))
+ return WTAP_OPEN_ERROR;
+
+ /* Check the data link type. */
+ if (version.network >= NUM_NGSNIFF_ENCAPS
+ || sniffer_encap[version.network] == WTAP_ENCAP_UNKNOWN) {
+ *err = WTAP_ERR_UNSUPPORTED;
+ *err_info = ws_strdup_printf("ngsniffer: network type %u unknown or unsupported",
+ version.network);
+ return WTAP_OPEN_ERROR;
+ }
+
+ /* Check the time unit */
+ if (version.timeunit >= NUM_NGSNIFF_TIMEUNITS) {
+ *err = WTAP_ERR_UNSUPPORTED;
+ *err_info = ws_strdup_printf("ngsniffer: Unknown timeunit %u", version.timeunit);
+ return WTAP_OPEN_ERROR;
+ }
+
+ /* Set encap type before reading header records because the
+ * header record may change encap type.
+ */
+ wth->file_encap = sniffer_encap[version.network];
+
+ /*
+ * We don't know how to handle the remaining header record types,
+ * so we just skip them - except for REC_HEADER2 records, which
+ * we look at, for "Internetwork analyzer" captures, to attempt to
+ * determine what the link-layer encapsulation is.
+ *
+ * XXX - in some version 1.16 internetwork analyzer files
+ * generated by the Windows Sniffer when saving Windows
+ * Sniffer files as DOS Sniffer files, there's no REC_HEADER2
+ * record, but the first "rsvd" word is 1 for PRI ISDN files, 2
+ * for BRI ISDN files, and 0 for non-ISDN files; is that something
+ * the DOS Sniffer understands?
+ */
+ maj_vers = pletoh16(&version.maj_vers);
+ if (process_header_records(wth, err, err_info, maj_vers,
+ version.network) < 0)
+ return WTAP_OPEN_ERROR;
+ if ((version.network == NETWORK_SYNCHRO ||
+ version.network == NETWORK_ASYNC) &&
+ wth->file_encap == WTAP_ENCAP_PER_PACKET) {
+ /*
+ * Well, we haven't determined the internetwork analyzer
+ * subtype yet...
+ */
+ switch (maj_vers) {
+
+ case 1:
+ /*
+ * ... and this is a version 1 capture; look
+ * at the first "rsvd" word.
+ */
+ switch (pletoh16(&version.rsvd[0])) {
+
+ case 1:
+ case 2:
+ wth->file_encap = WTAP_ENCAP_ISDN;
+ break;
+ }
+ break;
+
+ case 3:
+ /*
+ * ...and this is a version 3 capture; we've
+ * seen nothing in those that obviously
+ * indicates the capture type, but the only
+ * one we've seen is a Frame Relay capture,
+ * so mark it as Frame Relay for now.
+ */
+ wth->file_encap = WTAP_ENCAP_FRELAY_WITH_PHDR;
+ break;
+ }
+ }
+
+ current_offset = file_tell(wth->fh);
+
+ /*
+ * Now, if we have a random stream open, position it to the same
+ * location, which should be the beginning of the real data, and
+ * should be the beginning of the compressed data.
+ *
+ * XXX - will we see any records other than REC_FRAME2, REC_FRAME4,
+ * or REC_EOF after this? If not, we can get rid of the loop in
+ * "ngsniffer_read()".
+ */
+ if (wth->random_fh != NULL) {
+ if (file_seek(wth->random_fh, current_offset, SEEK_SET, err) == -1)
+ return WTAP_OPEN_ERROR;
+ }
+
+ /* This is a ngsniffer file */
+ ngsniffer = g_new(ngsniffer_t, 1);
+ wth->priv = (void *)ngsniffer;
+
+ /* compressed or uncompressed Sniffer file? */
+ if (version.format != 1) {
+ wth->file_type_subtype = ngsniffer_compressed_file_type_subtype;
+ ngsniffer->is_compressed = TRUE;
+ } else {
+ wth->file_type_subtype = ngsniffer_uncompressed_file_type_subtype;
+ ngsniffer->is_compressed = FALSE;
+ }
+
+ ngsniffer->maj_vers = maj_vers;
+ ngsniffer->min_vers = pletoh16(&version.min_vers);
+
+ /* We haven't allocated any uncompression buffers yet. */
+ ngsniffer->seq.buf = NULL;
+ ngsniffer->seq.nbytes = 0;
+ ngsniffer->seq.nextout = 0;
+ ngsniffer->rand.buf = NULL;
+ ngsniffer->rand.nbytes = 0;
+ ngsniffer->rand.nextout = 0;
+
+ /* Set the current file offset; the offset in the compressed file
+ and in the uncompressed data stream currently the same. */
+ ngsniffer->seq.uncomp_offset = current_offset;
+ ngsniffer->seq.comp_offset = current_offset;
+ ngsniffer->rand.uncomp_offset = current_offset;
+ ngsniffer->rand.comp_offset = current_offset;
+
+ /* We don't yet have any list of compressed blobs. */
+ ngsniffer->first_blob = NULL;
+ ngsniffer->last_blob = NULL;
+ ngsniffer->current_blob = NULL;
+
+ wth->subtype_read = ngsniffer_read;
+ wth->subtype_seek_read = ngsniffer_seek_read;
+ wth->subtype_sequential_close = ngsniffer_sequential_close;
+ wth->subtype_close = ngsniffer_close;
+ wth->snapshot_length = 0; /* not available in header, only in frame */
+ ngsniffer->timeunit = Psec[version.timeunit];
+ ngsniffer->network = version.network;
+
+ /* Get capture start time */
+ start_date = pletoh16(&version.date);
+ tm.tm_year = ((start_date&DOS_YEAR_MASK)>>DOS_YEAR_SHIFT) + DOS_YEAR_OFFSET;
+ tm.tm_mon = ((start_date&DOS_MONTH_MASK)>>DOS_MONTH_SHIFT) + DOS_MONTH_OFFSET;
+ tm.tm_mday = ((start_date&DOS_DAY_MASK)>>DOS_DAY_SHIFT);
+ /*
+ * The time does not appear to act as an offset; only the date.
+ * XXX - sometimes it does appear to act as an offset; is this
+ * version-dependent?
+ */
+#if 0
+ start_time = pletoh16(&version.time_dos);
+ tm.tm_hour = (start_time&0xf800)>>11;
+ tm.tm_min = (start_time&0x7e0)>>5;
+ tm.tm_sec = (start_time&0x1f)<<1;
+#else
+ tm.tm_hour = 0;
+ tm.tm_min = 0;
+ tm.tm_sec = 0;
+#endif
+ tm.tm_isdst = -1;
+ ngsniffer->start = mktime(&tm);
+ /*
+ * XXX - what if "secs" is -1? Unlikely,
+ * but if the capture was done in a time
+ * zone that switches between standard and
+ * summer time sometime other than when we
+ * do, and thus the time was one that doesn't
+ * exist here because a switch from standard
+ * to summer time zips over it, it could
+ * happen.
+ *
+ * On the other hand, if the capture was done
+ * in a different time zone, this won't work
+ * right anyway; unfortunately, the time zone
+ * isn't stored in the capture file.
+ */
+
+ wth->file_tsprec = WTAP_TSPREC_NSEC; /* XXX */
+
+ return WTAP_OPEN_MINE;
+}
+
+static int
+process_header_records(wtap *wth, int *err, gchar **err_info, gint16 maj_vers,
+ guint8 network)
+{
+ char record_type[2];
+ char record_length[4]; /* only the first 2 bytes are length,
+ the last 2 are "reserved" and are thrown away */
+ guint16 rec_type, rec_length_remaining;
+ int bytes_to_read;
+ unsigned char buffer[256];
+
+ for (;;) {
+ if (!wtap_read_bytes_or_eof(wth->fh, record_type, 2, err, err_info)) {
+ if (*err != 0)
+ return -1;
+ return 0; /* EOF */
+ }
+
+ rec_type = pletoh16(record_type);
+ if ((rec_type != REC_HEADER1) && (rec_type != REC_HEADER2)
+ && (rec_type != REC_HEADER3) && (rec_type != REC_HEADER4)
+ && (rec_type != REC_HEADER5) && (rec_type != REC_HEADER6)
+ && (rec_type != REC_HEADER7)
+ && ((rec_type != REC_V2DESC) || (maj_vers > 2)) ) {
+ /*
+ * Well, this is either some unknown header type
+ * (we ignore this case), an uncompressed data
+ * frame or the length of a compressed blob
+ * which implies data. Seek backwards over the
+ * two bytes we read, and return.
+ */
+ if (file_seek(wth->fh, -2, SEEK_CUR, err) == -1)
+ return -1;
+ return 0;
+ }
+
+ if (!wtap_read_bytes(wth->fh, record_length, 4,
+ err, err_info))
+ return -1;
+
+ rec_length_remaining = pletoh16(record_length);
+
+ /*
+ * Is this is an "Internetwork analyzer" capture, and
+ * is this a REC_HEADER2 record?
+ *
+ * If so, it appears to specify the particular type
+ * of network we're on.
+ *
+ * XXX - handle sync and async differently? (E.g.,
+ * does this apply only to sync?)
+ */
+ if ((network == NETWORK_SYNCHRO || network == NETWORK_ASYNC) &&
+ rec_type == REC_HEADER2) {
+ /*
+ * Yes, get the first up-to-256 bytes of the
+ * record data.
+ */
+ bytes_to_read = MIN(rec_length_remaining, (int)sizeof buffer);
+ if (!wtap_read_bytes(wth->fh, buffer,
+ bytes_to_read, err, err_info))
+ return -1;
+
+ switch (maj_vers) {
+
+ case 2:
+ if (process_rec_header2_v2(wth, buffer,
+ rec_length_remaining, err, err_info) < 0)
+ return -1;
+ break;
+
+ case 1:
+ case 4:
+ case 5:
+ if (process_rec_header2_v145(wth, buffer,
+ rec_length_remaining, maj_vers, err, err_info) < 0)
+ return -1;
+ break;
+ }
+
+ /*
+ * Skip the rest of the record.
+ */
+ if (rec_length_remaining > sizeof buffer) {
+ if (file_seek(wth->fh, rec_length_remaining - sizeof buffer,
+ SEEK_CUR, err) == -1)
+ return -1;
+ }
+ } else {
+ /* Nope, just skip over the data. */
+ if (file_seek(wth->fh, rec_length_remaining, SEEK_CUR, err) == -1)
+ return -1;
+ }
+ }
+}
+
+static int
+process_rec_header2_v2(wtap *wth, unsigned char *buffer, guint16 length,
+ int *err, gchar **err_info)
+{
+ static const char x_25_str[] = "HDLC\nX.25\n";
+
+ /*
+ * There appears to be a string in a REC_HEADER2 record, with
+ * a list of protocols. In one X.25 capture I've seen, the
+ * string was "HDLC\nX.25\nCLNP\nISO_TP\nSESS\nPRES\nVTP\nACSE".
+ * Presumably CLNP and everything else is per-packet, but
+ * we assume "HDLC\nX.25\n" indicates that it's an X.25 capture.
+ */
+ if (length < sizeof x_25_str - 1) {
+ /*
+ * There's not enough data to compare.
+ */
+ *err = WTAP_ERR_UNSUPPORTED;
+ *err_info = g_strdup("ngsniffer: WAN capture has too-short protocol list");
+ return -1;
+ }
+
+ if (strncmp((char *)buffer, x_25_str, sizeof x_25_str - 1) == 0) {
+ /*
+ * X.25.
+ */
+ wth->file_encap = WTAP_ENCAP_LAPB;
+ } else {
+ *err = WTAP_ERR_UNSUPPORTED;
+ *err_info = ws_strdup_printf("ngsniffer: WAN capture protocol string %.*s unknown",
+ length, buffer);
+ return -1;
+ }
+ return 0;
+}
+
+static int
+process_rec_header2_v145(wtap *wth, unsigned char *buffer, guint16 length,
+ gint16 maj_vers, int *err, gchar **err_info)
+{
+ /*
+ * The 5th byte of the REC_HEADER2 record appears to be a
+ * network type.
+ */
+ if (length < 5) {
+ /*
+ * There is no 5th byte; give up.
+ */
+ *err = WTAP_ERR_UNSUPPORTED;
+ *err_info = g_strdup("ngsniffer: WAN capture has no network subtype");
+ return -1;
+ }
+
+ /*
+ * The X.25 captures I've seen have a type of NET_HDLC, and the
+ * Sniffer documentation seems to imply that it's used for
+ * X.25, although it could be used for other purposes as well.
+ *
+ * NET_ROUTER is used for all sorts of point-to-point protocols,
+ * including ISDN. It appears, from the documentation, that the
+ * Sniffer attempts to infer the particular protocol by looking
+ * at the traffic; it's not clear whether it stores in the file
+ * an indication of the protocol it inferred was being used.
+ *
+ * Unfortunately, it also appears that NET_HDLC is used for
+ * stuff other than X.25 as well, so we can't just interpret
+ * it unconditionally as X.25.
+ *
+ * For now, we interpret both NET_HDLC and NET_ROUTER as "per-packet
+ * encapsulation". We remember that we saw NET_ROUTER, though,
+ * as it appears that we can infer whether a packet is PPP or
+ * ISDN based on the channel number subfield of the frame error
+ * status bits - if it's 0, it's PPP, otherwise it's ISDN and
+ * the channel number indicates which channel it is. We assume
+ * NET_HDLC isn't used for ISDN.
+ */
+ switch (buffer[4]) {
+
+ case NET_SDLC:
+ wth->file_encap = WTAP_ENCAP_SDLC;
+ break;
+
+ case NET_HDLC:
+ wth->file_encap = WTAP_ENCAP_PER_PACKET;
+ break;
+
+ case NET_FRAME_RELAY:
+ wth->file_encap = WTAP_ENCAP_FRELAY_WITH_PHDR;
+ break;
+
+ case NET_ROUTER:
+ /*
+ * For most of the version 4 capture files I've seen,
+ * 0xfa in buffer[1] means the file is an ISDN capture,
+ * but there's one PPP file with 0xfa there; does that
+ * mean that the 0xfa has nothing to do with ISDN,
+ * or is that just an ISDN file with no D channel
+ * packets? (The channel number is not 0 in any
+ * of the packets, so perhaps it is.)
+ *
+ * For one version 5 ISDN capture I've seen, there's
+ * a 0x01 in buffer[6]; none of the non-ISDN version
+ * 5 captures have it.
+ */
+ wth->file_encap = WTAP_ENCAP_PER_PACKET;
+ switch (maj_vers) {
+
+ case 4:
+ if (buffer[1] == 0xfa)
+ wth->file_encap = WTAP_ENCAP_ISDN;
+ break;
+
+ case 5:
+ if (length < 7) {
+ /*
+ * There is no 5th byte; give up.
+ */
+ *err = WTAP_ERR_UNSUPPORTED;
+ *err_info = g_strdup("ngsniffer: WAN bridge/router capture has no ISDN flag");
+ return -1;
+ }
+ if (buffer[6] == 0x01)
+ wth->file_encap = WTAP_ENCAP_ISDN;
+ break;
+ }
+ break;
+
+ case NET_PPP:
+ wth->file_encap = WTAP_ENCAP_PPP_WITH_PHDR;
+ break;
+
+ default:
+ /*
+ * Reject these until we can figure them out.
+ */
+ *err = WTAP_ERR_UNSUPPORTED;
+ *err_info = ws_strdup_printf("ngsniffer: WAN network subtype %u unknown or unsupported",
+ buffer[4]);
+ return -1;
+ }
+ return 0;
+}
+
+/* Read the next packet */
+static gboolean
+ngsniffer_read(wtap *wth, wtap_rec *rec, Buffer *buf, int *err,
+ gchar **err_info, gint64 *data_offset)
+{
+ ngsniffer_t *ngsniffer;
+ struct rec_header hdr;
+ guint padding;
+
+ ngsniffer = (ngsniffer_t *)wth->priv;
+ for (;;) {
+ /*
+ * We use the uncompressed offset, as that's what
+ * we need to use for compressed files.
+ */
+ *data_offset = ngsniffer->seq.uncomp_offset;
+
+ /*
+ * Read the record header.
+ */
+ if (!read_rec_header(wth, FALSE, &hdr, err, err_info)) {
+ /* Read error or short read */
+ return FALSE;
+ }
+
+ /*
+ * Process the record.
+ */
+ switch (hdr.type) {
+
+ case REC_FRAME2:
+ case REC_FRAME4:
+ case REC_FRAME6:
+ /* Frame record */
+ if (!process_frame_record(wth, FALSE, &padding,
+ &hdr, rec, buf, err, err_info)) {
+ /* Read error, short read, or other error */
+ return FALSE;
+ }
+
+ /*
+ * Skip any extra data in the record.
+ */
+ if (padding != 0) {
+ if (!ng_skip_bytes_seq(wth, padding, err,
+ err_info))
+ return FALSE;
+ }
+ return TRUE;
+
+ case REC_EOF:
+ /*
+ * End of file. Skip past any data (if any),
+ * the length of which is in hdr.length, and
+ * return an EOF indication.
+ */
+ if (hdr.length != 0) {
+ if (!ng_skip_bytes_seq(wth, hdr.length, err,
+ err_info))
+ return FALSE;
+ }
+ *err = 0; /* EOF, not error */
+ return FALSE;
+
+ default:
+ /*
+ * Well, we don't know what it is, or we know what
+ * it is but can't handle it. Skip past the data
+ * portion (if any), the length of which is in
+ * hdr.length, and keep looping.
+ */
+ if (hdr.length != 0) {
+ if (!ng_skip_bytes_seq(wth, hdr.length, err,
+ err_info))
+ return FALSE;
+ }
+ break;
+ }
+ }
+}
+
+static gboolean
+ngsniffer_seek_read(wtap *wth, gint64 seek_off,
+ wtap_rec *rec, Buffer *buf, int *err, gchar **err_info)
+{
+ struct rec_header hdr;
+
+ if (!ng_file_seek_rand(wth, seek_off, err, err_info))
+ return FALSE;
+
+ if (!read_rec_header(wth, TRUE, &hdr, err, err_info)) {
+ /* Read error or short read */
+ return FALSE;
+ }
+
+ /*
+ * hdr.type is the record type.
+ */
+ switch (hdr.type) {
+
+ case REC_FRAME2:
+ case REC_FRAME4:
+ case REC_FRAME6:
+ /* Frame record */
+ if (!process_frame_record(wth, TRUE, NULL, &hdr, rec, buf,
+ err, err_info)) {
+ /* Read error, short read, or other error */
+ return FALSE;
+ }
+ break;
+
+ default:
+ /*
+ * Other record type, or EOF.
+ * This "can't happen".
+ */
+ ws_assert_not_reached();
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/*
+ * Read the record header.
+ *
+ * Returns TRUE on success, FALSE on error.
+ */
+static gboolean
+read_rec_header(wtap *wth, gboolean is_random, struct rec_header *hdr,
+ int *err, gchar **err_info)
+{
+ char record_type[2];
+ char record_length[4]; /* only 1st 2 bytes are length */
+
+ /*
+ * Read the record type.
+ */
+ if (!ng_read_bytes_or_eof(wth, record_type, 2, is_random, err, err_info)) {
+ if (*err != 0)
+ return FALSE;
+ /*
+ * End-of-file; construct a fake EOF record.
+ * (A file might have an EOF record at the end, or
+ * it might just come to an end.)
+ * (XXX - is that true of all Sniffer files?)
+ */
+ hdr->type = REC_EOF;
+ hdr->length = 0;
+ return TRUE;
+ }
+
+ /*
+ * Read the record length.
+ */
+ if (!ng_read_bytes(wth, record_length, 4, is_random, err, err_info))
+ return FALSE;
+
+ hdr->type = pletoh16(record_type);
+ hdr->length = pletoh16(record_length);
+ return TRUE;
+}
+
+/*
+ * Returns TRUE on success, FALSE on error.
+ * If padding is non-null, sets *padding to the amount of padding at
+ * the end of the record.
+ */
+static gboolean
+process_frame_record(wtap *wth, gboolean is_random, guint *padding,
+ struct rec_header *hdr, wtap_rec *rec, Buffer *buf, int *err,
+ gchar **err_info)
+{
+ ngsniffer_t *ngsniffer;
+ guint rec_length_remaining;
+ struct frame2_rec frame2;
+ struct frame4_rec frame4;
+ struct frame6_rec frame6;
+ guint16 time_low, time_med, true_size, size;
+ guint8 time_high, time_day;
+ guint64 t, tsecs, tpsecs;
+
+ rec_length_remaining = hdr->length;
+
+ /* Initialize - we'll be setting some presence flags below. */
+ rec->rec_type = REC_TYPE_PACKET;
+ rec->block = wtap_block_create(WTAP_BLOCK_PACKET);
+ rec->presence_flags = 0;
+
+ ngsniffer = (ngsniffer_t *)wth->priv;
+ switch (hdr->type) {
+
+ case REC_FRAME2:
+ if (ngsniffer->network == NETWORK_ATM) {
+ /*
+ * We shouldn't get a frame2 record in
+ * an ATM capture.
+ */
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("ngsniffer: REC_FRAME2 record in an ATM Sniffer file");
+ return FALSE;
+ }
+
+ /* Do we have an f_frame2_struct worth of data? */
+ if (rec_length_remaining < sizeof frame2) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("ngsniffer: REC_FRAME2 record length is less than record header length");
+ return FALSE;
+ }
+
+ /* Read the f_frame2_struct */
+ if (!ng_read_bytes(wth, &frame2, (unsigned int)sizeof frame2,
+ is_random, err, err_info))
+ return FALSE;
+ time_low = pletoh16(&frame2.time_low);
+ time_med = pletoh16(&frame2.time_med);
+ time_high = frame2.time_high;
+ time_day = frame2.time_day;
+ size = pletoh16(&frame2.size);
+ true_size = pletoh16(&frame2.true_size);
+
+ rec_length_remaining -= (guint)sizeof frame2; /* we already read that much */
+
+ set_metadata_frame2(wth, rec, &frame2);
+ break;
+
+ case REC_FRAME4:
+ if (ngsniffer->network != NETWORK_ATM) {
+ /*
+ * We shouldn't get a frame2 record in
+ * a non-ATM capture.
+ */
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("ngsniffer: REC_FRAME4 record in a non-ATM Sniffer file");
+ return FALSE;
+ }
+
+ /*
+ * XXX - it looks as if some version 4 captures have
+ * a bogus record length, based on the assumption
+ * that the record is a frame2 record, i.e. the length
+ * was calculated based on the record being a frame2
+ * record, so it's too short by (sizeof frame4 - sizeof frame2).
+ */
+ if (ngsniffer->maj_vers < 5 && ngsniffer->min_vers >= 95)
+ rec_length_remaining += (guint)(sizeof frame4 - sizeof frame2);
+
+ /* Do we have an f_frame4_struct worth of data? */
+ if (rec_length_remaining < sizeof frame4) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("ngsniffer: REC_FRAME4 record length is less than record header length");
+ return FALSE;
+ }
+
+ /* Read the f_frame4_struct */
+ if (!ng_read_bytes(wth, &frame4, (unsigned int)sizeof frame4,
+ is_random, err, err_info))
+ return FALSE;
+ time_low = pletoh16(&frame4.time_low);
+ time_med = pletoh16(&frame4.time_med);
+ time_high = frame4.time_high;
+ time_day = frame4.time_day;
+ size = pletoh16(&frame4.size);
+ true_size = pletoh16(&frame4.true_size);
+
+ rec_length_remaining -= (guint)sizeof frame4; /* we already read that much */
+
+ set_pseudo_header_frame4(&rec->rec_header.packet_header.pseudo_header, &frame4);
+ break;
+
+ case REC_FRAME6:
+ /* Do we have an f_frame6_struct worth of data? */
+ if (rec_length_remaining < sizeof frame6) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("ngsniffer: REC_FRAME6 record length is less than record header length");
+ return FALSE;
+ }
+
+ /* Read the f_frame6_struct */
+ if (!ng_read_bytes(wth, &frame6, (unsigned int)sizeof frame6,
+ is_random, err, err_info))
+ return FALSE;
+ time_low = pletoh16(&frame6.time_low);
+ time_med = pletoh16(&frame6.time_med);
+ time_high = frame6.time_high;
+ time_day = frame6.time_day;
+ size = pletoh16(&frame6.size);
+ true_size = pletoh16(&frame6.true_size);
+
+ rec_length_remaining -= (guint)sizeof frame6; /* we already read that much */
+
+ set_pseudo_header_frame6(wth, &rec->rec_header.packet_header.pseudo_header, &frame6);
+ break;
+
+ default:
+ /*
+ * This should never happen.
+ */
+ ws_assert_not_reached();
+ return FALSE;
+ }
+
+ /*
+ * Is the frame data size greater than what's left of the
+ * record?
+ */
+ if (size > rec_length_remaining) {
+ /*
+ * Yes - treat this as an error.
+ */
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("ngsniffer: Record length is less than packet size");
+ return FALSE;
+ }
+
+ /*
+ * The maximum value of length is 65535, which is less than
+ * WTAP_MAX_PACKET_SIZE_STANDARD will ever be, so we don't need to check
+ * it.
+ */
+ if (padding != NULL) {
+ /*
+ * Padding, if the frame data size is less than what's
+ * left of the record.
+ */
+ *padding = rec_length_remaining - size;
+ }
+
+ rec->presence_flags |= true_size ? WTAP_HAS_TS|WTAP_HAS_CAP_LEN : WTAP_HAS_TS;
+ rec->rec_header.packet_header.len = true_size ? true_size : size;
+ rec->rec_header.packet_header.caplen = size;
+
+ /*
+ * Read the packet data.
+ */
+ ws_buffer_assure_space(buf, size);
+ if (!ng_read_bytes(wth, ws_buffer_start_ptr(buf), size, is_random,
+ err, err_info))
+ return FALSE;
+
+ rec->rec_header.packet_header.pkt_encap = fix_pseudo_header(wth->file_encap,
+ buf, size, &rec->rec_header.packet_header.pseudo_header);
+
+ /*
+ * 40-bit time stamp, in units of timeunit picoseconds.
+ */
+ t = (((guint64)time_high)<<32) | (((guint64)time_med) << 16) | time_low;
+
+ /*
+ * timeunit is always < 2^(64-40), so t * timeunit fits in 64
+ * bits. That gives a 64-bit time stamp, in units of
+ * picoseconds.
+ */
+ t *= ngsniffer->timeunit;
+
+ /*
+ * Convert to seconds and picoseconds.
+ */
+ tsecs = t/G_GUINT64_CONSTANT(1000000000000);
+ tpsecs = t - tsecs*G_GUINT64_CONSTANT(1000000000000);
+
+ /*
+ * Add in the time_day value (86400 seconds/day).
+ */
+ tsecs += time_day*86400;
+
+ /*
+ * Add in the capture start time.
+ */
+ tsecs += ngsniffer->start;
+
+ rec->ts.secs = (time_t)tsecs;
+ rec->ts.nsecs = (int)(tpsecs/1000); /* psecs to nsecs */
+
+ return TRUE; /* success */
+}
+
+static void
+set_metadata_frame2(wtap *wth, wtap_rec *rec, struct frame2_rec *frame2)
+{
+ ngsniffer_t *ngsniffer;
+ guint32 pack_flags;
+ union wtap_pseudo_header *pseudo_header;
+
+ ngsniffer = (ngsniffer_t *)wth->priv;
+
+ /*
+ * In one PPP "Internetwork analyzer" capture:
+ *
+ * The only bit seen in "frame2.fs" is the 0x80 bit, which
+ * probably indicates the packet's direction; all other
+ * bits were zero. The Expert Sniffer Network Analyzer
+ * 5.50 Operations manual says that bit is the FS_DTE bit
+ * for async/PPP data. The other bits are error bits
+ * plus bits indicating whether the frame is PPP or SLIP,
+ * but the PPP bit isn't set.
+ *
+ * All bits in "frame2.flags" were zero.
+ *
+ * In one X.25 "Internetwork analyzer" capture:
+ *
+ * The only bit seen in "frame2.fs" is the 0x80 bit, which
+ * probably indicates the packet's direction; all other
+ * bits were zero.
+ *
+ * "frame2.flags" was always 0x18; however, the Sniffer
+ * manual says that just means that a display filter was
+ * calculated for the frame, and it should be displayed,
+ * so perhaps that's just a quirk of that particular capture.
+ *
+ * In one Ethernet capture:
+ *
+ * "frame2.fs" was always 0; the Sniffer manual says they're
+ * error bits of various sorts.
+ *
+ * "frame2.flags" was either 0 or 0x18, with no obvious
+ * correlation with anything. See previous comment
+ * about display filters.
+ *
+ * In one Token Ring capture:
+ *
+ * "frame2.fs" was either 0 or 0xcc; the Sniffer manual says
+ * nothing about those bits for Token Ring captures.
+ *
+ * "frame2.flags" was either 0 or 0x18, with no obvious
+ * correlation with anything. See previous comment
+ * about display filters.
+ */
+ switch (ngsniffer->network) {
+
+ case NETWORK_ENET:
+ pack_flags = 0;
+ if (frame2->fs & FS_ETH_CRC)
+ pack_flags |= PACK_FLAGS_CRC_ERROR;
+ if (frame2->fs & FS_ETH_ALIGN)
+ pack_flags |= PACK_FLAGS_UNALIGNED_FRAME;
+ if (frame2->fs & FS_ETH_RUNT)
+ pack_flags |= PACK_FLAGS_PACKET_TOO_SHORT;
+ wtap_block_add_uint32_option(rec->block, OPT_PKT_FLAGS, pack_flags);
+ break;
+
+ case NETWORK_FDDI:
+ pack_flags = 0;
+ if (!(frame2->fs & FS_FDDI_INVALID) &&
+ (frame2->fs & (FS_FDDI_PCI_CRC|FS_FDDI_ISA_CRC)))
+ pack_flags |= PACK_FLAGS_CRC_ERROR;
+ wtap_block_add_uint32_option(rec->block, OPT_PKT_FLAGS, pack_flags);
+ break;
+
+ case NETWORK_SYNCHRO:
+ pack_flags = 0;
+ if (frame2->fs & FS_SYNC_CRC)
+ pack_flags |= PACK_FLAGS_CRC_ERROR;
+ wtap_block_add_uint32_option(rec->block, OPT_PKT_FLAGS, pack_flags);
+ break;
+ }
+
+ pseudo_header = &rec->rec_header.packet_header.pseudo_header;
+ switch (wth->file_encap) {
+
+ case WTAP_ENCAP_ETHERNET:
+ /*
+ * XXX - do we ever have an FCS? If not, why do we often
+ * have 4 extra bytes of stuff at the end? Do some
+ * PC Ethernet interfaces report the length including the
+ * FCS but not store the FCS in the packet, or do some
+ * Ethernet drivers work that way?
+ */
+ pseudo_header->eth.fcs_len = 0;
+ break;
+
+ case WTAP_ENCAP_PPP_WITH_PHDR:
+ case WTAP_ENCAP_SDLC:
+ pseudo_header->p2p.sent = (frame2->fs & FS_WAN_DTE) ? TRUE : FALSE;
+ break;
+
+ case WTAP_ENCAP_LAPB:
+ case WTAP_ENCAP_FRELAY_WITH_PHDR:
+ case WTAP_ENCAP_PER_PACKET:
+ pseudo_header->dte_dce.flags = (frame2->fs & FS_WAN_DTE) ? 0x00 : FROM_DCE;
+ break;
+
+ case WTAP_ENCAP_ISDN:
+ pseudo_header->isdn.uton = (frame2->fs & FS_WAN_DTE) ? FALSE : TRUE;
+ switch (frame2->fs & FS_ISDN_CHAN_MASK) {
+
+ case FS_ISDN_CHAN_D:
+ pseudo_header->isdn.channel = 0; /* D-channel */
+ break;
+
+ case FS_ISDN_CHAN_B1:
+ pseudo_header->isdn.channel = 1; /* B1-channel */
+ break;
+
+ case FS_ISDN_CHAN_B2:
+ pseudo_header->isdn.channel = 2; /* B2-channel */
+ break;
+
+ default:
+ pseudo_header->isdn.channel = 30; /* XXX */
+ break;
+ }
+ }
+}
+
+static void
+set_pseudo_header_frame4(union wtap_pseudo_header *pseudo_header,
+ struct frame4_rec *frame4)
+{
+ guint32 StatusWord;
+ guint8 aal_type, hl_type;
+ guint16 vpi, vci;
+
+ /*
+ * Map flags from frame4.atm_info.StatusWord.
+ */
+ pseudo_header->atm.flags = 0;
+ StatusWord = pletoh32(&frame4->atm_info.StatusWord);
+ if (StatusWord & SW_RAW_CELL)
+ pseudo_header->atm.flags |= ATM_RAW_CELL;
+
+ aal_type = frame4->atm_info.AppTrafType & ATT_AALTYPE;
+ hl_type = frame4->atm_info.AppTrafType & ATT_HLTYPE;
+ vpi = pletoh16(&frame4->atm_info.Vpi);
+ vci = pletoh16(&frame4->atm_info.Vci);
+
+ switch (aal_type) {
+
+ case ATT_AAL_UNKNOWN:
+ /*
+ * Map ATT_AAL_UNKNOWN on VPI 0, VCI 5 to ATT_AAL_SIGNALLING,
+ * as that's the VPCI used for signalling.
+ *
+ * XXX - is this necessary, or will frames to 0/5 always
+ * have ATT_AAL_SIGNALLING?
+ */
+ if (vpi == 0 && vci == 5)
+ pseudo_header->atm.aal = AAL_SIGNALLING;
+ else
+ pseudo_header->atm.aal = AAL_UNKNOWN;
+ pseudo_header->atm.type = TRAF_UNKNOWN;
+ pseudo_header->atm.subtype = TRAF_ST_UNKNOWN;
+ break;
+
+ case ATT_AAL1:
+ pseudo_header->atm.aal = AAL_1;
+ pseudo_header->atm.type = TRAF_UNKNOWN;
+ pseudo_header->atm.subtype = TRAF_ST_UNKNOWN;
+ break;
+
+ case ATT_AAL3_4:
+ pseudo_header->atm.aal = AAL_3_4;
+ pseudo_header->atm.type = TRAF_UNKNOWN;
+ pseudo_header->atm.subtype = TRAF_ST_UNKNOWN;
+ break;
+
+ case ATT_AAL5:
+ pseudo_header->atm.aal = AAL_5;
+ switch (hl_type) {
+
+ case ATT_HL_UNKNOWN:
+ pseudo_header->atm.type = TRAF_UNKNOWN;
+ pseudo_header->atm.subtype = TRAF_ST_UNKNOWN;
+ break;
+
+ case ATT_HL_LLCMX:
+ pseudo_header->atm.type = TRAF_LLCMX;
+ pseudo_header->atm.subtype = TRAF_ST_UNKNOWN;
+ break;
+
+ case ATT_HL_VCMX:
+ pseudo_header->atm.type = TRAF_VCMX;
+ switch (frame4->atm_info.AppHLType) {
+
+ case AHLT_UNKNOWN:
+ pseudo_header->atm.subtype = TRAF_ST_UNKNOWN;
+ break;
+
+ case AHLT_VCMX_802_3_FCS:
+ pseudo_header->atm.subtype =
+ TRAF_ST_VCMX_802_3_FCS;
+ break;
+
+ case AHLT_VCMX_802_4_FCS:
+ pseudo_header->atm.subtype =
+ TRAF_ST_VCMX_802_4_FCS;
+ break;
+
+ case AHLT_VCMX_802_5_FCS:
+ pseudo_header->atm.subtype =
+ TRAF_ST_VCMX_802_5_FCS;
+ break;
+
+ case AHLT_VCMX_FDDI_FCS:
+ pseudo_header->atm.subtype =
+ TRAF_ST_VCMX_FDDI_FCS;
+ break;
+
+ case AHLT_VCMX_802_6_FCS:
+ pseudo_header->atm.subtype =
+ TRAF_ST_VCMX_802_6_FCS;
+ break;
+
+ case AHLT_VCMX_802_3:
+ pseudo_header->atm.subtype = TRAF_ST_VCMX_802_3;
+ break;
+
+ case AHLT_VCMX_802_4:
+ pseudo_header->atm.subtype = TRAF_ST_VCMX_802_4;
+ break;
+
+ case AHLT_VCMX_802_5:
+ pseudo_header->atm.subtype = TRAF_ST_VCMX_802_5;
+ break;
+
+ case AHLT_VCMX_FDDI:
+ pseudo_header->atm.subtype = TRAF_ST_VCMX_FDDI;
+ break;
+
+ case AHLT_VCMX_802_6:
+ pseudo_header->atm.subtype = TRAF_ST_VCMX_802_6;
+ break;
+
+ case AHLT_VCMX_FRAGMENTS:
+ pseudo_header->atm.subtype =
+ TRAF_ST_VCMX_FRAGMENTS;
+ break;
+
+ case AHLT_VCMX_BPDU:
+ pseudo_header->atm.subtype = TRAF_ST_VCMX_BPDU;
+ break;
+
+ default:
+ pseudo_header->atm.subtype = TRAF_ST_UNKNOWN;
+ break;
+ }
+ break;
+
+ case ATT_HL_LANE:
+ pseudo_header->atm.type = TRAF_LANE;
+ switch (frame4->atm_info.AppHLType) {
+
+ case AHLT_UNKNOWN:
+ pseudo_header->atm.subtype = TRAF_ST_UNKNOWN;
+ break;
+
+ case AHLT_LANE_LE_CTRL:
+ pseudo_header->atm.subtype =
+ TRAF_ST_LANE_LE_CTRL;
+ break;
+
+ case AHLT_LANE_802_3:
+ pseudo_header->atm.subtype = TRAF_ST_LANE_802_3;
+ break;
+
+ case AHLT_LANE_802_5:
+ pseudo_header->atm.subtype = TRAF_ST_LANE_802_5;
+ break;
+
+ case AHLT_LANE_802_3_MC:
+ pseudo_header->atm.subtype =
+ TRAF_ST_LANE_802_3_MC;
+ break;
+
+ case AHLT_LANE_802_5_MC:
+ pseudo_header->atm.subtype =
+ TRAF_ST_LANE_802_5_MC;
+ break;
+
+ default:
+ pseudo_header->atm.subtype = TRAF_ST_UNKNOWN;
+ break;
+ }
+ break;
+
+ case ATT_HL_ILMI:
+ pseudo_header->atm.type = TRAF_ILMI;
+ pseudo_header->atm.subtype = TRAF_ST_UNKNOWN;
+ break;
+
+ case ATT_HL_FRMR:
+ pseudo_header->atm.type = TRAF_FR;
+ pseudo_header->atm.subtype = TRAF_ST_UNKNOWN;
+ break;
+
+ case ATT_HL_SPANS:
+ pseudo_header->atm.type = TRAF_SPANS;
+ pseudo_header->atm.subtype = TRAF_ST_UNKNOWN;
+ break;
+
+ case ATT_HL_IPSILON:
+ pseudo_header->atm.type = TRAF_IPSILON;
+ switch (frame4->atm_info.AppHLType) {
+
+ case AHLT_UNKNOWN:
+ pseudo_header->atm.subtype = TRAF_ST_UNKNOWN;
+ break;
+
+ case AHLT_IPSILON_FT0:
+ pseudo_header->atm.subtype =
+ TRAF_ST_IPSILON_FT0;
+ break;
+
+ case AHLT_IPSILON_FT1:
+ pseudo_header->atm.subtype =
+ TRAF_ST_IPSILON_FT1;
+ break;
+
+ case AHLT_IPSILON_FT2:
+ pseudo_header->atm.subtype =
+ TRAF_ST_IPSILON_FT2;
+ break;
+
+ default:
+ pseudo_header->atm.subtype = TRAF_ST_UNKNOWN;
+ break;
+ }
+ break;
+
+ default:
+ pseudo_header->atm.type = TRAF_UNKNOWN;
+ pseudo_header->atm.subtype = TRAF_ST_UNKNOWN;
+ break;
+ }
+ break;
+
+ case ATT_AAL_USER:
+ pseudo_header->atm.aal = AAL_USER;
+ pseudo_header->atm.type = TRAF_UNKNOWN;
+ pseudo_header->atm.subtype = TRAF_ST_UNKNOWN;
+ break;
+
+ case ATT_AAL_SIGNALLING:
+ pseudo_header->atm.aal = AAL_SIGNALLING;
+ pseudo_header->atm.type = TRAF_UNKNOWN;
+ pseudo_header->atm.subtype = TRAF_ST_UNKNOWN;
+ break;
+
+ case ATT_OAMCELL:
+ pseudo_header->atm.aal = AAL_OAMCELL;
+ pseudo_header->atm.type = TRAF_UNKNOWN;
+ pseudo_header->atm.subtype = TRAF_ST_UNKNOWN;
+ break;
+
+ default:
+ pseudo_header->atm.aal = AAL_UNKNOWN;
+ pseudo_header->atm.type = TRAF_UNKNOWN;
+ pseudo_header->atm.subtype = TRAF_ST_UNKNOWN;
+ break;
+ }
+ pseudo_header->atm.vpi = vpi;
+ pseudo_header->atm.vci = vci;
+ pseudo_header->atm.channel = pletoh16(&frame4->atm_info.channel);
+ pseudo_header->atm.cells = pletoh16(&frame4->atm_info.cells);
+ pseudo_header->atm.aal5t_u2u = pletoh16(&frame4->atm_info.Trailer.aal5t_u2u);
+ pseudo_header->atm.aal5t_len = pletoh16(&frame4->atm_info.Trailer.aal5t_len);
+ pseudo_header->atm.aal5t_chksum = pntoh32(&frame4->atm_info.Trailer.aal5t_chksum);
+}
+
+static void
+set_pseudo_header_frame6(wtap *wth, union wtap_pseudo_header *pseudo_header,
+ struct frame6_rec *frame6 _U_)
+{
+ /* XXX - Once the frame format is divined, something will most likely go here */
+
+ switch (wth->file_encap) {
+
+ case WTAP_ENCAP_ETHERNET:
+ /* XXX - is there an FCS? */
+ pseudo_header->eth.fcs_len = -1;
+ break;
+ }
+}
+
+/*
+ * OK, this capture is from an "Internetwork analyzer", and we either
+ * didn't see a type 7 record or it had a network type such as NET_HDLC
+ * that doesn't tell us which *particular* HDLC derivative this is;
+ * let's look at the first few bytes of the packet, a pointer to which
+ * was passed to us as an argument, and see whether it looks like PPP,
+ * Frame Relay, Wellfleet HDLC, Cisco HDLC, or LAPB - or, if it's none
+ * of those, assume it's LAPD.
+ *
+ * (XXX - are there any "Internetwork analyzer" captures that don't
+ * have type 7 records? If so, is there some other field that will
+ * tell us what type of capture it is?)
+ */
+static int
+infer_pkt_encap(const guint8 *pd, int len)
+{
+ int i;
+
+ if (len <= 0) {
+ /*
+ * Nothing to infer, but it doesn't matter how you
+ * dissect an empty packet. Let's just say PPP.
+ */
+ return WTAP_ENCAP_PPP_WITH_PHDR;
+ }
+
+ if (pd[0] == 0xFF) {
+ /*
+ * PPP. (XXX - check for 0xFF 0x03?)
+ */
+ return WTAP_ENCAP_PPP_WITH_PHDR;
+ }
+
+ if (len >= 2) {
+ if (pd[0] == 0x07 && pd[1] == 0x03) {
+ /*
+ * Wellfleet HDLC.
+ */
+ return WTAP_ENCAP_WFLEET_HDLC;
+ } else if ((pd[0] == 0x0F && pd[1] == 0x00) ||
+ (pd[0] == 0x8F && pd[1] == 0x00)) {
+ /*
+ * Cisco HDLC.
+ */
+ return WTAP_ENCAP_CHDLC_WITH_PHDR;
+ }
+
+ /*
+ * Check for Frame Relay. Look for packets with at least
+ * 3 bytes of header - 2 bytes of DLCI followed by 1 byte
+ * of control, which, for now, we require to be 0x03 (UI),
+ * although there might be other frame types as well.
+ * Scan forward until we see the last DLCI byte, with
+ * the low-order bit being 1, and then check the next
+ * byte, if it exists, to see if it's a control byte.
+ *
+ * XXX - in version 4 and 5 captures, wouldn't this just
+ * have a capture subtype of NET_FRAME_RELAY? Or is this
+ * here only to handle other versions of the capture
+ * file, where we might just not yet have found where
+ * the subtype is specified in the capture?
+ *
+ * Bay Networks/Nortel Networks had a mechanism in the Optivity
+ * software for some of their routers to save captures
+ * in Sniffer format; they use a version number of 4.9, but
+ * don't put out any header records before the first FRAME2
+ * record. That means we have to use heuristics to guess
+ * what type of packet we have.
+ */
+ for (i = 0; i < len && (pd[i] & 0x01) == 0; i++)
+ ;
+ if (i >= len - 1) {
+ /*
+ * Either all the bytes have the low-order bit
+ * clear, so we didn't even find the last DLCI
+ * byte, or the very last byte had the low-order
+ * bit set, so, if that's a DLCI, it fills the
+ * buffer, so there is no control byte after
+ * the last DLCI byte.
+ */
+ return WTAP_ENCAP_LAPB;
+ }
+ i++; /* advance to the byte after the last DLCI byte */
+ if (pd[i] == 0x03)
+ return WTAP_ENCAP_FRELAY_WITH_PHDR;
+ }
+
+ /*
+ * Assume LAPB, for now. If we support other HDLC encapsulations,
+ * we can check whether the low-order bit of the first byte is
+ * set (as it should be for LAPB) if no other checks pass.
+ *
+ * Or, if it's truly impossible to distinguish ISDN from non-ISDN
+ * captures, we could assume it's ISDN if it's not anything
+ * else.
+ */
+ return WTAP_ENCAP_LAPB;
+}
+
+static int
+fix_pseudo_header(int encap, Buffer *buf, int len,
+ union wtap_pseudo_header *pseudo_header)
+{
+ const guint8 *pd;
+
+ pd = ws_buffer_start_ptr(buf);
+ switch (encap) {
+
+ case WTAP_ENCAP_PER_PACKET:
+ /*
+ * Infer the packet type from the first two bytes.
+ */
+ encap = infer_pkt_encap(pd, len);
+
+ /*
+ * Fix up the pseudo-header to match the new
+ * encapsulation type.
+ */
+ switch (encap) {
+
+ case WTAP_ENCAP_WFLEET_HDLC:
+ case WTAP_ENCAP_CHDLC_WITH_PHDR:
+ case WTAP_ENCAP_PPP_WITH_PHDR:
+ if (pseudo_header->dte_dce.flags == 0)
+ pseudo_header->p2p.sent = TRUE;
+ else
+ pseudo_header->p2p.sent = FALSE;
+ break;
+
+ case WTAP_ENCAP_ISDN:
+ if (pseudo_header->dte_dce.flags == 0x00)
+ pseudo_header->isdn.uton = FALSE;
+ else
+ pseudo_header->isdn.uton = TRUE;
+
+ /*
+ * XXX - this is currently a per-packet
+ * encapsulation type, and we can't determine
+ * whether a capture is an ISDN capture before
+ * seeing any packets, and B-channel PPP packets
+ * look like PPP packets and are given
+ * WTAP_ENCAP_PPP_WITH_PHDR, not WTAP_ENCAP_ISDN,
+ * so we assume this is a D-channel packet and
+ * thus give it a channel number of 0.
+ */
+ pseudo_header->isdn.channel = 0;
+ break;
+ }
+ break;
+
+ case WTAP_ENCAP_ATM_PDUS:
+ /*
+ * If the Windows Sniffer writes out one of its ATM
+ * capture files in DOS Sniffer format, it doesn't
+ * distinguish between LE Control and LANE encapsulated
+ * LAN frames, it just marks them as LAN frames,
+ * so we fix that up here.
+ *
+ * I've also seen DOS Sniffer captures claiming that
+ * LANE packets that *don't* start with FF 00 are
+ * marked as LE Control frames, so we fix that up
+ * as well.
+ */
+ if (pseudo_header->atm.type == TRAF_LANE && len >= 2) {
+ if (pd[0] == 0xff && pd[1] == 0x00) {
+ /*
+ * This must be LE Control.
+ */
+ pseudo_header->atm.subtype =
+ TRAF_ST_LANE_LE_CTRL;
+ } else {
+ /*
+ * This can't be LE Control.
+ */
+ if (pseudo_header->atm.subtype ==
+ TRAF_ST_LANE_LE_CTRL) {
+ /*
+ * XXX - Ethernet or Token Ring?
+ */
+ pseudo_header->atm.subtype =
+ TRAF_ST_LANE_802_3;
+ }
+ }
+ }
+ break;
+ }
+ return encap;
+}
+
+/* Throw away the buffers used by the sequential I/O stream, but not
+ those used by the random I/O stream. */
+static void
+ngsniffer_sequential_close(wtap *wth)
+{
+ ngsniffer_t *ngsniffer;
+
+ ngsniffer = (ngsniffer_t *)wth->priv;
+ if (ngsniffer->seq.buf != NULL) {
+ g_free(ngsniffer->seq.buf);
+ ngsniffer->seq.buf = NULL;
+ }
+}
+
+static void
+free_blob(gpointer data, gpointer user_data _U_)
+{
+ g_free(data);
+}
+
+/* Close stuff used by the random I/O stream, if any, and free up any
+ private data structures. (If there's a "sequential_close" routine
+ for a capture file type, it'll be called before the "close" routine
+ is called, so we don't have to free the sequential buffer here.) */
+static void
+ngsniffer_close(wtap *wth)
+{
+ ngsniffer_t *ngsniffer;
+
+ ngsniffer = (ngsniffer_t *)wth->priv;
+ g_free(ngsniffer->rand.buf);
+ g_list_foreach(ngsniffer->first_blob, free_blob, NULL);
+ g_list_free(ngsniffer->first_blob);
+}
+
+typedef struct {
+ gboolean first_frame;
+ time_t start;
+} ngsniffer_dump_t;
+
+static const int wtap_encap[] = {
+ -1, /* WTAP_ENCAP_UNKNOWN -> unsupported */
+ 1, /* WTAP_ENCAP_ETHERNET */
+ 0, /* WTAP_ENCAP_TOKEN_RING */
+ -1, /* WTAP_ENCAP_SLIP -> unsupported */
+ 7, /* WTAP_ENCAP_PPP -> Internetwork analyzer (synchronous) FIXME ! */
+ 9, /* WTAP_ENCAP_FDDI */
+ 9, /* WTAP_ENCAP_FDDI_BITSWAPPED */
+ -1, /* WTAP_ENCAP_RAW_IP -> unsupported */
+ 2, /* WTAP_ENCAP_ARCNET */
+ -1, /* WTAP_ENCAP_ARCNET_LINUX -> unsupported */
+ -1, /* WTAP_ENCAP_ATM_RFC1483 */
+ -1, /* WTAP_ENCAP_LINUX_ATM_CLIP */
+ 7, /* WTAP_ENCAP_LAPB -> Internetwork analyzer (synchronous) */
+ -1, /* WTAP_ENCAP_ATM_PDUS */
+ -1, /* WTAP_ENCAP_NULL -> unsupported */
+ -1, /* WTAP_ENCAP_ASCEND -> unsupported */
+ -1, /* WTAP_ENCAP_ISDN -> unsupported */
+ -1, /* WTAP_ENCAP_IP_OVER_FC -> unsupported */
+ 7, /* WTAP_ENCAP_PPP_WITH_PHDR -> Internetwork analyzer (synchronous) FIXME ! */
+};
+#define NUM_WTAP_ENCAPS (sizeof wtap_encap / sizeof wtap_encap[0])
+
+/* Returns 0 if we could write the specified encapsulation type,
+ an error indication otherwise. */
+static int
+ngsniffer_dump_can_write_encap(int encap)
+{
+ /* Per-packet encapsulations aren't supported. */
+ if (encap == WTAP_ENCAP_PER_PACKET)
+ return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
+
+ if (encap < 0 || (unsigned)encap >= NUM_WTAP_ENCAPS || wtap_encap[encap] == -1)
+ return WTAP_ERR_UNWRITABLE_ENCAP;
+
+ return 0;
+}
+
+/* Returns TRUE on success, FALSE on failure; sets "*err" to an error code on
+ failure */
+static gboolean
+ngsniffer_dump_open(wtap_dumper *wdh, int *err, gchar **err_info _U_)
+{
+ ngsniffer_dump_t *ngsniffer;
+ char buf[6] = {REC_VERS, 0x00, 0x12, 0x00, 0x00, 0x00}; /* version record */
+
+ /* This is a sniffer file */
+ wdh->subtype_write = ngsniffer_dump;
+ wdh->subtype_finish = ngsniffer_dump_finish;
+
+ ngsniffer = g_new(ngsniffer_dump_t, 1);
+ wdh->priv = (void *)ngsniffer;
+ ngsniffer->first_frame = TRUE;
+ ngsniffer->start = 0;
+
+ /* Write the file header. */
+ if (!wtap_dump_file_write(wdh, ngsniffer_magic, sizeof ngsniffer_magic,
+ err))
+ return FALSE;
+ if (!wtap_dump_file_write(wdh, buf, 6, err))
+ return FALSE;
+
+ return TRUE;
+}
+
+/* Write a record for a packet to a dump file.
+ Returns TRUE on success, FALSE on failure. */
+static gboolean
+ngsniffer_dump(wtap_dumper *wdh, const wtap_rec *rec,
+ const guint8 *pd, int *err, gchar **err_info _U_)
+{
+ const union wtap_pseudo_header *pseudo_header = &rec->rec_header.packet_header.pseudo_header;
+ ngsniffer_dump_t *ngsniffer = (ngsniffer_dump_t *)wdh->priv;
+ struct frame2_rec rec_hdr;
+ char buf[6];
+ time_t tsecs;
+ guint64 t;
+ guint16 t_low, t_med;
+ guint8 t_high;
+ struct vers_rec version;
+ gint16 maj_vers, min_vers;
+ guint16 start_date;
+ struct tm *tm;
+
+ /* We can only write packet records. */
+ if (rec->rec_type != REC_TYPE_PACKET) {
+ *err = WTAP_ERR_UNWRITABLE_REC_TYPE;
+ return FALSE;
+ }
+
+ /*
+ * Make sure this packet doesn't have a link-layer type that
+ * differs from the one for the file.
+ */
+ if (wdh->file_encap != rec->rec_header.packet_header.pkt_encap) {
+ *err = WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
+ return FALSE;
+ }
+
+ /* The captured length field is 16 bits, so there's a hard
+ limit of 65535. */
+ if (rec->rec_header.packet_header.caplen > 65535) {
+ *err = WTAP_ERR_PACKET_TOO_LARGE;
+ return FALSE;
+ }
+
+ /* Sniffer files have a capture start date in the file header, and
+ have times relative to the beginning of that day in the packet
+ headers; pick the date of the first packet as the capture start
+ date. */
+ if (ngsniffer->first_frame) {
+ ngsniffer->first_frame=FALSE;
+ tm = localtime(&rec->ts.secs);
+ if (tm != NULL && tm->tm_year >= DOS_YEAR_OFFSET) {
+ start_date = (tm->tm_year - DOS_YEAR_OFFSET) << DOS_YEAR_SHIFT;
+ start_date |= (tm->tm_mon - DOS_MONTH_OFFSET) << DOS_MONTH_SHIFT;
+ start_date |= tm->tm_mday << DOS_DAY_SHIFT;
+ /* record the start date, not the start time */
+ ngsniffer->start = rec->ts.secs - (3600*tm->tm_hour + 60*tm->tm_min + tm->tm_sec);
+ } else {
+ start_date = 0;
+ ngsniffer->start = 0;
+ }
+
+ /* "sniffer" version ? */
+ maj_vers = 4;
+ min_vers = 0;
+ version.maj_vers = GUINT16_TO_LE(maj_vers);
+ version.min_vers = GUINT16_TO_LE(min_vers);
+ version.time_dos = 0;
+ version.date = GUINT16_TO_LE(start_date);
+ version.type = 4;
+ version.network = wtap_encap[wdh->file_encap];
+ version.format = 1;
+ version.timeunit = 1; /* 0.838096 */
+ version.cmprs_vers = 0;
+ version.cmprs_level = 0;
+ version.rsvd[0] = 0;
+ version.rsvd[1] = 0;
+ if (!wtap_dump_file_write(wdh, &version, sizeof version, err))
+ return FALSE;
+ }
+
+ buf[0] = REC_FRAME2;
+ buf[1] = 0x00;
+ buf[2] = (char)((rec->rec_header.packet_header.caplen + sizeof(struct frame2_rec))%256);
+ buf[3] = (char)((rec->rec_header.packet_header.caplen + sizeof(struct frame2_rec))/256);
+ buf[4] = 0x00;
+ buf[5] = 0x00;
+ if (!wtap_dump_file_write(wdh, buf, 6, err))
+ return FALSE;
+ /* Seconds since the start of the capture */
+ tsecs = rec->ts.secs - ngsniffer->start;
+ /* Extract the number of days since the start of the capture */
+ rec_hdr.time_day = (guint8)(tsecs / 86400); /* # days of capture - 86400 secs/day */
+ tsecs -= rec_hdr.time_day * 86400; /* time within day */
+ /* Convert to picoseconds */
+ t = tsecs*G_GUINT64_CONSTANT(1000000000000) +
+ rec->ts.nsecs*G_GUINT64_CONSTANT(1000);
+ /* Convert to units of timeunit = 1 */
+ t /= Psec[1];
+ t_low = (guint16)((t >> 0) & 0xFFFF);
+ t_med = (guint16)((t >> 16) & 0xFFFF);
+ t_high = (guint8)((t >> 32) & 0xFF);
+ rec_hdr.time_low = GUINT16_TO_LE(t_low);
+ rec_hdr.time_med = GUINT16_TO_LE(t_med);
+ rec_hdr.time_high = t_high;
+ rec_hdr.size = GUINT16_TO_LE(rec->rec_header.packet_header.caplen);
+ switch (wdh->file_encap) {
+
+ case WTAP_ENCAP_LAPB:
+ case WTAP_ENCAP_FRELAY_WITH_PHDR:
+ rec_hdr.fs = (pseudo_header->dte_dce.flags & FROM_DCE) ? 0x00 : FS_WAN_DTE;
+ break;
+
+ case WTAP_ENCAP_PPP_WITH_PHDR:
+ case WTAP_ENCAP_SDLC:
+ rec_hdr.fs = pseudo_header->p2p.sent ? 0x00 : FS_WAN_DTE;
+ break;
+
+ case WTAP_ENCAP_ISDN:
+ rec_hdr.fs = pseudo_header->isdn.uton ? FS_WAN_DTE : 0x00;
+ switch (pseudo_header->isdn.channel) {
+
+ case 0: /* D-channel */
+ rec_hdr.fs |= FS_ISDN_CHAN_D;
+ break;
+
+ case 1: /* B1-channel */
+ rec_hdr.fs |= FS_ISDN_CHAN_B1;
+ break;
+
+ case 2: /* B2-channel */
+ rec_hdr.fs |= FS_ISDN_CHAN_B2;
+ break;
+ }
+ break;
+
+ default:
+ rec_hdr.fs = 0;
+ break;
+ }
+ rec_hdr.flags = 0;
+ rec_hdr.true_size = rec->rec_header.packet_header.len != rec->rec_header.packet_header.caplen ? GUINT16_TO_LE(rec->rec_header.packet_header.len) : 0;
+ rec_hdr.rsvd = 0;
+ if (!wtap_dump_file_write(wdh, &rec_hdr, sizeof rec_hdr, err))
+ return FALSE;
+ if (!wtap_dump_file_write(wdh, pd, rec->rec_header.packet_header.caplen, err))
+ return FALSE;
+ return TRUE;
+}
+
+/* Finish writing to a dump file.
+ Returns TRUE on success, FALSE on failure. */
+static gboolean
+ngsniffer_dump_finish(wtap_dumper *wdh, int *err, gchar **err_info _U_)
+{
+ /* EOF record */
+ char buf[6] = {REC_EOF, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+ if (!wtap_dump_file_write(wdh, buf, 6, err))
+ return FALSE;
+ return TRUE;
+}
+
+/*
+ SnifferDecompress() decompresses a blob of compressed data from a
+ Sniffer(R) capture file.
+
+ This function is Copyright (c) 1999-2999 Tim Farley
+
+ Parameters
+ inbuf - buffer of compressed bytes from file, not including
+ the preceding length word
+ inlen - length of inbuf in bytes (max 64k)
+ outbuf - decompressed contents, could contain a partial Sniffer
+ record at the end.
+ outlen - length of outbuf.
+ err - return error code here
+ err_info - for WTAP_ERR_DECOMPRESS, return descriptive string here
+
+ Return value is the number of bytes in outbuf on return.
+*/
+
+/*
+ * Make sure we have at least "length" bytes remaining
+ * in the input buffer.
+ */
+#define CHECK_INPUT_POINTER( length ) \
+ if ( pin + (length - 1) >= pin_end ) \
+ { \
+ *err = WTAP_ERR_DECOMPRESS; \
+ *err_info = g_strdup("ngsniffer: Compressed data item goes past the end of the compressed block"); \
+ return ( -1 ); \
+ }
+
+/*
+ * Make sure the byte containing the high order part of a buffer
+ * offset is present.
+ *
+ * If it is, then fetch it and combine it with the low-order part.
+ */
+#define FETCH_OFFSET_HIGH \
+ CHECK_INPUT_POINTER( 1 ); \
+ offset = code_low + ((unsigned int)(*pin++) << 4) + 3;
+
+/*
+ * Make sure the output buffer is big enough to get "length"
+ * bytes added to it.
+ */
+#define CHECK_OUTPUT_LENGTH( length ) \
+ if ( pout + length > pout_end ) \
+ { \
+ *err = WTAP_ERR_UNC_OVERFLOW; \
+ return ( -1 ); \
+ }
+
+/*
+ * Make sure we have another byte to fetch, and then fetch it and
+ * append it to the buffer "length" times.
+ */
+#define APPEND_RLE_BYTE( length ) \
+ /* If length would put us past end of output, avoid overflow */ \
+ CHECK_OUTPUT_LENGTH( length ); \
+ CHECK_INPUT_POINTER( 1 ); \
+ memset( pout, *pin++, length ); \
+ pout += length;
+
+/*
+ * Make sure the specified offset and length refer, in the output
+ * buffer, to data that's entirely within the part of the output
+ * buffer that we've already filled in.
+ *
+ * Then append the string from the specified offset, with the
+ * specified length, to the output buffer.
+ */
+#define APPEND_LZW_STRING( offset, length ) \
+ /* If length would put us past end of output, avoid overflow */ \
+ CHECK_OUTPUT_LENGTH( length ); \
+ /* Check if offset would put us back past begin of buffer */ \
+ if ( pout - offset < outbuf ) \
+ { \
+ *err = WTAP_ERR_DECOMPRESS; \
+ *err_info = g_strdup("ngsniffer: LZ77 compressed data has bad offset to string"); \
+ return ( -1 ); \
+ } \
+ /* Check if offset would cause us to copy on top of ourselves */ \
+ if ( pout - offset + length > pout ) \
+ { \
+ *err = WTAP_ERR_DECOMPRESS; \
+ *err_info = g_strdup("ngsniffer: LZ77 compressed data has bad offset to string"); \
+ return ( -1 ); \
+ } \
+ /* Copy the string from previous text to output position, \
+ advance output pointer */ \
+ memcpy( pout, pout - offset, length ); \
+ pout += length;
+
+static int
+SnifferDecompress(unsigned char *inbuf, size_t inlen, unsigned char *outbuf,
+ size_t outlen, int *err, gchar **err_info)
+{
+ unsigned char * pin = inbuf;
+ unsigned char * pout = outbuf;
+ unsigned char * pin_end = pin + inlen;
+ unsigned char * pout_end = pout + outlen;
+ unsigned int bit_mask; /* one bit is set in this, to mask with bit_value */
+ unsigned int bit_value = 0; /* cache the last 16 coding bits we retrieved */
+ unsigned int code_type; /* encoding type, from high 4 bits of byte */
+ unsigned int code_low; /* other 4 bits from encoding byte */
+ int length; /* length of RLE sequence or repeated string */
+ int offset; /* offset of string to repeat */
+
+ if (inlen > G_MAXUINT16) {
+ return ( -1 );
+ }
+
+ bit_mask = 0; /* don't have any bits yet */
+ /* Process until we've consumed all the input */
+ while (pin < pin_end)
+ {
+ /* Shift down the bit mask we use to see what's encoded */
+ bit_mask = bit_mask >> 1;
+
+ /* If there are no bits left, time to get another 16 bits */
+ if ( 0 == bit_mask )
+ {
+ /* make sure there are at least *three* bytes
+ available - the two bytes of the bit value,
+ plus one byte after it */
+ CHECK_INPUT_POINTER( 3 );
+ bit_mask = 0x8000; /* start with the high bit */
+ bit_value = pletoh16(pin); /* get the next 16 bits */
+ pin += 2; /* skip over what we just grabbed */
+ }
+
+ /* Use the bits in bit_value to see what's encoded and what is raw data */
+ if ( !(bit_mask & bit_value) )
+ {
+ /* bit not set - raw byte we just copy */
+
+ /* If length would put us past end of output, avoid overflow */
+ CHECK_OUTPUT_LENGTH( 1 );
+ *(pout++) = *(pin++);
+ }
+ else
+ {
+ /* bit set - next item is encoded. Peel off high nybble
+ of next byte to see the encoding type. Set aside low
+ nybble while we are at it */
+ code_type = (unsigned int) ((*pin) >> 4 ) & 0xF;
+ code_low = (unsigned int) ((*pin) & 0xF );
+ pin++; /* increment over the code byte we just retrieved */
+
+ /* Based on the code type, decode the compressed string */
+ switch ( code_type )
+ {
+ case 0 : /* RLE short runs */
+ /*
+ Run length is the low nybble of the first code byte.
+ Byte to repeat immediately follows.
+ Total code size: 2 bytes.
+ */
+ length = code_low + 3;
+
+ /* check the length and then, if it's OK,
+ generate the repeated series of bytes */
+ APPEND_RLE_BYTE( length );
+ break;
+ case 1 : /* RLE long runs */
+ /*
+ Low 4 bits of run length is the low nybble of the
+ first code byte, upper 8 bits of run length is in
+ the next byte.
+ Byte to repeat immediately follows.
+ Total code size: 3 bytes.
+ */
+ CHECK_INPUT_POINTER( 1 );
+ length = code_low + ((unsigned int)(*pin++) << 4) + 19;
+
+ /* check the length and then, if it's OK,
+ generate the repeated series of bytes */
+ APPEND_RLE_BYTE( length );
+ break;
+ case 2 : /* LZ77 long strings */
+ /*
+ Low 4 bits of offset to string is the low nybble of the
+ first code byte, upper 8 bits of offset is in
+ the next byte.
+ Length of string immediately follows.
+ Total code size: 3 bytes.
+ */
+ FETCH_OFFSET_HIGH;
+
+ /* get length from next byte, make sure it won't overrun buf */
+ CHECK_INPUT_POINTER( 1 );
+ length = (unsigned int)(*pin++) + 16;
+
+ /* check the offset and length and then, if
+ they're OK, copy the data */
+ APPEND_LZW_STRING( offset, length );
+ break;
+ default : /* (3 to 15): LZ77 short strings */
+ /*
+ Low 4 bits of offset to string is the low nybble of the
+ first code byte, upper 8 bits of offset is in
+ the next byte.
+ Length of string to repeat is overloaded into code_type.
+ Total code size: 2 bytes.
+ */
+ FETCH_OFFSET_HIGH;
+
+ /* get length from code_type */
+ length = code_type;
+
+ /* check the offset and length and then, if
+ they're OK, copy the data */
+ APPEND_LZW_STRING( offset, length );
+ break;
+ }
+ }
+ }
+
+ return (int) ( pout - outbuf ); /* return length of expanded text */
+}
+
+/*
+ * XXX - is there any guarantee that 65535 bytes is big enough to hold the
+ * uncompressed data from any blob?
+ */
+#define OUTBUF_SIZE 65536
+#define INBUF_SIZE 65536
+
+/* Information about a compressed blob; we save the offset in the
+ underlying compressed file, and the offset in the uncompressed data
+ stream, of the blob. */
+typedef struct {
+ gint64 blob_comp_offset;
+ gint64 blob_uncomp_offset;
+} blob_info_t;
+
+static gboolean
+ng_read_bytes_or_eof(wtap *wth, void *buffer, unsigned int nbytes, gboolean is_random,
+ int *err, gchar **err_info)
+{
+ ngsniffer_t *ngsniffer;
+ FILE_T infile;
+ ngsniffer_comp_stream_t *comp_stream;
+ unsigned char *outbuffer = (unsigned char *)buffer; /* where to write next decompressed data */
+ blob_info_t *blob;
+ unsigned int bytes_to_copy;
+ unsigned int bytes_left;
+
+ ngsniffer = (ngsniffer_t *)wth->priv;
+ if (is_random) {
+ infile = wth->random_fh;
+ comp_stream = &ngsniffer->rand;
+ } else {
+ infile = wth->fh;
+ comp_stream = &ngsniffer->seq;
+ }
+
+ if (!ngsniffer->is_compressed) {
+ /* Uncompressed - just read bytes */
+ if (!wtap_read_bytes_or_eof(infile, buffer, nbytes, err, err_info))
+ return FALSE;
+ comp_stream->uncomp_offset += nbytes;
+ comp_stream->comp_offset += nbytes;
+ return TRUE;
+ }
+
+ /*
+ * Compressed.
+ *
+ * Allocate the stream buffer if it hasn't already been allocated.
+ */
+ if (comp_stream->buf == NULL) {
+ comp_stream->buf = (unsigned char *)g_malloc(OUTBUF_SIZE);
+
+ if (is_random) {
+ /* This is the first read of the random file, so we're at
+ the beginning of the sequence of blobs in the file
+ (as we've not done any random reads yet to move the
+ current position in the random stream); set the
+ current blob to be the first blob. */
+ ngsniffer->current_blob = ngsniffer->first_blob;
+ } else {
+ /* This is the first sequential read; if we also have a
+ random stream open, allocate the first element for the
+ list of blobs, and make it the last element as well. */
+ if (wth->random_fh != NULL) {
+ ws_assert(ngsniffer->first_blob == NULL);
+ blob = g_new(blob_info_t,1);
+ blob->blob_comp_offset = comp_stream->comp_offset;
+ blob->blob_uncomp_offset = comp_stream->uncomp_offset;
+ ngsniffer->first_blob = g_list_append(ngsniffer->first_blob,
+ blob);
+ ngsniffer->last_blob = ngsniffer->first_blob;
+ }
+ }
+
+ /* Now read the first blob into the buffer. */
+ if (!read_blob(infile, comp_stream, err, err_info))
+ return FALSE;
+ }
+ while (nbytes > 0) {
+ bytes_left = comp_stream->nbytes - comp_stream->nextout;
+ if (bytes_left == 0) {
+ /* There's no decompressed stuff left to copy from the current
+ blob; get the next blob. */
+
+ if (is_random) {
+ /* Move to the next blob in the list. */
+ ngsniffer->current_blob = g_list_next(ngsniffer->current_blob);
+ if (!ngsniffer->current_blob) {
+ /*
+ * XXX - this "can't happen"; we should have a
+ * blob for every byte in the file.
+ */
+ *err = WTAP_ERR_CANT_SEEK;
+ return FALSE;
+ }
+ } else {
+ /* If we also have a random stream open, add a new element,
+ for this blob, to the list of blobs; we know the list is
+ non-empty, as we initialized it on the first sequential
+ read, so we just add the new element at the end, and
+ adjust the pointer to the last element to refer to it. */
+ if (wth->random_fh != NULL) {
+ blob = g_new(blob_info_t,1);
+ blob->blob_comp_offset = comp_stream->comp_offset;
+ blob->blob_uncomp_offset = comp_stream->uncomp_offset;
+ ngsniffer->last_blob = g_list_append(ngsniffer->last_blob,
+ blob);
+ }
+ }
+
+ if (!read_blob(infile, comp_stream, err, err_info))
+ return FALSE;
+ bytes_left = comp_stream->nbytes - comp_stream->nextout;
+ }
+
+ bytes_to_copy = nbytes;
+ if (bytes_to_copy > bytes_left)
+ bytes_to_copy = bytes_left;
+ memcpy(outbuffer, &comp_stream->buf[comp_stream->nextout],
+ bytes_to_copy);
+ nbytes -= bytes_to_copy;
+ outbuffer += bytes_to_copy;
+ comp_stream->nextout += bytes_to_copy;
+ comp_stream->uncomp_offset += bytes_to_copy;
+ }
+ return TRUE;
+}
+
+static gboolean
+ng_read_bytes(wtap *wth, void *buffer, unsigned int nbytes, gboolean is_random,
+ int *err, gchar **err_info)
+{
+ if (!ng_read_bytes_or_eof(wth, buffer, nbytes, is_random, err, err_info)) {
+ /*
+ * In this case, even reading zero bytes, because we're at
+ * the end of the file, is a short read.
+ */
+ if (*err == 0)
+ *err = WTAP_ERR_SHORT_READ;
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/* Read a blob from a compressed stream.
+ Return FALSE and set "*err" and "*err_info" on error, otherwise return TRUE. */
+static gboolean
+read_blob(FILE_T infile, ngsniffer_comp_stream_t *comp_stream, int *err,
+ gchar **err_info)
+{
+ int in_len;
+ unsigned short blob_len;
+ gint16 blob_len_host;
+ gboolean uncompressed;
+ unsigned char *file_inbuf;
+ int out_len;
+
+ /* Read one 16-bit word which is length of next compressed blob */
+ if (!wtap_read_bytes_or_eof(infile, &blob_len, 2, err, err_info))
+ return FALSE;
+ comp_stream->comp_offset += 2;
+ blob_len_host = pletoh16(&blob_len);
+
+ /* Compressed or uncompressed? */
+ if (blob_len_host < 0) {
+ /* Uncompressed blob; blob length is absolute value of the number. */
+ in_len = -blob_len_host;
+ uncompressed = TRUE;
+ } else {
+ in_len = blob_len_host;
+ uncompressed = FALSE;
+ }
+
+ file_inbuf = (unsigned char *)g_malloc(INBUF_SIZE);
+
+ /* Read the blob */
+ if (!wtap_read_bytes(infile, file_inbuf, in_len, err, err_info)) {
+ g_free(file_inbuf);
+ return FALSE;
+ }
+ comp_stream->comp_offset += in_len;
+
+ if (uncompressed) {
+ memcpy(comp_stream->buf, file_inbuf, in_len);
+ out_len = in_len;
+ } else {
+ /* Decompress the blob */
+ out_len = SnifferDecompress(file_inbuf, in_len,
+ comp_stream->buf, OUTBUF_SIZE, err,
+ err_info);
+ if (out_len < 0) {
+ g_free(file_inbuf);
+ return FALSE;
+ }
+ }
+
+ g_free(file_inbuf);
+ comp_stream->nextout = 0;
+ comp_stream->nbytes = out_len;
+ return TRUE;
+}
+
+/* Skip some number of bytes forward in the sequential stream. */
+static gboolean
+ng_skip_bytes_seq(wtap *wth, unsigned int count, int *err, gchar **err_info)
+{
+ ngsniffer_t *ngsniffer;
+ char *buf;
+ unsigned int amount_to_read;
+
+ ngsniffer = (ngsniffer_t *)wth->priv;
+
+ if (!ngsniffer->is_compressed) {
+ /* Uncompressed - just read forward and discard data */
+ ngsniffer->seq.uncomp_offset += count;
+ return wtap_read_bytes(wth->fh, NULL, count, err, err_info);
+ }
+
+ /*
+ * Compressed.
+ *
+ * Now read and discard "count" bytes.
+ */
+ buf = (char *)g_malloc(INBUF_SIZE);
+ while (count != 0) {
+ if (count > INBUF_SIZE)
+ amount_to_read = INBUF_SIZE;
+ else
+ amount_to_read = count;
+
+ if (!ng_read_bytes(wth, buf, amount_to_read, FALSE, err, err_info)) {
+ g_free(buf);
+ return FALSE; /* error */
+ }
+
+ count -= amount_to_read;
+ }
+
+ g_free(buf);
+ return TRUE;
+}
+
+/* Seek to a given offset in the random data stream.
+
+ On compressed files, we see whether we're seeking to a position within
+ the blob we currently have in memory and, if not, we find in the list
+ of blobs the last blob that starts at or before the position to which
+ we're seeking, and read that blob in. We can then move to the appropriate
+ position within the blob we have in memory (whether it's the blob we
+ already had in memory or, if necessary, the one we read in). */
+static gboolean
+ng_file_seek_rand(wtap *wth, gint64 offset, int *err, gchar **err_info)
+{
+ ngsniffer_t *ngsniffer;
+ gint64 delta;
+ GList *new_list, *next_list;
+ blob_info_t *next_blob, *new_blob;
+
+ ngsniffer = (ngsniffer_t *)wth->priv;
+
+ if (!ngsniffer->is_compressed) {
+ /* Uncompressed - just seek. */
+ if (file_seek(wth->random_fh, offset, SEEK_SET, err) == -1)
+ return FALSE;
+ return TRUE;
+ }
+
+ /*
+ * Compressed.
+ *
+ * How many *uncompressed* should we move forward or
+ * backward?
+ */
+ delta = offset - ngsniffer->rand.uncomp_offset;
+
+ /* Is the place to which we're seeking within the current buffer, or
+ will we have to read a different blob into the buffer? */
+ new_list = NULL;
+ if (delta > 0) {
+ /* We're going forwards.
+ Is the place to which we're seeking within the current buffer? */
+ if ((size_t)(ngsniffer->rand.nextout + delta) >= ngsniffer->rand.nbytes) {
+ /* No. Search for a blob that contains the target
+ offset in the uncompressed byte stream. */
+ if (ngsniffer->current_blob == NULL) {
+ /* We haven't read anything from the random
+ file yet, so we have no current blob;
+ search all the blobs, starting with
+ the first one. */
+ new_list = ngsniffer->first_blob;
+ } else {
+ /* We're seeking forward, so start searching
+ with the blob after the current one. */
+ new_list = g_list_next(ngsniffer->current_blob);
+ }
+ while (new_list) {
+ next_list = g_list_next(new_list);
+ if (next_list == NULL) {
+ /* No more blobs; the current one is it. */
+ break;
+ }
+
+ next_blob = (blob_info_t *)next_list->data;
+ /* Does the next blob start after the target offset?
+ If so, the current blob is the one we want. */
+ if (next_blob->blob_uncomp_offset > offset)
+ break;
+
+ new_list = next_list;
+ }
+ if (new_list == NULL) {
+ /*
+ * We're seeking past the end of what
+ * we've read so far.
+ */
+ *err = WTAP_ERR_CANT_SEEK;
+ return FALSE;
+ }
+ }
+ } else if (delta < 0) {
+ /* We're going backwards.
+ Is the place to which we're seeking within the current buffer? */
+ if (ngsniffer->rand.nextout + delta < 0) {
+ /* No. Search for a blob that contains the target
+ offset in the uncompressed byte stream. */
+ if (ngsniffer->current_blob == NULL) {
+ /* We haven't read anything from the random
+ file yet, so we have no current blob;
+ search all the blobs, starting with
+ the last one. */
+ new_list = ngsniffer->last_blob;
+ } else {
+ /* We're seeking backward, so start searching
+ with the blob before the current one. */
+ new_list = g_list_previous(ngsniffer->current_blob);
+ }
+ while (new_list) {
+ /* Does this blob start at or before the target offset?
+ If so, the current blob is the one we want. */
+ new_blob = (blob_info_t *)new_list->data;
+ if (new_blob->blob_uncomp_offset <= offset)
+ break;
+
+ /* It doesn't - skip to the previous blob. */
+ new_list = g_list_previous(new_list);
+ }
+ if (new_list == NULL) {
+ /*
+ * XXX - shouldn't happen.
+ */
+ *err = WTAP_ERR_CANT_SEEK;
+ return FALSE;
+ }
+ }
+ }
+
+ if (new_list != NULL) {
+ /* The place to which we're seeking isn't in the current buffer;
+ move to a new blob. */
+ new_blob = (blob_info_t *)new_list->data;
+
+ /* Seek in the compressed file to the offset in the compressed file
+ of the beginning of that blob. */
+ if (file_seek(wth->random_fh, new_blob->blob_comp_offset, SEEK_SET, err) == -1)
+ return FALSE;
+
+ /*
+ * Do we have a buffer for the random stream yet?
+ */
+ if (ngsniffer->rand.buf == NULL) {
+ /*
+ * No - allocate it, as we'll be reading into it.
+ */
+ ngsniffer->rand.buf = (unsigned char *)g_malloc(OUTBUF_SIZE);
+ }
+
+ /* Make the blob we found the current one. */
+ ngsniffer->current_blob = new_list;
+
+ /* Now set the current offsets to the offsets of the beginning
+ of the blob. */
+ ngsniffer->rand.uncomp_offset = new_blob->blob_uncomp_offset;
+ ngsniffer->rand.comp_offset = new_blob->blob_comp_offset;
+
+ /* Now fill the buffer. */
+ if (!read_blob(wth->random_fh, &ngsniffer->rand, err, err_info))
+ return FALSE;
+
+ /* Set "delta" to the amount to move within this blob; it had
+ better be >= 0, and < the amount of uncompressed data in
+ the blob, as otherwise it'd mean we need to seek before
+ the beginning or after the end of this blob. */
+ delta = offset - ngsniffer->rand.uncomp_offset;
+ ws_assert(delta >= 0 && (unsigned long)delta < ngsniffer->rand.nbytes);
+ }
+
+ /* OK, the place to which we're seeking is in the buffer; adjust
+ "ngsniffer->rand.nextout" to point to the place to which
+ we're seeking, and adjust "ngsniffer->rand.uncomp_offset" to be
+ the destination offset. */
+ ngsniffer->rand.nextout += (int) delta;
+ ngsniffer->rand.uncomp_offset += delta;
+
+ return TRUE;
+}
+
+static const struct supported_block_type ngsniffer_uncompressed_blocks_supported[] = {
+ /*
+ * We support packet blocks, with no comments or other options.
+ */
+ { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
+};
+
+static const struct file_type_subtype_info ngsniffer_uncompressed_info = {
+ "Sniffer (DOS)", "ngsniffer", "cap", "enc;trc;fdc;syc",
+ FALSE, BLOCKS_SUPPORTED(ngsniffer_uncompressed_blocks_supported),
+ ngsniffer_dump_can_write_encap, ngsniffer_dump_open, NULL
+};
+
+static const struct supported_block_type ngsniffer_compressed_blocks_supported[] = {
+ /*
+ * We support packet blocks, with no comments or other options.
+ */
+ { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
+};
+
+static const struct file_type_subtype_info ngsniffer_compressed_info = {
+ "Sniffer (DOS), compressed", "ngsniffer_comp", "cap", "enc;trc;fdc;syc",
+ FALSE, BLOCKS_SUPPORTED(ngsniffer_compressed_blocks_supported),
+ NULL, NULL, NULL
+};
+
+void register_ngsniffer(void)
+{
+ ngsniffer_uncompressed_file_type_subtype = wtap_register_file_type_subtype(&ngsniffer_uncompressed_info);
+ ngsniffer_compressed_file_type_subtype = wtap_register_file_type_subtype(&ngsniffer_compressed_info);
+
+ /*
+ * Register names for backwards compatibility with the
+ * wtap_filetypes table in Lua.
+ */
+ wtap_register_backwards_compatibility_lua_name("NGSNIFFER_UNCOMPRESSED",
+ ngsniffer_uncompressed_file_type_subtype);
+ wtap_register_backwards_compatibility_lua_name("NGSNIFFER_COMPRESSED",
+ ngsniffer_compressed_file_type_subtype);
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/wiretap/ngsniffer.h b/wiretap/ngsniffer.h
new file mode 100644
index 00000000..0a391f85
--- /dev/null
+++ b/wiretap/ngsniffer.h
@@ -0,0 +1,17 @@
+/** @file
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __NGSNIFFER_H__
+#define __NGSNIFFER_H__
+
+#include <glib.h>
+#include "wtap.h"
+
+wtap_open_return_val ngsniffer_open(wtap *wth, int *err, gchar **err_info);
+
+#endif
diff --git a/wiretap/observer.c b/wiretap/observer.c
new file mode 100644
index 00000000..6a745c14
--- /dev/null
+++ b/wiretap/observer.c
@@ -0,0 +1,957 @@
+/***************************************************************************
+ observer.c - description
+ -------------------
+ begin : Wed Oct 29 2003
+ copyright : (C) 2003 by root
+ email : scotte[AT}netinst.com
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * SPDX-License-Identifier: GPL-2.0-or-later *
+ * *
+ ***************************************************************************/
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include "wtap-int.h"
+#include "file_wrappers.h"
+#include "observer.h"
+#include <wsutil/802_11-utils.h>
+
+static const char observer_magic[] = {"ObserverPktBufferVersion=15.00"};
+static const int true_magic_length = 17;
+
+static const guint32 observer_packet_magic = 0x88888888;
+
+/*
+ * This structure is used to keep state when writing files. An instance is
+ * allocated for each file, and its address is stored in the wtap_dumper.priv
+ * pointer field.
+ */
+typedef struct {
+ guint64 packet_count;
+ guint8 network_type;
+ guint32 time_format;
+} observer_dump_private_state;
+
+/*
+ * Some time offsets are calculated in advance here, when the first Observer
+ * file is opened for reading or writing, and are then used to adjust frame
+ * timestamps as they are read or written.
+ *
+ * The Wiretap API expects timestamps in nanoseconds relative to
+ * January 1, 1970, 00:00:00 GMT (the Wiretap epoch).
+ *
+ * Observer versions before 13.10 encode frame timestamps in nanoseconds
+ * relative to January 1, 2000, 00:00:00 local time (the Observer epoch).
+ * Versions 13.10 and later switch over to GMT encoding. Which encoding was used
+ * when saving the file is identified via the time format TLV following
+ * the file header.
+ *
+ * Unfortunately, even though Observer versions before 13.10 saved in local
+ * time, they didn't include the timezone from which the frames were captured,
+ * so converting to GMT correctly from all timezones is impossible. So an
+ * assumption is made that the file is being read from within the same timezone
+ * that it was written.
+ *
+ * All code herein is normalized to versions 13.10 and later, special casing for
+ * versions earlier. In other words, timestamps are worked with as if
+ * they are GMT-encoded, and adjustments from local time are made only if
+ * the source file warrants it.
+ *
+ * All destination files are saved in GMT format.
+ */
+static const time_t ansi_to_observer_epoch_offset = 946684800;
+static time_t gmt_to_localtime_offset = (time_t) -1;
+
+static const char *init_gmt_to_localtime_offset(void)
+{
+ if (gmt_to_localtime_offset == (time_t) -1) {
+ time_t ansi_epoch_plus_one_day = 86400;
+ struct tm *tm;
+ struct tm gmt_tm;
+ struct tm local_tm;
+
+ /*
+ * Compute the local time zone offset as the number of seconds west
+ * of GMT. There's no obvious cross-platform API for querying this
+ * directly. As a workaround, GMT and local tm structures are populated
+ * relative to the ANSI time_t epoch (plus one day to ensure that
+ * local time stays after 1970/1/1 00:00:00). They are then converted
+ * back to time_t as if they were both local times, resulting in the
+ * time zone offset being the difference between them.
+ */
+ tm = gmtime(&ansi_epoch_plus_one_day);
+ if (tm == NULL)
+ return "gmtime(one day past the Epoch) fails (this \"shouldn't happen\")";
+ gmt_tm = *tm;
+ tm = localtime(&ansi_epoch_plus_one_day);
+ if (tm == NULL)
+ return "localtime(one day past the Epoch) fails (this \"shouldn't happen\")";
+ local_tm = *tm;
+ local_tm.tm_isdst = 0;
+ gmt_to_localtime_offset = mktime(&gmt_tm) - mktime(&local_tm);
+ }
+ return NULL;
+}
+
+static gboolean observer_read(wtap *wth, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info, gint64 *data_offset);
+static gboolean observer_seek_read(wtap *wth, gint64 seek_off,
+ wtap_rec *rec, Buffer *buf, int *err, gchar **err_info);
+static int read_packet_header(wtap *wth, FILE_T fh, union wtap_pseudo_header *pseudo_header,
+ packet_entry_header *packet_header, int *err, gchar **err_info);
+static gboolean process_packet_header(wtap *wth,
+ packet_entry_header *packet_header, wtap_rec *rec, int *err,
+ gchar **err_info);
+static int read_packet_data(FILE_T fh, int offset_to_frame, int current_offset_from_packet_header,
+ Buffer *buf, int length, int *err, char **err_info);
+static gboolean skip_to_next_packet(wtap *wth, int offset_to_next_packet,
+ int current_offset_from_packet_header, int *err, char **err_info);
+static gboolean observer_dump(wtap_dumper *wdh, const wtap_rec *rec,
+ const guint8 *pd, int *err, gchar **err_info);
+static gint observer_to_wtap_encap(int observer_encap);
+static gint wtap_to_observer_encap(int wtap_encap);
+
+static int observer_file_type_subtype = -1;
+
+void register_observer(void);
+
+wtap_open_return_val observer_open(wtap *wth, int *err, gchar **err_info)
+{
+ guint offset;
+ capture_file_header file_header;
+ guint header_offset;
+ guint i;
+ tlv_header tlvh;
+ guint seek_increment;
+ packet_entry_header packet_header;
+ observer_dump_private_state * private_state = NULL;
+ const char *err_str;
+
+ offset = 0;
+
+ /* read in the buffer file header */
+ if (!wtap_read_bytes(wth->fh, &file_header, sizeof file_header,
+ err, err_info)) {
+ if (*err != WTAP_ERR_SHORT_READ)
+ return WTAP_OPEN_ERROR;
+ return WTAP_OPEN_NOT_MINE;
+ }
+ offset += (guint)sizeof file_header;
+ CAPTURE_FILE_HEADER_FROM_LE_IN_PLACE(file_header);
+
+ /* check if version info is present */
+ if (memcmp(file_header.observer_version, observer_magic, true_magic_length)!=0) {
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ /* get the location of the first packet */
+ /* v15 and newer uses high byte offset, in previous versions it will be 0 */
+ header_offset = file_header.offset_to_first_packet + ((guint)(file_header.offset_to_first_packet_high_byte)<<16);
+
+ if (offset > header_offset) {
+ /*
+ * The packet data begins before the file header ends.
+ */
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("Observer: The first packet begins in the middle of the file header");
+ return WTAP_OPEN_ERROR;
+ }
+
+ /* initialize the private state */
+ private_state = g_new(observer_dump_private_state, 1);
+ private_state->time_format = TIME_INFO_LOCAL;
+ wth->priv = (void *) private_state;
+
+ /* process extra information */
+ for (i = 0; i < file_header.number_of_information_elements; i++) {
+ guint tlv_data_length;
+
+ /*
+ * Make sure reading the TLV header won't put us in the middle
+ * of the packet data.
+ */
+ if (offset + (guint)sizeof tlvh > header_offset) {
+ /*
+ * We're at or past the point where the packet data begins,
+ * but we have the IE header to read.
+ */
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("Observer: TLVs run into the first packet data");
+ return WTAP_OPEN_ERROR;
+ }
+
+ /* read the TLV header */
+ if (!wtap_read_bytes(wth->fh, &tlvh, sizeof tlvh, err, err_info))
+ return WTAP_OPEN_ERROR;
+ offset += (guint)sizeof tlvh;
+ TLV_HEADER_FROM_LE_IN_PLACE(tlvh);
+
+ if (tlvh.length < sizeof tlvh) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("Observer: bad record (TLV length %u < %zu)",
+ tlvh.length, sizeof tlvh);
+ return WTAP_OPEN_ERROR;
+ }
+
+ tlv_data_length = tlvh.length - (guint)sizeof tlvh;
+ /*
+ * Make sure reading the TLV data won't put us in the middle
+ * of the packet data.
+ */
+ if (offset + tlv_data_length > header_offset) {
+ /*
+ * We're at or past the point where the packet data begins,
+ * but we have the IE data to read.
+ */
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("Observer: TLVs run into the first packet data");
+ return WTAP_OPEN_ERROR;
+ }
+
+
+ /* process (or skip over) the current TLV */
+ switch (tlvh.type) {
+ case INFORMATION_TYPE_TIME_INFO:
+ if (tlv_data_length != sizeof private_state->time_format) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("Observer: bad record (time information TLV length %u != %zu)",
+ tlvh.length,
+ sizeof tlvh + sizeof private_state->time_format);
+ return WTAP_OPEN_ERROR;
+ }
+ if (!wtap_read_bytes(wth->fh, &private_state->time_format,
+ sizeof private_state->time_format,
+ err, err_info))
+ return WTAP_OPEN_ERROR;
+ private_state->time_format = GUINT32_FROM_LE(private_state->time_format);
+ offset += (guint)sizeof private_state->time_format;
+ break;
+ default:
+ if (tlv_data_length != 0) {
+ if (!wtap_read_bytes(wth->fh, NULL, tlv_data_length, err, err_info))
+ return WTAP_OPEN_ERROR;
+ }
+ offset += tlv_data_length;
+ }
+ }
+
+ /* get to the first packet */
+ seek_increment = header_offset - offset;
+ if (seek_increment != 0) {
+ if (!wtap_read_bytes(wth->fh, NULL, seek_increment, err, err_info))
+ return WTAP_OPEN_ERROR;
+ }
+
+ /*
+ * We assume that all packets in a file have the same network type,
+ * whether they're data or expert information packets, and thus
+ * we can attempt to determine the network type by reading the
+ * first packet.
+ *
+ * If that's *not* the case, we need to use WTAP_ENCAP_PER_PACKET.
+ *
+ * Read the packet header. Don't assume there *is* a packet;
+ * if there isn't, report that as a bad file. (If we use
+ * WTAP_ENCAP_PER_PACKET, we don't need to handle that case, as
+ * we don't need to read the first packet.
+ */
+ if (!wtap_read_bytes_or_eof(wth->fh, &packet_header, sizeof packet_header,
+ err, err_info)) {
+ if (*err == 0) {
+ /*
+ * EOF, so there *are* no records.
+ */
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("Observer: No records in the file, so we can't determine the link-layer type");
+ }
+ return WTAP_OPEN_ERROR;
+ }
+ PACKET_ENTRY_HEADER_FROM_LE_IN_PLACE(packet_header);
+
+ /* check the packet's magic number */
+ if (packet_header.packet_magic != observer_packet_magic) {
+ *err = WTAP_ERR_UNSUPPORTED;
+ *err_info = ws_strdup_printf("Observer: unsupported packet version %ul", packet_header.packet_magic);
+ return WTAP_OPEN_ERROR;
+ }
+
+ /* check the data link type */
+ if (observer_to_wtap_encap(packet_header.network_type) == WTAP_ENCAP_UNKNOWN) {
+ *err = WTAP_ERR_UNSUPPORTED;
+ *err_info = ws_strdup_printf("Observer: network type %u unknown or unsupported", packet_header.network_type);
+ return WTAP_OPEN_ERROR;
+ }
+ wth->file_encap = observer_to_wtap_encap(packet_header.network_type);
+
+ /* set up the rest of the capture parameters */
+ private_state->packet_count = 0;
+ private_state->network_type = wtap_to_observer_encap(wth->file_encap);
+ wth->subtype_read = observer_read;
+ wth->subtype_seek_read = observer_seek_read;
+ wth->subtype_close = NULL;
+ wth->subtype_sequential_close = NULL;
+ wth->snapshot_length = 0; /* not available in header */
+ wth->file_tsprec = WTAP_TSPREC_NSEC;
+ wth->file_type_subtype = observer_file_type_subtype;
+
+ /* reset the pointer to the first packet */
+ if (file_seek(wth->fh, header_offset, SEEK_SET, err) == -1)
+ return WTAP_OPEN_ERROR;
+
+ err_str = init_gmt_to_localtime_offset();
+ if (err_str != NULL) {
+ *err = WTAP_ERR_INTERNAL;
+ *err_info = ws_strdup_printf("observer: %s", err_str);
+ return WTAP_OPEN_ERROR;
+ }
+
+ /*
+ * Add an IDB; we don't know how many interfaces were
+ * involved, so we just say one interface, about which
+ * we only know the link-layer type, snapshot length,
+ * and time stamp resolution.
+ */
+ wtap_add_generated_idb(wth);
+
+ return WTAP_OPEN_MINE;
+}
+
+/* Reads the next packet. */
+static gboolean observer_read(wtap *wth, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info, gint64 *data_offset)
+{
+ int header_bytes_consumed;
+ int data_bytes_consumed;
+ packet_entry_header packet_header;
+
+ /* skip records other than data records */
+ for (;;) {
+ *data_offset = file_tell(wth->fh);
+
+ /* process the packet header, including TLVs */
+ header_bytes_consumed = read_packet_header(wth, wth->fh, &rec->rec_header.packet_header.pseudo_header, &packet_header, err,
+ err_info);
+ if (header_bytes_consumed <= 0)
+ return FALSE; /* EOF or error */
+
+ if (packet_header.packet_type == PACKET_TYPE_DATA_PACKET)
+ break;
+
+ /* skip to next packet */
+ if (!skip_to_next_packet(wth, packet_header.offset_to_next_packet,
+ header_bytes_consumed, err, err_info)) {
+ return FALSE; /* EOF or error */
+ }
+ }
+
+ if (!process_packet_header(wth, &packet_header, rec, err, err_info))
+ return FALSE;
+
+ /* read the frame data */
+ data_bytes_consumed = read_packet_data(wth->fh, packet_header.offset_to_frame,
+ header_bytes_consumed, buf, rec->rec_header.packet_header.caplen,
+ err, err_info);
+ if (data_bytes_consumed < 0) {
+ return FALSE;
+ }
+
+ /* skip over any extra bytes following the frame data */
+ if (!skip_to_next_packet(wth, packet_header.offset_to_next_packet,
+ header_bytes_consumed + data_bytes_consumed, err, err_info)) {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/* Reads a packet at an offset. */
+static gboolean observer_seek_read(wtap *wth, gint64 seek_off,
+ wtap_rec *rec, Buffer *buf, int *err, gchar **err_info)
+{
+ union wtap_pseudo_header *pseudo_header = &rec->rec_header.packet_header.pseudo_header;
+ packet_entry_header packet_header;
+ int offset;
+ int data_bytes_consumed;
+
+ if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
+ return FALSE;
+
+ /* process the packet header, including TLVs */
+ offset = read_packet_header(wth, wth->random_fh, pseudo_header, &packet_header, err,
+ err_info);
+ if (offset <= 0)
+ return FALSE; /* EOF or error */
+
+ if (!process_packet_header(wth, &packet_header, rec, err, err_info))
+ return FALSE;
+
+ /* read the frame data */
+ data_bytes_consumed = read_packet_data(wth->random_fh, packet_header.offset_to_frame,
+ offset, buf, rec->rec_header.packet_header.caplen, err, err_info);
+ if (data_bytes_consumed < 0) {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static int
+read_packet_header(wtap *wth, FILE_T fh, union wtap_pseudo_header *pseudo_header,
+ packet_entry_header *packet_header, int *err, gchar **err_info)
+{
+ int offset;
+ guint i;
+ tlv_header tlvh;
+ tlv_wireless_info wireless_header;
+
+ offset = 0;
+
+ /* pull off the packet header */
+ if (!wtap_read_bytes_or_eof(fh, packet_header, sizeof *packet_header,
+ err, err_info)) {
+ if (*err != 0)
+ return -1;
+ return 0; /* EOF */
+ }
+ offset += (int)sizeof *packet_header;
+ PACKET_ENTRY_HEADER_FROM_LE_IN_PLACE(*packet_header);
+
+ /* check the packet's magic number */
+ if (packet_header->packet_magic != observer_packet_magic) {
+
+ /*
+ * Some files are zero-padded at the end. There is no warning of this
+ * in the previous packet header information, such as setting
+ * offset_to_next_packet to zero. So detect this situation by treating
+ * an all-zero header as a sentinel. Return EOF when it is encountered,
+ * rather than treat it as a bad record.
+ */
+ for (i = 0; i < sizeof *packet_header; i++) {
+ if (((guint8*) packet_header)[i] != 0)
+ break;
+ }
+ if (i == sizeof *packet_header) {
+ *err = 0;
+ return 0; /* EOF */
+ }
+
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("Observer: bad record: Invalid magic number 0x%08x",
+ packet_header->packet_magic);
+ return -1;
+ }
+
+ /* initialize the pseudo header */
+ switch (wth->file_encap) {
+ case WTAP_ENCAP_ETHERNET:
+ /* There is no FCS in the frame */
+ pseudo_header->eth.fcs_len = 0;
+ break;
+ case WTAP_ENCAP_IEEE_802_11_WITH_RADIO:
+ memset(&pseudo_header->ieee_802_11, 0, sizeof(pseudo_header->ieee_802_11));
+ pseudo_header->ieee_802_11.fcs_len = 0;
+ pseudo_header->ieee_802_11.decrypted = FALSE;
+ pseudo_header->ieee_802_11.datapad = FALSE;
+ pseudo_header->ieee_802_11.phy = PHDR_802_11_PHY_UNKNOWN;
+ /* Updated below */
+ break;
+ }
+
+ /* process extra information */
+ for (i = 0; i < packet_header->number_of_information_elements; i++) {
+ guint tlv_data_length;
+
+ /* read the TLV header */
+ if (!wtap_read_bytes(fh, &tlvh, sizeof tlvh, err, err_info))
+ return -1;
+ offset += (int)sizeof tlvh;
+ TLV_HEADER_FROM_LE_IN_PLACE(tlvh);
+
+ if (tlvh.length < sizeof tlvh) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("Observer: bad record (TLV length %u < %zu)",
+ tlvh.length, sizeof tlvh);
+ return -1;
+ }
+ tlv_data_length = tlvh.length - (guint)sizeof tlvh;
+
+ /* process (or skip over) the current TLV */
+ switch (tlvh.type) {
+ case INFORMATION_TYPE_WIRELESS:
+ if (tlv_data_length != sizeof wireless_header) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("Observer: bad record (wireless TLV length %u != %zu)",
+ tlvh.length, sizeof tlvh + sizeof wireless_header);
+ return -1;
+ }
+ if (!wtap_read_bytes(fh, &wireless_header, sizeof wireless_header,
+ err, err_info))
+ return -1;
+ /* set decryption status */
+ /* XXX - what other bits are there in conditions? */
+ pseudo_header->ieee_802_11.decrypted = (wireless_header.conditions & WIRELESS_WEP_SUCCESS) != 0;
+ pseudo_header->ieee_802_11.has_channel = TRUE;
+ pseudo_header->ieee_802_11.channel = wireless_header.frequency;
+ pseudo_header->ieee_802_11.has_data_rate = TRUE;
+ pseudo_header->ieee_802_11.data_rate = wireless_header.rate;
+ pseudo_header->ieee_802_11.has_signal_percent = TRUE;
+ pseudo_header->ieee_802_11.signal_percent = wireless_header.strengthPercent;
+
+ /*
+ * We don't know they PHY, but we do have the data rate;
+ * try to guess the PHY based on the data rate and channel.
+ */
+ if (RATE_IS_DSSS(pseudo_header->ieee_802_11.data_rate)) {
+ /* 11b */
+ pseudo_header->ieee_802_11.phy = PHDR_802_11_PHY_11B;
+ pseudo_header->ieee_802_11.phy_info.info_11b.has_short_preamble = FALSE;
+ } else if (RATE_IS_OFDM(pseudo_header->ieee_802_11.data_rate)) {
+ /* 11a or 11g, depending on the band. */
+ if (CHAN_IS_BG(pseudo_header->ieee_802_11.channel)) {
+ /* 11g */
+ pseudo_header->ieee_802_11.phy = PHDR_802_11_PHY_11G;
+ pseudo_header->ieee_802_11.phy_info.info_11g.has_mode = FALSE;
+ } else {
+ /* 11a */
+ pseudo_header->ieee_802_11.phy = PHDR_802_11_PHY_11A;
+ pseudo_header->ieee_802_11.phy_info.info_11a.has_channel_type = FALSE;
+ pseudo_header->ieee_802_11.phy_info.info_11a.has_turbo_type = FALSE;
+ }
+ }
+
+ offset += (int)sizeof wireless_header;
+ break;
+ default:
+ /* skip the TLV data */
+ if (tlv_data_length != 0) {
+ if (!wtap_read_bytes(fh, NULL, tlv_data_length, err, err_info))
+ return -1;
+ }
+ offset += tlv_data_length;
+ }
+ }
+
+ return offset;
+}
+
+static gboolean
+process_packet_header(wtap *wth, packet_entry_header *packet_header,
+ wtap_rec *rec, int *err, gchar **err_info)
+{
+ /* set the wiretap record metadata fields */
+ rec->rec_type = REC_TYPE_PACKET;
+ rec->block = wtap_block_create(WTAP_BLOCK_PACKET);
+ rec->presence_flags = WTAP_HAS_TS|WTAP_HAS_CAP_LEN;
+ rec->rec_header.packet_header.pkt_encap = observer_to_wtap_encap(packet_header->network_type);
+ if(wth->file_encap == WTAP_ENCAP_FIBRE_CHANNEL_FC2_WITH_FRAME_DELIMS) {
+ rec->rec_header.packet_header.len = packet_header->network_size;
+ rec->rec_header.packet_header.caplen = packet_header->captured_size;
+ } else {
+ /*
+ * XXX - what are those 4 bytes?
+ *
+ * The comment in the code said "neglect frame markers for wiretap",
+ * but in the captures I've seen, there's no actual data corresponding
+ * to them that might be a "frame marker".
+ *
+ * Instead, the packets had a network_size 4 bytes larger than the
+ * captured_size; does the network_size include the CRC, even
+ * though it's not included in a capture? If so, most other
+ * network analyzers that have a "network size" and a "captured
+ * size" don't include the CRC in the "network size" if they
+ * don't include the CRC in a full-length captured packet; the
+ * "captured size" is less than the "network size" only if a
+ * user-specified "snapshot length" caused the packet to be
+ * sliced at a particular point.
+ *
+ * That's the model that wiretap and Wireshark/TShark use, so
+ * we implement that model here.
+ */
+ if (packet_header->network_size < 4) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("Observer: bad record: Packet length %u < 4",
+ packet_header->network_size);
+ return FALSE;
+ }
+
+ rec->rec_header.packet_header.len = packet_header->network_size - 4;
+ rec->rec_header.packet_header.caplen = MIN(packet_header->captured_size, rec->rec_header.packet_header.len);
+ }
+ /*
+ * The maximum value of packet_header->captured_size is 65535, which
+ * is less than WTAP_MAX_PACKET_SIZE_STANDARD will ever be, so we don't need
+ * to check it.
+ */
+
+ /* set the wiretap timestamp, assuming for the moment that Observer encoded it in GMT */
+ rec->ts.secs = (time_t) ((packet_header->nano_seconds_since_2000 / 1000000000) + ansi_to_observer_epoch_offset);
+ rec->ts.nsecs = (int) (packet_header->nano_seconds_since_2000 % 1000000000);
+
+ /* adjust to local time, if necessary, also accounting for DST if the frame
+ was captured while it was in effect */
+ if (((observer_dump_private_state*)wth->priv)->time_format == TIME_INFO_LOCAL)
+ {
+ struct tm *tm;
+ struct tm daylight_tm;
+ struct tm standard_tm;
+ time_t dst_offset;
+
+ /* the Observer timestamp was encoded as local time, so add a
+ correction from local time to GMT */
+ rec->ts.secs += gmt_to_localtime_offset;
+
+ /* perform a DST adjustment if necessary */
+ tm = localtime(&rec->ts.secs);
+ if (tm != NULL) {
+ standard_tm = *tm;
+ if (standard_tm.tm_isdst > 0) {
+ daylight_tm = standard_tm;
+ standard_tm.tm_isdst = 0;
+ dst_offset = mktime(&standard_tm) - mktime(&daylight_tm);
+ rec->ts.secs -= dst_offset;
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+static int
+read_packet_data(FILE_T fh, int offset_to_frame, int current_offset_from_packet_header, Buffer *buf,
+ int length, int *err, char **err_info)
+{
+ int seek_increment;
+ int bytes_consumed = 0;
+
+ /* validate offsets */
+ if (offset_to_frame < current_offset_from_packet_header) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("Observer: bad record (offset to packet data %d < %d)",
+ offset_to_frame, current_offset_from_packet_header);
+ return -1;
+ }
+
+ /* skip to the packet data */
+ seek_increment = offset_to_frame - current_offset_from_packet_header;
+ if (seek_increment > 0) {
+ if (!wtap_read_bytes(fh, NULL, seek_increment, err, err_info)) {
+ return -1;
+ }
+ bytes_consumed += seek_increment;
+ }
+
+ /* read in the packet data */
+ if (!wtap_read_packet_bytes(fh, buf, length, err, err_info))
+ return FALSE;
+ bytes_consumed += length;
+
+ return bytes_consumed;
+}
+
+static gboolean
+skip_to_next_packet(wtap *wth, int offset_to_next_packet, int current_offset_from_packet_header, int *err,
+ char **err_info)
+{
+ int seek_increment;
+
+ /* validate offsets */
+ if (offset_to_next_packet < current_offset_from_packet_header) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("Observer: bad record (offset to next packet %d < %d)",
+ offset_to_next_packet, current_offset_from_packet_header);
+ return FALSE;
+ }
+
+ /* skip to the next packet header */
+ seek_increment = offset_to_next_packet - current_offset_from_packet_header;
+ if (seek_increment > 0) {
+ if (!wtap_read_bytes(wth->fh, NULL, seek_increment, err, err_info))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/* Returns 0 if we could write the specified encapsulation type,
+ an error indication otherwise. */
+static int observer_dump_can_write_encap(int encap)
+{
+ /* per-packet encapsulations aren't supported */
+ if (encap == WTAP_ENCAP_PER_PACKET)
+ return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
+
+ if (encap < 0 || (wtap_to_observer_encap(encap) == OBSERVER_UNDEFINED))
+ return WTAP_ERR_UNWRITABLE_ENCAP;
+
+ return 0;
+}
+
+/* Returns TRUE on success, FALSE on failure; sets "*err" to an error code on
+ failure. */
+static gboolean observer_dump_open(wtap_dumper *wdh, int *err,
+ gchar **err_info)
+{
+ observer_dump_private_state * private_state = NULL;
+ capture_file_header file_header;
+ guint header_offset;
+ const gchar *err_str;
+ tlv_header comment_header;
+ char comment[64];
+ size_t comment_length;
+ tlv_header time_info_header;
+ tlv_time_info time_info;
+ struct tm * current_time;
+ time_t system_time;
+
+ /* initialize the private state */
+ private_state = g_new(observer_dump_private_state, 1);
+ private_state->packet_count = 0;
+ private_state->network_type = wtap_to_observer_encap(wdh->file_encap);
+ private_state->time_format = TIME_INFO_GMT;
+
+ /* populate the fields of wdh */
+ wdh->priv = (void *) private_state;
+ wdh->subtype_write = observer_dump;
+
+ /* initialize the file header */
+ memset(&file_header, 0x00, sizeof(file_header));
+ (void) g_strlcpy(file_header.observer_version, observer_magic, 31);
+ header_offset = (guint16)sizeof(file_header);
+
+ /* create the file comment TLV */
+ {
+ time(&system_time);
+ current_time = localtime(&system_time);
+ memset(&comment, 0x00, sizeof(comment));
+ if (current_time != NULL)
+ snprintf(comment, 64, "This capture was saved from Wireshark on %s", asctime(current_time));
+ else
+ snprintf(comment, 64, "This capture was saved from Wireshark");
+ comment_length = strlen(comment);
+
+ comment_header.type = INFORMATION_TYPE_COMMENT;
+ comment_header.length = (guint16) (sizeof(comment_header) + comment_length);
+
+ /* update the file header to account for the comment TLV */
+ file_header.number_of_information_elements++;
+ header_offset += comment_header.length;
+ }
+
+ /* create the timestamp encoding TLV */
+ {
+ time_info_header.type = INFORMATION_TYPE_TIME_INFO;
+ time_info_header.length = (guint16) (sizeof(time_info_header) + sizeof(time_info));
+ time_info.time_format = TIME_INFO_GMT;
+
+ /* update the file header to account for the timestamp encoding TLV */
+ file_header.number_of_information_elements++;
+ header_offset += time_info_header.length;
+ }
+
+ /* Store the offset to the first packet */
+ file_header.offset_to_first_packet_high_byte = (header_offset >> 16);
+ file_header.offset_to_first_packet = (header_offset & 0xFFFF);
+
+ /* write the file header, swapping any multibyte fields first */
+ CAPTURE_FILE_HEADER_TO_LE_IN_PLACE(file_header);
+ if (!wtap_dump_file_write(wdh, &file_header, sizeof(file_header), err)) {
+ return FALSE;
+ }
+
+ /* write the comment TLV */
+ {
+ TLV_HEADER_TO_LE_IN_PLACE(comment_header);
+ if (!wtap_dump_file_write(wdh, &comment_header, sizeof(comment_header), err)) {
+ return FALSE;
+ }
+
+ if (!wtap_dump_file_write(wdh, &comment, comment_length, err)) {
+ return FALSE;
+ }
+ }
+
+ /* write the time info TLV */
+ {
+ TLV_HEADER_TO_LE_IN_PLACE(time_info_header);
+ if (!wtap_dump_file_write(wdh, &time_info_header, sizeof(time_info_header), err)) {
+ return FALSE;
+ }
+
+ TLV_TIME_INFO_TO_LE_IN_PLACE(time_info);
+ if (!wtap_dump_file_write(wdh, &time_info, sizeof(time_info), err)) {
+ return FALSE;
+ }
+ }
+
+ err_str = init_gmt_to_localtime_offset();
+ if (err_str != NULL) {
+ *err = WTAP_ERR_INTERNAL;
+ *err_info = ws_strdup_printf("observer: %s", err_str);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/* Write a record for a packet to a dump file.
+ Returns TRUE on success, FALSE on failure. */
+static gboolean observer_dump(wtap_dumper *wdh, const wtap_rec *rec,
+ const guint8 *pd,
+ int *err, gchar **err_info _U_)
+{
+ observer_dump_private_state * private_state = NULL;
+ packet_entry_header packet_header;
+ guint64 seconds_since_2000;
+
+ /* We can only write packet records. */
+ if (rec->rec_type != REC_TYPE_PACKET) {
+ *err = WTAP_ERR_UNWRITABLE_REC_TYPE;
+ return FALSE;
+ }
+
+ /*
+ * Make sure this packet doesn't have a link-layer type that
+ * differs from the one for the file.
+ */
+ if (wdh->file_encap != rec->rec_header.packet_header.pkt_encap) {
+ *err = WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
+ return FALSE;
+ }
+
+ /* The captured size field is 16 bits, so there's a hard limit of
+ 65535. */
+ if (rec->rec_header.packet_header.caplen > 65535) {
+ *err = WTAP_ERR_PACKET_TOO_LARGE;
+ return FALSE;
+ }
+
+ /* convert the number of seconds since epoch from ANSI-relative to
+ Observer-relative */
+ if (rec->ts.secs < ansi_to_observer_epoch_offset) {
+ if(rec->ts.secs > (time_t) 0) {
+ seconds_since_2000 = rec->ts.secs;
+ } else {
+ seconds_since_2000 = (time_t) 0;
+ }
+ } else {
+ seconds_since_2000 = rec->ts.secs - ansi_to_observer_epoch_offset;
+ }
+
+ /* populate the fields of the packet header */
+ private_state = (observer_dump_private_state *) wdh->priv;
+
+ memset(&packet_header, 0x00, sizeof(packet_header));
+ packet_header.packet_magic = observer_packet_magic;
+ packet_header.network_speed = 1000000;
+ packet_header.captured_size = (guint16) rec->rec_header.packet_header.caplen;
+ packet_header.network_size = (guint16) (rec->rec_header.packet_header.len + 4);
+ packet_header.offset_to_frame = sizeof(packet_header);
+ /* XXX - what if this doesn't fit in 16 bits? It's not guaranteed to... */
+ packet_header.offset_to_next_packet = (guint16)sizeof(packet_header) + rec->rec_header.packet_header.caplen;
+ packet_header.network_type = private_state->network_type;
+ packet_header.flags = 0x00;
+ packet_header.number_of_information_elements = 0;
+ packet_header.packet_type = PACKET_TYPE_DATA_PACKET;
+ packet_header.packet_number = private_state->packet_count;
+ packet_header.original_packet_number = packet_header.packet_number;
+ packet_header.nano_seconds_since_2000 = seconds_since_2000 * 1000000000 + rec->ts.nsecs;
+
+ private_state->packet_count++;
+
+ /* write the packet header */
+ PACKET_ENTRY_HEADER_TO_LE_IN_PLACE(packet_header);
+ if (!wtap_dump_file_write(wdh, &packet_header, sizeof(packet_header), err)) {
+ return FALSE;
+ }
+
+ /* write the packet data */
+ if (!wtap_dump_file_write(wdh, pd, rec->rec_header.packet_header.caplen, err)) {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gint observer_to_wtap_encap(int observer_encap)
+{
+ switch(observer_encap) {
+ case OBSERVER_ETHERNET:
+ return WTAP_ENCAP_ETHERNET;
+ case OBSERVER_TOKENRING:
+ return WTAP_ENCAP_TOKEN_RING;
+ case OBSERVER_FIBRE_CHANNEL:
+ return WTAP_ENCAP_FIBRE_CHANNEL_FC2_WITH_FRAME_DELIMS;
+ case OBSERVER_WIRELESS_802_11:
+ return WTAP_ENCAP_IEEE_802_11_WITH_RADIO;
+ case OBSERVER_UNDEFINED:
+ return WTAP_ENCAP_UNKNOWN;
+ }
+ return WTAP_ENCAP_UNKNOWN;
+}
+
+static gint wtap_to_observer_encap(int wtap_encap)
+{
+ switch(wtap_encap) {
+ case WTAP_ENCAP_ETHERNET:
+ return OBSERVER_ETHERNET;
+ case WTAP_ENCAP_TOKEN_RING:
+ return OBSERVER_TOKENRING;
+ case WTAP_ENCAP_FIBRE_CHANNEL_FC2_WITH_FRAME_DELIMS:
+ return OBSERVER_FIBRE_CHANNEL;
+ case WTAP_ENCAP_UNKNOWN:
+ return OBSERVER_UNDEFINED;
+ }
+ return OBSERVER_UNDEFINED;
+}
+
+static const struct supported_block_type observer_blocks_supported[] = {
+ /*
+ * We support packet blocks, with no comments or other options.
+ */
+ { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
+};
+
+static const struct file_type_subtype_info observer_info = {
+ "Viavi Observer", "observer", "bfr", NULL,
+ FALSE, BLOCKS_SUPPORTED(observer_blocks_supported),
+ observer_dump_can_write_encap, observer_dump_open, NULL
+};
+
+void register_observer(void)
+{
+ observer_file_type_subtype = wtap_register_file_type_subtype(&observer_info);
+
+ /*
+ * We now call this file format just "observer", but we allow
+ * it to be referred to as "niobserver" for backwards
+ * compatibility.
+ *
+ * Register "niobserver" for that purpose.
+ */
+ wtap_register_compatibility_file_subtype_name("niobserver", "observer");
+
+ /*
+ * Register name for backwards compatibility with the
+ * wtap_filetypes table in Lua.
+ */
+ wtap_register_backwards_compatibility_lua_name("NETWORK_INSTRUMENTS",
+ observer_file_type_subtype);
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * vi: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */
diff --git a/wiretap/observer.h b/wiretap/observer.h
new file mode 100644
index 00000000..d203841d
--- /dev/null
+++ b/wiretap/observer.h
@@ -0,0 +1,255 @@
+/** @file
+ observer.h - description
+ -------------------
+ begin : Wed Oct 29 2003
+ copyright : (C) 2003 by root
+ email : scotte[AT}netinst.com
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * SPDX-License-Identifier: GPL-2.0-or-later *
+ * *
+ ***************************************************************************/
+
+#ifndef __NETWORK_INSTRUMENTS_H__
+#define __NETWORK_INSTRUMENTS_H__
+
+#include <glib.h>
+#include "wtap.h"
+
+wtap_open_return_val observer_open(wtap *wth, int *err, gchar **err_info);
+
+/*
+ * In v15 the high_byte was added to allow a larger offset This was done by
+ * reducing the size of observer_version by 1 byte. Since version strings are
+ * only 30 characters the high_byte will always be 0 in previous versions.
+ */
+typedef struct capture_file_header
+{
+ char observer_version[31];
+ guint8 offset_to_first_packet_high_byte; /* allows to extend the offset to the first packet to 256*0x10000 = 16 MB */
+ guint16 offset_to_first_packet;
+ char probe_instance;
+ guint8 number_of_information_elements; /* number of TLVs in the header */
+} capture_file_header;
+
+#define CAPTURE_FILE_HEADER_FROM_LE_IN_PLACE(_capture_file_header) \
+ _capture_file_header.offset_to_first_packet = GUINT16_FROM_LE((_capture_file_header).offset_to_first_packet)
+
+#define CAPTURE_FILE_HEADER_TO_LE_IN_PLACE(_capture_file_header) \
+ _capture_file_header.offset_to_first_packet = GUINT16_TO_LE((_capture_file_header).offset_to_first_packet)
+
+typedef struct tlv_header
+{
+ guint16 type;
+ guint16 length; /* includes the length of the TLV header */
+} tlv_header;
+
+#define TLV_HEADER_FROM_LE_IN_PLACE(_tlv_header) \
+ (_tlv_header).type = GUINT16_FROM_LE((_tlv_header).type); \
+ (_tlv_header).length = GUINT16_FROM_LE((_tlv_header).length)
+
+#define TLV_HEADER_TO_LE_IN_PLACE(_tlv_header) \
+ (_tlv_header).type = GUINT16_TO_LE((_tlv_header).type); \
+ (_tlv_header).length = GUINT16_TO_LE((_tlv_header).length)
+
+/*
+ * TLV type values.
+ *
+ * Do TLVs without the 0x0100 bit set show up in packets, and
+ * do TLVs with that set show up in the file header, or are
+ * there two separate types of TLV?
+ *
+ * ALIAS_LIST contains an ASCII string (null-terminated, but
+ * we can't trust that, of course) that is the pathname of
+ * a file containing the alias list. Not much use to us.
+ *
+ * COMMENT contains an ASCII string (null-terminated, but
+ * we can't trust that, of course); in all the captures
+ * I've seen, it appears to be a note about the file added
+ * by Observer, not by a user. It appears to end with 0x0a
+ * 0x2e, i.e. '\n' '.'.
+ *
+ * REMOTE_PROBE contains, in all the captures I've seen, an
+ * ASCII string (null-terminated, but we cna't trust that,
+ * of course) of the form "Remote Probe [hex string]". THe
+ * hex string has 8 characters, i.e. 4 octets.
+ *
+ * The Observer document indicates that the types of expert information
+ * packets are:
+ *
+ * Network Load (markers used by Expert Time Interval and What If
+ * analysis modes)
+ *
+ * Start/Stop Packet Capture marker frames (with time stamps when
+ * captures start and stop)
+ *
+ * Wireless Channel Change (markers showing what channel was being
+ * currently listened to)
+ *
+ * That information appears to be contained in TLVs.
+ */
+#define INFORMATION_TYPE_ALIAS_LIST 0x0001
+#define INFORMATION_TYPE_COMMENT 0x0002 /* ASCII text */
+#define INFORMATION_TYPE_TIME_INFO 0x0004
+#define INFORMATION_TYPE_REMOTE_PROBE 0x0005
+#define INFORMATION_TYPE_NETWORK_LOAD 0x0100
+#define INFORMATION_TYPE_WIRELESS 0x0101
+#define INFORMATION_TYPE_CAPTURE_START_STOP 0x0104
+
+/*
+ * See in Fibre Channel captures; not seen elsewhere.
+ *
+ * It has 4 bytes of data in all captures I've seen.
+ */
+/* 0x0106 */
+
+typedef struct tlv_time_info {
+ guint16 type;
+ guint16 length;
+ guint32 time_format;
+} tlv_time_info;
+
+/*
+ * TIME_INFO time_format values.
+ */
+#define TIME_INFO_LOCAL 0
+#define TIME_INFO_GMT 1
+
+#define TLV_TIME_INFO_FROM_LE_IN_PLACE(_tlv_time_info) \
+ (_tlv_time_info).time_format = GUINT32_FROM_LE((_tlv_time_info).time_format)
+
+#define TLV_TIME_INFO_TO_LE_IN_PLACE(_tlv_time_info) \
+ (_tlv_time_info).time_format = GUINT32_TO_LE((_tlv_time_info).time_format)
+
+/*
+ * Might some of these be broadecast and multicast packet counts, or
+ * error counts, or both?
+ */
+typedef struct tlv_network_load
+{
+ guint32 utilization; /* network utilization, in .1% units */
+ guint32 unknown1; /* zero in all captures I've seen */
+ guint32 unknown2; /* zero in all captures I've seen */
+ guint32 packets_per_second;
+ guint32 unknown3; /* zero in all captures I've seen */
+ guint32 bytes_per_second;
+ guint32 unknown4; /* zero in all captures I've seen */
+} tlv_network_load;
+
+#define TLV_NETWORK_LOAD_FROM_LE_IN_PLACE(_tlv_network_load) \
+ (_tlv_network_load).utilization = GUINT32_FROM_LE((_tlv_network_load).utilization); \
+ (_tlv_network_load).unknown1 = GUINT32_FROM_LE((_tlv_network_load).unknown1); \
+ (_tlv_network_load).unknown2 = GUINT32_FROM_LE((_tlv_network_load).unknown2); \
+ (_tlv_network_load).packets_per_second = GUINT32_FROM_LE((_tlv_network_load).packets_per_second); \
+ (_tlv_network_load).unknown3 = GUINT32_FROM_LE((_tlv_network_load).unknown3); \
+ (_tlv_network_load).bytes_per_second = GUINT32_FROM_LE((_tlv_network_load).bytes_per_second); \
+ (_tlv_network_load).unknown4 = GUINT32_FROM_LE((_tlv_network_load).unknown4) \
+
+#define TLV_NETWORK_LOAD_TO_LE_IN_PLACE(_tlv_network_load) \
+ (_tlv_network_load).utilization = GUINT32_TO_LE((_tlv_network_load).utilization); \
+ (_tlv_network_load).unknown1 = GUINT32_TO_LE((_tlv_network_load).unknown1); \
+ (_tlv_network_load).unknown2 = GUINT32_TO_LE((_tlv_network_load).unknown2); \
+ (_tlv_network_load).packets_per_second = GUINT32_TO_LE((_tlv_network_load).packets_per_second); \
+ (_tlv_network_load).unknown3 = GUINT32_TO_LE((_tlv_network_load).unknown3); \
+ (_tlv_network_load).bytes_per_second = GUINT32_TO_LE((_tlv_network_load).bytes_per_second); \
+ (_tlv_network_load).unknown4 = GUINT32_TO_LE((_tlv_network_load).unknown4) \
+
+/*
+ * quality is presumably some measure of signal quality; in
+ * the captures I've seen, it has values of 15, 20-27, 50-54,
+ * 208, and 213.
+ *
+ * conditions has values of 0x00, 0x02, and 0x90.
+ *
+ * reserved is either 0x00 or 0x80; the 0x80 values
+ * are for TLVs where conditions is 0x90.
+ */
+typedef struct tlv_wireless_info {
+ guint8 quality;
+ guint8 signalStrength;
+ guint8 rate;
+ guint8 frequency;
+ guint8 qualityPercent;
+ guint8 strengthPercent;
+ guint8 conditions;
+ guint8 reserved;
+} tlv_wireless_info;
+
+/*
+ * Wireless conditions
+ */
+#define WIRELESS_WEP_SUCCESS 0x80
+/* 0x10 */
+/* 0x02 */
+
+typedef struct tlv_capture_start_stop
+{
+ guint32 start_stop;
+} tlv_capture_start_stop;
+
+#define START_STOP_TYPE_STOP 0
+#define START_STOP_TYPE_START 1
+
+typedef struct packet_entry_header
+{
+ guint32 packet_magic;
+ guint32 network_speed;
+ guint16 captured_size;
+ guint16 network_size;
+ guint16 offset_to_frame;
+ guint16 offset_to_next_packet;
+ guint8 network_type;
+ guint8 flags;
+ guint8 number_of_information_elements; /* number of TLVs in the header */
+ guint8 packet_type;
+ guint16 errors;
+ guint16 reserved;
+ guint64 packet_number;
+ guint64 original_packet_number;
+ guint64 nano_seconds_since_2000;
+} packet_entry_header;
+
+#define PACKET_ENTRY_HEADER_FROM_LE_IN_PLACE(_packet_entry_header) \
+ (_packet_entry_header).packet_magic = GUINT32_FROM_LE((_packet_entry_header).packet_magic); \
+ (_packet_entry_header).network_speed = GUINT32_FROM_LE((_packet_entry_header).network_speed); \
+ (_packet_entry_header).captured_size = GUINT16_FROM_LE((_packet_entry_header).captured_size); \
+ (_packet_entry_header).network_size = GUINT16_FROM_LE((_packet_entry_header).network_size); \
+ (_packet_entry_header).offset_to_frame = GUINT16_FROM_LE((_packet_entry_header).offset_to_frame); \
+ (_packet_entry_header).offset_to_next_packet = GUINT16_FROM_LE((_packet_entry_header).offset_to_next_packet); \
+ (_packet_entry_header).errors = GUINT16_FROM_LE((_packet_entry_header).errors); \
+ (_packet_entry_header).reserved = GUINT16_FROM_LE((_packet_entry_header).reserved); \
+ (_packet_entry_header).packet_number = GUINT64_FROM_LE((_packet_entry_header).packet_number); \
+ (_packet_entry_header).original_packet_number = GUINT64_FROM_LE((_packet_entry_header).original_packet_number); \
+ (_packet_entry_header).nano_seconds_since_2000 = GUINT64_FROM_LE((_packet_entry_header).nano_seconds_since_2000)
+
+#define PACKET_ENTRY_HEADER_TO_LE_IN_PLACE(_packet_entry_header) \
+ (_packet_entry_header).packet_magic = GUINT32_TO_LE((_packet_entry_header).packet_magic); \
+ (_packet_entry_header).network_speed = GUINT32_TO_LE((_packet_entry_header).network_speed); \
+ (_packet_entry_header).captured_size = GUINT16_TO_LE((_packet_entry_header).captured_size); \
+ (_packet_entry_header).network_size = GUINT16_TO_LE((_packet_entry_header).network_size); \
+ (_packet_entry_header).offset_to_frame = GUINT16_TO_LE((_packet_entry_header).offset_to_frame); \
+ (_packet_entry_header).offset_to_next_packet = GUINT16_TO_LE((_packet_entry_header).offset_to_next_packet); \
+ (_packet_entry_header).errors = GUINT16_TO_LE((_packet_entry_header).errors); \
+ (_packet_entry_header).reserved = GUINT16_TO_LE((_packet_entry_header).reserved); \
+ (_packet_entry_header).packet_number = GUINT64_TO_LE((_packet_entry_header).packet_number); \
+ (_packet_entry_header).original_packet_number = GUINT64_TO_LE((_packet_entry_header).original_packet_number); \
+ (_packet_entry_header).nano_seconds_since_2000 = GUINT64_TO_LE((_packet_entry_header).nano_seconds_since_2000)
+
+/*
+ * Network type values.
+ */
+#define OBSERVER_UNDEFINED 0xFF
+#define OBSERVER_ETHERNET 0x00
+#define OBSERVER_TOKENRING 0x01
+#define OBSERVER_FIBRE_CHANNEL 0x08
+#define OBSERVER_WIRELESS_802_11 0x09
+
+/*
+ * Packet type values.
+ */
+#define PACKET_TYPE_DATA_PACKET 0
+#define PACKET_TYPE_EXPERT_INFORMATION_PACKET 1
+
+#endif
diff --git a/wiretap/packetlogger.c b/wiretap/packetlogger.c
new file mode 100644
index 00000000..885d4505
--- /dev/null
+++ b/wiretap/packetlogger.c
@@ -0,0 +1,420 @@
+/* packetlogger.c
+ * Routines for opening Apple's (Bluetooth) PacketLogger file format captures
+ * Copyright 2008-2009, Stephen Fisher (see AUTHORS file)
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * Based on commview.c, Linux's BlueZ-Gnome Analyzer program and hexdumps of
+ * the output files from Apple's PacketLogger tool.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "wtap-int.h"
+#include "file_wrappers.h"
+#include "packetlogger.h"
+
+typedef struct {
+ gboolean byte_swapped;
+} packetlogger_t;
+
+typedef struct packetlogger_header {
+ guint32 len;
+ guint32 ts_secs;
+ guint32 ts_usecs;
+} packetlogger_header_t;
+
+/* Packet types. */
+#define PKT_HCI_COMMAND 0x00
+#define PKT_HCI_EVENT 0x01
+#define PKT_SENT_ACL_DATA 0x02
+#define PKT_RECV_ACL_DATA 0x03
+#define PKT_SENT_SCO_DATA 0x08
+#define PKT_RECV_SCO_DATA 0x09
+#define PKT_LMP_SEND 0x0A
+#define PKT_LMP_RECV 0x0B
+#define PKT_SYSLOG 0xF7
+#define PKT_KERNEL 0xF8
+#define PKT_KERNEL_DEBUG 0xF9
+#define PKT_ERROR 0xFA
+#define PKT_POWER 0xFB
+#define PKT_NOTE 0xFC
+#define PKT_CONFIG 0xFD
+#define PKT_NEW_CONTROLLER 0xFE
+
+static gboolean packetlogger_read(wtap *wth, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info,
+ gint64 *data_offset);
+static gboolean packetlogger_seek_read(wtap *wth, gint64 seek_off,
+ wtap_rec *rec,
+ Buffer *buf, int *err, gchar **err_info);
+static gboolean packetlogger_read_header(packetlogger_header_t *pl_hdr,
+ FILE_T fh, gboolean byte_swapped,
+ int *err, gchar **err_info);
+static void packetlogger_byte_swap_header(packetlogger_header_t *pl_hdr);
+static wtap_open_return_val packetlogger_check_record(wtap *wth,
+ packetlogger_header_t *pl_hdr,
+ int *err,
+ gchar **err_info);
+static gboolean packetlogger_read_packet(wtap *wth, FILE_T fh, wtap_rec *rec,
+ Buffer *buf, int *err,
+ gchar **err_info);
+
+static int packetlogger_file_type_subtype = -1;
+
+void register_packetlogger(void);
+
+/*
+ * Number of packets to try reading.
+ */
+#define PACKETS_TO_CHECK 5
+
+wtap_open_return_val packetlogger_open(wtap *wth, int *err, gchar **err_info)
+{
+ gboolean byte_swapped = FALSE;
+ packetlogger_header_t pl_hdr;
+ wtap_open_return_val ret;
+ packetlogger_t *packetlogger;
+
+ /*
+ * Try to read the first record.
+ */
+ if(!packetlogger_read_header(&pl_hdr, wth->fh, byte_swapped,
+ err, err_info)) {
+ /*
+ * Either an immediate EOF or a short read indicates
+ * that the file is probably not a PacketLogger file.
+ */
+ if (*err != 0 && *err != WTAP_ERR_SHORT_READ)
+ return WTAP_OPEN_ERROR;
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ /*
+ * If the upper 16 bits of the length are non-zero and the lower
+ * 16 bits are zero, assume the file is byte-swapped from our
+ * byte order.
+ */
+ if ((pl_hdr.len & 0x0000FFFF) == 0 &&
+ (pl_hdr.len & 0xFFFF0000) != 0) {
+ /*
+ * Byte-swap the header.
+ */
+ packetlogger_byte_swap_header(&pl_hdr);
+ byte_swapped = TRUE;
+ }
+
+ /*
+ * Check whether the first record looks like a PacketLogger
+ * record.
+ */
+ ret = packetlogger_check_record(wth, &pl_hdr, err, err_info);
+ if (ret != WTAP_OPEN_MINE) {
+ /*
+ * Either we got an error or it's not valid.
+ */
+ if (ret == WTAP_OPEN_NOT_MINE) {
+ /*
+ * Not valid, so not a PacketLogger file.
+ */
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ /*
+ * Error. If it failed with a short read, we don't fail,
+ * so we treat it as a valid file and can then report
+ * it as a truncated file.
+ */
+ if (*err != WTAP_ERR_SHORT_READ)
+ return WTAP_OPEN_ERROR;
+ } else {
+ /*
+ * Now try reading a few more packets.
+ */
+ for (int i = 1; i < PACKETS_TO_CHECK; i++) {
+ /*
+ * Read and check the file header; we've already
+ * decided whether this would be a byte-swapped file
+ * or not, so we swap iff we decided it was.
+ */
+ if (!packetlogger_read_header(&pl_hdr, wth->fh,
+ byte_swapped, err, err_info)) {
+ if (*err == 0) {
+ /* EOF; no more packets to try. */
+ break;
+ }
+
+ /*
+ * A short read indicates that the file
+ * is probably not a PacketLogger file.
+ */
+ if (*err != WTAP_ERR_SHORT_READ)
+ return WTAP_OPEN_ERROR;
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ /*
+ * Check whether this record looks like a PacketLogger
+ * record.
+ */
+ ret = packetlogger_check_record(wth, &pl_hdr, err,
+ err_info);
+ if (ret != WTAP_OPEN_MINE) {
+ /*
+ * Either we got an error or it's not valid.
+ */
+ if (ret == WTAP_OPEN_NOT_MINE) {
+ /*
+ * Not valid, so not a PacketLogger
+ * file.
+ */
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ /*
+ * Error. If it failed with a short read,
+ * we don't fail, we just stop checking
+ * records, so we treat it as a valid file
+ * and can then report it as a truncated file.
+ */
+ if (*err != WTAP_ERR_SHORT_READ)
+ return WTAP_OPEN_ERROR;
+ break;
+ }
+ }
+ }
+
+ /* No file header. Reset the fh to 0 so we can read the first packet */
+ if (file_seek(wth->fh, 0, SEEK_SET, err) == -1)
+ return WTAP_OPEN_ERROR;
+
+ /* This is a PacketLogger file */
+ packetlogger = g_new(packetlogger_t, 1);
+ packetlogger->byte_swapped = byte_swapped;
+ wth->priv = (void *)packetlogger;
+
+ /* Set up the pointers to the handlers for this file type */
+ wth->subtype_read = packetlogger_read;
+ wth->subtype_seek_read = packetlogger_seek_read;
+
+ wth->file_type_subtype = packetlogger_file_type_subtype;
+ wth->file_encap = WTAP_ENCAP_PACKETLOGGER;
+ wth->file_tsprec = WTAP_TSPREC_USEC;
+
+ /*
+ * Add an IDB; we don't know how many interfaces were
+ * involved, so we just say one interface, about which
+ * we only know the link-layer type, snapshot length,
+ * and time stamp resolution.
+ */
+ wtap_add_generated_idb(wth);
+
+ return WTAP_OPEN_MINE; /* Our kind of file */
+}
+
+static gboolean
+packetlogger_read(wtap *wth, wtap_rec *rec, Buffer *buf, int *err,
+ gchar **err_info, gint64 *data_offset)
+{
+ *data_offset = file_tell(wth->fh);
+
+ return packetlogger_read_packet(wth, wth->fh, rec, buf, err, err_info);
+}
+
+static gboolean
+packetlogger_seek_read(wtap *wth, gint64 seek_off, wtap_rec *rec,
+ Buffer *buf, int *err, gchar **err_info)
+{
+ if(file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
+ return FALSE;
+
+ if(!packetlogger_read_packet(wth, wth->random_fh, rec, buf, err, err_info)) {
+ if(*err == 0)
+ *err = WTAP_ERR_SHORT_READ;
+
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static gboolean
+packetlogger_read_header(packetlogger_header_t *pl_hdr, FILE_T fh,
+ gboolean byte_swapped, int *err, gchar **err_info)
+{
+ if (!wtap_read_bytes_or_eof(fh, &pl_hdr->len, 4, err, err_info))
+ return FALSE;
+ if (!wtap_read_bytes(fh, &pl_hdr->ts_secs, 4, err, err_info))
+ return FALSE;
+ if (!wtap_read_bytes(fh, &pl_hdr->ts_usecs, 4, err, err_info))
+ return FALSE;
+
+ /* Convert multi-byte values to host endian */
+ if (byte_swapped)
+ packetlogger_byte_swap_header(pl_hdr);
+
+ return TRUE;
+}
+
+static void
+packetlogger_byte_swap_header(packetlogger_header_t *pl_hdr)
+{
+ pl_hdr->len = GUINT32_SWAP_LE_BE(pl_hdr->len);
+ pl_hdr->ts_secs = GUINT32_SWAP_LE_BE(pl_hdr->ts_secs);
+ pl_hdr->ts_usecs = GUINT32_SWAP_LE_BE(pl_hdr->ts_usecs);
+}
+
+static wtap_open_return_val
+packetlogger_check_record(wtap *wth, packetlogger_header_t *pl_hdr, int *err,
+ gchar **err_info)
+{
+ guint32 length;
+ guint8 type;
+
+ /* Is the header length valid? If not, assume it's not ours. */
+ if (pl_hdr->len < 8 || pl_hdr->len >= 65536)
+ return WTAP_OPEN_NOT_MINE;
+
+ /* Is the microseconds field of the time stap out of range? */
+ if (pl_hdr->ts_usecs >= 1000000)
+ return WTAP_OPEN_NOT_MINE;
+
+ /*
+ * If we have any payload, it's a type field; read and check it.
+ */
+ length = pl_hdr->len - 8;
+ if (length != 0) {
+ /*
+ * Check the type field.
+ */
+ if (!wtap_read_bytes(wth->fh, &type, 1, err, err_info)) {
+ if (*err != WTAP_ERR_SHORT_READ)
+ return WTAP_OPEN_ERROR;
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ /* Verify this file belongs to us */
+ switch (type) {
+
+ case PKT_HCI_COMMAND:
+ case PKT_HCI_EVENT:
+ case PKT_SENT_ACL_DATA:
+ case PKT_RECV_ACL_DATA:
+ case PKT_SENT_SCO_DATA:
+ case PKT_RECV_SCO_DATA:
+ case PKT_LMP_SEND:
+ case PKT_LMP_RECV:
+ case PKT_SYSLOG:
+ case PKT_KERNEL:
+ case PKT_KERNEL_DEBUG:
+ case PKT_ERROR:
+ case PKT_POWER:
+ case PKT_NOTE:
+ case PKT_CONFIG:
+ case PKT_NEW_CONTROLLER:
+ break;
+
+ default:
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ length--;
+
+ if (length != 0) {
+ /*
+ * Now try to read past the rest of the packet bytes;
+ * if that fails with a short read, we don't fail,
+ * so that we can report the file as a truncated
+ * PacketLogger file.
+ */
+ if (!wtap_read_bytes(wth->fh, NULL, length,
+ err, err_info))
+ return WTAP_OPEN_ERROR;
+ }
+ }
+ return WTAP_OPEN_MINE;
+}
+
+static gboolean
+packetlogger_read_packet(wtap *wth, FILE_T fh, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info)
+{
+ packetlogger_t *packetlogger = (packetlogger_t *)wth->priv;
+ packetlogger_header_t pl_hdr;
+
+ if(!packetlogger_read_header(&pl_hdr, fh, packetlogger->byte_swapped,
+ err, err_info))
+ return FALSE;
+
+ if (pl_hdr.len < 8) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("packetlogger: record length %u is too small", pl_hdr.len);
+ return FALSE;
+ }
+ if (pl_hdr.len - 8 > WTAP_MAX_PACKET_SIZE_STANDARD) {
+ /*
+ * Probably a corrupt capture file; don't blow up trying
+ * to allocate space for an immensely-large packet.
+ */
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("packetlogger: File has %u-byte packet, bigger than maximum of %u",
+ pl_hdr.len - 8, WTAP_MAX_PACKET_SIZE_STANDARD);
+ return FALSE;
+ }
+
+ rec->rec_type = REC_TYPE_PACKET;
+ rec->block = wtap_block_create(WTAP_BLOCK_PACKET);
+ rec->presence_flags = WTAP_HAS_TS;
+
+ rec->rec_header.packet_header.len = pl_hdr.len - 8;
+ rec->rec_header.packet_header.caplen = pl_hdr.len - 8;
+
+ rec->ts.secs = (time_t)pl_hdr.ts_secs;
+ rec->ts.nsecs = (int)(pl_hdr.ts_usecs * 1000);
+
+ return wtap_read_packet_bytes(fh, buf, rec->rec_header.packet_header.caplen, err, err_info);
+}
+
+static const struct supported_block_type packetlogger_blocks_supported[] = {
+ /*
+ * We support packet blocks, with no comments or other options.
+ */
+ { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
+};
+
+static const struct file_type_subtype_info packetlogger_info = {
+ "macOS PacketLogger", "pklg", "pklg", NULL,
+ FALSE, BLOCKS_SUPPORTED(packetlogger_blocks_supported),
+ NULL, NULL, NULL
+};
+
+void register_packetlogger(void)
+{
+ packetlogger_file_type_subtype = wtap_register_file_type_subtype(&packetlogger_info);
+
+ /*
+ * Register name for backwards compatibility with the
+ * wtap_filetypes table in Lua.
+ */
+ wtap_register_backwards_compatibility_lua_name("PACKETLOGGER",
+ packetlogger_file_type_subtype);
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/wiretap/packetlogger.h b/wiretap/packetlogger.h
new file mode 100644
index 00000000..a82ef833
--- /dev/null
+++ b/wiretap/packetlogger.h
@@ -0,0 +1,19 @@
+/** @file
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __PACKETLOGGER_H__
+#define __PACKETLOGGER_H__
+
+#include <glib.h>
+#include "wtap.h"
+
+wtap_open_return_val packetlogger_open(wtap *wth, int *err, gchar **err_info _U_);
+
+#endif /* __PACKETLOGGER_H__ */
+
diff --git a/wiretap/pcap-common.c b/wiretap/pcap-common.c
new file mode 100644
index 00000000..3a607940
--- /dev/null
+++ b/wiretap/pcap-common.c
@@ -0,0 +1,2852 @@
+/* pcap-common.c
+ * Code common to pcap and pcapng file formats
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * File format support for pcapng file format
+ * Copyright (c) 2007 by Ulf Lamping <ulf.lamping@web.de>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include "wtap-int.h"
+#include "file_wrappers.h"
+#include "atm.h"
+#include "erf_record.h"
+#include "pcap-encap.h"
+#include "pcap-common.h"
+
+/*
+ * On some systems, the FDDI MAC addresses are bit-swapped.
+ *
+ * XXX - what we *really* need to know is whether the addresses are
+ * bit-swapped *in a particular capture*, which depends on the system
+ * on which it was captured, not on the system that's reading it.
+ * Unfortunately, we can't determine that.
+ */
+#if !defined(ultrix) && !defined(__alpha) && !defined(__bsdi__)
+#define BIT_SWAPPED_MAC_ADDRS
+#endif
+
+/*
+ * Map link-layer header types (LINKTYPE_ values) to Wiretap encapsulations.
+ *
+ * Either LBL NRG wasn't an adequate central registry (e.g., because of
+ * the slow rate of releases from them), or nobody bothered using them
+ * as a central registry, as many different groups have patched libpcap
+ * (and BPF, on the BSDs) to add new encapsulation types, and have ended
+ * up using the same DLT_ values for different encapsulation types.
+ *
+ * The Tcpdump Group now maintains the list of link-layer header types;
+ * they introduced a separate namespace of LINKTYPE_ values for the
+ * values to be used in capture files, and have libpcap map between
+ * those values in capture file headers and the DLT_ values that the
+ * pcap_datalink() and pcap_open_dead() APIs use. See
+ * https://www.tcpdump.org/linktypes.html for a list of LINKTYPE_ values.
+ *
+ * In most cases, the corresponding LINKTYPE_ and DLT_ values are the
+ * same. In the cases where the same link-layer header type was given
+ * different values in different OSes, a new LINKTYPE_ value was defined,
+ * different from all of the existing DLT_ values.
+ *
+ * This table maps LINKTYPE_ values to the corresponding Wiretap
+ * encapsulation. For cases where multiple DLT_ values were in use,
+ * it also checks what <pcap.h> defineds to determine how to interpret
+ * them, so that if a file was written by a version of libpcap prior
+ * to the introduction of the LINKTYPE_ values, and has a DLT_ value
+ * from the OS on which it was written rather than a LINKTYPE_ value
+ * as its linktype value in the file header, we map the numerical
+ * DLT_ value, as interpreted by the libpcap with which we're building
+ * Wireshark/Wiretap interprets them (which, if it doesn't support
+ * them at all, means we don't support them either - any capture files
+ * using them are foreign, and we don't hazard a guess as to which
+ * platform they came from; we could, I guess, choose the most likely
+ * platform), to the corresponding Wiretap encapsulation.
+ *
+ * Note: if you need a new encapsulation type for libpcap files, do
+ * *N*O*T* use *ANY* of the values listed here! I.e., do *NOT*
+ * add a new encapsulation type by changing an existing entry;
+ * leave the existing entries alone.
+ *
+ * Instead, send mail to tcpdump-workers@lists.tcpdump.org, asking for
+ * a new LINKTYPE_/DLT_ value, and specifying the purpose of the new
+ * value. When you get the new LINKTYPE_/DLT_ value, use that numerical
+ * value in the "linktype_value" field of "pcap_to_wtap_map[]".
+ */
+
+static const struct {
+ int linktype_value;
+ int wtap_encap_value;
+} pcap_to_wtap_map[] = {
+ /*
+ * These are the values that are almost certainly the same
+ * in all libpcaps (I've yet to find one where the values
+ * in question are used for some purpose other than the
+ * one below, but...), and thus assigned as LINKTYPE_ values,
+ * and that Wiretap and Wireshark currently support.
+ */
+ { 0, WTAP_ENCAP_NULL }, /* null encapsulation */
+ { 1, WTAP_ENCAP_ETHERNET },
+ { 2, WTAP_ENCAP_3MB_ETHERNET },
+ { 3, WTAP_ENCAP_AX25 },
+ { 6, WTAP_ENCAP_TOKEN_RING }, /* IEEE 802 Networks - assume token ring */
+ { 7, WTAP_ENCAP_ARCNET },
+ { 8, WTAP_ENCAP_SLIP },
+ { 9, WTAP_ENCAP_PPP },
+#ifdef BIT_SWAPPED_MAC_ADDRS
+ { 10, WTAP_ENCAP_FDDI_BITSWAPPED },
+#else
+ { 10, WTAP_ENCAP_FDDI },
+#endif
+
+ { 32, WTAP_ENCAP_REDBACK },
+
+ /*
+ * 50 is DLT_PPP_SERIAL in NetBSD; it appears that DLT_PPP
+ * on BSD (at least according to standard tcpdump) has, as
+ * the first octet, an indication of whether the packet was
+ * transmitted or received (rather than having the standard
+ * PPP address value of 0xff), but that DLT_PPP_SERIAL puts
+ * a real live PPP header there, or perhaps a Cisco PPP header
+ * as per section 4.3.1 of RFC 1547 (implementations of this
+ * exist in various BSDs in "sys/net/if_spppsubr.c", and
+ * I think also exist either in standard Linux or in
+ * various Linux patches; the implementations show how to handle
+ * Cisco keepalive packets).
+ *
+ * However, I don't see any obvious place in FreeBSD "if_ppp.c"
+ * where anything other than the standard PPP header would be
+ * passed up. I see some stuff that sets the first octet
+ * to 0 for incoming and 1 for outgoing packets before applying
+ * a BPF filter to see whether to drop packets whose protocol
+ * field has the 0x8000 bit set, i.e. network control protocols -
+ * those are handed up to userland - but that code puts the
+ * address field back before passing the packet up.
+ *
+ * I also don't see anything immediately obvious that munges
+ * the address field for sync PPP, either.
+ *
+ * Wireshark currently assumes that if the first octet of a
+ * PPP frame is 0xFF, it's the address field and is followed
+ * by a control field and a 2-byte protocol, otherwise the
+ * address and control fields are absent and the frame begins
+ * with a protocol field. If we ever see a BSD/OS PPP
+ * capture, we'll have to handle it differently, and we may
+ * have to handle standard BSD captures differently if, in fact,
+ * they don't have 0xff 0x03 as the first two bytes - but, as per
+ * the two paragraphs preceding this, it's not clear that
+ * the address field *is* munged into an incoming/outgoing
+ * field when the packet is handed to the BPF device.
+ *
+ * For now, we just map DLT_PPP_SERIAL to WTAP_ENCAP_PPP, as
+ * we treat WTAP_ENCAP_PPP packets as if those beginning with
+ * 0xff have the standard RFC 1662 "PPP in HDLC-like Framing"
+ * 0xff 0x03 address/control header, and DLT_PPP_SERIAL frames
+ * appear to contain that unless they're Cisco frames (if we
+ * ever see a capture with them, we'd need to implement the
+ * RFC 1547 stuff, and the keepalive protocol stuff).
+ *
+ * We may have to distinguish between "PPP where if it doesn't
+ * begin with 0xff there's no HDLC encapsulation and the frame
+ * begins with the protocol field" (which is how we handle
+ * WTAP_ENCAP_PPP now) and "PPP where there's either HDLC
+ * encapsulation or Cisco PPP" (which is what DLT_PPP_SERIAL
+ * is) at some point.
+ *
+ * XXX - NetBSD has DLT_HDLC, which appears to be used for
+ * Cisco HDLC. Ideally, they should use DLT_PPP_SERIAL
+ * only for real live HDLC-encapsulated PPP, not for Cisco
+ * HDLC.
+ */
+ { 50, WTAP_ENCAP_PPP },
+
+ /*
+ * Used by NetBSD and OpenBSD pppoe(4).
+ */
+ { 51, WTAP_ENCAP_PPP_ETHER },
+
+ /*
+ * Apparently used by the Axent Raptor firewall (now Symantec
+ * Enterprise Firewall).
+ * Thanks, Axent, for not reserving that type with tcpdump.org
+ * and not telling anybody about it.
+ */
+ { 99, WTAP_ENCAP_SYMANTEC },
+
+ /*
+ * These are the values that libpcap 0.5 and later use in
+ * capture file headers, in an attempt to work around the
+ * confusion decried above, and that Wiretap and Wireshark
+ * currently support. I.e., they're the LINKTYPE_ values
+ * for RFC 1483 ATM and "raw IP", respectively, not the
+ * DLT_ values for them on all platforms.
+ */
+ { 100, WTAP_ENCAP_ATM_RFC1483 },
+ { 101, WTAP_ENCAP_RAW_IP },
+#if 0
+ /*
+ * More values used by libpcap 0.5 as DLT_ values and used by the
+ * current CVS version of libpcap in capture file headers.
+ * They are not yet handled in Wireshark.
+ * If we get a capture that contains them, we'll implement them.
+ */
+ { 102, WTAP_ENCAP_SLIP_BSDOS },
+ { 103, WTAP_ENCAP_PPP_BSDOS },
+#endif
+
+ /*
+ * These ones are handled in Wireshark, though.
+ */
+ { 104, WTAP_ENCAP_CHDLC }, /* Cisco HDLC */
+ { 105, WTAP_ENCAP_IEEE_802_11 }, /* IEEE 802.11 */
+ { 106, WTAP_ENCAP_LINUX_ATM_CLIP },
+ { 107, WTAP_ENCAP_FRELAY }, /* Frame Relay */
+ { 108, WTAP_ENCAP_LOOP }, /* OpenBSD loopback */
+ { 109, WTAP_ENCAP_ENC }, /* OpenBSD IPSEC enc */
+#if 0
+ { 110, WTAP_ENCAP_LANE_802_3 },/* ATM LANE 802.3 */
+ { 111, WTAP_ENCAP_HIPPI }, /* NetBSD HIPPI */
+#endif
+ { 112, WTAP_ENCAP_CHDLC }, /* NetBSD HDLC framing */
+
+ /*
+ * Linux "cooked mode" captures, used by the current CVS version
+ * of libpcap
+ * OR
+ * it could be a packet in Cisco's ERSPAN encapsulation which uses
+ * this number as well (why can't people stick to protocols when it
+ * comes to allocating/using DLT types).
+ */
+ { 113, WTAP_ENCAP_SLL }, /* Linux cooked capture v1 */
+
+ { 114, WTAP_ENCAP_LOCALTALK }, /* Localtalk */
+
+ /*
+ * The tcpdump.org version of libpcap uses 117, rather than 17,
+ * for OpenBSD packet filter logging, so as to avoid conflicting
+ * with DLT_LANE8023 in SuSE 6.3 libpcap.
+ */
+ { 117, WTAP_ENCAP_PFLOG },
+
+ { 118, WTAP_ENCAP_CISCO_IOS },
+ { 119, WTAP_ENCAP_IEEE_802_11_PRISM }, /* 802.11 plus Prism monitor mode radio header */
+ { 121, WTAP_ENCAP_HHDLC }, /* HiPath HDLC */
+ { 122, WTAP_ENCAP_IP_OVER_FC }, /* RFC 2625 IP-over-FC */
+ { 123, WTAP_ENCAP_ATM_PDUS }, /* SunATM */
+ { 127, WTAP_ENCAP_IEEE_802_11_RADIOTAP }, /* 802.11 plus radiotap radio header */
+ { 128, WTAP_ENCAP_TZSP }, /* Tazmen Sniffer Protocol */
+ { 129, WTAP_ENCAP_ARCNET_LINUX },
+ { 130, WTAP_ENCAP_JUNIPER_MLPPP }, /* Juniper MLPPP on ML-, LS-, AS- PICs */
+ { 131, WTAP_ENCAP_JUNIPER_MLFR }, /* Juniper MLFR (FRF.15) on ML-, LS-, AS- PICs */
+ { 133, WTAP_ENCAP_JUNIPER_GGSN},
+ /*
+ * Values 132 and 134 not listed here are reserved for use
+ * in Juniper hardware.
+ */
+ { 135, WTAP_ENCAP_JUNIPER_ATM2 }, /* various encapsulations captured on the ATM2 PIC */
+ { 136, WTAP_ENCAP_JUNIPER_SVCS }, /* various encapsulations captured on the services PIC */
+ { 137, WTAP_ENCAP_JUNIPER_ATM1 }, /* various encapsulations captured on the ATM1 PIC */
+
+ { 138, WTAP_ENCAP_APPLE_IP_OVER_IEEE1394 },
+ /* Apple IP-over-IEEE 1394 */
+
+ { 139, WTAP_ENCAP_MTP2_WITH_PHDR },
+ { 140, WTAP_ENCAP_MTP2 },
+ { 141, WTAP_ENCAP_MTP3 },
+ { 142, WTAP_ENCAP_SCCP },
+ { 143, WTAP_ENCAP_DOCSIS },
+ { 144, WTAP_ENCAP_IRDA }, /* IrDA capture */
+
+ /* Reserved for private use. */
+ { 147, WTAP_ENCAP_USER0 },
+ { 148, WTAP_ENCAP_USER1 },
+ { 149, WTAP_ENCAP_USER2 },
+ { 150, WTAP_ENCAP_USER3 },
+ { 151, WTAP_ENCAP_USER4 },
+ { 152, WTAP_ENCAP_USER5 },
+ { 153, WTAP_ENCAP_USER6 },
+ { 154, WTAP_ENCAP_USER7 },
+ { 155, WTAP_ENCAP_USER8 },
+ { 156, WTAP_ENCAP_USER9 },
+ { 157, WTAP_ENCAP_USER10 },
+ { 158, WTAP_ENCAP_USER11 },
+ { 159, WTAP_ENCAP_USER12 },
+ { 160, WTAP_ENCAP_USER13 },
+ { 161, WTAP_ENCAP_USER14 },
+ { 162, WTAP_ENCAP_USER15 },
+
+ { 163, WTAP_ENCAP_IEEE_802_11_AVS }, /* 802.11 plus AVS radio header */
+
+ /*
+ * 164 is reserved for Juniper-private chassis-internal
+ * meta-information such as QoS profiles, etc..
+ */
+
+ { 165, WTAP_ENCAP_BACNET_MS_TP },
+
+ /*
+ * 166 is reserved for a PPP variant in which the first byte
+ * of the 0xff03 header, the 0xff, is replaced by a direction
+ * byte. I don't know whether any captures look like that,
+ * but it is used for some Linux IP filtering (ipfilter?).
+ */
+
+ /* Ethernet PPPoE frames captured on a service PIC */
+ { 167, WTAP_ENCAP_JUNIPER_PPPOE },
+
+ /*
+ * 168 is reserved for more Juniper private-chassis-
+ * internal meta-information.
+ */
+
+ { 169, WTAP_ENCAP_GPRS_LLC },
+
+ /* ITU-T G.7041/Y.1303 Generic Framing Procedure. */
+ { 170, WTAP_ENCAP_GFP_T },
+ { 171, WTAP_ENCAP_GFP_F },
+
+ /* Registered by Gcom, Inc. */
+ { 172, WTAP_ENCAP_GCOM_TIE1 },
+ { 173, WTAP_ENCAP_GCOM_SERIAL },
+
+ { 177, WTAP_ENCAP_LINUX_LAPD },
+
+ /* Ethernet frames prepended with meta-information */
+ { 178, WTAP_ENCAP_JUNIPER_ETHER },
+ /* PPP frames prepended with meta-information */
+ { 179, WTAP_ENCAP_JUNIPER_PPP },
+ /* Frame-Relay frames prepended with meta-information */
+ { 180, WTAP_ENCAP_JUNIPER_FRELAY },
+ /* C-HDLC frames prepended with meta-information */
+ { 181, WTAP_ENCAP_JUNIPER_CHDLC },
+ /* VOIP Frames prepended with meta-information */
+ { 183, WTAP_ENCAP_JUNIPER_VP },
+ /* Virtual Network Frames prepended with meta-information */
+ { 184, WTAP_ENCAP_JUNIPER_VN },
+ /* USB packets from FreeBSD's USB BPF tap */
+ { 186, WTAP_ENCAP_USB_FREEBSD },
+ /* Bluetooth HCI UART transport (part H:4) frames, like hcidump */
+ { 187, WTAP_ENCAP_BLUETOOTH_H4 },
+ /* IEEE 802.16 MAC Common Part Sublayer */
+ { 188, WTAP_ENCAP_IEEE802_16_MAC_CPS },
+ /* USB packets with Linux-specified header */
+ { 189, WTAP_ENCAP_USB_LINUX },
+ /* CAN 2.0b frame */
+ { 190, WTAP_ENCAP_CAN20B },
+ /* Per-Packet Information header */
+ { 192, WTAP_ENCAP_PPI },
+ /* IEEE 802.15.4 Wireless PAN */
+ { 195, WTAP_ENCAP_IEEE802_15_4 },
+ /* SITA File Encapsulation */
+ { 196, WTAP_ENCAP_SITA },
+ /* Endace Record File Encapsulation */
+ { 197, WTAP_ENCAP_ERF },
+ /* IPMB/I2C with Kontron pseudo-header */
+ { 199, WTAP_ENCAP_IPMB_KONTRON },
+ /* Juniper-private data link type, used for capturing data on a secure tunnel interface. */
+ { 200, WTAP_ENCAP_JUNIPER_ST },
+ /* Bluetooth HCI UART transport (part H:4) frames, like hcidump */
+ { 201, WTAP_ENCAP_BLUETOOTH_H4_WITH_PHDR },
+ /* AX.25 packet with a 1-byte KISS header */
+ { 202, WTAP_ENCAP_AX25_KISS },
+ /* LAPD frame */
+ { 203, WTAP_ENCAP_LAPD },
+ /* PPP with pseudoheader */
+ { 204, WTAP_ENCAP_PPP_WITH_PHDR },
+ /* IPMB/I2C with a Linux-specific header (defined by Pigeon Point Systems) */
+ { 209, WTAP_ENCAP_I2C_LINUX },
+ /* FlexRay frame */
+ { 210, WTAP_ENCAP_FLEXRAY },
+ /* MOST frame */
+ { 211, WTAP_ENCAP_MOST },
+ /* LIN frame */
+ { 212, WTAP_ENCAP_LIN },
+ /* X2E Xoraya serial frame */
+ { 213, WTAP_ENCAP_X2E_SERIAL },
+ /* X2E Xoraya frame */
+ { 214, WTAP_ENCAP_X2E_XORAYA },
+ /* IEEE 802.15.4 Wireless PAN non-ASK PHY */
+ { 215, WTAP_ENCAP_IEEE802_15_4_NONASK_PHY },
+ /* USB packets with padded Linux-specified header */
+ { 220, WTAP_ENCAP_USB_LINUX_MMAPPED },
+ /* Fibre Channel FC-2 frame */
+ { 224, WTAP_ENCAP_FIBRE_CHANNEL_FC2 },
+ /* Fibre Channel FC-2 frame with Delimiter */
+ { 225, WTAP_ENCAP_FIBRE_CHANNEL_FC2_WITH_FRAME_DELIMS },
+ /* Solaris IPNET */
+ { 226, WTAP_ENCAP_IPNET },
+ /* SocketCAN frame */
+ { 227, WTAP_ENCAP_SOCKETCAN },
+ /* Raw IPv4 */
+ { 228, WTAP_ENCAP_RAW_IP4 },
+ /* Raw IPv6 */
+ { 229, WTAP_ENCAP_RAW_IP6 },
+ /* IEEE 802.15.4 Wireless PAN no fcs */
+ { 230, WTAP_ENCAP_IEEE802_15_4_NOFCS },
+ /* D-BUS */
+ { 231, WTAP_ENCAP_DBUS },
+ /* DVB-CI (Common Interface) */
+ { 235, WTAP_ENCAP_DVBCI },
+ /* MUX27010 */
+ { 236, WTAP_ENCAP_MUX27010 },
+ /* STANAG 5066 - DTS(Data Transfer Sublayer) PDU */
+ { 237, WTAP_ENCAP_STANAG_5066_D_PDU },
+ /* NFLOG */
+ { 239, WTAP_ENCAP_NFLOG },
+ /* netANALYZER pseudo-header followed by Ethernet with CRC */
+ { 240, WTAP_ENCAP_NETANALYZER },
+ /* netANALYZER pseudo-header in transparent mode */
+ { 241, WTAP_ENCAP_NETANALYZER_TRANSPARENT },
+ /* IP-over-Infiniband, as specified by RFC 4391 section 6 */
+ { 242, WTAP_ENCAP_IP_OVER_IB_PCAP },
+ /* ISO/IEC 13818-1 MPEG2-TS packets */
+ { 243, WTAP_ENCAP_MPEG_2_TS },
+ /* NFC LLCP */
+ { 245, WTAP_ENCAP_NFC_LLCP },
+ /* SCTP */
+ { 248, WTAP_ENCAP_SCTP},
+ /* USBPcap */
+ { 249, WTAP_ENCAP_USBPCAP},
+ /* RTAC SERIAL */
+ { 250, WTAP_ENCAP_RTAC_SERIAL},
+ /* Bluetooth Low Energy Link Layer */
+ { 251, WTAP_ENCAP_BLUETOOTH_LE_LL},
+ /* Wireshark Upper PDU export */
+ { 252, WTAP_ENCAP_WIRESHARK_UPPER_PDU},
+ /* Netlink Protocol (nlmon devices) */
+ { 253, WTAP_ENCAP_NETLINK },
+ /* Bluetooth Linux Monitor */
+ { 254, WTAP_ENCAP_BLUETOOTH_LINUX_MONITOR },
+ /* Bluetooth BR/EDR Baseband RF captures */
+ { 255, WTAP_ENCAP_BLUETOOTH_BREDR_BB },
+ /* Bluetooth Low Energy Link Layer RF captures */
+ { 256, WTAP_ENCAP_BLUETOOTH_LE_LL_WITH_PHDR },
+
+ /* Apple PKTAP */
+ { 258, WTAP_ENCAP_PKTAP },
+
+ /* Ethernet Passive Optical Network */
+ { 259, WTAP_ENCAP_EPON },
+
+ /* IPMI Trace Data Collection */
+ { 260, WTAP_ENCAP_IPMI_TRACE },
+
+ /* ISO 14443 contactless smartcard standards */
+ { 264, WTAP_ENCAP_ISO14443 },
+
+ /* USB packets from Darwin (macOS, iOS) BPF tap */
+ { 266, WTAP_ENCAP_USB_DARWIN },
+
+ /* IBM SDLC frames containing SNA PDUs */
+ { 268, WTAP_ENCAP_SDLC },
+
+ /* LoRaTap */
+ { 270, WTAP_ENCAP_LORATAP },
+
+ /* Linux vsock */
+ { 271, WTAP_ENCAP_VSOCK },
+
+ /* nRF Sniffer for Bluetooth LE */
+ { 272, WTAP_ENCAP_NORDIC_BLE },
+
+ /* DOCSIS31 XRA31 Sniffer */
+ { 273, WTAP_ENCAP_DOCSIS31_XRA31 },
+
+ /* mPackets as specified by 802.3br */
+ { 274, WTAP_ENCAP_ETHERNET_MPACKET },
+
+ /* DisplayPort AUX channel monitor */
+ { 275, WTAP_ENCAP_DPAUXMON },
+
+ /* Linux cooked capture v2 */
+ { 276, WTAP_ENCAP_SLL2 },
+
+ /* Elektrobit High Speed Capture and Replay */
+ { 279, WTAP_ENCAP_EBHSCR },
+
+ /* VPP dispatch trace */
+ { 280, WTAP_ENCAP_VPP },
+
+ /* IEEE 802.15.4 TAP */
+ { 283, WTAP_ENCAP_IEEE802_15_4_TAP },
+
+ /* Z-Wave Serial API */
+ { 287, WTAP_ENCAP_ZWAVE_SERIAL },
+
+ /* USB 2.0/1.1/1.0 packets as transmitted over the cable */
+ { 288, WTAP_ENCAP_USB_2_0 },
+
+ /* ATSC Link-Layer Protocol (A/330) packets */
+ { 289, WTAP_ENCAP_ATSC_ALP },
+
+ /* Event Tracing for Windows records */
+ { 290, WTAP_ENCAP_ETW },
+
+ /* Serial NCP (Network Co-Processor) protocol for Zigbee stack ZBOSS */
+ { 292, WTAP_ENCAP_ZBNCP },
+
+ /* USB 2.0/1.1/1.0 packets captured on Low/Full/High speed link */
+ { 293, WTAP_ENCAP_USB_2_0_LOW_SPEED },
+ { 294, WTAP_ENCAP_USB_2_0_FULL_SPEED },
+ { 295, WTAP_ENCAP_USB_2_0_HIGH_SPEED },
+
+ /* Auerswald log file captured from any supported Auerswald device */
+ { 296, WTAP_ENCAP_AUERSWALD_LOG },
+
+ /* Silicon Labs debug channel */
+ { 298, WTAP_ENCAP_SILABS_DEBUG_CHANNEL },
+
+ /* Ultra-wideband (UWB) controller interface protocol (UCI) */
+ { 299, WTAP_ENCAP_FIRA_UCI },
+
+ /* MDB (Multi-Drop Bus) */
+ { 300, WTAP_ENCAP_MDB },
+
+ /*
+ * To repeat:
+ *
+ * If you need a new encapsulation type for pcap and pcapng files,
+ * do *N*O*T* use *ANY* of the values listed here! I.e., do *NOT*
+ * add a new encapsulation type by changing an existing entry;
+ * leave the existing entries alone.
+ *
+ * Instead, send mail to tcpdump-workers@lists.tcpdump.org, asking
+ * for a new DLT_ value, and specifying the purpose of the new value.
+ * When you get the new DLT_ value, use that numerical value in
+ * the "linktype_value" field of "pcap_to_wtap_map[]".
+ */
+
+ /*
+ * The following are entries for libpcap type values that have
+ * different meanings on different OSes. I.e., these are DLT_
+ * values that are different on different OSes, and that have
+ * a separate LINKTYPE_ value assigned to them.
+ *
+ * We put these *after* the entries for the LINKTYPE_ values for
+ * those Wiretap encapsulation types, so that, when writing a
+ * pcap or pcapng file, Wireshark writes the LINKTYPE_ value,
+ * not the OS's DLT_ value, as the file's link-layer header type
+ * for pcap or the interface's link-layer header type.
+ */
+
+ /*
+ * 11 is DLT_ATM_RFC1483 on most platforms; the only version of
+ * libpcap I've seen that define anything other than DLT_ATM_RFC1483
+ * as 11 is the BSD/OS one, which defines DLT_FR as 11. We handle
+ * it as Frame Relay on BSD/OS and LLC-encapsulated ATM on all other
+ * platforms.
+ */
+#if defined(__bsdi__) /* BSD/OS */
+ { 11, WTAP_ENCAP_FRELAY },
+#else
+ { 11, WTAP_ENCAP_ATM_RFC1483 },
+#endif
+
+ /*
+ * 12 is DLT_RAW on most platforms, but it's DLT_C_HDLC on
+ * BSD/OS, and DLT_LOOP on OpenBSD.
+ *
+ * We don't yet handle DLT_C_HDLC, but we can handle DLT_LOOP
+ * (it's just like DLT_NULL, only with the AF_ value in network
+ * rather than host byte order - Wireshark figures out the
+ * byte order from the data, so we don't care what byte order
+ * it's in), so, on OpenBSD, interpret 12 as WTAP_ENCAP_LOOP,
+ * otherwise, if we're not on BSD/OS, interpret it as
+ * WTAP_ENCAP_RAW_IP.
+ */
+#if defined(__OpenBSD__)
+ { 12, WTAP_ENCAP_LOOP },
+#elif defined(__bsdi__) /* BSD/OS */
+ /*
+ * Put entry for Cisco HDLC here.
+ * XXX - is this just WTAP_ENCAP_CHDLC, i.e. does the frame
+ * start with a 4-byte Cisco HDLC header?
+ */
+#else
+ { 12, WTAP_ENCAP_RAW_IP },
+#endif
+
+ /*
+ * 13 is DLT_SLIP_BSDOS on FreeBSD and NetBSD, but those OSes
+ * don't actually generate it. I infer that BSD/OS translates
+ * DLT_SLIP from the kernel BPF code to DLT_SLIP_BSDOS in
+ * libpcap, as the BSD/OS link-layer header is different;
+ * however, in BSD/OS, DLT_SLIP_BSDOS is 15.
+ *
+ * From this, I infer that there's no point in handling 13
+ * as DLT_SLIP_BSDOS.
+ *
+ * 13 is DLT_ATM_RFC1483 on BSD/OS.
+ *
+ * 13 is DLT_ENC in OpenBSD, which is, I suspect, some kind
+ * of decrypted IPsec traffic.
+ *
+ * We treat 13 as WTAP_ENCAP_ENC on all systems except those
+ * that define DLT_ATM_RFC1483 as 13 - presumably only
+ * BSD/OS does so - so that, on BSD/OS systems, we still
+ * treat 13 as WTAP_ENCAP_ATM_RFC1483, but, on all other
+ * systems, we can read OpenBSD DLT_ENC captures.
+ */
+#if defined(__bsdi__) /* BSD/OS */
+ { 13, WTAP_ENCAP_ATM_RFC1483 },
+#else
+ { 13, WTAP_ENCAP_ENC },
+#endif
+
+ /*
+ * 14 is DLT_PPP_BSDOS on FreeBSD and NetBSD, but those OSes
+ * don't actually generate it. I infer that BSD/OS translates
+ * DLT_PPP from the kernel BPF code to DLT_PPP_BSDOS in
+ * libpcap, as the BSD/OS link-layer header is different;
+ * however, in BSD/OS, DLT_PPP_BSDOS is 16.
+ *
+ * From this, I infer that there's no point in handling 14
+ * as DLT_PPP_BSDOS.
+ *
+ * 14 is DLT_RAW on BSD/OS and OpenBSD.
+ */
+ { 14, WTAP_ENCAP_RAW_IP },
+
+ /*
+ * 15 is:
+ *
+ * DLT_SLIP_BSDOS on BSD/OS;
+ *
+ * DLT_HIPPI on NetBSD;
+ *
+ * DLT_LANE8023 with Alexey Kuznetzov's patches for
+ * Linux libpcap;
+ *
+ * DLT_I4L_RAWIP with the ISDN4Linux patches for libpcap
+ * (and on SuSE 6.3);
+ *
+ * but we don't currently handle any of those.
+ */
+
+ /*
+ * 16 is:
+ *
+ * DLT_PPP_BSDOS on BSD/OS;
+ *
+ * DLT_HDLC on NetBSD (Cisco HDLC);
+ *
+ * DLT_CIP with Alexey Kuznetzov's patches for
+ * Linux libpcap - this is WTAP_ENCAP_LINUX_ATM_CLIP;
+ *
+ * DLT_I4L_IP with the ISDN4Linux patches for libpcap
+ * (and on SuSE 6.3).
+ */
+#if defined(__NetBSD__)
+ { 16, WTAP_ENCAP_CHDLC },
+#elif !defined(__bsdi__)
+ /*
+ * If you care about the two different Linux interpretations
+ * of 16, fix it yourself.
+ */
+ { 16, WTAP_ENCAP_LINUX_ATM_CLIP },
+#endif
+
+ /*
+ * 17 is DLT_LANE8023 in SuSE 6.3 libpcap; we don't currently
+ * handle it.
+ * It is also used as the PF (Packet Filter) logging format beginning
+ * with OpenBSD 3.0; we use 17 for PF logs on OpenBSD and don't
+ * use it otherwise.
+ */
+#if defined(__OpenBSD__)
+ { 17, WTAP_ENCAP_OLD_PFLOG },
+#endif
+
+ /*
+ * 18 is DLT_CIP in SuSE 6.3 libpcap; if it's the same as the
+ * DLT_CIP of 16 that the Alexey Kuznetzov patches for
+ * libpcap/tcpdump define, it's WTAP_ENCAP_LINUX_ATM_CLIP.
+ * I've not found any version of libpcap that uses it for any
+ * other purpose - hopefully nobody will do so in the future.
+ */
+ { 18, WTAP_ENCAP_LINUX_ATM_CLIP },
+
+ /*
+ * 19 is DLT_ATM_CLIP in the libpcap/tcpdump patches in the
+ * recent versions I've seen of the Linux ATM distribution;
+ * I've not yet found any version of libpcap file that uses it
+ * for any other purpose - hopefully nobody will do so in
+ * the future.
+ */
+ { 19, WTAP_ENCAP_LINUX_ATM_CLIP },
+
+ /*
+ * To repeat:
+ *
+ * If you need a new encapsulation type for pcap and pcapng files,
+ * do *N*O*T* use *ANY* of the values listed here! I.e., do *NOT*
+ * add a new encapsulation type by changing an existing entry;
+ * leave the existing entries alone.
+ *
+ * Instead, send mail to tcpdump-workers@lists.tcpdump.org, asking
+ * for a new DLT_ value, and specifying the purpose of the new value.
+ * When you get the new DLT_ value, use that numerical value in
+ * the "linktype_value" field of "pcap_to_wtap_map[]".
+ */
+};
+#define NUM_PCAP_ENCAPS (sizeof pcap_to_wtap_map / sizeof pcap_to_wtap_map[0])
+
+int
+wtap_pcap_encap_to_wtap_encap(int encap)
+{
+ unsigned int i;
+
+ for (i = 0; i < NUM_PCAP_ENCAPS; i++) {
+ if (pcap_to_wtap_map[i].linktype_value == encap)
+ return pcap_to_wtap_map[i].wtap_encap_value;
+ }
+ return WTAP_ENCAP_UNKNOWN;
+}
+
+int
+wtap_wtap_encap_to_pcap_encap(int encap)
+{
+ unsigned int i;
+
+ switch (encap) {
+
+ case WTAP_ENCAP_FDDI:
+ case WTAP_ENCAP_FDDI_BITSWAPPED:
+ /*
+ * Special-case WTAP_ENCAP_FDDI and
+ * WTAP_ENCAP_FDDI_BITSWAPPED; both of them get mapped
+ * to DLT_FDDI (even though that may mean that the bit
+ * order in the FDDI MAC addresses is wrong; so it goes
+ * - libpcap format doesn't record the byte order,
+ * so that's not fixable).
+ *
+ * The pcap_to_wtap_map[] table will only have an
+ * entry for one of the above, which is why we have
+ * to special-case them.
+ */
+ return 10; /* that's DLT_FDDI */
+
+ case WTAP_ENCAP_NETTL_FDDI:
+ /*
+ * This will discard the nettl information, as that's
+ * in the pseudo-header.
+ *
+ * XXX - what about Ethernet and Token Ring?
+ */
+ return 10; /* that's DLT_FDDI */
+
+ case WTAP_ENCAP_FRELAY_WITH_PHDR:
+ /*
+ * This will discard the pseudo-header information.
+ */
+ return 107;
+
+ case WTAP_ENCAP_IEEE_802_11_WITH_RADIO:
+ /*
+ * Map this to DLT_IEEE802_11, for now, even though
+ * that means the radio information will be lost.
+ * We should try to map those values to radiotap
+ * values and write this out as a radiotap file,
+ * if possible.
+ */
+ return 105;
+ }
+
+ for (i = 0; i < NUM_PCAP_ENCAPS; i++) {
+ if (pcap_to_wtap_map[i].wtap_encap_value == encap)
+ return pcap_to_wtap_map[i].linktype_value;
+ }
+ return -1;
+}
+
+/*
+ * For most encapsulations, we use WTAP_MAX_PACKET_SIZE_STANDARD, as
+ * that should be enough for most link-layer types, and shouldn't be
+ * too big.
+ *
+ * For some link-layer types, we use larger types, because, for each
+ * of them, the maximum packet size is larger than the standard
+ * maximum, and is bigger than we'd want for all link-layer types - files
+ * with that snapshot length might cause some programs reading them to
+ * allocate a huge and wasteful buffer and, at least on 32-bit platforms,
+ * run the risk of running out of memory.
+ */
+guint
+wtap_max_snaplen_for_encap(int wtap_encap)
+{
+ switch (wtap_encap) {
+
+ case WTAP_ENCAP_DBUS:
+ return WTAP_MAX_PACKET_SIZE_DBUS;
+
+ case WTAP_ENCAP_EBHSCR:
+ return WTAP_MAX_PACKET_SIZE_EBHSCR;
+
+ case WTAP_ENCAP_USBPCAP:
+ case WTAP_ENCAP_USB_LINUX:
+ case WTAP_ENCAP_USB_LINUX_MMAPPED:
+ case WTAP_ENCAP_USB_DARWIN:
+ case WTAP_ENCAP_USB_FREEBSD:
+ return WTAP_MAX_PACKET_SIZE_USBPCAP;
+
+ default:
+ return WTAP_MAX_PACKET_SIZE_STANDARD;
+ }
+}
+
+/*
+ * Various pseudo-headers that appear at the beginning of packet data.
+ *
+ * We represent them as sets of offsets, as they might not be aligned on
+ * an appropriate structure boundary in the buffer, and as that makes them
+ * independent of the way the compiler might align fields.
+ */
+
+/*
+ * The link-layer header on Nokia IPSO ATM packets.
+ */
+#define NOKIAATM_FLAGS 0 /* destination - 1 byte */
+#define NOKIAATM_VPI 1 /* VPI - 1 byte */
+#define NOKIAATM_VCI 2 /* VCI - 2 bytes */
+#define NOKIAATM_LEN 4 /* length of the header */
+
+static int
+pcap_read_nokiaatm_pseudoheader(FILE_T fh,
+ union wtap_pseudo_header *pseudo_header, guint packet_size,
+ int *err, gchar **err_info)
+{
+ guint8 atm_phdr[NOKIAATM_LEN];
+ guint8 vpi;
+ guint16 vci;
+
+ if (packet_size < NOKIAATM_LEN) {
+ /*
+ * Uh-oh, the packet isn't big enough to even
+ * have a pseudo-header.
+ */
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("pcap/pcapng: Nokia IPSO ATM file has a %u-byte packet, too small to have even an ATM pseudo-header",
+ packet_size);
+ return -1;
+ }
+ if (!wtap_read_bytes(fh, atm_phdr, NOKIAATM_LEN, err, err_info))
+ return -1;
+
+ vpi = atm_phdr[NOKIAATM_VPI];
+ vci = pntoh16(&atm_phdr[NOKIAATM_VCI]);
+
+ pseudo_header->atm.vpi = vpi;
+ pseudo_header->atm.vci = vci;
+ pseudo_header->atm.channel = (atm_phdr[NOKIAATM_FLAGS] & 0x80) ? 0 : 1;
+
+ /* We don't have this information */
+ pseudo_header->atm.flags = 0;
+ pseudo_header->atm.cells = 0;
+ pseudo_header->atm.aal5t_u2u = 0;
+ pseudo_header->atm.aal5t_len = 0;
+ pseudo_header->atm.aal5t_chksum = 0;
+
+ return NOKIAATM_LEN;
+}
+
+/*
+ * The link-layer header on SunATM packets.
+ */
+#define SUNATM_FLAGS 0 /* destination and traffic type - 1 byte */
+#define SUNATM_VPI 1 /* VPI - 1 byte */
+#define SUNATM_VCI 2 /* VCI - 2 bytes */
+#define SUNATM_LEN 4 /* length of the header */
+
+static int
+pcap_read_sunatm_pseudoheader(FILE_T fh,
+ union wtap_pseudo_header *pseudo_header, guint packet_size,
+ int *err, gchar **err_info)
+{
+ guint8 atm_phdr[SUNATM_LEN];
+ guint8 vpi;
+ guint16 vci;
+
+ if (packet_size < SUNATM_LEN) {
+ /*
+ * Uh-oh, the packet isn't big enough to even
+ * have a pseudo-header.
+ */
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("pcap/pcapng: SunATM file has a %u-byte packet, too small to have even an ATM pseudo-header",
+ packet_size);
+ return -1;
+ }
+ if (!wtap_read_bytes(fh, atm_phdr, SUNATM_LEN, err, err_info))
+ return -1;
+
+ vpi = atm_phdr[SUNATM_VPI];
+ vci = pntoh16(&atm_phdr[SUNATM_VCI]);
+
+ switch (atm_phdr[SUNATM_FLAGS] & 0x0F) {
+
+ case 0x01: /* LANE */
+ pseudo_header->atm.aal = AAL_5;
+ pseudo_header->atm.type = TRAF_LANE;
+ break;
+
+ case 0x02: /* RFC 1483 LLC multiplexed traffic */
+ pseudo_header->atm.aal = AAL_5;
+ pseudo_header->atm.type = TRAF_LLCMX;
+ break;
+
+ case 0x05: /* ILMI */
+ pseudo_header->atm.aal = AAL_5;
+ pseudo_header->atm.type = TRAF_ILMI;
+ break;
+
+ case 0x06: /* Q.2931 */
+ pseudo_header->atm.aal = AAL_SIGNALLING;
+ pseudo_header->atm.type = TRAF_UNKNOWN;
+ break;
+
+ case 0x03: /* MARS (RFC 2022) */
+ pseudo_header->atm.aal = AAL_5;
+ pseudo_header->atm.type = TRAF_UNKNOWN;
+ break;
+
+ case 0x04: /* IFMP (Ipsilon Flow Management Protocol; see RFC 1954) */
+ pseudo_header->atm.aal = AAL_5;
+ pseudo_header->atm.type = TRAF_UNKNOWN; /* XXX - TRAF_IPSILON? */
+ break;
+
+ default:
+ /*
+ * Assume it's AAL5, unless it's VPI 0 and VCI 5, in which
+ * case assume it's AAL_SIGNALLING; we know nothing more
+ * about it.
+ *
+ * XXX - is this necessary? Or are we guaranteed that
+ * all signalling traffic has a type of 0x06?
+ *
+ * XXX - is this guaranteed to be AAL5? Or, if the type is
+ * 0x00 ("raw"), might it be non-AAL5 traffic?
+ */
+ if (vpi == 0 && vci == 5)
+ pseudo_header->atm.aal = AAL_SIGNALLING;
+ else
+ pseudo_header->atm.aal = AAL_5;
+ pseudo_header->atm.type = TRAF_UNKNOWN;
+ break;
+ }
+ pseudo_header->atm.subtype = TRAF_ST_UNKNOWN;
+
+ pseudo_header->atm.vpi = vpi;
+ pseudo_header->atm.vci = vci;
+ pseudo_header->atm.channel = (atm_phdr[SUNATM_FLAGS] & 0x80) ? 0 : 1;
+
+ /* We don't have this information */
+ pseudo_header->atm.flags = 0;
+ pseudo_header->atm.cells = 0;
+ pseudo_header->atm.aal5t_u2u = 0;
+ pseudo_header->atm.aal5t_len = 0;
+ pseudo_header->atm.aal5t_chksum = 0;
+
+ return SUNATM_LEN;
+}
+
+static gboolean
+pcap_write_sunatm_pseudoheader(wtap_dumper *wdh,
+ const union wtap_pseudo_header *pseudo_header, int *err)
+{
+ guint8 atm_hdr[SUNATM_LEN];
+
+ /*
+ * Write the ATM header.
+ */
+ atm_hdr[SUNATM_FLAGS] =
+ (pseudo_header->atm.channel == 0) ? 0x80 : 0x00;
+ switch (pseudo_header->atm.aal) {
+
+ case AAL_SIGNALLING:
+ /* Q.2931 */
+ atm_hdr[SUNATM_FLAGS] |= 0x06;
+ break;
+
+ case AAL_5:
+ switch (pseudo_header->atm.type) {
+
+ case TRAF_LANE:
+ /* LANE */
+ atm_hdr[SUNATM_FLAGS] |= 0x01;
+ break;
+
+ case TRAF_LLCMX:
+ /* RFC 1483 LLC multiplexed traffic */
+ atm_hdr[SUNATM_FLAGS] |= 0x02;
+ break;
+
+ case TRAF_ILMI:
+ /* ILMI */
+ atm_hdr[SUNATM_FLAGS] |= 0x05;
+ break;
+ }
+ break;
+ }
+ atm_hdr[SUNATM_VPI] = (guint8)pseudo_header->atm.vpi;
+ phtons(&atm_hdr[SUNATM_VCI], pseudo_header->atm.vci);
+ if (!wtap_dump_file_write(wdh, atm_hdr, sizeof(atm_hdr), err))
+ return FALSE;
+ return TRUE;
+}
+
+/*
+ * The fake link-layer header of IrDA packets as introduced by Jean Tourrilhes
+ * to libpcap.
+ */
+#define IRDA_SLL_PKTTYPE_OFFSET 0 /* packet type - 2 bytes */
+/* 12 unused bytes */
+#define IRDA_SLL_PROTOCOL_OFFSET 14 /* protocol, should be ETH_P_LAPD - 2 bytes */
+#define IRDA_SLL_LEN 16 /* length of the header */
+
+static int
+pcap_read_irda_pseudoheader(FILE_T fh, union wtap_pseudo_header *pseudo_header,
+ guint packet_size, int *err, gchar **err_info)
+{
+ guint8 irda_phdr[IRDA_SLL_LEN];
+
+ if (packet_size < IRDA_SLL_LEN) {
+ /*
+ * Uh-oh, the packet isn't big enough to even
+ * have a pseudo-header.
+ */
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("pcap/pcapng: IrDA file has a %u-byte packet, too small to have even an IrDA pseudo-header",
+ packet_size);
+ return -1;
+ }
+ if (!wtap_read_bytes(fh, irda_phdr, IRDA_SLL_LEN, err, err_info))
+ return -1;
+
+ if (pntoh16(&irda_phdr[IRDA_SLL_PROTOCOL_OFFSET]) != 0x0017) {
+ *err = WTAP_ERR_BAD_FILE;
+ if (err_info != NULL)
+ *err_info = g_strdup("pcap/pcapng: IrDA capture has a packet with an invalid sll_protocol field");
+ return -1;
+ }
+
+ pseudo_header->irda.pkttype = pntoh16(&irda_phdr[IRDA_SLL_PKTTYPE_OFFSET]);
+
+ return IRDA_SLL_LEN;
+}
+
+static gboolean
+pcap_write_irda_pseudoheader(wtap_dumper *wdh,
+ const union wtap_pseudo_header *pseudo_header, int *err)
+{
+ guint8 irda_hdr[IRDA_SLL_LEN];
+
+ /*
+ * Write the IrDA header.
+ */
+ memset(irda_hdr, 0, sizeof(irda_hdr));
+ phtons(&irda_hdr[IRDA_SLL_PKTTYPE_OFFSET], pseudo_header->irda.pkttype);
+ phtons(&irda_hdr[IRDA_SLL_PROTOCOL_OFFSET], 0x0017);
+ if (!wtap_dump_file_write(wdh, irda_hdr, sizeof(irda_hdr), err))
+ return FALSE;
+ return TRUE;
+}
+
+/*
+ * A header containing additional MTP information.
+ */
+#define MTP2_SENT_OFFSET 0 /* 1 byte */
+#define MTP2_ANNEX_A_USED_OFFSET 1 /* 1 byte */
+#define MTP2_LINK_NUMBER_OFFSET 2 /* 2 bytes */
+#define MTP2_HDR_LEN 4 /* length of the header */
+
+static int
+pcap_read_mtp2_pseudoheader(FILE_T fh, union wtap_pseudo_header *pseudo_header,
+ guint packet_size, int *err, gchar **err_info)
+{
+ guint8 mtp2_hdr[MTP2_HDR_LEN];
+
+ if (packet_size < MTP2_HDR_LEN) {
+ /*
+ * Uh-oh, the packet isn't big enough to even
+ * have a pseudo-header.
+ */
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("pcap/pcapng: MTP2 file has a %u-byte packet, too small to have even an MTP2 pseudo-header",
+ packet_size);
+ return -1;
+ }
+ if (!wtap_read_bytes(fh, mtp2_hdr, MTP2_HDR_LEN, err, err_info))
+ return -1;
+
+ pseudo_header->mtp2.sent = mtp2_hdr[MTP2_SENT_OFFSET];
+ pseudo_header->mtp2.annex_a_used = mtp2_hdr[MTP2_ANNEX_A_USED_OFFSET];
+ pseudo_header->mtp2.link_number = pntoh16(&mtp2_hdr[MTP2_LINK_NUMBER_OFFSET]);
+
+ return MTP2_HDR_LEN;
+}
+
+static gboolean
+pcap_write_mtp2_pseudoheader(wtap_dumper *wdh,
+ const union wtap_pseudo_header *pseudo_header, int *err)
+{
+ guint8 mtp2_hdr[MTP2_HDR_LEN];
+
+ /*
+ * Write the MTP2 header.
+ */
+ memset(&mtp2_hdr, 0, sizeof(mtp2_hdr));
+ mtp2_hdr[MTP2_SENT_OFFSET] = pseudo_header->mtp2.sent;
+ mtp2_hdr[MTP2_ANNEX_A_USED_OFFSET] = pseudo_header->mtp2.annex_a_used;
+ phtons(&mtp2_hdr[MTP2_LINK_NUMBER_OFFSET],
+ pseudo_header->mtp2.link_number);
+ if (!wtap_dump_file_write(wdh, mtp2_hdr, sizeof(mtp2_hdr), err))
+ return FALSE;
+ return TRUE;
+}
+
+/*
+ * The fake link-layer header of LAPD packets.
+ */
+#ifndef ETH_P_LAPD
+#define ETH_P_LAPD 0x0030
+#endif
+
+#define LAPD_SLL_PKTTYPE_OFFSET 0 /* packet type - 2 bytes */
+#define LAPD_SLL_HATYPE_OFFSET 2 /* hardware address type - 2 bytes */
+#define LAPD_SLL_HALEN_OFFSET 4 /* hardware address length - 2 bytes */
+#define LAPD_SLL_ADDR_OFFSET 6 /* address - 8 bytes */
+#define LAPD_SLL_PROTOCOL_OFFSET 14 /* protocol, should be ETH_P_LAPD - 2 bytes */
+#define LAPD_SLL_LEN 16 /* length of the header */
+
+static int
+pcap_read_lapd_pseudoheader(FILE_T fh, union wtap_pseudo_header *pseudo_header,
+ guint packet_size, int *err, gchar **err_info)
+{
+ guint8 lapd_phdr[LAPD_SLL_LEN];
+
+ if (packet_size < LAPD_SLL_LEN) {
+ /*
+ * Uh-oh, the packet isn't big enough to even
+ * have a pseudo-header.
+ */
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("pcap/pcapng: LAPD file has a %u-byte packet, too small to have even a LAPD pseudo-header",
+ packet_size);
+ return -1;
+ }
+ if (!wtap_read_bytes(fh, lapd_phdr, LAPD_SLL_LEN, err, err_info))
+ return -1;
+
+ if (pntoh16(&lapd_phdr[LAPD_SLL_PROTOCOL_OFFSET]) != ETH_P_LAPD) {
+ *err = WTAP_ERR_BAD_FILE;
+ if (err_info != NULL)
+ *err_info = g_strdup("pcap/pcapng: LAPD capture has a packet with an invalid sll_protocol field");
+ return -1;
+ }
+
+ pseudo_header->lapd.pkttype = pntoh16(&lapd_phdr[LAPD_SLL_PKTTYPE_OFFSET]);
+ pseudo_header->lapd.we_network = !!lapd_phdr[LAPD_SLL_ADDR_OFFSET+0];
+
+ return LAPD_SLL_LEN;
+}
+
+static gboolean
+pcap_write_lapd_pseudoheader(wtap_dumper *wdh,
+ const union wtap_pseudo_header *pseudo_header, int *err)
+{
+ guint8 lapd_hdr[LAPD_SLL_LEN];
+
+ /*
+ * Write the LAPD header.
+ */
+ memset(&lapd_hdr, 0, sizeof(lapd_hdr));
+ phtons(&lapd_hdr[LAPD_SLL_PKTTYPE_OFFSET], pseudo_header->lapd.pkttype);
+ phtons(&lapd_hdr[LAPD_SLL_PROTOCOL_OFFSET], ETH_P_LAPD);
+ lapd_hdr[LAPD_SLL_ADDR_OFFSET + 0] =
+ pseudo_header->lapd.we_network?0x01:0x00;
+ if (!wtap_dump_file_write(wdh, lapd_hdr, sizeof(lapd_hdr), err))
+ return FALSE;
+ return TRUE;
+}
+
+/*
+ * A header containing additional SITA WAN information.
+ */
+#define SITA_FLAGS_OFFSET 0 /* 1 byte */
+#define SITA_SIGNALS_OFFSET 1 /* 1 byte */
+#define SITA_ERRORS1_OFFSET 2 /* 1 byte */
+#define SITA_ERRORS2_OFFSET 3 /* 1 byte */
+#define SITA_PROTO_OFFSET 4 /* 1 byte */
+#define SITA_HDR_LEN 5 /* length of the header */
+
+static int
+pcap_read_sita_pseudoheader(FILE_T fh, union wtap_pseudo_header *pseudo_header,
+ guint packet_size, int *err, gchar **err_info)
+{
+ guint8 sita_phdr[SITA_HDR_LEN];
+
+ if (packet_size < SITA_HDR_LEN) {
+ /*
+ * Uh-oh, the packet isn't big enough to even
+ * have a pseudo-header.
+ */
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("pcap/pcapng: SITA file has a %u-byte packet, too small to have even a SITA pseudo-header",
+ packet_size);
+ return -1;
+ }
+ if (!wtap_read_bytes(fh, sita_phdr, SITA_HDR_LEN, err, err_info))
+ return -1;
+
+ pseudo_header->sita.sita_flags = sita_phdr[SITA_FLAGS_OFFSET];
+ pseudo_header->sita.sita_signals = sita_phdr[SITA_SIGNALS_OFFSET];
+ pseudo_header->sita.sita_errors1 = sita_phdr[SITA_ERRORS1_OFFSET];
+ pseudo_header->sita.sita_errors2 = sita_phdr[SITA_ERRORS2_OFFSET];
+ pseudo_header->sita.sita_proto = sita_phdr[SITA_PROTO_OFFSET];
+
+ return SITA_HDR_LEN;
+}
+
+static gboolean
+pcap_write_sita_pseudoheader(wtap_dumper *wdh,
+ const union wtap_pseudo_header *pseudo_header, int *err)
+{
+ guint8 sita_hdr[SITA_HDR_LEN];
+
+ /*
+ * Write the SITA header.
+ */
+ memset(&sita_hdr, 0, sizeof(sita_hdr));
+ sita_hdr[SITA_FLAGS_OFFSET] = pseudo_header->sita.sita_flags;
+ sita_hdr[SITA_SIGNALS_OFFSET] = pseudo_header->sita.sita_signals;
+ sita_hdr[SITA_ERRORS1_OFFSET] = pseudo_header->sita.sita_errors1;
+ sita_hdr[SITA_ERRORS2_OFFSET] = pseudo_header->sita.sita_errors2;
+ sita_hdr[SITA_PROTO_OFFSET] = pseudo_header->sita.sita_proto;
+ if (!wtap_dump_file_write(wdh, sita_hdr, sizeof(sita_hdr), err))
+ return FALSE;
+ return TRUE;
+}
+
+/*
+ * Pseudo-header at the beginning of DLT_BLUETOOTH_HCI_H4_WITH_PHDR frames.
+ * Values in network byte order.
+ */
+struct pcap_bt_phdr {
+ guint32 direction; /* Bit 0 hold the frame direction. */
+};
+
+#define LIBPCAP_BT_PHDR_SENT 0
+#define LIBPCAP_BT_PHDR_RECV 1
+
+static int
+pcap_read_bt_pseudoheader(FILE_T fh, union wtap_pseudo_header *pseudo_header,
+ guint packet_size, int *err, gchar **err_info)
+{
+ struct pcap_bt_phdr phdr;
+
+ if (packet_size < sizeof (struct pcap_bt_phdr)) {
+ /*
+ * Uh-oh, the packet isn't big enough to even
+ * have a pseudo-header.
+ */
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("pcap/pcapng: Bluetooth file has a %u-byte packet, too small to have even a pseudo-header",
+ packet_size);
+ return -1;
+ }
+ if (!wtap_read_bytes(fh, &phdr, sizeof (struct pcap_bt_phdr),
+ err, err_info))
+ return -1;
+ pseudo_header->p2p.sent = ((g_ntohl(phdr.direction) & LIBPCAP_BT_PHDR_RECV) == 0)? TRUE: FALSE;
+ return (int)sizeof (struct pcap_bt_phdr);
+}
+
+static gboolean
+pcap_write_bt_pseudoheader(wtap_dumper *wdh,
+ const union wtap_pseudo_header *pseudo_header, int *err)
+{
+ guint32 direction;
+ struct pcap_bt_phdr bt_hdr;
+
+ direction = pseudo_header->p2p.sent ? LIBPCAP_BT_PHDR_SENT : LIBPCAP_BT_PHDR_RECV;
+ bt_hdr.direction = GUINT32_TO_BE(direction);
+ if (!wtap_dump_file_write(wdh, &bt_hdr, sizeof bt_hdr, err))
+ return FALSE;
+ return TRUE;
+}
+
+/*
+ * Pseudo-header at the beginning of DLT_BLUETOOTH_LINUX_MONITOR frames.
+ * Values in network byte order.
+ */
+struct pcap_bt_monitor_phdr {
+ guint16 adapter_id;
+ guint16 opcode;
+};
+
+static int
+pcap_read_bt_monitor_pseudoheader(FILE_T fh,
+ union wtap_pseudo_header *pseudo_header, guint packet_size,
+ int *err, gchar **err_info)
+{
+ struct pcap_bt_monitor_phdr phdr;
+
+ if (packet_size < sizeof (struct pcap_bt_monitor_phdr)) {
+ /*
+ * Uh-oh, the packet isn't big enough to even
+ * have a pseudo-header.
+ */
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("pcap/pcapng: Bluetooth monitor file has a %u-byte packet, too small to have even a pseudo-header",
+ packet_size);
+ return -1;
+ }
+ if (!wtap_read_bytes(fh, &phdr, sizeof (struct pcap_bt_monitor_phdr),
+ err, err_info))
+ return -1;
+
+ pseudo_header->btmon.adapter_id = g_ntohs(phdr.adapter_id);
+ pseudo_header->btmon.opcode = g_ntohs(phdr.opcode);
+ return (int)sizeof (struct pcap_bt_monitor_phdr);
+}
+
+static gboolean
+pcap_write_bt_monitor_pseudoheader(wtap_dumper *wdh,
+ const union wtap_pseudo_header *pseudo_header, int *err)
+{
+ struct pcap_bt_monitor_phdr bt_monitor_hdr;
+
+ bt_monitor_hdr.adapter_id = GUINT16_TO_BE(pseudo_header->btmon.adapter_id);
+ bt_monitor_hdr.opcode = GUINT16_TO_BE(pseudo_header->btmon.opcode);
+
+ if (!wtap_dump_file_write(wdh, &bt_monitor_hdr, sizeof bt_monitor_hdr, err))
+ return FALSE;
+ return TRUE;
+}
+
+/*
+ * The NFC LLCP per-packet header.
+ */
+#define LLCP_ADAPTER_OFFSET 0
+#define LLCP_FLAGS_OFFSET 1
+#define LLCP_HEADER_LEN 2
+
+static int
+pcap_read_llcp_pseudoheader(FILE_T fh,
+ union wtap_pseudo_header *pseudo_header, guint packet_size,
+ int *err, gchar **err_info)
+{
+ guint8 phdr[LLCP_HEADER_LEN];
+
+ if (packet_size < LLCP_HEADER_LEN) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("pcap/pcapng: NFC LLCP file has a %u-byte packet, too small to have even a pseudo-header",
+ packet_size);
+ return -1;
+ }
+ if (!wtap_read_bytes(fh, phdr, LLCP_HEADER_LEN, err, err_info))
+ return -1;
+ pseudo_header->llcp.adapter = phdr[LLCP_ADAPTER_OFFSET];
+ pseudo_header->llcp.flags = phdr[LLCP_FLAGS_OFFSET];
+ return LLCP_HEADER_LEN;
+}
+
+static gboolean
+pcap_write_llcp_pseudoheader(wtap_dumper *wdh,
+ const union wtap_pseudo_header *pseudo_header, int *err)
+{
+ guint8 phdr[LLCP_HEADER_LEN];
+
+ phdr[LLCP_ADAPTER_OFFSET] = pseudo_header->llcp.adapter;
+ phdr[LLCP_FLAGS_OFFSET] = pseudo_header->llcp.flags;
+ if (!wtap_dump_file_write(wdh, &phdr, sizeof phdr, err))
+ return FALSE;
+ return TRUE;
+}
+
+/*
+ * Pseudo-header at the beginning of DLT_PPP_WITH_DIR frames.
+ */
+struct pcap_ppp_phdr {
+ guint8 direction;
+};
+
+/*
+ * Pseudo-header at the beginning of DLT_PPP_WITH_DIR frames.
+ */
+static int
+pcap_read_ppp_pseudoheader(FILE_T fh, union wtap_pseudo_header *pseudo_header,
+ guint packet_size, int *err, gchar **err_info)
+{
+ struct pcap_ppp_phdr phdr;
+
+ if (packet_size < sizeof (struct pcap_ppp_phdr)) {
+ /*
+ * Uh-oh, the packet isn't big enough to even
+ * have a pseudo-header.
+ */
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("pcap/pcapng: PPP file has a %u-byte packet, too small to have even a pseudo-header",
+ packet_size);
+ return -1;
+ }
+ if (!wtap_read_bytes(fh, &phdr, sizeof (struct pcap_ppp_phdr),
+ err, err_info))
+ return -1;
+ /* Any non-zero value means "sent" */
+ pseudo_header->p2p.sent = (phdr.direction != 0) ? TRUE: FALSE;
+ return (int)sizeof (struct pcap_ppp_phdr);
+}
+
+static gboolean
+pcap_write_ppp_pseudoheader(wtap_dumper *wdh,
+ const union wtap_pseudo_header *pseudo_header, int *err)
+{
+ struct pcap_ppp_phdr ppp_hdr;
+
+ /* Any non-zero value means "sent" */
+ ppp_hdr.direction = (pseudo_header->p2p.sent ? 1 : 0);
+ if (!wtap_dump_file_write(wdh, &ppp_hdr, sizeof ppp_hdr, err))
+ return FALSE;
+ return TRUE;
+}
+
+static int
+pcap_read_erf_pseudoheader(FILE_T fh, wtap_rec *rec,
+ union wtap_pseudo_header *pseudo_header, guint packet_size,
+ int *err, gchar **err_info)
+{
+ guint8 erf_hdr[sizeof(struct erf_phdr)];
+ guint8 erf_subhdr[sizeof(union erf_subhdr)];
+ int phdr_len;
+
+ if (packet_size < sizeof(struct erf_phdr)) {
+ /*
+ * Uh-oh, the packet isn't big enough to even
+ * have a pseudo-header.
+ */
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("pcap/pcapng: ERF file has a %u-byte packet, too small to have even an ERF pseudo-header",
+ packet_size);
+ return -1;
+ }
+ if (!wtap_read_bytes(fh, erf_hdr, sizeof(struct erf_phdr), err, err_info))
+ return -1;
+ phdr_len = (int)sizeof(struct erf_phdr);
+ pseudo_header->erf.phdr.ts = pletoh64(&erf_hdr[0]); /* timestamp */
+ pseudo_header->erf.phdr.type = erf_hdr[8];
+ pseudo_header->erf.phdr.flags = erf_hdr[9];
+ pseudo_header->erf.phdr.rlen = pntoh16(&erf_hdr[10]);
+ pseudo_header->erf.phdr.lctr = pntoh16(&erf_hdr[12]);
+ pseudo_header->erf.phdr.wlen = pntoh16(&erf_hdr[14]);
+
+ /* The high 32 bits of the timestamp contain the integer number of seconds
+ * while the lower 32 bits contain the binary fraction of the second.
+ * This allows an ultimate resolution of 1/(2^32) seconds, or approximately 233 picoseconds */
+ if (rec) {
+ guint64 ts = pseudo_header->erf.phdr.ts;
+ rec->ts.secs = (time_t) (ts >> 32);
+ ts = ((ts & 0xffffffff) * 1000 * 1000 * 1000);
+ ts += (ts & 0x80000000) << 1; /* rounding */
+ rec->ts.nsecs = ((guint32) (ts >> 32));
+ if (rec->ts.nsecs >= 1000000000) {
+ rec->ts.nsecs -= 1000000000;
+ rec->ts.secs += 1;
+ }
+
+ /*
+ * This time stamp came from the ERF header, not from the
+ * pcap packet header or pcapng block header, so its
+ * precision is that of ERF time stamps, not the pcap
+ * file's time stamp or the pcapng interface's time
+ * stamp.
+ */
+ rec->tsprec = WTAP_TSPREC_NSEC;
+ }
+
+ /*
+ * If the type of record given in the pseudo header indicates
+ * the presence of an extension header, then read all the
+ * extension headers.
+ */
+ if (pseudo_header->erf.phdr.type & 0x80) {
+ int i = 0, max = sizeof(pseudo_header->erf.ehdr_list)/sizeof(struct erf_ehdr);
+ guint8 erf_exhdr[8];
+ guint8 type;
+
+ do {
+ if (phdr_len > INT_MAX - 8) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("pcap/pcapng: ERF file has a packet larger than %d bytes",
+ INT_MAX);
+ return -1;
+ }
+ if (packet_size < (guint)phdr_len + 8) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("pcap/pcapng: ERF file has a %u-byte packet, too small to include the extension headers",
+ packet_size);
+ return -1;
+ }
+ if (!wtap_read_bytes(fh, erf_exhdr, 8, err, err_info))
+ return -1;
+ type = erf_exhdr[0];
+ if (i < max) {
+ guint64 erf_exhdr_sw;
+
+ erf_exhdr_sw = pntoh64(erf_exhdr);
+ memcpy(&pseudo_header->erf.ehdr_list[i].ehdr, &erf_exhdr_sw, sizeof(erf_exhdr_sw));
+ }
+ phdr_len += 8;
+ i++;
+ } while (type & 0x80);
+ }
+
+ /* check the optional subheader */
+ switch (pseudo_header->erf.phdr.type & 0x7F) {
+ case ERF_TYPE_MC_HDLC:
+ case ERF_TYPE_MC_RAW:
+ case ERF_TYPE_MC_ATM:
+ case ERF_TYPE_MC_RAW_CHANNEL:
+ case ERF_TYPE_MC_AAL5:
+ case ERF_TYPE_MC_AAL2:
+ case ERF_TYPE_COLOR_MC_HDLC_POS:
+ /* Extract the Multi Channel header to include it in the pseudo header part */
+ if (phdr_len > INT_MAX - (int)sizeof(erf_mc_header_t)) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("pcap/pcapng: ERF file has a packet larger than %d bytes",
+ INT_MAX);
+ return -1;
+ }
+ if (packet_size < (guint)(phdr_len + (int)sizeof(erf_mc_header_t))) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("pcap/pcapng: ERF file has a %u-byte packet, too small to include the Multi Channel header",
+ packet_size);
+ return -1;
+ }
+ if (!wtap_read_bytes(fh, erf_subhdr, sizeof(erf_mc_header_t), err, err_info))
+ return -1;
+ pseudo_header->erf.subhdr.mc_hdr = pntoh32(&erf_subhdr[0]);
+ phdr_len += sizeof(erf_mc_header_t);
+ break;
+ case ERF_TYPE_AAL2:
+ /* Extract the AAL2 header to include it in the pseudo header part */
+ if (phdr_len > INT_MAX - (int)sizeof(erf_aal2_header_t)) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("pcap/pcapng: ERF file has a packet larger than %d bytes",
+ INT_MAX);
+ return -1;
+ }
+ if (packet_size < (guint)(phdr_len + (int)sizeof(erf_aal2_header_t))) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("pcap/pcapng: ERF file has a %u-byte packet, too small to include the AAL2 header",
+ packet_size);
+ return -1;
+ }
+ if (!wtap_read_bytes(fh, erf_subhdr, sizeof(erf_aal2_header_t), err, err_info))
+ return -1;
+ pseudo_header->erf.subhdr.aal2_hdr = pntoh32(&erf_subhdr[0]);
+ phdr_len += sizeof(erf_aal2_header_t);
+ break;
+ case ERF_TYPE_ETH:
+ case ERF_TYPE_COLOR_ETH:
+ case ERF_TYPE_DSM_COLOR_ETH:
+ case ERF_TYPE_COLOR_HASH_ETH:
+ /* Extract the Ethernet additional header to include it in the pseudo header part */
+ if (phdr_len > INT_MAX - (int)sizeof(erf_eth_header_t)) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("pcap/pcapng: ERF file has a packet larger than %d bytes",
+ INT_MAX);
+ return -1;
+ }
+ if (packet_size < (guint)(phdr_len + (int)sizeof(erf_eth_header_t))) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("pcap/pcapng: ERF file has a %u-byte packet, too small to include the Ethernet additional header",
+ packet_size);
+ return -1;
+ }
+ if (!wtap_read_bytes(fh, erf_subhdr, sizeof(erf_eth_header_t), err, err_info))
+ return -1;
+ memcpy(&pseudo_header->erf.subhdr.eth_hdr, erf_subhdr, sizeof pseudo_header->erf.subhdr.eth_hdr);
+ phdr_len += sizeof(erf_eth_header_t);
+ break;
+ default:
+ /* No optional pseudo header for this ERF type */
+ break;
+ }
+ return phdr_len;
+}
+
+static gboolean
+pcap_write_erf_pseudoheader(wtap_dumper *wdh,
+ const union wtap_pseudo_header *pseudo_header, int *err)
+{
+ guint8 erf_hdr[sizeof(struct erf_phdr)];
+ guint8 erf_subhdr[sizeof(union erf_subhdr)];
+
+ /*
+ * Write the ERF header.
+ */
+ memset(&erf_hdr, 0, sizeof(erf_hdr));
+ phtolell(&erf_hdr[0], pseudo_header->erf.phdr.ts);
+ erf_hdr[8] = pseudo_header->erf.phdr.type;
+ erf_hdr[9] = pseudo_header->erf.phdr.flags;
+
+ /*
+ * Recalculate rlen as padding (and maybe extension headers)
+ * have been stripped from caplen.
+ *
+ * XXX: Since we don't have rec->rec_header.packet_header.caplen
+ * here, assume caplen was calculated correctly and
+ * recalculate from wlen.
+ */
+ phtons(&erf_hdr[10],
+ MIN(pseudo_header->erf.phdr.rlen, pseudo_header->erf.phdr.wlen + pcap_get_phdr_size(WTAP_ENCAP_ERF, pseudo_header)));
+
+ phtons(&erf_hdr[12], pseudo_header->erf.phdr.lctr);
+ phtons(&erf_hdr[14], pseudo_header->erf.phdr.wlen);
+ if (!wtap_dump_file_write(wdh, erf_hdr, sizeof(struct erf_phdr), err))
+ return FALSE;
+
+ /*
+ * Now write out the extension headers.
+ */
+ if (pseudo_header->erf.phdr.type & 0x80) {
+ int i = 0, max = sizeof(pseudo_header->erf.ehdr_list)/sizeof(struct erf_ehdr);
+ guint8 erf_exhdr[8];
+ guint8 type;
+
+ do {
+ phtonll(erf_exhdr, pseudo_header->erf.ehdr_list[i].ehdr);
+ type = erf_exhdr[0];
+ /* Clear more extension headers bit if > 8 */
+ if(i == max-1)
+ erf_exhdr[0] = erf_exhdr[0] & 0x7F;
+ if (!wtap_dump_file_write(wdh, erf_exhdr, 8, err))
+ return FALSE;
+ i++;
+ } while (type & 0x80 && i < max);
+ }
+
+ /*
+ * Now write out the subheader, if any
+ */
+ switch (pseudo_header->erf.phdr.type & 0x7F) {
+ case ERF_TYPE_MC_HDLC:
+ case ERF_TYPE_MC_RAW:
+ case ERF_TYPE_MC_ATM:
+ case ERF_TYPE_MC_RAW_CHANNEL:
+ case ERF_TYPE_MC_AAL5:
+ case ERF_TYPE_MC_AAL2:
+ case ERF_TYPE_COLOR_MC_HDLC_POS:
+ phtonl(&erf_subhdr[0], pseudo_header->erf.subhdr.mc_hdr);
+ if (!wtap_dump_file_write(wdh, erf_subhdr,
+ sizeof(struct erf_mc_hdr), err))
+ return FALSE;
+ break;
+ case ERF_TYPE_AAL2:
+ phtonl(&erf_subhdr[0], pseudo_header->erf.subhdr.aal2_hdr);
+ if (!wtap_dump_file_write(wdh, erf_subhdr,
+ sizeof(struct erf_aal2_hdr), err))
+ return FALSE;
+ break;
+ case ERF_TYPE_ETH:
+ case ERF_TYPE_COLOR_ETH:
+ case ERF_TYPE_DSM_COLOR_ETH:
+ case ERF_TYPE_COLOR_HASH_ETH:
+ memcpy(&erf_subhdr[0], &pseudo_header->erf.subhdr.eth_hdr, sizeof pseudo_header->erf.subhdr.eth_hdr);
+ if (!wtap_dump_file_write(wdh, erf_subhdr,
+ sizeof(struct erf_eth_hdr), err))
+ return FALSE;
+ break;
+ default:
+ break;
+ }
+ return TRUE;
+}
+
+/*
+ * I2C-with=Linux-pseudoheader link-layer on-disk format, as defined by
+ * Pigeon Point Systems.
+ */
+struct i2c_linux_file_hdr {
+ guint8 bus;
+ guint8 flags[4];
+};
+
+static int
+pcap_read_i2c_linux_pseudoheader(FILE_T fh, union wtap_pseudo_header *pseudo_header,
+ guint packet_size, int *err, gchar **err_info)
+{
+ struct i2c_linux_file_hdr i2c_linux_hdr;
+
+ if (packet_size < sizeof (struct i2c_linux_file_hdr)) {
+ /*
+ * Uh-oh, the packet isn't big enough to even
+ * have a pseudo-header.
+ */
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("pcap/pcapng: I2C file has a %u-byte packet, too small to have even a I2C pseudo-header",
+ packet_size);
+ return -1;
+ }
+ if (!wtap_read_bytes(fh, &i2c_linux_hdr, sizeof (i2c_linux_hdr), err, err_info))
+ return -1;
+
+ pseudo_header->i2c.is_event = i2c_linux_hdr.bus & 0x80 ? 1 : 0;
+ pseudo_header->i2c.bus = i2c_linux_hdr.bus & 0x7f;
+ pseudo_header->i2c.flags = pntoh32(&i2c_linux_hdr.flags);
+
+ return (int)sizeof (struct i2c_linux_file_hdr);
+}
+
+static gboolean
+pcap_write_i2c_linux_pseudoheader(wtap_dumper *wdh,
+ const union wtap_pseudo_header *pseudo_header, int *err)
+{
+ struct i2c_linux_file_hdr i2c_linux_hdr;
+
+ /*
+ * Write the I2C Linux-specific pseudo-header.
+ */
+ memset(&i2c_linux_hdr, 0, sizeof(i2c_linux_hdr));
+ i2c_linux_hdr.bus = pseudo_header->i2c.bus |
+ (pseudo_header->i2c.is_event ? 0x80 : 0x00);
+ phtonl((guint8 *)&i2c_linux_hdr.flags, pseudo_header->i2c.flags);
+ if (!wtap_dump_file_write(wdh, &i2c_linux_hdr, sizeof(i2c_linux_hdr), err))
+ return FALSE;
+ return TRUE;
+}
+
+/*
+ * The link-layer header on Nokia IPSO packets.
+ */
+#define NOKIA_LEN 4 /* length of the header */
+
+static gboolean
+pcap_read_nokia_pseudoheader(FILE_T fh,
+ union wtap_pseudo_header *pseudo_header, int *err, gchar **err_info)
+{
+ guint8 phdr[NOKIA_LEN];
+
+
+ /* backtrack to read the 4 mysterious bytes that aren't considered
+ * part of the packet size
+ */
+ if (file_seek(fh, -NOKIA_LEN, SEEK_CUR, err) == -1)
+ {
+ *err = file_error(fh, err_info);
+ if (*err == 0)
+ *err = WTAP_ERR_SHORT_READ;
+ return FALSE;
+ }
+
+ if (!wtap_read_bytes(fh, phdr, NOKIA_LEN, err, err_info))
+ return FALSE;
+
+ memcpy(pseudo_header->nokia.stuff, phdr, NOKIA_LEN);
+
+ return TRUE;
+}
+
+/*
+ * When not using the memory-mapped interface to capture USB events,
+ * code that reads those events can use the MON_IOCX_GET ioctl to
+ * read a 48-byte header consisting of a "struct linux_usb_phdr", as
+ * defined below, followed immediately by one of:
+ *
+ * 8 bytes of a "struct usb_device_setup_hdr", if "setup_flag"
+ * in the preceding "struct linux_usb_phdr" is 0;
+ *
+ * in Linux 2.6.30 or later, 8 bytes of a "struct iso_rec", if
+ * this is an isochronous transfer;
+ *
+ * 8 bytes of junk, otherwise.
+ *
+ * In Linux 2.6.31 and later, it can also use the MON_IOCX_GETX ioctl
+ * to read a 64-byte header; that header consists of the 48 bytes
+ * above, followed immediately by 16 bytes of a "struct linux_usb_phdr_ext",
+ * as defined below.
+ *
+ * In Linux 2.6.21 and later, there's a memory-mapped interface to
+ * capture USB events. In that interface, the events in the memory-mapped
+ * buffer have a 64-byte header, followed immediately by the data.
+ * In Linux 2.6.21 through 2.6.30.x, the 64-byte header is the 48-byte
+ * header described above, followed by 16 bytes of zeroes; in Linux
+ * 2.6.31 and later, the 64-byte header is the 64-byte header described
+ * above.
+ *
+ * See linux/Documentation/usb/usbmon.txt and libpcap/pcap/usb.h for details.
+ *
+ * With WTAP_ENCAP_USB_LINUX, packets have the 48-byte header; with
+ * WTAP_ENCAP_USB_LINUX_MMAPPED, they have the 64-byte header. There
+ * is no indication of whether the header has the "struct iso_rec", or
+ * whether the last 16 bytes of a 64-byte header are all zeros or are
+ * a "struct linux_usb_phdr_ext".
+ */
+
+/*
+ * URB transfer_type values
+ */
+#define URB_ISOCHRONOUS 0x0
+#define URB_INTERRUPT 0x1
+#define URB_CONTROL 0x2
+#define URB_BULK 0x3
+
+/*
+ * Information from the URB for Isochronous transfers.
+ *
+ * This structure is 8 bytes long.
+ */
+struct iso_rec {
+ gint32 error_count;
+ gint32 numdesc;
+};
+
+/*
+ * Header prepended by Linux kernel to each USB event.
+ *
+ * (Setup flag is '-', 'D', 'Z', or 0. Data flag is '<', '>', 'Z', or 0.)
+ *
+ * The values are in *host* byte order.
+ */
+struct linux_usb_phdr {
+ guint64 id; /* urb id, to link submission and completion events */
+ guint8 event_type; /* Submit ('S'), Completed ('C'), Error ('E') */
+ guint8 transfer_type; /* ISO (0), Intr, Control, Bulk (3) */
+ guint8 endpoint_number; /* Endpoint number (0-15) and transfer direction */
+ guint8 device_address; /* 0-127 */
+ guint16 bus_id;
+ gint8 setup_flag; /* 0, if the urb setup header is meaningful */
+ gint8 data_flag; /* 0, if urb data is present */
+ gint64 ts_sec;
+ gint32 ts_usec;
+ gint32 status;
+ guint32 urb_len; /* whole len of urb this event refers to */
+ guint32 data_len; /* amount of urb data really present in this event */
+
+ /*
+ * Packet-type-dependent data.
+ * USB setup information of setup_flag is true.
+ * Otherwise, some isochronous transfer information.
+ */
+ union {
+ guint8 data[8];
+ struct iso_rec iso;
+ } s;
+
+ /*
+ * This data is provided by Linux 2.6.31 and later kernels.
+ *
+ * For WTAP_ENCAP_USB_LINUX, it's not in the pseudo-header, so
+ * the pseudo-header is always 48 bytes long, including the
+ * packet-type-dependent data.
+ *
+ * For WTAP_ENCAP_USB_LINUX_MMAPPED, the pseudo-header is always
+ * 64 bytes long, with the packet-type-dependent data preceding
+ * these last 16 bytes. In pre-2.6.31 kernels, it's zero padding;
+ * in 2.6.31 and later, it's the following data.
+ */
+ gint32 interval; /* only for Interrupt and Isochronous events */
+ gint32 start_frame; /* for Isochronous */
+ guint32 xfer_flags; /* copy of URB's transfer_flags */
+ guint32 ndesc; /* actual number of isochronous descriptors */
+};
+
+/*
+ * event_type values
+ */
+#define URB_SUBMIT 'S'
+#define URB_COMPLETE 'C'
+#define URB_ERROR 'E'
+
+/*
+ * URB transfer_type values
+ */
+#define URB_ISOCHRONOUS 0x0
+#define URB_INTERRUPT 0x1
+#define URB_CONTROL 0x2
+#define URB_BULK 0x3
+#define URB_UNKNOWN 0xFF
+
+#define URB_TRANSFER_IN 0x80 /* to host */
+
+struct linux_usb_isodesc {
+ gint32 iso_status;
+ guint32 iso_off;
+ guint32 iso_len;
+ guint32 _pad;
+};
+
+/*
+ * USB setup header as defined in USB specification
+ * See usb_20.pdf, Chapter 9.3 'USB Device Requests' for details.
+ * https://www.usb.org/document-library/usb-20-specification
+ *
+ * This structure is 8 bytes long.
+ */
+struct usb_device_setup_hdr {
+ gint8 bmRequestType;
+ guint8 bRequest;
+ guint16 wValue;
+ guint16 wIndex;
+ guint16 wLength;
+};
+
+/*
+ * Offset of the *end* of a field within a particular structure.
+ */
+#define END_OFFSETOF(basep, fieldp) \
+ (((char *)(void *)(fieldp)) - ((char *)(void *)(basep)) + \
+ sizeof(*fieldp))
+
+/*
+ * Is that offset within the bounds of the packet?
+ */
+#define WITHIN_PACKET(basep, fieldp) \
+ (packet_size >= END_OFFSETOF((basep), (fieldp)))
+
+#define CHECK_AND_SWAP16(fieldp) \
+ { \
+ if (!WITHIN_PACKET(usb_phdr, fieldp)) \
+ return; \
+ PBSWAP16((guint8 *)fieldp); \
+ }
+
+#define CHECK_AND_SWAP32(fieldp) \
+ { \
+ if (!WITHIN_PACKET(usb_phdr, fieldp)) \
+ return; \
+ PBSWAP32((guint8 *)fieldp); \
+ }
+
+#define CHECK_AND_SWAP64(fieldp) \
+ { \
+ if (!WITHIN_PACKET(usb_phdr, fieldp)) \
+ return; \
+ PBSWAP64((guint8 *)fieldp); \
+ }
+
+struct can_socketcan_hdr {
+ guint32 can_id; /* CAN ID and flags */
+ guint8 payload_length; /* Frame payload length */
+ guint8 padding;
+ guint8 reserved1;
+ guint8 reserved2;
+};
+
+/*
+ * CAN fake link-layer headers in Linux cooked packets.
+ */
+#define LINUX_SLL_PROTOCOL_OFFSET 14 /* protocol */
+#define LINUX_SLL_LEN 16 /* length of the header */
+
+#define LINUX_SLL2_PROTOCOL_OFFSET 0 /* protocol */
+#define LINUX_SLL2_LEN 20 /* length of the header */
+
+/*
+ * The protocols we have to check for.
+ */
+#define LINUX_SLL_P_CAN 0x000C /* Controller Area Network */
+#define LINUX_SLL_P_CANFD 0x000D /* Controller Area Network flexible data rate */
+
+static void
+pcap_byteswap_linux_sll_pseudoheader(wtap_rec *rec, guint8 *pd)
+{
+ guint packet_size;
+ guint16 protocol;
+ struct can_socketcan_hdr *can_socketcan_phdr;
+
+ /*
+ * Minimum of captured and actual length (just in case the
+ * actual length < the captured length, which Should Never
+ * Happen).
+ */
+ packet_size = rec->rec_header.packet_header.caplen;
+ if (packet_size > rec->rec_header.packet_header.len)
+ packet_size = rec->rec_header.packet_header.len;
+
+ if (packet_size < LINUX_SLL_LEN) {
+ /* Not enough data to have the protocol */
+ return;
+ }
+
+ protocol = pntoh16(&pd[LINUX_SLL_PROTOCOL_OFFSET]);
+ if (protocol != LINUX_SLL_P_CAN && protocol != LINUX_SLL_P_CANFD) {
+ /* Not a CAN packet; nothing to fix */
+ return;
+ }
+
+ /*
+ * Greasy hack, but we never directly dereference any of
+ * the fields in *can_socketcan_phdr, we just get offsets
+ * of and addresses of its members and byte-swap it with a
+ * byte-at-a-time macro, so it's alignment-safe.
+ */
+ can_socketcan_phdr = (struct can_socketcan_hdr *)(void *)(pd + LINUX_SLL_LEN);
+
+ if (packet_size < LINUX_SLL_LEN + sizeof(can_socketcan_phdr->can_id)) {
+ /* Not enough data to have the full CAN ID */
+ return;
+ }
+
+ PBSWAP32((guint8 *)&can_socketcan_phdr->can_id);
+}
+
+static void
+pcap_byteswap_linux_sll2_pseudoheader(wtap_rec *rec, guint8 *pd)
+{
+ guint packet_size;
+ guint16 protocol;
+ struct can_socketcan_hdr *can_socketcan_phdr;
+
+ /*
+ * Minimum of captured and actual length (just in case the
+ * actual length < the captured length, which Should Never
+ * Happen).
+ */
+ packet_size = rec->rec_header.packet_header.caplen;
+ if (packet_size > rec->rec_header.packet_header.len)
+ packet_size = rec->rec_header.packet_header.len;
+
+ if (packet_size < LINUX_SLL2_LEN) {
+ /* Not enough data to have the protocol */
+ return;
+ }
+
+ protocol = pntoh16(&pd[LINUX_SLL2_PROTOCOL_OFFSET]);
+ if (protocol != LINUX_SLL_P_CAN && protocol != LINUX_SLL_P_CANFD) {
+ /* Not a CAN packet; nothing to fix */
+ return;
+ }
+
+ /*
+ * Greasy hack, but we never directly dereference any of
+ * the fields in *can_socketcan_phdr, we just get offsets
+ * of and addresses of its members and byte-swap it with a
+ * byte-at-a-time macro, so it's alignment-safe.
+ */
+ can_socketcan_phdr = (struct can_socketcan_hdr *)(void *)(pd + LINUX_SLL2_LEN);
+
+ if (packet_size < LINUX_SLL2_LEN + sizeof(can_socketcan_phdr->can_id)) {
+ /* Not enough data to have the full CAN ID */
+ return;
+ }
+
+ PBSWAP32((guint8 *)&can_socketcan_phdr->can_id);
+}
+
+static void
+pcap_byteswap_linux_usb_pseudoheader(wtap_rec *rec, guint8 *pd,
+ gboolean header_len_64_bytes)
+{
+ guint packet_size;
+ struct linux_usb_phdr *usb_phdr;
+ struct linux_usb_isodesc *pisodesc;
+ gint32 iso_numdesc, i;
+
+ /*
+ * Minimum of captured and actual length (just in case the
+ * actual length < the captured length, which Should Never
+ * Happen).
+ */
+ packet_size = rec->rec_header.packet_header.caplen;
+ if (packet_size > rec->rec_header.packet_header.len)
+ packet_size = rec->rec_header.packet_header.len;
+
+ /*
+ * Greasy hack, but we never directly dereference any of
+ * the fields in *usb_phdr, we just get offsets of and
+ * addresses of its members and byte-swap it with a
+ * byte-at-a-time macro, so it's alignment-safe.
+ */
+ usb_phdr = (struct linux_usb_phdr *)(void *)pd;
+
+ CHECK_AND_SWAP64(&usb_phdr->id);
+ CHECK_AND_SWAP16(&usb_phdr->bus_id);
+ CHECK_AND_SWAP64(&usb_phdr->ts_sec);
+ CHECK_AND_SWAP32(&usb_phdr->ts_usec);
+ CHECK_AND_SWAP32(&usb_phdr->status);
+ CHECK_AND_SWAP32(&usb_phdr->urb_len);
+ CHECK_AND_SWAP32(&usb_phdr->data_len);
+
+ if (usb_phdr->transfer_type == URB_ISOCHRONOUS) {
+ CHECK_AND_SWAP32(&usb_phdr->s.iso.error_count);
+ CHECK_AND_SWAP32(&usb_phdr->s.iso.numdesc);
+ }
+
+ if (header_len_64_bytes) {
+ /*
+ * This is either the "version 1" header, with
+ * 16 bytes of additional fields at the end, or
+ * a "version 0" header from a memory-mapped
+ * capture, with 16 bytes of zeroed-out padding
+ * at the end. Byte swap them as if this were
+ * a "version 1" header.
+ *
+ * Yes, the first argument to END_OFFSETOF() should
+ * be usb_phdr, not usb_phdr_ext; we want the offset of
+ * the additional fields from the beginning of
+ * the packet.
+ */
+ CHECK_AND_SWAP32(&usb_phdr->interval);
+ CHECK_AND_SWAP32(&usb_phdr->start_frame);
+ CHECK_AND_SWAP32(&usb_phdr->xfer_flags);
+ CHECK_AND_SWAP32(&usb_phdr->ndesc);
+ }
+
+ if (usb_phdr->transfer_type == URB_ISOCHRONOUS) {
+ /* swap the values in struct linux_usb_isodesc */
+
+ /*
+ * See previous "Greasy hack" comment.
+ */
+ if (header_len_64_bytes) {
+ pisodesc = (struct linux_usb_isodesc*)(void *)(pd + 64);
+ } else {
+ pisodesc = (struct linux_usb_isodesc*)(void *)(pd + 48);
+ }
+ iso_numdesc = usb_phdr->s.iso.numdesc;
+ for (i = 0; i < iso_numdesc; i++) {
+ CHECK_AND_SWAP32(&pisodesc->iso_status);
+ CHECK_AND_SWAP32(&pisodesc->iso_off);
+ CHECK_AND_SWAP32(&pisodesc->iso_len);
+ CHECK_AND_SWAP32(&pisodesc->_pad);
+
+ pisodesc++;
+ }
+ }
+}
+
+struct nflog_hdr {
+ guint8 nflog_family; /* address family */
+ guint8 nflog_version; /* version */
+ guint16 nflog_rid; /* resource ID */
+};
+
+struct nflog_tlv {
+ guint16 tlv_length; /* tlv length */
+ guint16 tlv_type; /* tlv type */
+ /* value follows this */
+};
+
+static void
+pcap_byteswap_nflog_pseudoheader(wtap_rec *rec, guint8 *pd)
+{
+ guint packet_size;
+ guint8 *p;
+ struct nflog_hdr *nfhdr;
+ struct nflog_tlv *tlv;
+ guint size;
+
+ /*
+ * Minimum of captured and actual length (just in case the
+ * actual length < the captured length, which Should Never
+ * Happen).
+ */
+ packet_size = rec->rec_header.packet_header.caplen;
+ if (packet_size > rec->rec_header.packet_header.len)
+ packet_size = rec->rec_header.packet_header.len;
+
+ if (packet_size < sizeof(struct nflog_hdr)) {
+ /* Not enough data to have any TLVs. */
+ return;
+ }
+
+ p = pd;
+ nfhdr = (struct nflog_hdr *)pd;
+ if (nfhdr->nflog_version != 0) {
+ /* Unknown NFLOG version */
+ return;
+ }
+
+ packet_size -= (guint)sizeof(struct nflog_hdr);
+ p += sizeof(struct nflog_hdr);
+
+ while (packet_size >= sizeof(struct nflog_tlv)) {
+ tlv = (struct nflog_tlv *) p;
+
+ /* Swap the type and length. */
+ PBSWAP16((guint8 *)&tlv->tlv_type);
+ PBSWAP16((guint8 *)&tlv->tlv_length);
+
+ /* Get the length of the TLV. */
+ size = tlv->tlv_length;
+ if (size % 4 != 0)
+ size += 4 - size % 4;
+
+ /* Is the TLV's length less than the minimum? */
+ if (size < sizeof(struct nflog_tlv)) {
+ /* Yes. Give up now. */
+ return;
+ }
+
+ /* Do we have enough data for the full TLV? */
+ if (packet_size < size) {
+ /* No. */
+ return;
+ }
+
+ /* Skip over the TLV. */
+ packet_size -= size;
+ p += size;
+ }
+}
+
+/*
+ * pflog headers, at least as they exist now.
+ */
+#define PFLOG_IFNAMSIZ 16
+#define PFLOG_RULESET_NAME_SIZE 16
+
+struct pfloghdr {
+ guint8 length;
+ guint8 af;
+ guint8 action;
+ guint8 reason;
+ char ifname[PFLOG_IFNAMSIZ];
+ char ruleset[PFLOG_RULESET_NAME_SIZE];
+ guint32 rulenr;
+ guint32 subrulenr;
+ guint32 uid;
+ gint32 pid;
+ guint32 rule_uid;
+ gint32 rule_pid;
+ guint8 dir;
+ /* More follows, depending on the header length */
+};
+
+static void
+pcap_byteswap_pflog_pseudoheader(wtap_rec *rec, guint8 *pd)
+{
+ guint packet_size;
+ struct pfloghdr *pflhdr;
+
+ /*
+ * Minimum of captured and actual length (just in case the
+ * actual length < the captured length, which Should Never
+ * Happen).
+ */
+ packet_size = rec->rec_header.packet_header.caplen;
+ if (packet_size > rec->rec_header.packet_header.len)
+ packet_size = rec->rec_header.packet_header.len;
+
+ if (packet_size < sizeof(struct pfloghdr)) {
+ /* Not enough data to have the UID and PID fields */
+ return;
+ }
+
+ pflhdr = (struct pfloghdr *)pd;
+ if (pflhdr->length < (guint) (offsetof(struct pfloghdr, rule_pid) + sizeof pflhdr->rule_pid)) {
+ /* Header doesn't include the UID and PID fields */
+ return;
+ }
+ PBSWAP32((guint8 *)&pflhdr->uid);
+ PBSWAP32((guint8 *)&pflhdr->pid);
+ PBSWAP32((guint8 *)&pflhdr->rule_uid);
+ PBSWAP32((guint8 *)&pflhdr->rule_pid);
+}
+
+int
+pcap_process_pseudo_header(FILE_T fh, gboolean is_nokia, int wtap_encap,
+ guint packet_size, wtap_rec *rec, int *err, gchar **err_info)
+{
+ int phdr_len = 0;
+
+ switch (wtap_encap) {
+
+ case WTAP_ENCAP_ATM_PDUS:
+ if (is_nokia) {
+ /*
+ * Nokia IPSO ATM.
+ */
+ phdr_len = pcap_read_nokiaatm_pseudoheader(fh,
+ &rec->rec_header.packet_header.pseudo_header,
+ packet_size, err, err_info);
+ if (phdr_len == -1)
+ return -1; /* Read error */
+ } else {
+ /*
+ * SunATM.
+ */
+ phdr_len = pcap_read_sunatm_pseudoheader(fh,
+ &rec->rec_header.packet_header.pseudo_header,
+ packet_size, err, err_info);
+ if (phdr_len == -1)
+ return -1; /* Read error */
+ }
+ break;
+
+ case WTAP_ENCAP_ETHERNET:
+ if (is_nokia) {
+ /*
+ * Nokia IPSO. Pseudo header has already been read, but it's not considered
+ * part of the packet size, so reread it to store the data for later (when saving)
+ */
+ if (!pcap_read_nokia_pseudoheader(fh, &rec->rec_header.packet_header.pseudo_header, err, err_info))
+ return -1; /* Read error */
+ }
+
+ /*
+ * We don't know whether there's an FCS in this frame or not.
+ */
+ rec->rec_header.packet_header.pseudo_header.eth.fcs_len = -1;
+ break;
+
+ case WTAP_ENCAP_IEEE_802_11:
+ case WTAP_ENCAP_IEEE_802_11_PRISM:
+ case WTAP_ENCAP_IEEE_802_11_RADIOTAP:
+ case WTAP_ENCAP_IEEE_802_11_AVS:
+ /*
+ * We don't know whether there's an FCS in this frame or not,
+ * at least in pcap files. For radiotap, that's indicated in
+ * the radiotap header.
+ *
+ * XXX - in pcapng, there *could* be a packet option
+ * indicating the FCS length.
+ */
+ memset(&rec->rec_header.packet_header.pseudo_header.ieee_802_11, 0, sizeof(rec->rec_header.packet_header.pseudo_header.ieee_802_11));
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.fcs_len = -1;
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.decrypted = FALSE;
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.datapad = FALSE;
+ break;
+
+ case WTAP_ENCAP_IRDA:
+ phdr_len = pcap_read_irda_pseudoheader(fh,
+ &rec->rec_header.packet_header.pseudo_header,
+ packet_size, err, err_info);
+ if (phdr_len == -1)
+ return -1; /* Read error */
+ break;
+
+ case WTAP_ENCAP_MTP2_WITH_PHDR:
+ phdr_len = pcap_read_mtp2_pseudoheader(fh,
+ &rec->rec_header.packet_header.pseudo_header,
+ packet_size, err, err_info);
+ if (phdr_len == -1)
+ return -1; /* Read error */
+ break;
+
+ case WTAP_ENCAP_LINUX_LAPD:
+ phdr_len = pcap_read_lapd_pseudoheader(fh,
+ &rec->rec_header.packet_header.pseudo_header,
+ packet_size, err, err_info);
+ if (phdr_len == -1)
+ return -1; /* Read error */
+ break;
+
+ case WTAP_ENCAP_SITA:
+ phdr_len = pcap_read_sita_pseudoheader(fh,
+ &rec->rec_header.packet_header.pseudo_header,
+ packet_size, err, err_info);
+ if (phdr_len == -1)
+ return -1; /* Read error */
+ break;
+
+ case WTAP_ENCAP_BLUETOOTH_H4:
+ /* We don't have pseudoheader, so just pretend we received everything. */
+ rec->rec_header.packet_header.pseudo_header.p2p.sent = FALSE;
+ break;
+
+ case WTAP_ENCAP_BLUETOOTH_H4_WITH_PHDR:
+ phdr_len = pcap_read_bt_pseudoheader(fh,
+ &rec->rec_header.packet_header.pseudo_header,
+ packet_size, err, err_info);
+ if (phdr_len == -1)
+ return -1; /* Read error */
+ break;
+
+ case WTAP_ENCAP_BLUETOOTH_LINUX_MONITOR:
+ phdr_len = pcap_read_bt_monitor_pseudoheader(fh,
+ &rec->rec_header.packet_header.pseudo_header,
+ packet_size, err, err_info);
+ if (phdr_len == -1)
+ return -1; /* Read error */
+ break;
+
+ case WTAP_ENCAP_NFC_LLCP:
+ phdr_len = pcap_read_llcp_pseudoheader(fh,
+ &rec->rec_header.packet_header.pseudo_header,
+ packet_size, err, err_info);
+ if (phdr_len == -1)
+ return -1; /* Read error */
+ break;
+
+ case WTAP_ENCAP_PPP_WITH_PHDR:
+ phdr_len = pcap_read_ppp_pseudoheader(fh,
+ &rec->rec_header.packet_header.pseudo_header,
+ packet_size, err, err_info);
+ if (phdr_len == -1)
+ return -1; /* Read error */
+ break;
+
+ case WTAP_ENCAP_ERF:
+ phdr_len = pcap_read_erf_pseudoheader(fh, rec,
+ &rec->rec_header.packet_header.pseudo_header,
+ packet_size, err, err_info);
+ if (phdr_len == -1)
+ return -1; /* Read error */
+ break;
+
+ case WTAP_ENCAP_I2C_LINUX:
+ phdr_len = pcap_read_i2c_linux_pseudoheader(fh,
+ &rec->rec_header.packet_header.pseudo_header,
+ packet_size, err, err_info);
+ if (phdr_len == -1)
+ return -1; /* Read error */
+ break;
+ }
+
+ return phdr_len;
+}
+
+/*
+ * Compute, from the data provided by the Linux USB memory-mapped capture
+ * mechanism, the amount of packet data that would have been provided
+ * had the capture mechanism not chopped off any data at the end, if, in
+ * fact, it did so.
+ *
+ * Set the "unsliced length" field of the packet header to that value.
+ */
+static void
+fix_linux_usb_mmapped_length(wtap_rec *rec, const u_char *bp)
+{
+ const struct linux_usb_phdr *hdr;
+ u_int bytes_left;
+
+ /*
+ * All callers of this routine must ensure that pkth->caplen is
+ * >= sizeof (struct linux_usb_phdr).
+ */
+ bytes_left = rec->rec_header.packet_header.caplen;
+ bytes_left -= sizeof (struct linux_usb_phdr);
+
+ hdr = (const struct linux_usb_phdr *) bp;
+ if (!hdr->data_flag && hdr->transfer_type == URB_ISOCHRONOUS &&
+ hdr->event_type == URB_COMPLETE &&
+ (hdr->endpoint_number & URB_TRANSFER_IN) &&
+ rec->rec_header.packet_header.len == sizeof(struct linux_usb_phdr) +
+ (hdr->ndesc * sizeof (struct linux_usb_isodesc)) + hdr->urb_len) {
+ struct linux_usb_isodesc *descs;
+ u_int pre_truncation_data_len, pre_truncation_len;
+
+ descs = (struct linux_usb_isodesc *) (bp + sizeof(struct linux_usb_phdr));
+
+ /*
+ * We have data (yes, data_flag is 0 if we *do* have data),
+ * and this is a "this is complete" incoming isochronous
+ * transfer event, and the length was calculated based
+ * on the URB length.
+ *
+ * That's not correct, because the data isn't contiguous,
+ * and the isochronous descriptos show how it's scattered.
+ *
+ * Find the end of the last chunk of data in the buffer
+ * referred to by the isochronous descriptors; that indicates
+ * how far into the buffer the data would have gone.
+ *
+ * Make sure we don't run past the end of the captured data
+ * while processing the isochronous descriptors.
+ */
+ pre_truncation_data_len = 0;
+ for (guint32 desc = 0;
+ desc < hdr->ndesc && bytes_left >= sizeof (struct linux_usb_isodesc);
+ desc++, bytes_left -= sizeof (struct linux_usb_isodesc)) {
+ u_int desc_end;
+
+ if (descs[desc].iso_len != 0) {
+ desc_end = descs[desc].iso_off + descs[desc].iso_len;
+ if (desc_end > pre_truncation_data_len)
+ pre_truncation_data_len = desc_end;
+ }
+ }
+
+ /*
+ * Now calculate the total length based on that data
+ * length.
+ */
+ pre_truncation_len = sizeof(struct linux_usb_phdr) +
+ (hdr->ndesc * sizeof (struct linux_usb_isodesc)) +
+ pre_truncation_data_len;
+
+ /*
+ * If that's greater than or equal to the captured length,
+ * use that as the length.
+ */
+ if (pre_truncation_len >= rec->rec_header.packet_header.caplen)
+ rec->rec_header.packet_header.len = pre_truncation_len;
+
+ /*
+ * If the captured length is greater than the length,
+ * use the captured length.
+ *
+ * For completion events for incoming isochronous transfers,
+ * it's based on data_len, which is calculated the same way
+ * we calculated pre_truncation_data_len above, except that
+ * it has access to all the isochronous descriptors, not
+ * just the ones that the kernel were able to provide us or,
+ * for a capture file, that weren't sliced off by a snapshot
+ * length.
+ *
+ * However, it might have been reduced by the USB capture
+ * mechanism arbitrarily limiting the amount of data it
+ * provides to userland, or by the libpcap capture code
+ * limiting it to being no more than the snapshot, so
+ * we don't want to just use it all the time; we only
+ * do so to try to get a better estimate of the actual
+ * length - and to make sure the on-the-network length
+ * is always >= the captured length.
+ */
+ if (rec->rec_header.packet_header.caplen > rec->rec_header.packet_header.len)
+ rec->rec_header.packet_header.len = rec->rec_header.packet_header.caplen;
+ }
+}
+
+static void
+pcap_fixup_len(wtap_rec *rec, const guint8 *pd)
+{
+ struct linux_usb_phdr *usb_phdr;
+
+ /*
+ * Greasy hack, but we never directly dereference any of
+ * the fields in *usb_phdr, we just get offsets of and
+ * addresses of its members and byte-swap it with a
+ * byte-at-a-time macro, so it's alignment-safe.
+ */
+ usb_phdr = (struct linux_usb_phdr *)(void *)pd;
+
+ if (rec->rec_header.packet_header.caplen >=
+ sizeof (struct linux_usb_phdr)) {
+ /*
+ * In older versions of libpcap, in memory-mapped captures,
+ * the "on-the-bus length" for completion events for
+ * incoming isochronous transfers was miscalculated; it
+ * needed to be calculated based on the* offsets and lengths
+ * in the descriptors, not on the raw URB length, but it
+ * wasn't.
+ *
+ * If this packet contains transferred data (yes, data_flag
+ * is 0 if we *do* have data), and the total on-the-network
+ * length is equal to the value calculated from the raw URB
+ * length, then it might be one of those transfers.
+ *
+ * We only do this if we have the full USB pseudo-header.
+ */
+ if (!usb_phdr->data_flag &&
+ rec->rec_header.packet_header.len == sizeof (struct linux_usb_phdr) +
+ (usb_phdr->ndesc * sizeof (struct linux_usb_isodesc)) + usb_phdr->urb_len) {
+ /*
+ * It might need fixing; fix it if it's a completion
+ * event for an incoming isochronous transfer.
+ */
+ fix_linux_usb_mmapped_length(rec, pd);
+ }
+ }
+}
+
+void
+pcap_read_post_process(gboolean is_nokia, int wtap_encap,
+ wtap_rec *rec, guint8 *pd, gboolean bytes_swapped, int fcs_len)
+{
+ switch (wtap_encap) {
+
+ case WTAP_ENCAP_ATM_PDUS:
+ if (is_nokia) {
+ /*
+ * Nokia IPSO ATM.
+ *
+ * Guess the traffic type based on the packet
+ * contents.
+ */
+ atm_guess_traffic_type(rec, pd);
+ } else {
+ /*
+ * SunATM.
+ *
+ * If this is ATM LANE traffic, try to guess what
+ * type of LANE traffic it is based on the packet
+ * contents.
+ */
+ if (rec->rec_header.packet_header.pseudo_header.atm.type == TRAF_LANE)
+ atm_guess_lane_type(rec, pd);
+ }
+ break;
+
+ case WTAP_ENCAP_ETHERNET:
+ /*
+ * The FCS length is supposed to be in bits.
+ * If it's < 8, assume it's in bytes; otherwise,
+ * convert it to bytes.
+ */
+ if (fcs_len < 8)
+ rec->rec_header.packet_header.pseudo_header.eth.fcs_len = fcs_len;
+ else
+ rec->rec_header.packet_header.pseudo_header.eth.fcs_len = fcs_len/8;
+ break;
+
+ case WTAP_ENCAP_SLL:
+ if (bytes_swapped)
+ pcap_byteswap_linux_sll_pseudoheader(rec, pd);
+ break;
+
+ case WTAP_ENCAP_SLL2:
+ if (bytes_swapped)
+ pcap_byteswap_linux_sll2_pseudoheader(rec, pd);
+ break;
+
+ case WTAP_ENCAP_USB_LINUX:
+ if (bytes_swapped)
+ pcap_byteswap_linux_usb_pseudoheader(rec, pd, FALSE);
+ break;
+
+ case WTAP_ENCAP_USB_LINUX_MMAPPED:
+ if (bytes_swapped)
+ pcap_byteswap_linux_usb_pseudoheader(rec, pd, TRUE);
+
+ /*
+ * Fix up the on-the-network length if necessary.
+ */
+ pcap_fixup_len(rec, pd);
+ break;
+
+ case WTAP_ENCAP_NETANALYZER:
+ /*
+ * Not strictly necessary, as the netANALYZER
+ * dissector calls the "Ethernet with FCS"
+ * dissector, but we might as well set it.
+ */
+ rec->rec_header.packet_header.pseudo_header.eth.fcs_len = 4;
+ break;
+
+ case WTAP_ENCAP_NFLOG:
+ if (bytes_swapped)
+ pcap_byteswap_nflog_pseudoheader(rec, pd);
+ break;
+
+ case WTAP_ENCAP_ERF:
+ /*
+ * Update packet size to account for ERF padding and snapping.
+ * Captured length is minimum of wlen and previously calculated
+ * caplen (which would have included padding but not phdr).
+ */
+ rec->rec_header.packet_header.len = rec->rec_header.packet_header.pseudo_header.erf.phdr.wlen;
+ rec->rec_header.packet_header.caplen = MIN(rec->rec_header.packet_header.len, rec->rec_header.packet_header.caplen);
+ break;
+
+ case WTAP_ENCAP_PFLOG:
+ if (bytes_swapped)
+ pcap_byteswap_pflog_pseudoheader(rec, pd);
+ break;
+
+ default:
+ break;
+ }
+}
+
+gboolean
+wtap_encap_requires_phdr(int wtap_encap)
+{
+ switch (wtap_encap) {
+
+ case WTAP_ENCAP_ATM_PDUS:
+ case WTAP_ENCAP_IRDA:
+ case WTAP_ENCAP_MTP2_WITH_PHDR:
+ case WTAP_ENCAP_LINUX_LAPD:
+ case WTAP_ENCAP_SITA:
+ case WTAP_ENCAP_BLUETOOTH_H4_WITH_PHDR:
+ case WTAP_ENCAP_BLUETOOTH_LINUX_MONITOR:
+ case WTAP_ENCAP_NFC_LLCP:
+ case WTAP_ENCAP_PPP_WITH_PHDR:
+ case WTAP_ENCAP_ERF:
+ case WTAP_ENCAP_I2C_LINUX:
+ return TRUE;
+ }
+ return FALSE;
+}
+
+int
+pcap_get_phdr_size(int encap, const union wtap_pseudo_header *pseudo_header)
+{
+ int hdrsize;
+
+ switch (encap) {
+
+ case WTAP_ENCAP_ATM_PDUS:
+ hdrsize = SUNATM_LEN;
+ break;
+
+ case WTAP_ENCAP_IRDA:
+ hdrsize = IRDA_SLL_LEN;
+ break;
+
+ case WTAP_ENCAP_MTP2_WITH_PHDR:
+ hdrsize = MTP2_HDR_LEN;
+ break;
+
+ case WTAP_ENCAP_LINUX_LAPD:
+ hdrsize = LAPD_SLL_LEN;
+ break;
+
+ case WTAP_ENCAP_SITA:
+ hdrsize = SITA_HDR_LEN;
+ break;
+
+ case WTAP_ENCAP_BLUETOOTH_H4_WITH_PHDR:
+ hdrsize = (int)sizeof (struct pcap_bt_phdr);
+ break;
+
+ case WTAP_ENCAP_BLUETOOTH_LINUX_MONITOR:
+ hdrsize = (int)sizeof (struct pcap_bt_monitor_phdr);
+ break;
+
+ case WTAP_ENCAP_NFC_LLCP:
+ hdrsize = LLCP_HEADER_LEN;
+ break;
+
+ case WTAP_ENCAP_PPP_WITH_PHDR:
+ hdrsize = (int)sizeof (struct pcap_ppp_phdr);
+ break;
+
+ case WTAP_ENCAP_ERF:
+ hdrsize = (int)sizeof (struct erf_phdr);
+
+ /*
+ * If the type of record given in the pseudo header
+ * indicates the presence of an extension header, then
+ * add in the lengths of the extension headers.
+ */
+ if (pseudo_header->erf.phdr.type & 0x80) {
+ int i = 0, max = sizeof(pseudo_header->erf.ehdr_list)/sizeof(struct erf_ehdr);
+ guint8 erf_exhdr[8];
+ guint8 type;
+
+ do {
+ phtonll(erf_exhdr, pseudo_header->erf.ehdr_list[i].ehdr);
+ type = erf_exhdr[0];
+ hdrsize += 8;
+ i++;
+ } while (type & 0x80 && i < max);
+ }
+
+ /*
+ * Now add in the length of the subheader, if any.
+ */
+ switch (pseudo_header->erf.phdr.type & 0x7F) {
+
+ case ERF_TYPE_MC_HDLC:
+ case ERF_TYPE_MC_RAW:
+ case ERF_TYPE_MC_ATM:
+ case ERF_TYPE_MC_RAW_CHANNEL:
+ case ERF_TYPE_MC_AAL5:
+ case ERF_TYPE_MC_AAL2:
+ case ERF_TYPE_COLOR_MC_HDLC_POS:
+ hdrsize += (int)sizeof(struct erf_mc_hdr);
+ break;
+ case ERF_TYPE_AAL2:
+ hdrsize += (int)sizeof(struct erf_aal2_hdr);
+ break;
+
+ case ERF_TYPE_ETH:
+ case ERF_TYPE_COLOR_ETH:
+ case ERF_TYPE_DSM_COLOR_ETH:
+ case ERF_TYPE_COLOR_HASH_ETH:
+ hdrsize += (int)sizeof(struct erf_eth_hdr);
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ case WTAP_ENCAP_I2C_LINUX:
+ hdrsize = (int)sizeof (struct i2c_linux_file_hdr);
+ break;
+
+ default:
+ hdrsize = 0;
+ break;
+ }
+
+ return hdrsize;
+}
+
+gboolean
+pcap_write_phdr(wtap_dumper *wdh, int encap, const union wtap_pseudo_header *pseudo_header,
+ int *err)
+{
+ switch (encap) {
+
+ case WTAP_ENCAP_ATM_PDUS:
+ if (!pcap_write_sunatm_pseudoheader(wdh, pseudo_header, err))
+ return FALSE;
+ break;
+
+ case WTAP_ENCAP_IRDA:
+ if (!pcap_write_irda_pseudoheader(wdh, pseudo_header, err))
+ return FALSE;
+ break;
+
+ case WTAP_ENCAP_MTP2_WITH_PHDR:
+ if (!pcap_write_mtp2_pseudoheader(wdh, pseudo_header, err))
+ return FALSE;
+ break;
+
+ case WTAP_ENCAP_LINUX_LAPD:
+ if (!pcap_write_lapd_pseudoheader(wdh, pseudo_header, err))
+ return FALSE;
+ break;
+
+ case WTAP_ENCAP_SITA:
+ if (!pcap_write_sita_pseudoheader(wdh, pseudo_header, err))
+ return FALSE;
+ break;
+
+ case WTAP_ENCAP_BLUETOOTH_H4_WITH_PHDR:
+ if (!pcap_write_bt_pseudoheader(wdh, pseudo_header, err))
+ return FALSE;
+ break;
+
+ case WTAP_ENCAP_BLUETOOTH_LINUX_MONITOR:
+ if (!pcap_write_bt_monitor_pseudoheader(wdh, pseudo_header, err))
+ return FALSE;
+ break;
+
+ case WTAP_ENCAP_NFC_LLCP:
+ if (!pcap_write_llcp_pseudoheader(wdh, pseudo_header, err))
+ return FALSE;
+ break;
+
+ case WTAP_ENCAP_PPP_WITH_PHDR:
+ if (!pcap_write_ppp_pseudoheader(wdh, pseudo_header, err))
+ return FALSE;
+ break;
+
+ case WTAP_ENCAP_ERF:
+ if (!pcap_write_erf_pseudoheader(wdh, pseudo_header, err))
+ return FALSE;
+ break;
+
+ case WTAP_ENCAP_I2C_LINUX:
+ if (!pcap_write_i2c_linux_pseudoheader(wdh, pseudo_header, err))
+ return FALSE;
+ break;
+ }
+ return TRUE;
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/wiretap/pcap-common.h b/wiretap/pcap-common.h
new file mode 100644
index 00000000..af6c8218
--- /dev/null
+++ b/wiretap/pcap-common.h
@@ -0,0 +1,36 @@
+/** @file
+ *
+ * Declarations for code common to pcap and pcapng file formats
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * File format support for pcapng file format
+ * Copyright (c) 2007 by Ulf Lamping <ulf.lamping@web.de>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __W_PCAP_COMMON_H__
+#define __W_PCAP_COMMON_H__
+
+#include <glib.h>
+#include "wtap.h"
+#include "ws_symbol_export.h"
+
+extern guint wtap_max_snaplen_for_encap(int wtap_encap);
+
+extern int pcap_process_pseudo_header(FILE_T fh, gboolean is_nokia,
+ int wtap_encap, guint packet_size, wtap_rec *rec,
+ int *err, gchar **err_info);
+
+extern void pcap_read_post_process(gboolean is_nokia, int wtap_encap,
+ wtap_rec *rec, guint8 *pd, gboolean bytes_swapped, int fcs_len);
+
+extern int pcap_get_phdr_size(int encap,
+ const union wtap_pseudo_header *pseudo_header);
+
+extern gboolean pcap_write_phdr(wtap_dumper *wdh, int wtap_encap,
+ const union wtap_pseudo_header *pseudo_header, int *err);
+
+#endif
diff --git a/wiretap/pcap-encap.h b/wiretap/pcap-encap.h
new file mode 100644
index 00000000..53af9c3e
--- /dev/null
+++ b/wiretap/pcap-encap.h
@@ -0,0 +1,31 @@
+/** @file
+ * Declarations for routines to handle pcap/pcapng linktype values
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * File format support for pcapng file format
+ * Copyright (c) 2007 by Ulf Lamping <ulf.lamping@web.de>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __W_PCAP_ENCAP_H__
+#define __W_PCAP_ENCAP_H__
+
+#include <glib.h>
+#include <wiretap/wtap.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+WS_DLL_PUBLIC int wtap_pcap_encap_to_wtap_encap(int encap);
+int wtap_wtap_encap_to_pcap_encap(int encap);
+WS_DLL_PUBLIC gboolean wtap_encap_requires_phdr(int encap);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif
diff --git a/wiretap/pcapng.c b/wiretap/pcapng.c
new file mode 100644
index 00000000..85c635fc
--- /dev/null
+++ b/wiretap/pcapng.c
@@ -0,0 +1,6876 @@
+/* pcapng.c
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * File format support for pcapng file format
+ * Copyright (c) 2007 by Ulf Lamping <ulf.lamping@web.de>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+/* File format specification:
+ * https://github.com/pcapng/pcapng
+ * Related Wiki page:
+ * https://gitlab.com/wireshark/wireshark/-/wikis/Development/PcapNg
+ */
+
+#include "config.h"
+#include "wtap_opttypes.h"
+
+#define WS_LOG_DOMAIN LOG_DOMAIN_WIRETAP
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <wsutil/wslog.h>
+#include <wsutil/strtoi.h>
+#include <wsutil/glib-compat.h>
+#include <wsutil/ws_assert.h>
+#include <wsutil/ws_roundup.h>
+#include <wsutil/unicode-utils.h>
+
+#include "wtap-int.h"
+#include "file_wrappers.h"
+#include "required_file_handlers.h"
+#include "pcap-common.h"
+#include "pcap-encap.h"
+#include "pcapng.h"
+#include "pcapng_module.h"
+#include "secrets-types.h"
+
+#define ROUND_TO_4BYTE(len) WS_ROUNDUP_4(len)
+
+static gboolean
+pcapng_read(wtap *wth, wtap_rec *rec, Buffer *buf, int *err,
+ gchar **err_info, gint64 *data_offset);
+static gboolean
+pcapng_seek_read(wtap *wth, gint64 seek_off,
+ wtap_rec *rec, Buffer *buf, int *err, gchar **err_info);
+static void
+pcapng_close(wtap *wth);
+
+static gboolean
+pcapng_encap_is_ft_specific(int encap);
+
+static gboolean
+pcapng_write_if_descr_block(wtap_dumper *wdh, wtap_block_t int_data, int *err);
+
+/*
+ * Minimum block size = size of block header + size of block trailer.
+ */
+#define MIN_BLOCK_SIZE ((guint32)(sizeof(pcapng_block_header_t) + sizeof(guint32)))
+
+/*
+ * Minimum SHB size = minimum block size + size of fixed length portion of SHB.
+ */
+#define MIN_SHB_SIZE ((guint32)(MIN_BLOCK_SIZE + sizeof(pcapng_section_header_block_t)))
+
+/* pcapng: packet block file encoding (obsolete) */
+typedef struct pcapng_packet_block_s {
+ guint16 interface_id;
+ guint16 drops_count;
+ guint32 timestamp_high;
+ guint32 timestamp_low;
+ guint32 captured_len;
+ guint32 packet_len;
+ /* ... Packet Data ... */
+ /* ... Padding ... */
+ /* ... Options ... */
+} pcapng_packet_block_t;
+
+/*
+ * Minimum PB size = minimum block size + size of fixed length portion of PB.
+ */
+#define MIN_PB_SIZE ((guint32)(MIN_BLOCK_SIZE + sizeof(pcapng_packet_block_t)))
+
+/* pcapng: enhanced packet block file encoding */
+typedef struct pcapng_enhanced_packet_block_s {
+ guint32 interface_id;
+ guint32 timestamp_high;
+ guint32 timestamp_low;
+ guint32 captured_len;
+ guint32 packet_len;
+ /* ... Packet Data ... */
+ /* ... Padding ... */
+ /* ... Options ... */
+} pcapng_enhanced_packet_block_t;
+
+/*
+ * Minimum EPB size = minimum block size + size of fixed length portion of EPB.
+ */
+#define MIN_EPB_SIZE ((guint32)(MIN_BLOCK_SIZE + sizeof(pcapng_enhanced_packet_block_t)))
+
+/* pcapng: simple packet block file encoding */
+typedef struct pcapng_simple_packet_block_s {
+ guint32 packet_len;
+ /* ... Packet Data ... */
+ /* ... Padding ... */
+} pcapng_simple_packet_block_t;
+
+/*
+ * Minimum SPB size = minimum block size + size of fixed length portion of SPB.
+ */
+#define MIN_SPB_SIZE ((guint32)(MIN_BLOCK_SIZE + sizeof(pcapng_simple_packet_block_t)))
+
+/* pcapng: name resolution block file encoding */
+typedef struct pcapng_name_resolution_block_s {
+ guint16 record_type;
+ guint16 record_len;
+ /* ... Record ... */
+} pcapng_name_resolution_block_t;
+
+/*
+ * Minimum NRB size = minimum block size + size of smallest NRB record
+ * (there must at least be an "end of records" record).
+ */
+#define MIN_NRB_SIZE ((guint32)(MIN_BLOCK_SIZE + sizeof(pcapng_name_resolution_block_t)))
+
+/* pcapng: custom block file encoding */
+typedef struct pcapng_custom_block_s {
+ guint32 pen;
+ /* Custom data and options */
+} pcapng_custom_block_t;
+
+/*
+ * Minimum CB size = minimum block size + size of fixed length portion of CB.
+ */
+
+#define MIN_CB_SIZE ((guint32)(MIN_BLOCK_SIZE + sizeof(pcapng_custom_block_t)))
+
+/*
+ * Minimum ISB size = minimum block size + size of fixed length portion of ISB.
+ */
+#define MIN_ISB_SIZE ((guint32)(MIN_BLOCK_SIZE + sizeof(pcapng_interface_statistics_block_t)))
+
+/*
+ * Minimum Sysdig size = minimum block size + packed size of sysdig_event_phdr.
+ * Minimum Sysdig event v2 header size = minimum block size + packed size of sysdig_event_v2_phdr (which, in addition
+ * to sysdig_event_phdr, includes the nparams 32bit value).
+ */
+#define SYSDIG_EVENT_HEADER_SIZE ((16 + 64 + 64 + 32 + 16)/8) /* CPU ID + TS + TID + Event len + Event type */
+#define MIN_SYSDIG_EVENT_SIZE ((guint32)(MIN_BLOCK_SIZE + SYSDIG_EVENT_HEADER_SIZE))
+#define SYSDIG_EVENT_V2_HEADER_SIZE ((16 + 64 + 64 + 32 + 16 + 32)/8) /* CPU ID + TS + TID + Event len + Event type + nparams */
+#define MIN_SYSDIG_EVENT_V2_SIZE ((guint32)(MIN_BLOCK_SIZE + SYSDIG_EVENT_V2_HEADER_SIZE))
+
+/*
+ * We require __REALTIME_TIMESTAMP in the Journal Export Format reader in
+ * order to set each packet timestamp. Require it here as well, although
+ * it's not strictly necessary.
+ */
+#define SDJ__REALTIME_TIMESTAMP "__REALTIME_TIMESTAMP="
+#define MIN_SYSTEMD_JOURNAL_EXPORT_ENTRY_SIZE 23 // "__REALTIME_TIMESTAMP=0\n"
+#define MIN_SYSTEMD_JOURNAL_EXPORT_BLOCK_SIZE ((guint32)(MIN_SYSTEMD_JOURNAL_EXPORT_ENTRY_SIZE + MIN_BLOCK_SIZE))
+
+/* pcapng: common option header file encoding for every option type */
+typedef struct pcapng_option_header_s {
+ guint16 option_code;
+ guint16 option_length;
+ /* ... x bytes Option Body ... */
+ /* ... Padding ... */
+} pcapng_option_header_t;
+
+struct pcapng_option {
+ guint16 type;
+ guint16 value_length;
+};
+
+/* Option codes: 16-bit field */
+#define OPT_EPB_FLAGS 0x0002
+#define OPT_EPB_HASH 0x0003
+#define OPT_EPB_DROPCOUNT 0x0004
+#define OPT_EPB_PACKETID 0x0005
+#define OPT_EPB_QUEUE 0x0006
+#define OPT_EPB_VERDICT 0x0007
+
+#define OPT_NRB_DNSNAME 0x0002
+#define OPT_NRB_DNSV4ADDR 0x0003
+#define OPT_NRB_DNSV6ADDR 0x0004
+
+/* MSBit of option code means "local type" */
+#define OPT_LOCAL_FLAG 0x8000
+
+/* OPT_EPB_VERDICT sub-types */
+#define OPT_VERDICT_TYPE_HW 0
+#define OPT_VERDICT_TYPE_TC 1
+#define OPT_VERDICT_TYPE_XDP 2
+
+/* OPT_EPB_HASH sub-types */
+#define OPT_HASH_2COMP 0
+#define OPT_HASH_XOR 1
+#define OPT_HASH_CRC32 2
+#define OPT_HASH_MD5 3
+#define OPT_HASH_SHA1 4
+#define OPT_HASH_TOEPLITZ 5
+
+/*
+ * In order to keep from trying to allocate large chunks of memory,
+ * which could either fail or, even if it succeeds, chew up so much
+ * address space or memory+backing store as not to leave room for
+ * anything else, we impose upper limits on the size of blocks we're
+ * willing to handle.
+ *
+ * We pick a limit of an EPB with a maximum-sized D-Bus packet and 128 KiB
+ * worth of options; we use the maximum D-Bus packet size as that's larger
+ * than the maximum packet size for other link-layer types, and the maximum
+ * packet size for other link-layer types is currently small enough that
+ * the resulting block size would be less than the previous 16 MiB limit.
+ */
+#define MAX_BLOCK_SIZE (MIN_EPB_SIZE + WTAP_MAX_PACKET_SIZE_DBUS + 131072)
+
+/* Note: many of the defined structures for block data are defined in wtap.h */
+
+/* Packet data - used for both Enhanced Packet Block and the obsolete Packet Block data */
+typedef struct wtapng_packet_s {
+ /* mandatory */
+ guint32 ts_high; /* seconds since 1.1.1970 */
+ guint32 ts_low; /* fraction of seconds, depends on if_tsresol */
+ guint32 cap_len; /* data length in the file */
+ guint32 packet_len; /* data length on the wire */
+ guint32 interface_id; /* identifier of the interface. */
+ guint16 drops_count; /* drops count, only valid for packet block */
+ /* 0xffff if information no available */
+ /* pack_hash */
+ /* XXX - put the packet data / pseudo_header here as well? */
+} wtapng_packet_t;
+
+/* Simple Packet data */
+typedef struct wtapng_simple_packet_s {
+ /* mandatory */
+ guint32 cap_len; /* data length in the file */
+ guint32 packet_len; /* data length on the wire */
+ /* XXX - put the packet data / pseudo_header here as well? */
+} wtapng_simple_packet_t;
+
+/* Interface data in private struct */
+typedef struct interface_info_s {
+ int wtap_encap;
+ guint32 snap_len;
+ guint64 time_units_per_second;
+ int tsprecision;
+ gint64 tsoffset;
+ int fcslen;
+} interface_info_t;
+
+typedef struct {
+ guint current_section_number; /**< Section number of the current section being read sequentially */
+ GArray *sections; /**< Sections found in the capture file. */
+} pcapng_t;
+
+/*
+ * Table for plugins to handle particular block types.
+ *
+ * A handler has a "read" routine and a "write" routine.
+ *
+ * A "read" routine returns a block as a libwiretap record, filling
+ * in the wtap_rec structure with the appropriate record type and
+ * other information, and filling in the supplied Buffer with
+ * data for which there's no place in the wtap_rec structure.
+ *
+ * A "write" routine takes a libwiretap record and Buffer and writes
+ * out a block.
+ */
+typedef struct {
+ block_reader reader;
+ block_writer writer;
+} block_handler;
+
+static GHashTable *block_handlers;
+
+void
+register_pcapng_block_type_handler(guint block_type, block_reader reader,
+ block_writer writer)
+{
+ block_handler *handler;
+
+ /*
+ * Is this a known block type?
+ */
+ switch (block_type) {
+
+ case BLOCK_TYPE_SHB:
+ case BLOCK_TYPE_IDB:
+ case BLOCK_TYPE_PB:
+ case BLOCK_TYPE_SPB:
+ case BLOCK_TYPE_NRB:
+ case BLOCK_TYPE_ISB:
+ case BLOCK_TYPE_EPB:
+ case BLOCK_TYPE_DSB:
+ case BLOCK_TYPE_CB_COPY:
+ case BLOCK_TYPE_CB_NO_COPY:
+ case BLOCK_TYPE_SYSDIG_MI:
+ case BLOCK_TYPE_SYSDIG_PL_V1:
+ case BLOCK_TYPE_SYSDIG_FDL_V1:
+ case BLOCK_TYPE_SYSDIG_EVENT:
+ case BLOCK_TYPE_SYSDIG_IL_V1:
+ case BLOCK_TYPE_SYSDIG_UL_V1:
+ case BLOCK_TYPE_SYSDIG_PL_V2:
+ case BLOCK_TYPE_SYSDIG_EVF:
+ case BLOCK_TYPE_SYSDIG_PL_V3:
+ case BLOCK_TYPE_SYSDIG_PL_V4:
+ case BLOCK_TYPE_SYSDIG_PL_V5:
+ case BLOCK_TYPE_SYSDIG_PL_V6:
+ case BLOCK_TYPE_SYSDIG_PL_V7:
+ case BLOCK_TYPE_SYSDIG_PL_V8:
+ case BLOCK_TYPE_SYSDIG_PL_V9:
+ case BLOCK_TYPE_SYSDIG_EVENT_V2:
+ case BLOCK_TYPE_SYSDIG_EVF_V2:
+ case BLOCK_TYPE_SYSDIG_FDL_V2:
+ case BLOCK_TYPE_SYSDIG_IL_V2:
+ case BLOCK_TYPE_SYSDIG_UL_V2:
+ case BLOCK_TYPE_SYSTEMD_JOURNAL_EXPORT:
+ /*
+ * Yes; we already handle it, and don't allow a replacement to
+ * be registered (if there's a bug in our code, or there's
+ * something we don't handle in that block, submit a change
+ * to the main Wireshark source).
+ */
+ ws_warning("Attempt to register plugin for block type 0x%08x not allowed",
+ block_type);
+ return;
+
+ case BLOCK_TYPE_IRIG_TS:
+ case BLOCK_TYPE_ARINC_429:
+ /*
+ * Yes, and we don't already handle it. Allow a plugin to
+ * handle it.
+ *
+ * (But why not submit the plugin source to Wireshark?)
+ */
+ break;
+
+ default:
+ /*
+ * No; is it a local block type?
+ */
+ if (!(block_type & 0x80000000)) {
+ /*
+ * No; don't allow a plugin to be registered for it, as
+ * the block type needs to be registered before it's used.
+ */
+ ws_warning("Attempt to register plugin for reserved block type 0x%08x not allowed",
+ block_type);
+ return;
+ }
+
+ /*
+ * Yes; allow the registration.
+ */
+ break;
+ }
+
+ if (block_handlers == NULL) {
+ /*
+ * Create the table of block handlers.
+ *
+ * XXX - there's no "g_uint_hash()" or "g_uint_equal()",
+ * so we use "g_direct_hash()" and "g_direct_equal()".
+ */
+ block_handlers = g_hash_table_new_full(g_direct_hash,
+ g_direct_equal,
+ NULL, g_free);
+ }
+ handler = g_new(block_handler, 1);
+ handler->reader = reader;
+ handler->writer = writer;
+ g_hash_table_insert(block_handlers, GUINT_TO_POINTER(block_type),
+ handler);
+}
+
+/*
+ * Tables for plugins to handle particular options for particular block
+ * types.
+ *
+ * An option has three handler routines:
+ *
+ * An option parser, used when reading an option from a file:
+ *
+ * The option parser is passed an indication of whether this section
+ * of the file is byte-swapped, the length of the option, the data of
+ * the option, a pointer to an error code, and a pointer to a pointer
+ * variable for an error string.
+ *
+ * It checks whether the length and option are valid, and, if they
+ * aren't, returns FALSE, setting the error code to the appropriate
+ * error (normally WTAP_ERR_BAD_FILE) and the error string to an
+ * appropriate string indicating the problem.
+ *
+ * Otherwise, if this section of the file is byte-swapped, it byte-swaps
+ * multi-byte numerical values, so that it's in the host byte order.
+ *
+ * An option sizer, used when writing an option to a file:
+ *
+ * The option sizer is passed the option identifier for the option
+ * and a wtap_optval_t * that points to the data for the option.
+ *
+ * It calculates how many bytes the option's data requires, not
+ * including any padding bytes, and returns that value.
+ *
+ * An option writer, used when writing an option to a file:
+ *
+ * The option writer is passed a wtap_dumper * to which the
+ * option data should be written, the option identifier for
+ * the option, a wtap_optval_t * that points to the data for
+ * the option, and an int * into which an error code should
+ * be stored if an error occurs when writing the option.
+ *
+ * It returns a gboolean value of TRUE if the attempt to
+ * write the option succeeds and FALSE if the attempt to
+ * write the option gets an error.
+ */
+
+/*
+ * Block types indices in the table of tables of option handlers.
+ *
+ * Block types are not guaranteed to be sequential, so we map the
+ * block types we support to a sequential set. Furthermore, all
+ * packet block types have the same set of options.
+ */
+#define BT_INDEX_SHB 0
+#define BT_INDEX_IDB 1
+#define BT_INDEX_PBS 2 /* all packet blocks */
+#define BT_INDEX_NRB 3
+#define BT_INDEX_ISB 4
+#define BT_INDEX_EVT 5
+#define BT_INDEX_DSB 6
+
+#define NUM_BT_INDICES 7
+
+typedef struct {
+ option_parser parser;
+ option_sizer sizer;
+ option_writer writer;
+} option_handler;
+
+static GHashTable *option_handlers[NUM_BT_INDICES];
+
+/* Return whether this block type is handled interally, or
+ * if it is returned to the caller in pcapng_read().
+ * This is used by pcapng_open() to decide if it can process
+ * the block.
+ * Note that for block types that are registered from plugins,
+ * we don't know the true answer without actually reading the block,
+ * or even if there is a fixed answer for all blocks of that type,
+ * so we err on the side of not processing.
+ */
+static bool
+get_block_type_internal(unsigned block_type)
+{
+ switch (block_type) {
+
+ case BLOCK_TYPE_SHB:
+ case BLOCK_TYPE_IDB:
+ case BLOCK_TYPE_NRB:
+ case BLOCK_TYPE_DSB:
+ case BLOCK_TYPE_ISB: /* XXX: ISBs should probably not be internal. */
+ case BLOCK_TYPE_SYSDIG_MI:
+ case BLOCK_TYPE_SYSDIG_PL_V1:
+ case BLOCK_TYPE_SYSDIG_FDL_V1:
+ case BLOCK_TYPE_SYSDIG_IL_V1:
+ case BLOCK_TYPE_SYSDIG_UL_V1:
+ case BLOCK_TYPE_SYSDIG_PL_V2:
+ case BLOCK_TYPE_SYSDIG_PL_V3:
+ case BLOCK_TYPE_SYSDIG_PL_V4:
+ case BLOCK_TYPE_SYSDIG_PL_V5:
+ case BLOCK_TYPE_SYSDIG_PL_V6:
+ case BLOCK_TYPE_SYSDIG_PL_V7:
+ case BLOCK_TYPE_SYSDIG_PL_V8:
+ case BLOCK_TYPE_SYSDIG_PL_V9:
+ case BLOCK_TYPE_SYSDIG_FDL_V2:
+ case BLOCK_TYPE_SYSDIG_IL_V2:
+ case BLOCK_TYPE_SYSDIG_UL_V2:
+ return true;
+
+ case BLOCK_TYPE_PB:
+ case BLOCK_TYPE_EPB:
+ case BLOCK_TYPE_SPB:
+ return false;
+
+ case BLOCK_TYPE_CB_COPY:
+ case BLOCK_TYPE_CB_NO_COPY:
+ case BLOCK_TYPE_SYSDIG_EVENT:
+ case BLOCK_TYPE_SYSDIG_EVENT_V2:
+ case BLOCK_TYPE_SYSDIG_EVENT_V2_LARGE:
+ case BLOCK_TYPE_SYSTEMD_JOURNAL_EXPORT:
+ return false;
+
+ default:
+#ifdef HAVE_PLUGINS
+ /*
+ * Do we have a handler for this block type?
+ */
+ if (block_handlers != NULL &&
+ (g_hash_table_lookup(block_handlers, GUINT_TO_POINTER(block_type))) != NULL) {
+ /* Yes. We don't know if the handler sets this block internal
+ * or needs to return it to the pcap_read() caller without
+ * reading it. Since this is called by pcap_open(), play it
+ * safe and tell pcap_open() to stop processing blocks.
+ * (XXX: Maybe the block type handler registration interface
+ * should include some way of indicating whether blocks are
+ * handled internally, which should hopefully be the same
+ * for all blocks of a type.)
+ */
+ return FALSE;
+ }
+#endif
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static gboolean
+get_block_type_index(guint block_type, guint *bt_index)
+{
+ ws_assert(bt_index);
+
+ switch (block_type) {
+
+ case BLOCK_TYPE_SHB:
+ *bt_index = BT_INDEX_SHB;
+ break;
+
+ case BLOCK_TYPE_IDB:
+ *bt_index = BT_INDEX_IDB;
+ break;
+
+ case BLOCK_TYPE_PB:
+ case BLOCK_TYPE_EPB:
+ case BLOCK_TYPE_SPB:
+ *bt_index = BT_INDEX_PBS;
+ break;
+
+ case BLOCK_TYPE_NRB:
+ *bt_index = BT_INDEX_NRB;
+ break;
+
+ case BLOCK_TYPE_ISB:
+ *bt_index = BT_INDEX_ISB;
+ break;
+
+ case BLOCK_TYPE_SYSDIG_EVENT:
+ case BLOCK_TYPE_SYSDIG_EVENT_V2:
+ case BLOCK_TYPE_SYSDIG_EVENT_V2_LARGE:
+ case BLOCK_TYPE_SYSDIG_MI:
+ case BLOCK_TYPE_SYSDIG_PL_V1:
+ case BLOCK_TYPE_SYSDIG_FDL_V1:
+ case BLOCK_TYPE_SYSDIG_IL_V1:
+ case BLOCK_TYPE_SYSDIG_UL_V1:
+ case BLOCK_TYPE_SYSDIG_PL_V2:
+ case BLOCK_TYPE_SYSDIG_PL_V3:
+ case BLOCK_TYPE_SYSDIG_PL_V4:
+ case BLOCK_TYPE_SYSDIG_PL_V5:
+ case BLOCK_TYPE_SYSDIG_PL_V6:
+ case BLOCK_TYPE_SYSDIG_PL_V7:
+ case BLOCK_TYPE_SYSDIG_PL_V8:
+ case BLOCK_TYPE_SYSDIG_PL_V9:
+ case BLOCK_TYPE_SYSDIG_FDL_V2:
+ case BLOCK_TYPE_SYSDIG_IL_V2:
+ case BLOCK_TYPE_SYSDIG_UL_V2:
+ *bt_index = BT_INDEX_EVT;
+ break;
+
+ case BLOCK_TYPE_DSB:
+ *bt_index = BT_INDEX_DSB;
+ break;
+
+ default:
+ /*
+ * This is a block type we don't process; either we ignore it,
+ * in which case the options don't get processed, or there's
+ * a plugin routine to handle it, in which case that routine
+ * will do the option processing itself.
+ *
+ * XXX - report an error?
+ */
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+void
+register_pcapng_option_handler(guint block_type, guint option_code,
+ option_parser parser,
+ option_sizer sizer,
+ option_writer writer)
+{
+ guint bt_index;
+ option_handler *handler;
+
+ if (!get_block_type_index(block_type, &bt_index))
+ return;
+
+ if (option_handlers[bt_index] == NULL) {
+ /*
+ * Create the table of option handlers for this block type.
+ *
+ * XXX - there's no "g_uint_hash()" or "g_uint_equal()",
+ * so we use "g_direct_hash()" and "g_direct_equal()".
+ */
+ option_handlers[bt_index] = g_hash_table_new_full(g_direct_hash,
+ g_direct_equal,
+ NULL, g_free);
+ }
+ handler = g_new(option_handler, 1);
+ handler->parser = parser;
+ handler->sizer = sizer;
+ handler->writer = writer;
+ g_hash_table_insert(option_handlers[bt_index],
+ GUINT_TO_POINTER(option_code), handler);
+}
+
+void
+pcapng_process_uint8_option(wtapng_block_t *wblock,
+ guint16 option_code, guint16 option_length,
+ const guint8 *option_content)
+{
+ if (option_length == 1) {
+ /*
+ * If this option can appear only once in a block, this call
+ * will fail on the second and later occurrences of the option;
+ * we silently ignore the failure.
+ */
+ wtap_block_add_uint8_option(wblock->block, option_code, option_content[0]);
+ }
+}
+
+void
+pcapng_process_uint32_option(wtapng_block_t *wblock,
+ const section_info_t *section_info,
+ pcapng_opt_byte_order_e byte_order,
+ guint16 option_code, guint16 option_length,
+ const guint8 *option_content)
+{
+ guint32 uint32;
+
+ if (option_length == 4) {
+ /* Don't cast a guint8 * into a guint32 *--the
+ * guint8 * may not point to something that's
+ * aligned correctly.
+ *
+ * XXX - options are aligned on 32-bit boundaries, so, while
+ * it may be true that 64-bit options aren't guaranteed to be
+ * aligned on 64-bit bounaries, it shouldn't be true that 32-bit
+ * options aren't guaranteed to be aligned on 32-bit boundaries.
+ */
+ memcpy(&uint32, option_content, sizeof(guint32));
+ switch (byte_order) {
+
+ case OPT_SECTION_BYTE_ORDER:
+ if (section_info->byte_swapped) {
+ uint32 = GUINT32_SWAP_LE_BE(uint32);
+ }
+ break;
+
+ case OPT_BIG_ENDIAN:
+ uint32 = GUINT32_FROM_BE(uint32);
+ break;
+
+ case OPT_LITTLE_ENDIAN:
+ uint32 = GUINT32_FROM_LE(uint32);
+ break;
+
+ default:
+ /*
+ * This should not happen - this is called by pcapng_process_options(),
+ * which returns an error for an invalid byte_order argument, and
+ * otherwise passes the known-to-be-valid byte_order argument to
+ * us.
+ *
+ * Just ignore the option.
+ */
+ return;
+ }
+
+ /*
+ * If this option can appear only once in a block, this call
+ * will fail on the second and later occurrences of the option;
+ * we silently ignore the failure.
+ */
+ wtap_block_add_uint32_option(wblock->block, option_code, uint32);
+ }
+}
+
+void
+pcapng_process_timestamp_option(wtapng_block_t *wblock,
+ const section_info_t *section_info,
+ pcapng_opt_byte_order_e byte_order,
+ guint16 option_code, guint16 option_length,
+ const guint8 *option_content)
+{
+ if (option_length == 8) {
+ guint32 high, low;
+ guint64 timestamp;
+
+ /* Don't cast a guint8 * into a guint32 *--the
+ * guint8 * may not point to something that's
+ * aligned correctly.
+ */
+ memcpy(&high, option_content, sizeof(guint32));
+ memcpy(&low, option_content + sizeof(guint32), sizeof(guint32));
+ switch (byte_order) {
+
+ case OPT_SECTION_BYTE_ORDER:
+ if (section_info->byte_swapped) {
+ high = GUINT32_SWAP_LE_BE(high);
+ low = GUINT32_SWAP_LE_BE(low);
+ }
+ break;
+
+ case OPT_BIG_ENDIAN:
+ high = GUINT32_FROM_BE(high);
+ low = GUINT32_FROM_BE(low);
+ break;
+
+ case OPT_LITTLE_ENDIAN:
+ high = GUINT32_FROM_LE(high);
+ low = GUINT32_FROM_LE(low);
+ break;
+
+ default:
+ /*
+ * This should not happen - this is called by pcapng_process_options(),
+ * which returns an error for an invalid byte_order argument, and
+ * otherwise passes the known-to-be-valid byte_order argument to
+ * us.
+ *
+ * Just ignore the option.
+ */
+ return;
+ }
+ timestamp = (guint64)high;
+ timestamp <<= 32;
+ timestamp += (guint64)low;
+ /*
+ * If this option can appear only once in a block, this call
+ * will fail on the second and later occurrences of the option;
+ * we silently ignore the failure.
+ */
+ wtap_block_add_uint64_option(wblock->block, option_code, timestamp);
+ }
+}
+
+void
+pcapng_process_uint64_option(wtapng_block_t *wblock,
+ const section_info_t *section_info,
+ pcapng_opt_byte_order_e byte_order,
+ guint16 option_code, guint16 option_length,
+ const guint8 *option_content)
+{
+ guint64 uint64;
+
+ if (option_length == 8) {
+ /* Don't cast a guint8 * into a guint64 *--the
+ * guint8 * may not point to something that's
+ * aligned correctly.
+ */
+ memcpy(&uint64, option_content, sizeof(guint64));
+ switch (byte_order) {
+
+ case OPT_SECTION_BYTE_ORDER:
+ if (section_info->byte_swapped) {
+ uint64 = GUINT64_SWAP_LE_BE(uint64);
+ }
+ break;
+
+ case OPT_BIG_ENDIAN:
+ uint64 = GUINT64_FROM_BE(uint64);
+ break;
+
+ case OPT_LITTLE_ENDIAN:
+ uint64 = GUINT64_FROM_LE(uint64);
+ break;
+
+ default:
+ /*
+ * This should not happen - this is called by pcapng_process_options(),
+ * which returns an error for an invalid byte_order argument, and
+ * otherwise passes the known-to-be-valid byte_order argument to
+ * us.
+ *
+ * Just ignore the option.
+ */
+ return;
+ }
+
+ /*
+ * If this option can appear only once in a block, this call
+ * will fail on the second and later occurrences of the option;
+ * we silently ignore the failure.
+ */
+ wtap_block_add_uint64_option(wblock->block, option_code, uint64);
+ }
+}
+
+void
+pcapng_process_int64_option(wtapng_block_t *wblock,
+ const section_info_t *section_info,
+ pcapng_opt_byte_order_e byte_order,
+ guint16 option_code, guint16 option_length,
+ const guint8 *option_content)
+{
+ gint64 int64;
+
+ if (option_length == 8) {
+ /* Don't cast a gint8 * into a gint64 *--the
+ * guint8 * may not point to something that's
+ * aligned correctly.
+ */
+ memcpy(&int64, option_content, sizeof(gint64));
+ switch (byte_order) {
+
+ case OPT_SECTION_BYTE_ORDER:
+ if (section_info->byte_swapped) {
+ int64 = GUINT64_SWAP_LE_BE(int64);
+ }
+ break;
+
+ case OPT_BIG_ENDIAN:
+ int64 = GUINT64_FROM_BE(int64);
+ break;
+
+ case OPT_LITTLE_ENDIAN:
+ int64 = GUINT64_FROM_LE(int64);
+ break;
+
+ default:
+ /*
+ * This should not happen - this is called by pcapng_process_options(),
+ * which returns an error for an invalid byte_order argument, and
+ * otherwise passes the known-to-be-valid byte_order argument to
+ * us.
+ *
+ * Just ignore the option.
+ */
+ return;
+ }
+
+ /*
+ * If this option can appear only once in a block, this call
+ * will fail on the second and later occurrences of the option;
+ * we silently ignore the failure.
+ */
+ wtap_block_add_int64_option(wblock->block, option_code, int64);
+ }
+}
+
+void
+pcapng_process_string_option(wtapng_block_t *wblock, guint16 option_code,
+ guint16 option_length, const guint8 *option_content)
+{
+ const char *opt = (const char *)option_content;
+ size_t optlen = option_length;
+ char *str;
+
+ /* Validate UTF-8 encoding. */
+ str = ws_utf8_make_valid(NULL, opt, optlen);
+
+ wtap_block_add_string_option_owned(wblock->block, option_code, str);
+}
+
+void
+pcapng_process_bytes_option(wtapng_block_t *wblock, guint16 option_code,
+ guint16 option_length, const guint8 *option_content)
+{
+ wtap_block_add_bytes_option(wblock->block, option_code, (const char *)option_content, option_length);
+}
+
+static gboolean
+pcapng_process_nflx_custom_option(wtapng_block_t *wblock,
+ section_info_t *section_info,
+ const guint8 *value, guint16 length)
+{
+ struct nflx_dumpinfo dumpinfo;
+ guint32 type, version;
+ gint64 dumptime, temp;
+
+ if (length < 4) {
+ ws_debug("Length = %u too small", length);
+ return FALSE;
+ }
+ memcpy(&type, value, sizeof(guint32));
+ type = GUINT32_FROM_LE(type);
+ value += 4;
+ length -= 4;
+ ws_debug("Handling type = %u, payload of length = %u", type, length);
+ switch (type) {
+ case NFLX_OPT_TYPE_VERSION:
+ if (length == sizeof(guint32)) {
+ memcpy(&version, value, sizeof(guint32));
+ version = GUINT32_FROM_LE(version);
+ ws_debug("BBLog version: %u", version);
+ section_info->bblog_version = version;
+ } else {
+ ws_debug("BBLog version parameter has strange length: %u", length);
+ }
+ break;
+ case NFLX_OPT_TYPE_TCPINFO:
+ ws_debug("BBLog tcpinfo of length: %u", length);
+ if (wblock->type == BLOCK_TYPE_CB_COPY) {
+ ws_buffer_assure_space(wblock->frame_buffer, length);
+ wblock->rec->rec_header.custom_block_header.length = length + 4;
+ memcpy(ws_buffer_start_ptr(wblock->frame_buffer), value, length);
+ memcpy(&temp, value, sizeof(guint64));
+ temp = GUINT64_FROM_LE(temp);
+ wblock->rec->ts.secs = section_info->bblog_offset_tv_sec + temp;
+ memcpy(&temp, value + sizeof(guint64), sizeof(guint64));
+ temp = GUINT64_FROM_LE(temp);
+ wblock->rec->ts.nsecs = (guint32)(section_info->bblog_offset_tv_usec + temp) * 1000;
+ if (wblock->rec->ts.nsecs >= 1000000000) {
+ wblock->rec->ts.secs += 1;
+ wblock->rec->ts.nsecs -= 1000000000;
+ }
+ wblock->rec->presence_flags = WTAP_HAS_TS;
+ wblock->internal = FALSE;
+ }
+ break;
+ case NFLX_OPT_TYPE_DUMPINFO:
+ if (length == sizeof(struct nflx_dumpinfo)) {
+ memcpy(&dumpinfo, value, sizeof(struct nflx_dumpinfo));
+ section_info->bblog_offset_tv_sec = GUINT64_FROM_LE(dumpinfo.tlh_offset_tv_sec);
+ section_info->bblog_offset_tv_usec = GUINT64_FROM_LE(dumpinfo.tlh_offset_tv_usec);
+ ws_debug("BBLog dumpinfo time offset: %" PRIu64, section_info->bblog_offset_tv_sec);
+ } else {
+ ws_debug("BBLog dumpinfo parameter has strange length: %u", length);
+ }
+ break;
+ case NFLX_OPT_TYPE_DUMPTIME:
+ if (length == sizeof(gint64)) {
+ memcpy(&dumptime, value, sizeof(gint64));
+ dumptime = GINT64_FROM_LE(dumptime);
+ ws_debug("BBLog dumpinfo time offset: %" PRIu64, dumptime);
+ } else {
+ ws_debug("BBLog dumptime parameter has strange length: %u", length);
+ }
+ break;
+ case NFLX_OPT_TYPE_STACKNAME:
+ if (length >= 2) {
+ ws_debug("BBLog stack name: %.*s(%u)", length - 1, value + 1, *(guint8 *)value);
+ } else {
+ ws_debug("BBLog stack name has strange length: %u)", length);
+ }
+ break;
+ default:
+ ws_debug("Unknown type: %u, length: %u", type, length);
+ break;
+ }
+ return wtap_block_add_nflx_custom_option(wblock->block, type, value, length) == WTAP_OPTTYPE_SUCCESS;
+}
+
+static gboolean
+pcapng_process_custom_option(wtapng_block_t *wblock,
+ section_info_t *section_info,
+ guint16 option_code, guint16 option_length,
+ const guint8 *option_content,
+ pcapng_opt_byte_order_e byte_order,
+ int *err, gchar **err_info)
+{
+ guint32 pen;
+ gboolean ret;
+
+ if (option_length < 4) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("pcapng: option length (%d) too small for custom option",
+ option_length);
+ return FALSE;
+ }
+ memcpy(&pen, option_content, sizeof(guint32));
+ switch (byte_order) {
+
+ case OPT_SECTION_BYTE_ORDER:
+ if (section_info->byte_swapped) {
+ pen = GUINT32_SWAP_LE_BE(pen);
+ }
+ break;
+
+ case OPT_BIG_ENDIAN:
+ pen = GUINT32_FROM_BE(pen);
+ break;
+
+ case OPT_LITTLE_ENDIAN:
+ pen = GUINT32_FROM_LE(pen);
+ break;
+
+ default:
+ /*
+ * This should not happen - this is called by pcapng_process_options(),
+ * which returns an error for an invalid byte_order argument, and
+ * otherwise passes the known-to-be-valid byte_order argument to
+ * us.
+ */
+ *err = WTAP_ERR_INTERNAL;
+ *err_info = ws_strdup_printf("pcapng: invalid byte order %d passed to pcapng_process_custom_option()",
+ byte_order);
+ return FALSE;
+ }
+ switch (pen) {
+ case PEN_NFLX:
+ ret = pcapng_process_nflx_custom_option(wblock, section_info, option_content + 4, option_length - 4);
+ break;
+ default:
+ ret = wtap_block_add_custom_option(wblock->block, option_code, pen, option_content + 4, option_length - 4) == WTAP_OPTTYPE_SUCCESS;
+ ws_debug("Custom option type %u (0x%04x) with unknown pen %u with custom data of length %u", option_code, option_code, pen, option_length - 4);
+ break;
+ }
+ ws_debug("returning %d", ret);
+ return ret;
+}
+
+#ifdef HAVE_PLUGINS
+static gboolean
+pcapng_process_unhandled_option(wtapng_block_t *wblock,
+ guint bt_index,
+ const section_info_t *section_info,
+ guint16 option_code, guint16 option_length,
+ const guint8 *option_content,
+ int *err, gchar **err_info)
+{
+ option_handler *handler;
+
+ /*
+ * Do we have a handler for this packet block option code?
+ */
+ if (option_handlers[bt_index] != NULL &&
+ (handler = (option_handler *)g_hash_table_lookup(option_handlers[bt_index],
+ GUINT_TO_POINTER((guint)option_code))) != NULL) {
+ /* Yes - call the handler. */
+ if (!handler->parser(wblock->block, section_info->byte_swapped,
+ option_length, option_content, err, err_info))
+ /* XXX - free anything? */
+ return FALSE;
+ }
+ return TRUE;
+}
+#else
+static gboolean
+pcapng_process_unhandled_option(wtapng_block_t *wblock _U_,
+ guint bt_index _U_,
+ const section_info_t *section_info _U_,
+ guint16 option_code _U_, guint16 option_length _U_,
+ const guint8 *option_content _U_,
+ int *err _U_, gchar **err_info _U_)
+{
+ return TRUE;
+}
+#endif
+
+gboolean
+pcapng_process_options(FILE_T fh, wtapng_block_t *wblock,
+ section_info_t *section_info,
+ guint opt_cont_buf_len,
+ gboolean (*process_option)(wtapng_block_t *,
+ const section_info_t *,
+ guint16, guint16,
+ const guint8 *,
+ int *, gchar **),
+ pcapng_opt_byte_order_e byte_order,
+ int *err, gchar **err_info)
+{
+ guint8 *option_content; /* Allocate as large as the options block */
+ guint opt_bytes_remaining;
+ const guint8 *option_ptr;
+ const pcapng_option_header_t *oh;
+ guint16 option_code, option_length;
+ guint rounded_option_length;
+
+ ws_debug("Options %u bytes", opt_cont_buf_len);
+ if (opt_cont_buf_len == 0) {
+ /* No options, so nothing to do */
+ return TRUE;
+ }
+
+ /* Allocate enough memory to hold all options */
+ option_content = (guint8 *)g_try_malloc(opt_cont_buf_len);
+ if (option_content == NULL) {
+ *err = ENOMEM; /* we assume we're out of memory */
+ return FALSE;
+ }
+
+ /* Read all the options into the buffer */
+ if (!wtap_read_bytes(fh, option_content, opt_cont_buf_len, err, err_info)) {
+ ws_debug("failed to read options");
+ g_free(option_content);
+ return FALSE;
+ }
+
+ /*
+ * Now process them.
+ * option_ptr starts out aligned on at least a 4-byte boundary, as
+ * that's what g_try_malloc() gives us, and each option is padded
+ * to a length that's a multiple of 4 bytes, so it remains aligned.
+ */
+ option_ptr = &option_content[0];
+ opt_bytes_remaining = opt_cont_buf_len;
+ while (opt_bytes_remaining != 0) {
+ /* Get option header. */
+ oh = (const pcapng_option_header_t *)(const void *)option_ptr;
+ /* Sanity check: don't run past the end of the options. */
+ if (sizeof (*oh) > opt_bytes_remaining) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("pcapng: Not enough data for option header");
+ g_free(option_content);
+ return FALSE;
+ }
+ option_code = oh->option_code;
+ option_length = oh->option_length;
+ switch (byte_order) {
+
+ case OPT_SECTION_BYTE_ORDER:
+ if (section_info->byte_swapped) {
+ option_code = GUINT16_SWAP_LE_BE(option_code);
+ option_length = GUINT16_SWAP_LE_BE(option_length);
+ }
+ break;
+
+ case OPT_BIG_ENDIAN:
+ option_code = GUINT16_FROM_BE(option_code);
+ option_length = GUINT16_FROM_BE(option_length);
+ break;
+
+ case OPT_LITTLE_ENDIAN:
+ option_code = GUINT16_FROM_LE(option_code);
+ option_length = GUINT16_FROM_LE(option_length);
+ break;
+
+ default:
+ /* Don't do that. */
+ *err = WTAP_ERR_INTERNAL;
+ *err_info = ws_strdup_printf("pcapng: invalid byte order %d passed to pcapng_process_options()",
+ byte_order);
+ return FALSE;
+ }
+ option_ptr += sizeof (*oh); /* 4 bytes, so it remains aligned */
+ opt_bytes_remaining -= sizeof (*oh);
+
+ /* Round up option length to a multiple of 4. */
+ rounded_option_length = ROUND_TO_4BYTE(option_length);
+
+ /* Sanity check: don't run past the end of the options. */
+ if (rounded_option_length > opt_bytes_remaining) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("pcapng: Not enough data to handle option of length %u",
+ option_length);
+ g_free(option_content);
+ return FALSE;
+ }
+
+ switch (option_code) {
+ case(OPT_EOFOPT): /* opt_endofopt */
+ if (opt_bytes_remaining != 0) {
+ ws_debug("%u bytes after opt_endofopt", opt_bytes_remaining);
+ }
+ /* padding should be ok here, just get out of this */
+ opt_bytes_remaining = rounded_option_length;
+ break;
+ case(OPT_COMMENT):
+ pcapng_process_string_option(wblock, option_code, option_length,
+ option_ptr);
+ break;
+ case(OPT_CUSTOM_STR_COPY):
+ case(OPT_CUSTOM_BIN_COPY):
+ case(OPT_CUSTOM_STR_NO_COPY):
+ case(OPT_CUSTOM_BIN_NO_COPY):
+ if (!pcapng_process_custom_option(wblock, section_info,
+ option_code, option_length,
+ option_ptr,
+ byte_order,
+ err, err_info)) {
+ g_free(option_content);
+ return FALSE;
+ }
+ break;
+
+ default:
+ if (process_option == NULL ||
+ !(*process_option)(wblock, (const section_info_t *)section_info, option_code,
+ option_length, option_ptr,
+ err, err_info)) {
+ g_free(option_content);
+ return FALSE;
+ }
+ }
+ option_ptr += rounded_option_length; /* multiple of 4 bytes, so it remains aligned */
+ opt_bytes_remaining -= rounded_option_length;
+ }
+ g_free(option_content);
+ return TRUE;
+}
+
+typedef enum {
+ PCAPNG_BLOCK_OK,
+ PCAPNG_BLOCK_NOT_SHB,
+ PCAPNG_BLOCK_ERROR
+} block_return_val;
+
+static gboolean
+pcapng_process_section_header_block_option(wtapng_block_t *wblock,
+ const section_info_t *section_info,
+ guint16 option_code,
+ guint16 option_length,
+ const guint8 *option_content,
+ int *err, gchar **err_info)
+{
+ /*
+ * Handle option content.
+ *
+ * ***DO NOT*** add any items to this table that are not
+ * standardized option codes in either section 3.5 "Options"
+ * of the current pcapng spec, at
+ *
+ * https://pcapng.github.io/pcapng/draft-ietf-opsawg-pcapng.html#name-options
+ *
+ * or in the list of options in section 4.1 "Section Header Block"
+ * of the current pcapng spec, at
+ *
+ * https://pcapng.github.io/pcapng/draft-ietf-opsawg-pcapng.html#name-section-header-block
+ *
+ * All option codes in this switch statement here must be listed
+ * in one of those places as standardized option types.
+ */
+ switch (option_code) {
+ case(OPT_SHB_HARDWARE):
+ pcapng_process_string_option(wblock, option_code, option_length,
+ option_content);
+ break;
+ case(OPT_SHB_OS):
+ pcapng_process_string_option(wblock, option_code, option_length,
+ option_content);
+ break;
+ case(OPT_SHB_USERAPPL):
+ pcapng_process_string_option(wblock, option_code, option_length,
+ option_content);
+ break;
+ default:
+ if (!pcapng_process_unhandled_option(wblock, BT_INDEX_SHB,
+ section_info, option_code,
+ option_length, option_content,
+ err, err_info))
+ return FALSE;
+ break;
+ }
+ return TRUE;
+}
+
+static block_return_val
+pcapng_read_section_header_block(FILE_T fh, pcapng_block_header_t *bh,
+ section_info_t *section_info,
+ wtapng_block_t *wblock,
+ int *err, gchar **err_info)
+{
+ gboolean byte_swapped;
+ guint16 version_major;
+ guint16 version_minor;
+ guint opt_cont_buf_len;
+ pcapng_section_header_block_t shb;
+ wtapng_section_mandatory_t* section_data;
+
+ /* read fixed-length part of the block */
+ if (!wtap_read_bytes(fh, &shb, sizeof shb, err, err_info)) {
+ /*
+ * Even if this is just a short read, report it as an error.
+ * It *is* a read error except when we're doing an open, in
+ * which case it's a "this isn't a pcapng file" indication.
+ * The open code will call us directly, and treat a short
+ * read error as such an indication.
+ */
+ return PCAPNG_BLOCK_ERROR;
+ }
+
+ /* is the magic number one we expect? */
+ switch (shb.magic) {
+ case(0x1A2B3C4D):
+ /* this seems pcapng with correct byte order */
+ byte_swapped = FALSE;
+ version_major = shb.version_major;
+ version_minor = shb.version_minor;
+
+ ws_debug("SHB (our byte order) V%u.%u, len %u",
+ version_major, version_minor, bh->block_total_length);
+ break;
+ case(0x4D3C2B1A):
+ /* this seems pcapng with swapped byte order */
+ byte_swapped = TRUE;
+ version_major = GUINT16_SWAP_LE_BE(shb.version_major);
+ version_minor = GUINT16_SWAP_LE_BE(shb.version_minor);
+
+ /* tweak the block length to meet current swapping that we know now */
+ bh->block_total_length = GUINT32_SWAP_LE_BE(bh->block_total_length);
+
+ ws_debug("SHB (byte-swapped) V%u.%u, len %u",
+ version_major, version_minor, bh->block_total_length);
+ break;
+ default:
+ /* Not a "pcapng" magic number we know about. */
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("pcapng: unknown byte-order magic number 0x%08x", shb.magic);
+
+ /*
+ * See above comment about PCAPNG_BLOCK_NOT_SHB.
+ */
+ return PCAPNG_BLOCK_NOT_SHB;
+ }
+
+ /*
+ * Add padding bytes to the block total length.
+ *
+ * See the comment in pcapng_read_block() for a long discussion
+ * of this.
+ */
+ bh->block_total_length = ROUND_TO_4BYTE(bh->block_total_length);
+
+ /*
+ * Is this block long enough to be an SHB?
+ */
+ if (bh->block_total_length < MIN_SHB_SIZE) {
+ /*
+ * No.
+ */
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("pcapng: total block length %u of an SHB is less than the minimum SHB size %u",
+ bh->block_total_length, MIN_SHB_SIZE);
+ return PCAPNG_BLOCK_ERROR;
+ }
+
+ /* OK, at this point we assume it's a pcapng file.
+
+ Don't try to allocate memory for a huge number of options, as
+ that might fail and, even if it succeeds, it might not leave
+ any address space or memory+backing store for anything else.
+
+ We do that by imposing a maximum block size of MAX_BLOCK_SIZE.
+ We check for this *after* checking the SHB for its byte
+ order magic number, so that non-pcapng files are less
+ likely to be treated as bad pcapng files. */
+ if (bh->block_total_length > MAX_BLOCK_SIZE) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("pcapng: total block length %u is too large (> %u)",
+ bh->block_total_length, MAX_BLOCK_SIZE);
+ return PCAPNG_BLOCK_ERROR;
+ }
+
+ /* Currently only SHB versions 1.0 and 1.2 are supported;
+ version 1.2 is treated as being the same as version 1.0.
+ See the current version of the pcapng specification.
+
+ Version 1.2 is written by some programs that write additional
+ block types (which can be read by any code that handles them,
+ regarless of whether the minor version if 0 or 2, so that's
+ not a reason to change the minor version number).
+
+ XXX - the pcapng specification says that readers should
+ just ignore sections with an unsupported version number;
+ presumably they can also report an error if they skip
+ all the way to the end of the file without finding
+ any versions that they support. */
+ if (!(version_major == 1 &&
+ (version_minor == 0 || version_minor == 2))) {
+ *err = WTAP_ERR_UNSUPPORTED;
+ *err_info = ws_strdup_printf("pcapng: unknown SHB version %u.%u",
+ version_major, version_minor);
+ return PCAPNG_BLOCK_ERROR;
+ }
+
+ memset(section_info, 0, sizeof(section_info_t));
+ section_info->byte_swapped = byte_swapped;
+ section_info->version_major = version_major;
+ section_info->version_minor = version_minor;
+
+ /*
+ * Set wblock->block to a newly-allocated section header block.
+ */
+ wblock->block = wtap_block_create(WTAP_BLOCK_SECTION);
+
+ /*
+ * Set the mandatory values for the block.
+ */
+ section_data = (wtapng_section_mandatory_t*)wtap_block_get_mandatory_data(wblock->block);
+ /* 64bit section_length (currently unused) */
+ if (section_info->byte_swapped) {
+ section_data->section_length = GUINT64_SWAP_LE_BE(shb.section_length);
+ } else {
+ section_data->section_length = shb.section_length;
+ }
+
+ /* Options */
+ opt_cont_buf_len = bh->block_total_length - MIN_SHB_SIZE;
+ if (!pcapng_process_options(fh, wblock, section_info, opt_cont_buf_len,
+ pcapng_process_section_header_block_option,
+ OPT_SECTION_BYTE_ORDER, err, err_info))
+ return PCAPNG_BLOCK_ERROR;
+
+ /*
+ * We don't return these to the caller in pcapng_read().
+ */
+ wblock->internal = TRUE;
+
+ return PCAPNG_BLOCK_OK;
+}
+
+static gboolean
+pcapng_process_if_descr_block_option(wtapng_block_t *wblock,
+ const section_info_t *section_info,
+ guint16 option_code,
+ guint16 option_length,
+ const guint8 *option_content,
+ int *err, gchar **err_info)
+{
+ if_filter_opt_t if_filter;
+
+ /*
+ * Handle option content.
+ *
+ * ***DO NOT*** add any items to this table that are not
+ * standardized option codes in either section 3.5 "Options"
+ * of the current pcapng spec, at
+ *
+ * https://pcapng.github.io/pcapng/draft-ietf-opsawg-pcapng.html#name-options
+ *
+ * or in the list of options in section 4.1 "Section Header Block"
+ * of the current pcapng spec, at
+ *
+ * https://pcapng.github.io/pcapng/draft-ietf-opsawg-pcapng.html#name-section-header-block
+ *
+ * All option codes in this switch statement here must be listed
+ * in one of those places as standardized option types.
+ */
+ switch (option_code) {
+ case(OPT_IDB_NAME): /* if_name */
+ pcapng_process_string_option(wblock, option_code, option_length,
+ option_content);
+ break;
+ case(OPT_IDB_DESCRIPTION): /* if_description */
+ pcapng_process_string_option(wblock, option_code, option_length,
+ option_content);
+ break;
+ case(OPT_IDB_SPEED): /* if_speed */
+ pcapng_process_uint64_option(wblock, section_info,
+ OPT_SECTION_BYTE_ORDER,
+ option_code, option_length,
+ option_content);
+ break;
+ case(OPT_IDB_TSRESOL): /* if_tsresol */
+ pcapng_process_uint8_option(wblock, option_code, option_length,
+ option_content);
+ break;
+ /*
+ * if_tzone 10 Time zone for GMT support (TODO: specify better). TODO: give a good example
+ */
+ case(OPT_IDB_FILTER): /* if_filter */
+ if (option_length < 1) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("pcapng: packet block verdict option length %u is < 1",
+ option_length);
+ /* XXX - free anything? */
+ return FALSE;
+ }
+ /* The first byte of the Option Data keeps a code of the filter used (e.g. if this is a libpcap string,
+ * or BPF bytecode.
+ */
+ if (option_content[0] == 0) {
+ if_filter.type = if_filter_pcap;
+ if_filter.data.filter_str = g_strndup((char *)option_content+1, option_length-1);
+ ws_debug("filter_str %s option_length %u",
+ if_filter.data.filter_str, option_length);
+ /* Fails with multiple options; we silently ignore the failure */
+ wtap_block_add_if_filter_option(wblock->block, option_code, &if_filter);
+ g_free(if_filter.data.filter_str);
+ } else if (option_content[0] == 1) {
+ /*
+ * XXX - byte-swap the code and k fields
+ * of each instruction as needed!
+ *
+ * XXX - what if option_length-1 is not a
+ * multiple of the size of a BPF instruction?
+ */
+ guint num_insns;
+ const guint8 *insn_in;
+
+ if_filter.type = if_filter_bpf;
+ num_insns = (option_length-1)/8;
+ insn_in = option_content+1;
+ if_filter.data.bpf_prog.bpf_prog_len = num_insns;
+ if_filter.data.bpf_prog.bpf_prog = g_new(wtap_bpf_insn_t, num_insns);
+ for (guint i = 0; i < num_insns; i++) {
+ wtap_bpf_insn_t *insn = &if_filter.data.bpf_prog.bpf_prog[i];
+
+ memcpy(&insn->code, insn_in, 2);
+ if (section_info->byte_swapped)
+ insn->code = GUINT16_SWAP_LE_BE(insn->code);
+ insn_in += 2;
+ memcpy(&insn->jt, insn_in, 1);
+ insn_in += 1;
+ memcpy(&insn->jf, insn_in, 1);
+ insn_in += 1;
+ memcpy(&insn->k, insn_in, 4);
+ if (section_info->byte_swapped)
+ insn->k = GUINT32_SWAP_LE_BE(insn->k);
+ insn_in += 4;
+ }
+ /* Fails with multiple options; we silently ignore the failure */
+ wtap_block_add_if_filter_option(wblock->block, option_code, &if_filter);
+ g_free(if_filter.data.bpf_prog.bpf_prog);
+ }
+ break;
+ case(OPT_IDB_OS): /* if_os */
+ /*
+ * if_os 12 A UTF-8 string containing the name of the operating system of the machine in which this interface is installed.
+ * This can be different from the same information that can be contained by the Section Header Block (Section 3.1 (Section Header Block (mandatory)))
+ * because the capture can have been done on a remote machine. "Windows XP SP2" / "openSUSE 10.2" / ...
+ */
+ pcapng_process_string_option(wblock, option_code, option_length,
+ option_content);
+ break;
+ case(OPT_IDB_FCSLEN): /* if_fcslen */
+ pcapng_process_uint8_option(wblock, option_code, option_length,
+ option_content);
+ break;
+ case(OPT_IDB_HARDWARE): /* if_hardware */
+ pcapng_process_string_option(wblock, option_code, option_length,
+ option_content);
+ break;
+ /* TODO: process these! */
+ case(OPT_IDB_IP4ADDR):
+ /*
+ * Interface network address and netmask. This option can be
+ * repeated multiple times within the same Interface
+ * Description Block when multiple IPv4 addresses are assigned
+ * to the interface. 192 168 1 1 255 255 255 0
+ */
+ break;
+ case(OPT_IDB_IP6ADDR):
+ /*
+ * Interface network address and prefix length (stored in the
+ * last byte). This option can be repeated multiple times
+ * within the same Interface Description Block when multiple
+ * IPv6 addresses are assigned to the interface.
+ * 2001:0db8:85a3:08d3:1319:8a2e:0370:7344/64 is written (in
+ * hex) as "20 01 0d b8 85 a3 08 d3 13 19 8a 2e 03 70 73 44
+ * 40"
+ */
+ break;
+ case(OPT_IDB_MACADDR):
+ /*
+ * Interface Hardware MAC address (48 bits). 00 01 02 03 04 05
+ */
+ break;
+ case(OPT_IDB_EUIADDR):
+ /*
+ * Interface Hardware EUI address (64 bits), if available.
+ * TODO: give a good example
+ */
+ break;
+ case(OPT_IDB_TZONE):
+ /*
+ * Time zone for GMT support. This option has never been
+ * specified in greater detail and, unless it were to identify
+ * something such as an IANA time zone database timezone,
+ * would be insufficient for converting between UTC and local
+ * time. Therefore, it SHOULD NOT be used; instead, the
+ * if_iana_tzname option SHOULD be used if time zone
+ * information is to be specified.
+ *
+ * Given that, we don't do anything with it.
+ */
+ break;
+ case(OPT_IDB_TSOFFSET):
+ /*
+ * A 64-bit integer value that specifies an offset (in
+ * seconds) that must be added to the timestamp of each packet
+ * to obtain the absolute timestamp of a packet. If this optio
+ * is not present, an offset of 0 is assumed (i.e., timestamps
+ * in blocks are absolute timestamps.)
+ */
+ pcapng_process_int64_option(wblock, section_info,
+ OPT_SECTION_BYTE_ORDER,
+ option_code, option_length,
+ option_content);
+ break;
+ default:
+ if (!pcapng_process_unhandled_option(wblock, BT_INDEX_IDB,
+ section_info, option_code,
+ option_length, option_content,
+ err, err_info))
+ return FALSE;
+ break;
+ }
+ return TRUE;
+}
+
+/* "Interface Description Block" */
+static gboolean
+pcapng_read_if_descr_block(wtap *wth, FILE_T fh, pcapng_block_header_t *bh,
+ section_info_t *section_info,
+ wtapng_block_t *wblock, int *err, gchar **err_info)
+{
+ /* Default time stamp resolution is 10^6 */
+ guint64 time_units_per_second = 1000000;
+ int tsprecision = 6;
+ guint opt_cont_buf_len;
+ pcapng_interface_description_block_t idb;
+ wtapng_if_descr_mandatory_t* if_descr_mand;
+ guint link_type;
+ guint8 if_tsresol;
+
+ /*
+ * Is this block long enough to be an IDB?
+ */
+ if (bh->block_total_length < MIN_IDB_SIZE) {
+ /*
+ * No.
+ */
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("pcapng: total block length %u of an IDB is less than the minimum IDB size %u",
+ bh->block_total_length, MIN_IDB_SIZE);
+ return FALSE;
+ }
+
+ /* read block content */
+ if (!wtap_read_bytes(fh, &idb, sizeof idb, err, err_info)) {
+ ws_debug("failed to read IDB");
+ return FALSE;
+ }
+
+ /*
+ * Set wblock->block to a newly-allocated interface ID and information
+ * block.
+ */
+ wblock->block = wtap_block_create(WTAP_BLOCK_IF_ID_AND_INFO);
+
+ /*
+ * Set the mandatory values for the block.
+ */
+ if_descr_mand = (wtapng_if_descr_mandatory_t*)wtap_block_get_mandatory_data(wblock->block);
+ if (section_info->byte_swapped) {
+ link_type = GUINT16_SWAP_LE_BE(idb.linktype);
+ if_descr_mand->snap_len = GUINT32_SWAP_LE_BE(idb.snaplen);
+ } else {
+ link_type = idb.linktype;
+ if_descr_mand->snap_len = idb.snaplen;
+ }
+
+ if_descr_mand->wtap_encap = wtap_pcap_encap_to_wtap_encap(link_type);
+
+ ws_debug("IDB link_type %u (%s), snap %u",
+ link_type,
+ wtap_encap_description(if_descr_mand->wtap_encap),
+ if_descr_mand->snap_len);
+
+ if (if_descr_mand->snap_len > wtap_max_snaplen_for_encap(if_descr_mand->wtap_encap)) {
+ /*
+ * We do not use this value, maybe we should check the
+ * snap_len of the packets against it. For now, only warn.
+ */
+ ws_debug("snapshot length %u unrealistic.",
+ if_descr_mand->snap_len);
+ /*if_descr_mand->snap_len = WTAP_MAX_PACKET_SIZE_STANDARD;*/
+ }
+
+ /* Options */
+ opt_cont_buf_len = bh->block_total_length - MIN_IDB_SIZE;
+ if (!pcapng_process_options(fh, wblock, section_info, opt_cont_buf_len,
+ pcapng_process_if_descr_block_option,
+ OPT_SECTION_BYTE_ORDER, err, err_info))
+ return FALSE;
+
+ /*
+ * Did we get a time stamp precision option?
+ */
+ if (wtap_block_get_uint8_option_value(wblock->block, OPT_IDB_TSRESOL,
+ &if_tsresol) == WTAP_OPTTYPE_SUCCESS) {
+ /*
+ * Yes. Set time_units_per_second appropriately.
+ */
+ guint8 exponent;
+
+ exponent = (guint8)(if_tsresol & 0x7f);
+ if (if_tsresol & 0x80) {
+ /*
+ * 2^63 fits in a 64-bit unsigned number; 2^64 does not.
+ *
+ * ((2^64-1)/(2^63) is about 1.99, so, in practice, that
+ * fine a time stamp resolution works only if you start
+ * capturing at the Unix/POSIX epoch and capture for about
+ * 1.9 seconds, so the maximum useful power-of-2 exponent
+ * in a pcapng file is less than 63.)
+ */
+ if (exponent > 63) {
+ /*
+ * Time units per second won't fit in a 64-bit integer,
+ * so Wireshark's current code can't read the file.
+ */
+ *err = WTAP_ERR_UNSUPPORTED;
+ *err_info = ws_strdup_printf("pcapng: IDB power-of-2 time stamp resolution %u > 63",
+ exponent);
+ return FALSE;
+ }
+
+ /* 2^exponent */
+ time_units_per_second = G_GUINT64_CONSTANT(1) << exponent;
+
+ /*
+ * Set the display precision to a value large enough to
+ * show the fractional time units we get, so that we
+ * don't display more digits than are justified.
+ *
+ * (That's also used as the base-10 if_tsresol value we use
+ * if we write this file as a pcapng file. Yes, that means
+ * that we won't write out the exact value we read in.
+ *
+ * Dealing with base-2 time stamps is a bit of a mess,
+ * thanks to humans counting with their fingers rather
+ * than their hands, and it applies to mroe files than
+ * pcapng files, e.g. ERF files.)
+ */
+ if (time_units_per_second >= 1000000000)
+ tsprecision = WTAP_TSPREC_NSEC;
+ else if (time_units_per_second >= 100000000)
+ tsprecision = WTAP_TSPREC_10_NSEC;
+ else if (time_units_per_second >= 10000000)
+ tsprecision = WTAP_TSPREC_100_NSEC;
+ else if (time_units_per_second >= 1000000)
+ tsprecision = WTAP_TSPREC_USEC;
+ else if (time_units_per_second >= 100000)
+ tsprecision = WTAP_TSPREC_10_USEC;
+ else if (time_units_per_second >= 10000)
+ tsprecision = WTAP_TSPREC_100_USEC;
+ else if (time_units_per_second >= 1000)
+ tsprecision = WTAP_TSPREC_MSEC;
+ else if (time_units_per_second >= 100)
+ tsprecision = WTAP_TSPREC_10_MSEC;
+ else if (time_units_per_second >= 10)
+ tsprecision = WTAP_TSPREC_100_MSEC;
+ else
+ tsprecision = WTAP_TSPREC_SEC;
+ } else {
+ /*
+ * 10^19 fits in a 64-bit unsigned number; 10^20 does not.
+ *
+ * ((2^64-1)/(10^19) is about 1.84, so, in practice, that
+ * fine a time stamp resolution works only if you start
+ * capturing at the Unix/POSIX epoch and capture for about
+ * 1.8 seconds, so the maximum useful power-of-10 exponent
+ * in a pcapng file is less than 19.)
+ */
+ guint64 result;
+
+ if (exponent > 19) {
+ /*
+ * Time units per second won't fit in a 64-bit integer,
+ * so Wireshark's current code can't read the file.
+ */
+ *err = WTAP_ERR_UNSUPPORTED;
+ *err_info = ws_strdup_printf("pcapng: IDB power-of-10 time stamp resolution %u > 19",
+ exponent);
+ return FALSE;
+ }
+
+ /* 10^exponent */
+ result = 1;
+ for (guint i = 0; i < exponent; i++) {
+ result *= 10U;
+ }
+ time_units_per_second = result;
+
+ /*
+ * Set the display precision to min(exponent, WS_TSPREC_MAX),
+ * so that we don't display more digits than are justified.
+ * (That's also used as the base-10 if_tsresol value we use
+ * if we write this file as a pcapng file.)
+ */
+ if (exponent <= WS_TSPREC_MAX) {
+ tsprecision = exponent;
+ } else {
+ tsprecision = WS_TSPREC_MAX;
+ }
+ }
+ if (time_units_per_second > (((guint64)1) << 32)) {
+ ws_debug("time conversion might be inaccurate");
+ }
+ }
+
+ /*
+ * Set the time units per second for this interface.
+ */
+ if_descr_mand->time_units_per_second = time_units_per_second;
+
+ /*
+ * Set the number of digits of precision to display (and the
+ * number to use for this interface if saving to a pcapng
+ * file).
+ */
+ if_descr_mand->tsprecision = tsprecision;
+
+ /*
+ * If the per-file encapsulation isn't known, set it to this
+ * interface's encapsulation.
+ *
+ * If it *is* known, and it isn't this interface's encapsulation,
+ * set it to WTAP_ENCAP_PER_PACKET, as this file doesn't
+ * have a single encapsulation for all interfaces in the file,
+ * so it probably doesn't have a single encapsulation for all
+ * packets in the file.
+ */
+ if (wth->file_encap == WTAP_ENCAP_NONE) {
+ wth->file_encap = if_descr_mand->wtap_encap;
+ } else {
+ if (wth->file_encap != if_descr_mand->wtap_encap) {
+ wth->file_encap = WTAP_ENCAP_PER_PACKET;
+ }
+ }
+
+ /*
+ * The same applies to the per-file time stamp resolution.
+ */
+ if (wth->file_tsprec == WTAP_TSPREC_UNKNOWN) {
+ wth->file_tsprec = if_descr_mand->tsprecision;
+ } else {
+ if (wth->file_tsprec != if_descr_mand->tsprecision) {
+ wth->file_tsprec = WTAP_TSPREC_PER_PACKET;
+ }
+ }
+
+ /*
+ * We don't return these to the caller in pcapng_read().
+ */
+ wblock->internal = TRUE;
+
+ return TRUE;
+}
+
+static gboolean
+pcapng_read_decryption_secrets_block(FILE_T fh, pcapng_block_header_t *bh,
+ const section_info_t *section_info,
+ wtapng_block_t *wblock,
+ int *err, gchar **err_info)
+{
+ guint to_read;
+ pcapng_decryption_secrets_block_t dsb;
+ wtapng_dsb_mandatory_t *dsb_mand;
+
+ /* read block content */
+ if (!wtap_read_bytes(fh, &dsb, sizeof(dsb), err, err_info)) {
+ ws_debug("failed to read DSB");
+ return FALSE;
+ }
+
+ /*
+ * Set wblock->block to a newly-allocated decryption secrets block.
+ */
+ wblock->block = wtap_block_create(WTAP_BLOCK_DECRYPTION_SECRETS);
+
+ /*
+ * Set the mandatory values for the block.
+ */
+ dsb_mand = (wtapng_dsb_mandatory_t *)wtap_block_get_mandatory_data(wblock->block);
+ if (section_info->byte_swapped) {
+ dsb_mand->secrets_type = GUINT32_SWAP_LE_BE(dsb.secrets_type);
+ dsb_mand->secrets_len = GUINT32_SWAP_LE_BE(dsb.secrets_len);
+ } else {
+ dsb_mand->secrets_type = dsb.secrets_type;
+ dsb_mand->secrets_len = dsb.secrets_len;
+ }
+ /* Sanity check: assume the secrets are not larger than 1 GiB */
+ if (dsb_mand->secrets_len > 1024 * 1024 * 1024) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("pcapng: secrets block is too large: %u", dsb_mand->secrets_len);
+ return FALSE;
+ }
+ dsb_mand->secrets_data = (guint8 *)g_malloc0(dsb_mand->secrets_len);
+ if (!wtap_read_bytes(fh, dsb_mand->secrets_data, dsb_mand->secrets_len, err, err_info)) {
+ ws_debug("failed to read DSB");
+ return FALSE;
+ }
+
+ /* Skip past padding and discard options (not supported yet). */
+ to_read = bh->block_total_length - MIN_DSB_SIZE - dsb_mand->secrets_len;
+ if (!wtap_read_bytes(fh, NULL, to_read, err, err_info)) {
+ ws_debug("failed to read DSB options");
+ return FALSE;
+ }
+
+ /*
+ * We don't return these to the caller in pcapng_read().
+ */
+ wblock->internal = TRUE;
+
+ return TRUE;
+}
+
+static bool
+pcapng_read_sysdig_meta_event_block(FILE_T fh, pcapng_block_header_t *bh,
+ wtapng_block_t *wblock,
+ int *err, gchar **err_info)
+{
+ guint to_read;
+ wtapng_sysdig_mev_mandatory_t *mev_mand;
+
+ /*
+ * Set wblock->block to a newly-allocated Sysdig meta event block.
+ */
+ wblock->block = wtap_block_create(WTAP_BLOCK_SYSDIG_META_EVENT);
+
+ /*
+ * Set the mandatory values for the block.
+ */
+ mev_mand = (wtapng_sysdig_mev_mandatory_t *)wtap_block_get_mandatory_data(wblock->block);
+ mev_mand->mev_type = bh->block_type;
+ mev_mand->mev_data_len = bh->block_total_length -
+ (int)sizeof(pcapng_block_header_t) -
+ (int)sizeof(bh->block_total_length);
+
+ /* Sanity check: assume event data can't be larger than 1 GiB */
+ if (mev_mand->mev_data_len > 1024 * 1024 * 1024) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("pcapng: Sysdig mev block is too large: %u", mev_mand->mev_data_len);
+ return false;
+ }
+ mev_mand->mev_data = (uint8_t *)g_malloc(mev_mand->mev_data_len);
+ if (!wtap_read_bytes(fh, mev_mand->mev_data, mev_mand->mev_data_len, err, err_info)) {
+ ws_debug("failed to read Sysdig mev");
+ return false;
+ }
+
+ /* Skip past padding and discard options (not supported yet). */
+ to_read = bh->block_total_length - MIN_BLOCK_SIZE - mev_mand->mev_data_len;
+ if (!wtap_read_bytes(fh, NULL, to_read, err, err_info)) {
+ ws_debug("failed to read Sysdig mev options");
+ return FALSE;
+ }
+
+ /*
+ * We don't return these to the caller in pcapng_read().
+ */
+ wblock->internal = true;
+
+ return true;
+}
+
+static gboolean
+pcapng_process_packet_block_option(wtapng_block_t *wblock,
+ const section_info_t *section_info,
+ guint16 option_code,
+ guint16 option_length,
+ const guint8 *option_content,
+ int *err, gchar **err_info)
+{
+ guint64 tmp64;
+ packet_verdict_opt_t packet_verdict;
+ packet_hash_opt_t packet_hash;
+
+ /*
+ * Handle option content.
+ *
+ * ***DO NOT*** add any items to this table that are not
+ * standardized option codes in either section 3.5 "Options"
+ * of the current pcapng spec, at
+ *
+ * https://pcapng.github.io/pcapng/draft-ietf-opsawg-pcapng.html#name-options
+ *
+ * or in the list of options in section 4.3 "Enhanced Packet Block"
+ * of the current pcapng spec, at
+ *
+ * https://pcapng.github.io/pcapng/draft-ietf-opsawg-pcapng.html#name-enhanced-packet-block
+ *
+ * All option codes in this switch statement here must be listed
+ * in one of those places as standardized option types.
+ */
+ switch (option_code) {
+ case(OPT_EPB_FLAGS):
+ if (option_length != 4) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("pcapng: packet block flags option length %u is not 4",
+ option_length);
+ /* XXX - free anything? */
+ return FALSE;
+ }
+ pcapng_process_uint32_option(wblock, section_info,
+ OPT_SECTION_BYTE_ORDER,
+ option_code, option_length,
+ option_content);
+ break;
+ case(OPT_EPB_HASH):
+ if (option_length < 1) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("pcapng: packet block hash option length %u is < 1",
+ option_length);
+ /* XXX - free anything? */
+ return FALSE;
+ }
+ packet_hash.type = option_content[0];
+ packet_hash.hash_bytes =
+ g_byte_array_new_take((guint8 *)g_memdup2(&option_content[1],
+ option_length - 1),
+ option_length - 1);
+ wtap_block_add_packet_hash_option(wblock->block, option_code, &packet_hash);
+ wtap_packet_hash_free(&packet_hash);
+ ws_debug("hash type %u, data len %u",
+ option_content[0], option_length - 1);
+ break;
+ case(OPT_EPB_DROPCOUNT):
+ if (option_length != 8) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("pcapng: packet block drop count option length %u is not 8",
+ option_length);
+ /* XXX - free anything? */
+ return FALSE;
+ }
+ pcapng_process_uint64_option(wblock, section_info,
+ OPT_SECTION_BYTE_ORDER,
+ option_code, option_length,
+ option_content);
+ break;
+ case(OPT_EPB_PACKETID):
+ if (option_length != 8) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("pcapng: packet block packet id option length %u is not 8",
+ option_length);
+ /* XXX - free anything? */
+ return FALSE;
+ }
+ pcapng_process_uint64_option(wblock, section_info,
+ OPT_SECTION_BYTE_ORDER,
+ option_code, option_length,
+ option_content);
+ break;
+ case(OPT_EPB_QUEUE):
+ if (option_length != 4) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("pcapng: packet block queue option length %u is not 4",
+ option_length);
+ /* XXX - free anything? */
+ return FALSE;
+ }
+ pcapng_process_uint32_option(wblock, section_info,
+ OPT_SECTION_BYTE_ORDER,
+ option_code, option_length,
+ option_content);
+ break;
+ case(OPT_EPB_VERDICT):
+ if (option_length < 1) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("pcapng: packet block verdict option length %u is < 1",
+ option_length);
+ /* XXX - free anything? */
+ return FALSE;
+ }
+ switch (option_content[0]) {
+
+ case(OPT_VERDICT_TYPE_HW):
+ packet_verdict.type = packet_verdict_hardware;
+ packet_verdict.data.verdict_bytes =
+ g_byte_array_new_take((guint8 *)g_memdup2(&option_content[1],
+ option_length - 1),
+ option_length - 1);
+ break;
+
+ case(OPT_VERDICT_TYPE_TC):
+ if (option_length != 9) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("pcapng: packet block TC verdict option length %u is != 9",
+ option_length);
+ /* XXX - free anything? */
+ return FALSE;
+ }
+ /* Don't cast a guint8 * into a guint64 *--the
+ * guint8 * may not point to something that's
+ * aligned correctly.
+ */
+ memcpy(&tmp64, &option_content[1], sizeof(guint64));
+ if (section_info->byte_swapped)
+ tmp64 = GUINT64_SWAP_LE_BE(tmp64);
+ packet_verdict.type = packet_verdict_linux_ebpf_tc;
+ packet_verdict.data.verdict_linux_ebpf_tc = tmp64;
+ break;
+
+ case(OPT_VERDICT_TYPE_XDP):
+ if (option_length != 9) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("pcapng: packet block XDP verdict option length %u is != 9",
+ option_length);
+ /* XXX - free anything? */
+ return FALSE;
+ }
+ /* Don't cast a guint8 * into a guint64 *--the
+ * guint8 * may not point to something that's
+ * aligned correctly.
+ */
+ memcpy(&tmp64, &option_content[1], sizeof(guint64));
+ if (section_info->byte_swapped)
+ tmp64 = GUINT64_SWAP_LE_BE(tmp64);
+ packet_verdict.type = packet_verdict_linux_ebpf_xdp;
+ packet_verdict.data.verdict_linux_ebpf_xdp = tmp64;
+ break;
+
+ default:
+ /* Silently ignore unknown verdict types */
+ return TRUE;
+ }
+ wtap_block_add_packet_verdict_option(wblock->block, option_code, &packet_verdict);
+ wtap_packet_verdict_free(&packet_verdict);
+ ws_debug("verdict type %u, data len %u",
+ option_content[0], option_length - 1);
+ break;
+ default:
+ if (!pcapng_process_unhandled_option(wblock, BT_INDEX_PBS,
+ section_info, option_code,
+ option_length, option_content,
+ err, err_info))
+ return FALSE;
+ break;
+ }
+ return TRUE;
+}
+
+static gboolean
+pcapng_read_packet_block(FILE_T fh, pcapng_block_header_t *bh,
+ section_info_t *section_info,
+ wtapng_block_t *wblock,
+ int *err, gchar **err_info, gboolean enhanced)
+{
+ guint block_read;
+ guint opt_cont_buf_len;
+ pcapng_enhanced_packet_block_t epb;
+ pcapng_packet_block_t pb;
+ wtapng_packet_t packet;
+ guint32 padding;
+ guint32 flags;
+ guint64 tmp64;
+ interface_info_t iface_info;
+ guint64 ts;
+ int pseudo_header_len;
+ int fcslen;
+
+ wblock->block = wtap_block_create(WTAP_BLOCK_PACKET);
+
+ /* "(Enhanced) Packet Block" read fixed part */
+ if (enhanced) {
+ /*
+ * Is this block long enough to be an EPB?
+ */
+ if (bh->block_total_length < MIN_EPB_SIZE) {
+ /*
+ * No.
+ */
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("pcapng: total block length %u of an EPB is less than the minimum EPB size %u",
+ bh->block_total_length, MIN_EPB_SIZE);
+ return FALSE;
+ }
+ if (!wtap_read_bytes(fh, &epb, sizeof epb, err, err_info)) {
+ ws_debug("failed to read packet data");
+ return FALSE;
+ }
+ block_read = (guint)sizeof epb;
+
+ if (section_info->byte_swapped) {
+ packet.interface_id = GUINT32_SWAP_LE_BE(epb.interface_id);
+ packet.drops_count = 0xFFFF; /* invalid */
+ packet.ts_high = GUINT32_SWAP_LE_BE(epb.timestamp_high);
+ packet.ts_low = GUINT32_SWAP_LE_BE(epb.timestamp_low);
+ packet.cap_len = GUINT32_SWAP_LE_BE(epb.captured_len);
+ packet.packet_len = GUINT32_SWAP_LE_BE(epb.packet_len);
+ } else {
+ packet.interface_id = epb.interface_id;
+ packet.drops_count = 0xFFFF; /* invalid */
+ packet.ts_high = epb.timestamp_high;
+ packet.ts_low = epb.timestamp_low;
+ packet.cap_len = epb.captured_len;
+ packet.packet_len = epb.packet_len;
+ }
+ ws_debug("EPB on interface_id %d, cap_len %d, packet_len %d",
+ packet.interface_id, packet.cap_len, packet.packet_len);
+ } else {
+ /*
+ * Is this block long enough to be a PB?
+ */
+ if (bh->block_total_length < MIN_PB_SIZE) {
+ /*
+ * No.
+ */
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("pcapng: total block length %u of a PB is less than the minimum PB size %u",
+ bh->block_total_length, MIN_PB_SIZE);
+ return FALSE;
+ }
+ if (!wtap_read_bytes(fh, &pb, sizeof pb, err, err_info)) {
+ ws_debug("failed to read packet data");
+ return FALSE;
+ }
+ block_read = (guint)sizeof pb;
+
+ if (section_info->byte_swapped) {
+ packet.interface_id = GUINT16_SWAP_LE_BE(pb.interface_id);
+ packet.drops_count = GUINT16_SWAP_LE_BE(pb.drops_count);
+ packet.ts_high = GUINT32_SWAP_LE_BE(pb.timestamp_high);
+ packet.ts_low = GUINT32_SWAP_LE_BE(pb.timestamp_low);
+ packet.cap_len = GUINT32_SWAP_LE_BE(pb.captured_len);
+ packet.packet_len = GUINT32_SWAP_LE_BE(pb.packet_len);
+ } else {
+ packet.interface_id = pb.interface_id;
+ packet.drops_count = pb.drops_count;
+ packet.ts_high = pb.timestamp_high;
+ packet.ts_low = pb.timestamp_low;
+ packet.cap_len = pb.captured_len;
+ packet.packet_len = pb.packet_len;
+ }
+ ws_debug("PB on interface_id %d, cap_len %d, packet_len %d",
+ packet.interface_id, packet.cap_len, packet.packet_len);
+ }
+
+ /*
+ * How much padding is there at the end of the packet data?
+ */
+ if ((packet.cap_len % 4) != 0)
+ padding = 4 - (packet.cap_len % 4);
+ else
+ padding = 0;
+
+ /*
+ * Is this block long enough to hold the packet data?
+ */
+ if (enhanced) {
+ if (bh->block_total_length <
+ MIN_EPB_SIZE + packet.cap_len + padding) {
+ /*
+ * No.
+ */
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("pcapng: total block length %u of an EPB is too small for %u bytes of packet data",
+ bh->block_total_length, packet.cap_len);
+ return FALSE;
+ }
+ } else {
+ if (bh->block_total_length <
+ MIN_PB_SIZE + packet.cap_len + padding) {
+ /*
+ * No.
+ */
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("pcapng: total block length %u of a PB is too small for %u bytes of packet data",
+ bh->block_total_length, packet.cap_len);
+ return FALSE;
+ }
+ }
+
+ ws_debug("packet data: packet_len %u captured_len %u interface_id %u",
+ packet.packet_len,
+ packet.cap_len,
+ packet.interface_id);
+
+ if (packet.interface_id >= section_info->interfaces->len) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("pcapng: interface index %u is not less than section interface count %u",
+ packet.interface_id,
+ section_info->interfaces->len);
+ return FALSE;
+ }
+ iface_info = g_array_index(section_info->interfaces, interface_info_t,
+ packet.interface_id);
+
+ if (packet.cap_len > wtap_max_snaplen_for_encap(iface_info.wtap_encap)) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("pcapng: cap_len %u is larger than %u",
+ packet.cap_len,
+ wtap_max_snaplen_for_encap(iface_info.wtap_encap));
+ return FALSE;
+ }
+
+ wblock->rec->rec_type = REC_TYPE_PACKET;
+ wblock->rec->presence_flags = WTAP_HAS_TS|WTAP_HAS_CAP_LEN|WTAP_HAS_INTERFACE_ID;
+
+ ws_debug("encapsulation = %d (%s), pseudo header size = %d.",
+ iface_info.wtap_encap,
+ wtap_encap_description(iface_info.wtap_encap),
+ pcap_get_phdr_size(iface_info.wtap_encap, &wblock->rec->rec_header.packet_header.pseudo_header));
+ wblock->rec->rec_header.packet_header.interface_id = packet.interface_id;
+ wblock->rec->rec_header.packet_header.pkt_encap = iface_info.wtap_encap;
+ wblock->rec->tsprec = iface_info.tsprecision;
+
+ memset((void *)&wblock->rec->rec_header.packet_header.pseudo_header, 0, sizeof(union wtap_pseudo_header));
+ pseudo_header_len = pcap_process_pseudo_header(fh,
+ FALSE, /* not a Nokia pcap - not a pcap at all */
+ iface_info.wtap_encap,
+ packet.cap_len,
+ wblock->rec,
+ err,
+ err_info);
+ if (pseudo_header_len < 0) {
+ return FALSE;
+ }
+ block_read += pseudo_header_len;
+ wblock->rec->rec_header.packet_header.caplen = packet.cap_len - pseudo_header_len;
+ wblock->rec->rec_header.packet_header.len = packet.packet_len - pseudo_header_len;
+
+ /* Combine the two 32-bit pieces of the timestamp into one 64-bit value */
+ ts = (((guint64)packet.ts_high) << 32) | ((guint64)packet.ts_low);
+
+ /* Convert it to seconds and nanoseconds. */
+ wblock->rec->ts.secs = (time_t)(ts / iface_info.time_units_per_second);
+ wblock->rec->ts.nsecs = (int)(((ts % iface_info.time_units_per_second) * 1000000000) / iface_info.time_units_per_second);
+
+ /* Add the time stamp offset. */
+ wblock->rec->ts.secs = (time_t)(wblock->rec->ts.secs + iface_info.tsoffset);
+
+ /* "(Enhanced) Packet Block" read capture data */
+ if (!wtap_read_packet_bytes(fh, wblock->frame_buffer,
+ packet.cap_len - pseudo_header_len, err, err_info))
+ return FALSE;
+ block_read += packet.cap_len - pseudo_header_len;
+
+ /* jump over potential padding bytes at end of the packet data */
+ if (padding != 0) {
+ if (!wtap_read_bytes(fh, NULL, padding, err, err_info))
+ return FALSE;
+ block_read += padding;
+ }
+
+ /* FCS length default */
+ fcslen = iface_info.fcslen;
+
+ /* Options */
+ opt_cont_buf_len = bh->block_total_length -
+ (int)sizeof(pcapng_block_header_t) -
+ block_read - /* fixed and variable part, including padding */
+ (int)sizeof(bh->block_total_length);
+ if (!pcapng_process_options(fh, wblock, section_info, opt_cont_buf_len,
+ pcapng_process_packet_block_option,
+ OPT_SECTION_BYTE_ORDER, err, err_info))
+ return FALSE;
+
+ /*
+ * Did we get a packet flags option?
+ */
+ if (WTAP_OPTTYPE_SUCCESS == wtap_block_get_uint32_option_value(wblock->block, OPT_PKT_FLAGS, &flags)) {
+ if (PACK_FLAGS_FCS_LENGTH(flags) != 0) {
+ /*
+ * The FCS length is present, but in units of octets, not
+ * bits; convert it to bits.
+ */
+ fcslen = PACK_FLAGS_FCS_LENGTH(flags)*8;
+ }
+ }
+ /*
+ * How about a drop_count option? If not, set it from other sources
+ */
+ if (WTAP_OPTTYPE_SUCCESS != wtap_block_get_uint64_option_value(wblock->block, OPT_PKT_DROPCOUNT, &tmp64) && packet.drops_count != 0xFFFF) {
+ wtap_block_add_uint64_option(wblock->block, OPT_PKT_DROPCOUNT, (guint64)packet.drops_count);
+ }
+
+ pcap_read_post_process(FALSE, iface_info.wtap_encap,
+ wblock->rec, ws_buffer_start_ptr(wblock->frame_buffer),
+ section_info->byte_swapped, fcslen);
+
+ /*
+ * We return these to the caller in pcapng_read().
+ */
+ wblock->internal = FALSE;
+
+ /*
+ * We want dissectors (particularly packet_frame) to be able to
+ * access packet comments and whatnot that are in the block. wblock->block
+ * will be unref'd by pcapng_seek_read(), so move the block to where
+ * dissectors can find it.
+ */
+ wblock->rec->block = wblock->block;
+ wblock->block = NULL;
+
+ return TRUE;
+}
+
+
+static gboolean
+pcapng_read_simple_packet_block(FILE_T fh, pcapng_block_header_t *bh,
+ const section_info_t *section_info,
+ wtapng_block_t *wblock,
+ int *err, gchar **err_info)
+{
+ interface_info_t iface_info;
+ pcapng_simple_packet_block_t spb;
+ wtapng_simple_packet_t simple_packet;
+ guint32 padding;
+ int pseudo_header_len;
+
+ /*
+ * Is this block long enough to be an SPB?
+ */
+ if (bh->block_total_length < MIN_SPB_SIZE) {
+ /*
+ * No.
+ */
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("pcapng: total block length %u of an SPB is less than the minimum SPB size %u",
+ bh->block_total_length, MIN_SPB_SIZE);
+ return FALSE;
+ }
+
+ /* "Simple Packet Block" read fixed part */
+ if (!wtap_read_bytes(fh, &spb, sizeof spb, err, err_info)) {
+ ws_debug("failed to read packet data");
+ return FALSE;
+ }
+
+ if (0 >= section_info->interfaces->len) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("pcapng: SPB appeared before any IDBs in the section");
+ return FALSE;
+ }
+ iface_info = g_array_index(section_info->interfaces, interface_info_t, 0);
+
+ if (section_info->byte_swapped) {
+ simple_packet.packet_len = GUINT32_SWAP_LE_BE(spb.packet_len);
+ } else {
+ simple_packet.packet_len = spb.packet_len;
+ }
+
+ /*
+ * The captured length is not a field in the SPB; it can be
+ * calculated as the minimum of the snapshot length from the
+ * IDB and the packet length, as per the pcapng spec. An IDB
+ * snapshot length of 0 means no limit.
+ */
+ simple_packet.cap_len = simple_packet.packet_len;
+ if (simple_packet.cap_len > iface_info.snap_len && iface_info.snap_len != 0)
+ simple_packet.cap_len = iface_info.snap_len;
+
+ /*
+ * How much padding is there at the end of the packet data?
+ */
+ if ((simple_packet.cap_len % 4) != 0)
+ padding = 4 - (simple_packet.cap_len % 4);
+ else
+ padding = 0;
+
+ /*
+ * Is this block long enough to hold the packet data?
+ */
+ if (bh->block_total_length < MIN_SPB_SIZE + simple_packet.cap_len + padding) {
+ /*
+ * No. That means that the problem is with the packet
+ * length; the snapshot length can be bigger than the amount
+ * of packet data in the block, as it's a *maximum* length,
+ * not a *minimum* length.
+ */
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("pcapng: total block length %u of an SPB is too small for %u bytes of packet data",
+ bh->block_total_length, simple_packet.packet_len);
+ return FALSE;
+ }
+
+ if (simple_packet.cap_len > wtap_max_snaplen_for_encap(iface_info.wtap_encap)) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("pcapng: cap_len %u is larger than %u",
+ simple_packet.cap_len,
+ wtap_max_snaplen_for_encap(iface_info.wtap_encap));
+ return FALSE;
+ }
+ ws_debug("packet data: packet_len %u",
+ simple_packet.packet_len);
+
+ ws_debug("Need to read pseudo header of size %d",
+ pcap_get_phdr_size(iface_info.wtap_encap, &wblock->rec->rec_header.packet_header.pseudo_header));
+
+ /* No time stamp in a simple packet block; no options, either */
+ wblock->rec->rec_type = REC_TYPE_PACKET;
+ wblock->rec->presence_flags = WTAP_HAS_CAP_LEN|WTAP_HAS_INTERFACE_ID;
+ wblock->rec->rec_header.packet_header.interface_id = 0;
+ wblock->rec->rec_header.packet_header.pkt_encap = iface_info.wtap_encap;
+ wblock->rec->tsprec = iface_info.tsprecision;
+ wblock->rec->ts.secs = 0;
+ wblock->rec->ts.nsecs = 0;
+ wblock->rec->rec_header.packet_header.interface_id = 0;
+
+ memset((void *)&wblock->rec->rec_header.packet_header.pseudo_header, 0, sizeof(union wtap_pseudo_header));
+ pseudo_header_len = pcap_process_pseudo_header(fh,
+ FALSE,
+ iface_info.wtap_encap,
+ simple_packet.cap_len,
+ wblock->rec,
+ err,
+ err_info);
+ if (pseudo_header_len < 0) {
+ return FALSE;
+ }
+ wblock->rec->rec_header.packet_header.caplen = simple_packet.cap_len - pseudo_header_len;
+ wblock->rec->rec_header.packet_header.len = simple_packet.packet_len - pseudo_header_len;
+
+ memset((void *)&wblock->rec->rec_header.packet_header.pseudo_header, 0, sizeof(union wtap_pseudo_header));
+
+ /* "Simple Packet Block" read capture data */
+ if (!wtap_read_packet_bytes(fh, wblock->frame_buffer,
+ simple_packet.cap_len, err, err_info))
+ return FALSE;
+
+ /* jump over potential padding bytes at end of the packet data */
+ if ((simple_packet.cap_len % 4) != 0) {
+ if (!wtap_read_bytes(fh, NULL, 4 - (simple_packet.cap_len % 4), err, err_info))
+ return FALSE;
+ }
+
+ pcap_read_post_process(FALSE, iface_info.wtap_encap,
+ wblock->rec, ws_buffer_start_ptr(wblock->frame_buffer),
+ section_info->byte_swapped, iface_info.fcslen);
+
+ /*
+ * We return these to the caller in pcapng_read().
+ */
+ wblock->internal = FALSE;
+
+ return TRUE;
+}
+
+#define NRES_ENDOFRECORD 0
+#define NRES_IP4RECORD 1
+#define NRES_IP6RECORD 2
+#define PADDING4(x) ((((x + 3) >> 2) << 2) - x)
+/* IPv6 + MAXNAMELEN */
+#define INITIAL_NRB_REC_SIZE (16 + 64)
+
+/*
+ * Find the end of the NUL-terminated name the beginning of which is pointed
+ * to by p; record_len is the number of bytes remaining in the record.
+ *
+ * Return the length of the name, including the terminating NUL.
+ *
+ * If we don't find a terminating NUL, return -1 and set *err and
+ * *err_info appropriately.
+ */
+static int
+name_resolution_block_find_name_end(const char *p, guint record_len, int *err,
+ gchar **err_info)
+{
+ int namelen;
+
+ namelen = 0;
+ for (;;) {
+ if (record_len == 0) {
+ /*
+ * We ran out of bytes in the record without
+ * finding a NUL.
+ */
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("pcapng: NRB record has non-null-terminated host name");
+ return -1;
+ }
+ if (*p == '\0')
+ break; /* that's the terminating NUL */
+ p++;
+ record_len--;
+ namelen++; /* count this byte */
+ }
+
+ /* Include the NUL in the name length. */
+ return namelen + 1;
+}
+
+static gboolean
+pcapng_process_name_resolution_block_option(wtapng_block_t *wblock,
+ const section_info_t *section_info,
+ guint16 option_code,
+ guint16 option_length,
+ const guint8 *option_content,
+ int *err, gchar **err_info)
+{
+ /*
+ * Handle option content.
+ *
+ * ***DO NOT*** add any items to this table that are not
+ * standardized option codes in either section 3.5 "Options"
+ * of the current pcapng spec, at
+ *
+ * https://pcapng.github.io/pcapng/draft-ietf-opsawg-pcapng.html#name-options
+ *
+ * or in the list of options in section 4.1 "Section Header Block"
+ * of the current pcapng spec, at
+ *
+ * https://pcapng.github.io/pcapng/draft-ietf-opsawg-pcapng.html#name-section-header-block
+ *
+ * All option codes in this switch statement here must be listed
+ * in one of those places as standardized option types.
+ */
+ switch (option_code) {
+ /* TODO:
+ * ns_dnsname 2
+ * ns_dnsIP4addr 3
+ * ns_dnsIP6addr 4
+ */
+ default:
+ if (!pcapng_process_unhandled_option(wblock, BT_INDEX_NRB,
+ section_info, option_code,
+ option_length, option_content,
+ err, err_info))
+ return FALSE;
+ break;
+ }
+ return TRUE;
+}
+
+static gboolean
+pcapng_read_name_resolution_block(FILE_T fh, pcapng_block_header_t *bh,
+ section_info_t *section_info,
+ wtapng_block_t *wblock,
+ int *err, gchar **err_info)
+{
+ int block_read;
+ int to_read;
+ pcapng_name_resolution_block_t nrb;
+ Buffer nrb_rec;
+ guint32 v4_addr;
+ guint record_len, opt_cont_buf_len;
+ char *namep;
+ int namelen;
+ wtapng_nrb_mandatory_t *nrb_mand;
+
+ /*
+ * Is this block long enough to be an NRB?
+ */
+ if (bh->block_total_length < MIN_NRB_SIZE) {
+ /*
+ * No.
+ */
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("pcapng: total block length %u of an NRB is less than the minimum NRB size %u",
+ bh->block_total_length, MIN_NRB_SIZE);
+ return FALSE;
+ }
+
+ to_read = bh->block_total_length - 8 - 4; /* We have read the header and should not read the final block_total_length */
+
+ ws_debug("total %d bytes", bh->block_total_length);
+
+ /* Ensure we have a name resolution block */
+ if (wblock->block == NULL) {
+ wblock->block = wtap_block_create(WTAP_BLOCK_NAME_RESOLUTION);
+ }
+
+ /*
+ * Set the mandatory values for the block.
+ */
+ nrb_mand = (wtapng_nrb_mandatory_t *)wtap_block_get_mandatory_data(wblock->block);
+
+ /*
+ * Start out with a buffer big enough for an IPv6 address and one
+ * 64-byte name; we'll make the buffer bigger if necessary.
+ */
+ ws_buffer_init(&nrb_rec, INITIAL_NRB_REC_SIZE);
+ block_read = 0;
+ while (block_read < to_read) {
+ /*
+ * There must be at least one record's worth of data
+ * here.
+ */
+ if ((size_t)(to_read - block_read) < sizeof nrb) {
+ ws_buffer_free(&nrb_rec);
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("pcapng: %d bytes left in the block < NRB record header size %u",
+ to_read - block_read,
+ (guint)sizeof nrb);
+ return FALSE;
+ }
+ if (!wtap_read_bytes(fh, &nrb, sizeof nrb, err, err_info)) {
+ ws_buffer_free(&nrb_rec);
+ ws_debug("failed to read record header");
+ return FALSE;
+ }
+ block_read += (int)sizeof nrb;
+
+ if (section_info->byte_swapped) {
+ nrb.record_type = GUINT16_SWAP_LE_BE(nrb.record_type);
+ nrb.record_len = GUINT16_SWAP_LE_BE(nrb.record_len);
+ }
+
+ if (to_read - block_read < nrb.record_len + PADDING4(nrb.record_len)) {
+ ws_buffer_free(&nrb_rec);
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("pcapng: %d bytes left in the block < NRB record length + padding %u",
+ to_read - block_read,
+ nrb.record_len + PADDING4(nrb.record_len));
+ return FALSE;
+ }
+ switch (nrb.record_type) {
+ case NRES_ENDOFRECORD:
+ /* There shouldn't be any more data - but there MAY be options */
+ goto read_options;
+ break;
+ case NRES_IP4RECORD:
+ /*
+ * The smallest possible record must have
+ * a 4-byte IPv4 address, hence a minimum
+ * of 4 bytes.
+ *
+ * (The pcapng spec really indicates
+ * that it must be at least 5 bytes,
+ * as there must be at least one name,
+ * and it really must be at least 6
+ * bytes, as the name mustn't be null,
+ * but there's no need to fail if there
+ * aren't any names at all, and we
+ * should report a null name as such.)
+ */
+ if (nrb.record_len < 4) {
+ ws_buffer_free(&nrb_rec);
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("pcapng: NRB record length for IPv4 record %u < minimum length 4",
+ nrb.record_len);
+ return FALSE;
+ }
+ ws_buffer_assure_space(&nrb_rec, nrb.record_len);
+ if (!wtap_read_bytes(fh, ws_buffer_start_ptr(&nrb_rec),
+ nrb.record_len, err, err_info)) {
+ ws_buffer_free(&nrb_rec);
+ ws_debug("failed to read IPv4 record data");
+ return FALSE;
+ }
+ block_read += nrb.record_len;
+
+ /*
+ * Scan through all the names in
+ * the record and add them.
+ */
+ memcpy(&v4_addr,
+ ws_buffer_start_ptr(&nrb_rec), 4);
+ /* IPv4 address is in big-endian order in the file always, which is how we store
+ it internally as well, so don't byte-swap it */
+ for (namep = (char *)ws_buffer_start_ptr(&nrb_rec) + 4, record_len = nrb.record_len - 4;
+ record_len != 0;
+ namep += namelen, record_len -= namelen) {
+ /*
+ * Scan forward for a null
+ * byte.
+ */
+ namelen = name_resolution_block_find_name_end(namep, record_len, err, err_info);
+ if (namelen == -1) {
+ ws_buffer_free(&nrb_rec);
+ return FALSE; /* fail */
+ }
+ hashipv4_t *tp = g_new0(hashipv4_t, 1);
+ tp->addr = v4_addr;
+ (void) g_strlcpy(tp->name, namep, MAXNAMELEN);
+ nrb_mand->ipv4_addr_list = g_list_prepend(nrb_mand->ipv4_addr_list, tp);
+ }
+
+ if (!wtap_read_bytes(fh, NULL, PADDING4(nrb.record_len), err, err_info)) {
+ ws_buffer_free(&nrb_rec);
+ return FALSE;
+ }
+ block_read += PADDING4(nrb.record_len);
+ break;
+ case NRES_IP6RECORD:
+ /*
+ * The smallest possible record must have
+ * a 16-byte IPv6 address, hence a minimum
+ * of 16 bytes.
+ *
+ * (The pcapng spec really indicates
+ * that it must be at least 17 bytes,
+ * as there must be at least one name,
+ * and it really must be at least 18
+ * bytes, as the name mustn't be null,
+ * but there's no need to fail if there
+ * aren't any names at all, and we
+ * should report a null name as such.)
+ */
+ if (nrb.record_len < 16) {
+ ws_buffer_free(&nrb_rec);
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("pcapng: NRB record length for IPv6 record %u < minimum length 16",
+ nrb.record_len);
+ return FALSE;
+ }
+ if (to_read < nrb.record_len) {
+ ws_buffer_free(&nrb_rec);
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("pcapng: NRB record length for IPv6 record %u > remaining data in NRB",
+ nrb.record_len);
+ return FALSE;
+ }
+ ws_buffer_assure_space(&nrb_rec, nrb.record_len);
+ if (!wtap_read_bytes(fh, ws_buffer_start_ptr(&nrb_rec),
+ nrb.record_len, err, err_info)) {
+ ws_buffer_free(&nrb_rec);
+ return FALSE;
+ }
+ block_read += nrb.record_len;
+
+ for (namep = (char *)ws_buffer_start_ptr(&nrb_rec) + 16, record_len = nrb.record_len - 16;
+ record_len != 0;
+ namep += namelen, record_len -= namelen) {
+ /*
+ * Scan forward for a null
+ * byte.
+ */
+ namelen = name_resolution_block_find_name_end(namep, record_len, err, err_info);
+ if (namelen == -1) {
+ ws_buffer_free(&nrb_rec);
+ return FALSE; /* fail */
+ }
+ hashipv6_t *tp = g_new0(hashipv6_t, 1);
+ memcpy(tp->addr, ws_buffer_start_ptr(&nrb_rec), sizeof tp->addr);
+ (void) g_strlcpy(tp->name, namep, MAXNAMELEN);
+ nrb_mand->ipv6_addr_list = g_list_prepend(nrb_mand->ipv6_addr_list, tp);
+ }
+
+ if (!wtap_read_bytes(fh, NULL, PADDING4(nrb.record_len), err, err_info)) {
+ ws_buffer_free(&nrb_rec);
+ return FALSE;
+ }
+ block_read += PADDING4(nrb.record_len);
+ break;
+ default:
+ ws_debug("unknown record type 0x%x", nrb.record_type);
+ if (!wtap_read_bytes(fh, NULL, nrb.record_len + PADDING4(nrb.record_len), err, err_info)) {
+ ws_buffer_free(&nrb_rec);
+ return FALSE;
+ }
+ block_read += nrb.record_len + PADDING4(nrb.record_len);
+ break;
+ }
+ }
+
+read_options:
+ to_read -= block_read;
+
+ /* Options */
+ opt_cont_buf_len = to_read;
+ if (!pcapng_process_options(fh, wblock, section_info, opt_cont_buf_len,
+ pcapng_process_name_resolution_block_option,
+ OPT_SECTION_BYTE_ORDER, err, err_info))
+ return FALSE;
+
+ ws_buffer_free(&nrb_rec);
+
+ /*
+ * We don't return these to the caller in pcapng_read().
+ */
+ wblock->internal = TRUE;
+
+ return TRUE;
+}
+
+static gboolean
+pcapng_process_interface_statistics_block_option(wtapng_block_t *wblock,
+ const section_info_t *section_info,
+ guint16 option_code,
+ guint16 option_length,
+ const guint8 *option_content,
+ int *err, gchar **err_info)
+{
+ /*
+ * Handle option content.
+ *
+ * ***DO NOT*** add any items to this table that are not
+ * standardized option codes in either section 3.5 "Options"
+ * of the current pcapng spec, at
+ *
+ * https://pcapng.github.io/pcapng/draft-ietf-opsawg-pcapng.html#name-options
+ *
+ * or in the list of options in section 4.1 "Section Header Block"
+ * of the current pcapng spec, at
+ *
+ * https://pcapng.github.io/pcapng/draft-ietf-opsawg-pcapng.html#name-section-header-block
+ *
+ * All option codes in this switch statement here must be listed
+ * in one of those places as standardized option types.
+ */
+ switch (option_code) {
+ case(OPT_ISB_STARTTIME): /* isb_starttime */
+ pcapng_process_timestamp_option(wblock, section_info,
+ OPT_SECTION_BYTE_ORDER,
+ option_code, option_length,
+ option_content);
+ break;
+ case(OPT_ISB_ENDTIME): /* isb_endtime */
+ pcapng_process_timestamp_option(wblock, section_info,
+ OPT_SECTION_BYTE_ORDER,
+ option_code, option_length,
+ option_content);
+ break;
+ case(OPT_ISB_IFRECV): /* isb_ifrecv */
+ pcapng_process_uint64_option(wblock, section_info,
+ OPT_SECTION_BYTE_ORDER,
+ option_code, option_length,
+ option_content);
+ break;
+ case(OPT_ISB_IFDROP): /* isb_ifdrop */
+ pcapng_process_uint64_option(wblock, section_info,
+ OPT_SECTION_BYTE_ORDER,
+ option_code, option_length,
+ option_content);
+ break;
+ case(OPT_ISB_FILTERACCEPT): /* isb_filteraccept 6 */
+ pcapng_process_uint64_option(wblock, section_info,
+ OPT_SECTION_BYTE_ORDER,
+ option_code, option_length,
+ option_content);
+ break;
+ case(OPT_ISB_OSDROP): /* isb_osdrop 7 */
+ pcapng_process_uint64_option(wblock, section_info,
+ OPT_SECTION_BYTE_ORDER,
+ option_code, option_length,
+ option_content);
+ break;
+ case(OPT_ISB_USRDELIV): /* isb_usrdeliv 8 */
+ pcapng_process_uint64_option(wblock, section_info,
+ OPT_SECTION_BYTE_ORDER,
+ option_code, option_length,
+ option_content);
+ break;
+ default:
+ if (!pcapng_process_unhandled_option(wblock, BT_INDEX_ISB,
+ section_info, option_code,
+ option_length, option_content,
+ err, err_info))
+ return FALSE;
+ break;
+ }
+ return TRUE;
+}
+
+static gboolean
+pcapng_read_interface_statistics_block(FILE_T fh, pcapng_block_header_t *bh,
+ section_info_t *section_info,
+ wtapng_block_t *wblock,
+ int *err, gchar **err_info)
+{
+ guint opt_cont_buf_len;
+ pcapng_interface_statistics_block_t isb;
+ wtapng_if_stats_mandatory_t* if_stats_mand;
+
+ /*
+ * Is this block long enough to be an ISB?
+ */
+ if (bh->block_total_length < MIN_ISB_SIZE) {
+ /*
+ * No.
+ */
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("pcapng: total block length %u of an ISB is too small (< %u)",
+ bh->block_total_length, MIN_ISB_SIZE);
+ return FALSE;
+ }
+
+ /* "Interface Statistics Block" read fixed part */
+ if (!wtap_read_bytes(fh, &isb, sizeof isb, err, err_info)) {
+ ws_debug("failed to read packet data");
+ return FALSE;
+ }
+
+ /*
+ * Set wblock->block to a newly-allocated interface statistics block.
+ */
+ wblock->block = wtap_block_create(WTAP_BLOCK_IF_STATISTICS);
+
+ /*
+ * Set the mandatory values for the block.
+ */
+ if_stats_mand = (wtapng_if_stats_mandatory_t*)wtap_block_get_mandatory_data(wblock->block);
+ if (section_info->byte_swapped) {
+ if_stats_mand->interface_id = GUINT32_SWAP_LE_BE(isb.interface_id);
+ if_stats_mand->ts_high = GUINT32_SWAP_LE_BE(isb.timestamp_high);
+ if_stats_mand->ts_low = GUINT32_SWAP_LE_BE(isb.timestamp_low);
+ } else {
+ if_stats_mand->interface_id = isb.interface_id;
+ if_stats_mand->ts_high = isb.timestamp_high;
+ if_stats_mand->ts_low = isb.timestamp_low;
+ }
+ ws_debug("interface_id %u", if_stats_mand->interface_id);
+
+ /* Options */
+ opt_cont_buf_len = bh->block_total_length -
+ (MIN_BLOCK_SIZE + (guint)sizeof isb); /* fixed and variable part, including padding */
+ if (!pcapng_process_options(fh, wblock, section_info, opt_cont_buf_len,
+ pcapng_process_interface_statistics_block_option,
+ OPT_SECTION_BYTE_ORDER, err, err_info))
+ return FALSE;
+
+ /*
+ * We don't return these to the caller in pcapng_read().
+ */
+ wblock->internal = TRUE;
+
+ return TRUE;
+}
+
+#define NFLX_BLOCK_TYPE_EVENT 1
+#define NFLX_BLOCK_TYPE_SKIP 2
+
+typedef struct pcapng_nflx_custom_block_s {
+ guint32 nflx_type;
+} pcapng_nflx_custom_block_t;
+
+#define MIN_NFLX_CB_SIZE ((guint32)(MIN_CB_SIZE + sizeof(pcapng_nflx_custom_block_t)))
+
+static gboolean
+pcapng_read_nflx_custom_block(FILE_T fh, pcapng_block_header_t *bh,
+ section_info_t *section_info,
+ wtapng_block_t *wblock,
+ int *err, gchar **err_info)
+{
+ pcapng_nflx_custom_block_t nflx_cb;
+ guint opt_cont_buf_len;
+ guint32 type, skipped;
+
+ if (bh->block_total_length < MIN_NFLX_CB_SIZE) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("pcapng: total block length %u of a Netflix CB is too small (< %u)",
+ bh->block_total_length, MIN_NFLX_CB_SIZE);
+ return FALSE;
+ }
+
+ wblock->rec->rec_type = REC_TYPE_CUSTOM_BLOCK;
+ wblock->rec->rec_header.custom_block_header.pen = PEN_NFLX;
+ /* "NFLX Custom Block" read fixed part */
+ if (!wtap_read_bytes(fh, &nflx_cb, sizeof nflx_cb, err, err_info)) {
+ ws_debug("Failed to read nflx type");
+ return FALSE;
+ }
+ type = GUINT32_FROM_LE(nflx_cb.nflx_type);
+ ws_debug("BBLog type: %u", type);
+ switch (type) {
+ case NFLX_BLOCK_TYPE_EVENT:
+ /*
+ * The fixed-length portion is MIN_NFLX_CB_SIZE bytes.
+ * We already know we have that much data in the block.
+ */
+ wblock->rec->rec_header.custom_block_header.custom_data_header.nflx_custom_data_header.type = BBLOG_TYPE_EVENT_BLOCK;
+ opt_cont_buf_len = bh->block_total_length - MIN_NFLX_CB_SIZE;
+ ws_debug("event");
+ break;
+ case NFLX_BLOCK_TYPE_SKIP:
+ /*
+ * The fixed-length portion is MIN_NFLX_CB_SIZE bytes plus a
+ * 32-bit value.
+ *
+ * Make sure we have that much data in the block.
+ */
+ if (bh->block_total_length < MIN_NFLX_CB_SIZE + (guint32)sizeof(guint32)) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("pcapng: total block length %u of a Netflix skip CB is too small (< %u)",
+ bh->block_total_length,
+ MIN_NFLX_CB_SIZE + (guint32)sizeof(guint32));
+ return FALSE;
+ }
+ if (!wtap_read_bytes(fh, &skipped, sizeof(guint32), err, err_info)) {
+ ws_debug("Failed to read skipped");
+ return FALSE;
+ }
+ wblock->rec->presence_flags = 0;
+ wblock->rec->rec_header.custom_block_header.length = 4;
+ wblock->rec->rec_header.custom_block_header.custom_data_header.nflx_custom_data_header.type = BBLOG_TYPE_SKIPPED_BLOCK;
+ wblock->rec->rec_header.custom_block_header.custom_data_header.nflx_custom_data_header.skipped = GUINT32_FROM_LE(skipped);
+ wblock->internal = FALSE;
+ opt_cont_buf_len = bh->block_total_length - MIN_NFLX_CB_SIZE - sizeof(guint32);
+ ws_debug("skipped: %u", wblock->rec->rec_header.custom_block_header.custom_data_header.nflx_custom_data_header.skipped);
+ break;
+ default:
+ ws_debug("Unknown type %u", type);
+ return FALSE;
+ }
+
+ /* Options */
+ if (!pcapng_process_options(fh, wblock, section_info, opt_cont_buf_len,
+ NULL, OPT_LITTLE_ENDIAN, err, err_info))
+ return FALSE;
+
+ return TRUE;
+}
+
+static gboolean
+pcapng_handle_generic_custom_block(FILE_T fh, pcapng_block_header_t *bh,
+ guint32 pen, wtapng_block_t *wblock,
+ int *err, gchar **err_info)
+{
+ guint to_read;
+
+ ws_debug("unknown pen %u", pen);
+ if (bh->block_total_length % 4) {
+ to_read = bh->block_total_length + 4 - (bh->block_total_length % 4);
+ } else {
+ to_read = bh->block_total_length;
+ }
+ to_read -= MIN_CB_SIZE;
+ wblock->rec->rec_type = REC_TYPE_CUSTOM_BLOCK;
+ wblock->rec->presence_flags = 0;
+ wblock->rec->rec_header.custom_block_header.length = bh->block_total_length - MIN_CB_SIZE;
+ wblock->rec->rec_header.custom_block_header.pen = pen;
+ wblock->rec->rec_header.custom_block_header.copy_allowed = (bh->block_type == BLOCK_TYPE_CB_COPY);
+ if (!wtap_read_packet_bytes(fh, wblock->frame_buffer, to_read, err, err_info)) {
+ return FALSE;
+ }
+ /*
+ * We return these to the caller in pcapng_read().
+ */
+ wblock->internal = FALSE;
+ return TRUE;
+}
+
+static gboolean
+pcapng_read_custom_block(FILE_T fh, pcapng_block_header_t *bh,
+ section_info_t *section_info,
+ wtapng_block_t *wblock,
+ int *err, gchar **err_info)
+{
+ pcapng_custom_block_t cb;
+ guint32 pen;
+
+ /* Is this block long enough to be an CB? */
+ if (bh->block_total_length < MIN_CB_SIZE) {
+ /*
+ * No.
+ */
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("pcapng: total block length %u of a CB is too small (< %u)",
+ bh->block_total_length, MIN_CB_SIZE);
+ return FALSE;
+ }
+
+ wblock->block = wtap_block_create(WTAP_BLOCK_CUSTOM);
+
+ /* Custom block read fixed part */
+ if (!wtap_read_bytes(fh, &cb, sizeof cb, err, err_info)) {
+ ws_debug("failed to read pen");
+ return FALSE;
+ }
+ if (section_info->byte_swapped) {
+ pen = GUINT32_SWAP_LE_BE(cb.pen);
+ } else {
+ pen = cb.pen;
+ }
+ ws_debug("pen %u, custom data and option length %u", pen, bh->block_total_length - MIN_CB_SIZE);
+
+ switch (pen) {
+ case PEN_NFLX:
+ if (!pcapng_read_nflx_custom_block(fh, bh, section_info, wblock, err, err_info))
+ return FALSE;
+ break;
+ default:
+ if (!pcapng_handle_generic_custom_block(fh, bh, pen, wblock, err, err_info)) {
+ return FALSE;
+ }
+ break;
+ }
+
+ wblock->rec->block = wblock->block;
+ wblock->block = NULL;
+ wblock->internal = FALSE;
+
+ return TRUE;
+}
+
+static gboolean
+pcapng_read_sysdig_event_block(FILE_T fh, pcapng_block_header_t *bh,
+ const section_info_t *section_info,
+ wtapng_block_t *wblock,
+ int *err, gchar **err_info)
+{
+ unsigned block_read;
+ guint16 cpu_id;
+ guint64 wire_ts;
+ guint64 ts;
+ guint64 thread_id;
+ guint32 event_len;
+ guint16 event_type;
+ guint32 nparams = 0;
+ guint min_event_size;
+
+ switch (bh->block_type) {
+ case BLOCK_TYPE_SYSDIG_EVENT_V2_LARGE:
+ case BLOCK_TYPE_SYSDIG_EVENT_V2:
+ min_event_size = MIN_SYSDIG_EVENT_V2_SIZE;
+ break;
+ default:
+ min_event_size = MIN_SYSDIG_EVENT_SIZE;
+ break;
+ }
+
+ if (bh->block_total_length < min_event_size) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("pcapng: total block length %u of a Sysdig event block is too small (< %u)",
+ bh->block_total_length, min_event_size);
+ return FALSE;
+ }
+
+ wblock->rec->rec_type = REC_TYPE_SYSCALL;
+ wblock->rec->rec_header.syscall_header.record_type = bh->block_type;
+ wblock->rec->presence_flags = WTAP_HAS_TS|WTAP_HAS_CAP_LEN /*|WTAP_HAS_INTERFACE_ID */;
+ wblock->rec->tsprec = WTAP_TSPREC_NSEC;
+
+ if (!wtap_read_bytes(fh, &cpu_id, sizeof cpu_id, err, err_info)) {
+ ws_debug("failed to read sysdig event cpu id");
+ return FALSE;
+ }
+ if (!wtap_read_bytes(fh, &wire_ts, sizeof wire_ts, err, err_info)) {
+ ws_debug("failed to read sysdig event timestamp");
+ return FALSE;
+ }
+ if (!wtap_read_bytes(fh, &thread_id, sizeof thread_id, err, err_info)) {
+ ws_debug("failed to read sysdig event thread id");
+ return FALSE;
+ }
+ if (!wtap_read_bytes(fh, &event_len, sizeof event_len, err, err_info)) {
+ ws_debug("failed to read sysdig event length");
+ return FALSE;
+ }
+ if (!wtap_read_bytes(fh, &event_type, sizeof event_type, err, err_info)) {
+ ws_debug("failed to read sysdig event type");
+ return FALSE;
+ }
+ if (bh->block_type == BLOCK_TYPE_SYSDIG_EVENT_V2 || bh->block_type == BLOCK_TYPE_SYSDIG_EVENT_V2_LARGE) {
+ if (!wtap_read_bytes(fh, &nparams, sizeof nparams, err, err_info)) {
+ ws_debug("failed to read sysdig number of parameters");
+ return FALSE;
+ }
+ }
+
+ wblock->rec->rec_header.syscall_header.byte_order = G_BYTE_ORDER;
+
+ /* XXX Use Gxxx_FROM_LE macros instead? */
+ if (section_info->byte_swapped) {
+ wblock->rec->rec_header.syscall_header.byte_order =
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+ G_BIG_ENDIAN;
+#else
+ G_LITTLE_ENDIAN;
+#endif
+ wblock->rec->rec_header.syscall_header.cpu_id = GUINT16_SWAP_LE_BE(cpu_id);
+ ts = GUINT64_SWAP_LE_BE(wire_ts);
+ wblock->rec->rec_header.syscall_header.thread_id = GUINT64_SWAP_LE_BE(thread_id);
+ wblock->rec->rec_header.syscall_header.event_len = GUINT32_SWAP_LE_BE(event_len);
+ wblock->rec->rec_header.syscall_header.event_type = GUINT16_SWAP_LE_BE(event_type);
+ wblock->rec->rec_header.syscall_header.nparams = GUINT32_SWAP_LE_BE(nparams);
+ } else {
+ wblock->rec->rec_header.syscall_header.cpu_id = cpu_id;
+ ts = wire_ts;
+ wblock->rec->rec_header.syscall_header.thread_id = thread_id;
+ wblock->rec->rec_header.syscall_header.event_len = event_len;
+ wblock->rec->rec_header.syscall_header.event_type = event_type;
+ wblock->rec->rec_header.syscall_header.nparams = nparams;
+ }
+
+ wblock->rec->ts.secs = (time_t) (ts / 1000000000);
+ wblock->rec->ts.nsecs = (int) (ts % 1000000000);
+
+ block_read = bh->block_total_length - min_event_size;
+
+ wblock->rec->rec_header.syscall_header.event_filelen = block_read;
+
+ /* "Sysdig Event Block" read event data */
+ if (!wtap_read_packet_bytes(fh, wblock->frame_buffer,
+ block_read, err, err_info))
+ return FALSE;
+
+ /* XXX Read comment? */
+
+ /*
+ * We return these to the caller in pcapng_read().
+ */
+ wblock->internal = FALSE;
+
+ return TRUE;
+}
+
+static gboolean
+pcapng_read_systemd_journal_export_block(wtap *wth, FILE_T fh, pcapng_block_header_t *bh, pcapng_t *pn _U_, wtapng_block_t *wblock, int *err, gchar **err_info)
+{
+ guint32 entry_length;
+ guint64 rt_ts;
+ gboolean have_ts = FALSE;
+
+ if (bh->block_total_length < MIN_SYSTEMD_JOURNAL_EXPORT_BLOCK_SIZE) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("pcapng: total block length %u of a systemd journal export block is too small (< %u)",
+ bh->block_total_length, MIN_SYSTEMD_JOURNAL_EXPORT_BLOCK_SIZE);
+ return FALSE;
+ }
+
+ entry_length = bh->block_total_length - MIN_BLOCK_SIZE;
+
+ /* Includes padding bytes. */
+ if (!wtap_read_packet_bytes(fh, wblock->frame_buffer,
+ entry_length, err, err_info)) {
+ return FALSE;
+ }
+
+ /*
+ * We don't have memmem available everywhere, so we get to add space for
+ * a trailing \0 for strstr below.
+ */
+ ws_buffer_assure_space(wblock->frame_buffer, entry_length+1);
+
+ gchar *buf_ptr = (gchar *) ws_buffer_start_ptr(wblock->frame_buffer);
+ while (entry_length > 0 && buf_ptr[entry_length-1] == '\0') {
+ entry_length--;
+ }
+
+ if (entry_length < MIN_SYSTEMD_JOURNAL_EXPORT_ENTRY_SIZE) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("pcapng: entry length %u is too small (< %u)",
+ bh->block_total_length, MIN_SYSTEMD_JOURNAL_EXPORT_ENTRY_SIZE);
+ return FALSE;
+ }
+
+ ws_debug("entry_length %u", entry_length);
+
+ size_t rt_ts_len = strlen(SDJ__REALTIME_TIMESTAMP);
+
+ buf_ptr[entry_length] = '\0';
+ char *ts_pos = strstr(buf_ptr, SDJ__REALTIME_TIMESTAMP);
+
+ if (!ts_pos) {
+ ws_debug("no timestamp");
+ } else if (ts_pos+rt_ts_len >= (char *) buf_ptr+entry_length) {
+ ws_debug("timestamp past end of buffer");
+ } else {
+ const char *ts_end;
+ have_ts = ws_strtou64(ts_pos+rt_ts_len, &ts_end, &rt_ts);
+
+ if (!have_ts) {
+ ws_debug("invalid timestamp");
+ }
+ }
+
+ wblock->rec->rec_type = REC_TYPE_SYSTEMD_JOURNAL_EXPORT;
+ wblock->rec->rec_header.systemd_journal_export_header.record_len = entry_length;
+ wblock->rec->presence_flags = WTAP_HAS_CAP_LEN;
+ if (have_ts) {
+ wblock->rec->presence_flags |= WTAP_HAS_TS;
+ wblock->rec->tsprec = WTAP_TSPREC_USEC;
+ wblock->rec->ts.secs = (time_t) (rt_ts / 1000000);
+ wblock->rec->ts.nsecs = (rt_ts % 1000000) * 1000;
+ }
+
+ /*
+ * We return these to the caller in pcapng_read().
+ */
+ wblock->internal = FALSE;
+
+ if (wth->file_encap == WTAP_ENCAP_NONE) {
+ /*
+ * Nothing (most notably an IDB) has set a file encap at this point.
+ * Do so here.
+ * XXX Should we set WTAP_ENCAP_SYSTEMD_JOURNAL if appropriate?
+ */
+ wth->file_encap = WTAP_ENCAP_PER_PACKET;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+pcapng_read_unknown_block(FILE_T fh, pcapng_block_header_t *bh,
+#ifdef HAVE_PLUGINS
+ const section_info_t *section_info,
+#else
+ const section_info_t *section_info _U_,
+#endif
+ wtapng_block_t *wblock,
+ int *err, gchar **err_info)
+{
+ guint32 block_read;
+#ifdef HAVE_PLUGINS
+ block_handler *handler;
+#endif
+
+ if (bh->block_total_length < MIN_BLOCK_SIZE) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("pcapng: total block length %u of an unknown block type is less than the minimum block size %u",
+ bh->block_total_length, MIN_BLOCK_SIZE);
+ return FALSE;
+ }
+
+ block_read = bh->block_total_length - MIN_BLOCK_SIZE;
+
+#ifdef HAVE_PLUGINS
+ /*
+ * Do we have a handler for this block type?
+ */
+ if (block_handlers != NULL &&
+ (handler = (block_handler *)g_hash_table_lookup(block_handlers,
+ GUINT_TO_POINTER(bh->block_type))) != NULL) {
+ /* Yes - call it to read this block type. */
+ if (!handler->reader(fh, block_read, section_info->byte_swapped, wblock,
+ err, err_info))
+ return FALSE;
+ } else
+#endif
+ {
+ /* No. Skip over this unknown block. */
+ if (!wtap_read_bytes(fh, NULL, block_read, err, err_info)) {
+ return FALSE;
+ }
+
+ /*
+ * We're skipping this, so we won't return these to the caller
+ * in pcapng_read().
+ */
+ wblock->internal = TRUE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+pcapng_read_and_check_block_trailer(FILE_T fh, pcapng_block_header_t *bh,
+ section_info_t *section_info,
+ int *err, gchar **err_info)
+{
+ guint32 block_total_length;
+
+ /* sanity check: first and second block lengths must match */
+ if (!wtap_read_bytes(fh, &block_total_length, sizeof block_total_length,
+ err, err_info)) {
+ ws_debug("couldn't read second block length");
+ return FALSE;
+ }
+
+ if (section_info->byte_swapped)
+ block_total_length = GUINT32_SWAP_LE_BE(block_total_length);
+
+ /*
+ * According to the pcapng spec, this should equal the block total
+ * length value at the beginning of the block, which MUST (in the
+ * IANA sense) be a multiple of 4.
+ *
+ * We round the value at the beginning of the block to a multiple
+ * of 4, so do so with this value as well. This *does* mean that
+ * the two values, if they're not both multiples of 4, can differ
+ * and this code won't detect that, but we're already not detecting
+ * non-multiple-of-4 total lengths.
+ */
+ block_total_length = ROUND_TO_4BYTE(block_total_length);
+
+ if (block_total_length != bh->block_total_length) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("pcapng: total block lengths (first %u and second %u) don't match",
+ bh->block_total_length, block_total_length);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static gboolean
+pcapng_read_block(wtap *wth, FILE_T fh, pcapng_t *pn,
+ section_info_t *section_info,
+ section_info_t *new_section_info,
+ wtapng_block_t *wblock,
+ int *err, gchar **err_info)
+{
+ block_return_val ret;
+ pcapng_block_header_t bh;
+
+ wblock->block = NULL;
+
+ /* Try to read the (next) block header */
+ if (!wtap_read_bytes_or_eof(fh, &bh, sizeof bh, err, err_info)) {
+ ws_debug("wtap_read_bytes_or_eof() failed, err = %d.", *err);
+ return FALSE;
+ }
+
+ /*
+ * SHBs have to be treated differently from other blocks, because
+ * the byte order of the fields in the block can only be determined
+ * by looking at the byte-order magic number inside the block, not
+ * by using the byte order of the section to which it belongs, as
+ * it is the block that *defines* the byte order of the section to
+ * which it belongs.
+ */
+ if (bh.block_type == BLOCK_TYPE_SHB) {
+ /*
+ * BLOCK_TYPE_SHB has the same value regardless of byte order,
+ * so we don't need to byte-swap it.
+ *
+ * We *might* need to byte-swap the total length, but we
+ * can't determine whether we do until we look inside the
+ * block and find the byte-order magic number, so we rely
+ * on pcapng_read_section_header_block() to do that and
+ * to swap the total length (as it needs to get the total
+ * length in the right byte order in order to read the
+ * entire block).
+ */
+ wblock->type = bh.block_type;
+
+ ws_debug("block_type BLOCK_TYPE_SHB (0x%08x)", bh.block_type);
+
+ /*
+ * Fill in the section_info_t passed to us for use when
+ * there's a new SHB; don't overwrite the existing SHB,
+ * if there is one.
+ */
+ ret = pcapng_read_section_header_block(fh, &bh, new_section_info,
+ wblock, err, err_info);
+ if (ret != PCAPNG_BLOCK_OK) {
+ return FALSE;
+ }
+
+ /*
+ * This is the current section; use its byte order, not that
+ * of the section pointed to by section_info (which could be
+ * null).
+ */
+ section_info = new_section_info;
+ } else {
+ /*
+ * Not an SHB.
+ */
+ if (section_info->byte_swapped) {
+ bh.block_type = GUINT32_SWAP_LE_BE(bh.block_type);
+ bh.block_total_length = GUINT32_SWAP_LE_BE(bh.block_total_length);
+ }
+
+ /*
+ * Add padding bytes to the block total length.
+ * (The "block total length" fields of some example files
+ * don't contain the packet data padding bytes!)
+ *
+ * For all block types currently defined in the pcapng
+ * specification, the portion of the block that precedes
+ * the options is, if necessary, padded to be a multiple
+ * of 4 octets, the header of an option is 4 octets long,
+ * and the value of an option is also padded to be a
+ * multiple of 4 octets, so the total length of a block
+ * is always a multiple of 4 octets.
+ *
+ * If you have defined a block where that is not true, you
+ * have violated the pcapng specification - hwere it says
+ * that "[The value fo the Block Total Length] MUST be a
+ * multiple of 4.", with MUST as described in BCP 14 (RFC 2119/
+ * RFC 8174).
+ *
+ * Therefore, if adjusting the block total length causes the
+ * code to read your block type not to work, that's your
+ * problem. It's bad enough that some blocks were written
+ * out with the block total length not including the padding.
+ * (Please note that libpcap is less forgiving that we are;
+ * it reports an error if the block total length isn't a
+ * multiple of 4.)
+ */
+ bh.block_total_length = ROUND_TO_4BYTE(bh.block_total_length);
+
+ wblock->type = bh.block_type;
+
+ ws_noisy("block_type 0x%08x", bh.block_type);
+
+ /* Don't try to allocate memory for a huge number of options, as
+ that might fail and, even if it succeeds, it might not leave
+ any address space or memory+backing store for anything else.
+
+ We do that by imposing a maximum block size of MAX_BLOCK_SIZE. */
+ if (bh.block_total_length > MAX_BLOCK_SIZE) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("pcapng: total block length %u is too large (> %u)",
+ bh.block_total_length, MAX_BLOCK_SIZE);
+ return FALSE;
+ }
+
+ /*
+ * ***DO NOT*** add any items to this table that are not
+ * standardized block types in the current pcapng spec at
+ *
+ * https://pcapng.github.io/pcapng/draft-ietf-opsawg-pcapng.html
+ *
+ * All block types in this switch statement here must be
+ * listed there as standardized block types, ideally with
+ * a description.
+ */
+ switch (bh.block_type) {
+ case(BLOCK_TYPE_IDB):
+ if (!pcapng_read_if_descr_block(wth, fh, &bh, section_info, wblock, err, err_info))
+ return FALSE;
+ break;
+ case(BLOCK_TYPE_PB):
+ if (!pcapng_read_packet_block(fh, &bh, section_info, wblock, err, err_info, FALSE))
+ return FALSE;
+ break;
+ case(BLOCK_TYPE_SPB):
+ if (!pcapng_read_simple_packet_block(fh, &bh, section_info, wblock, err, err_info))
+ return FALSE;
+ break;
+ case(BLOCK_TYPE_EPB):
+ if (!pcapng_read_packet_block(fh, &bh, section_info, wblock, err, err_info, TRUE))
+ return FALSE;
+ break;
+ case(BLOCK_TYPE_NRB):
+ if (!pcapng_read_name_resolution_block(fh, &bh, section_info, wblock, err, err_info))
+ return FALSE;
+ break;
+ case(BLOCK_TYPE_ISB):
+ if (!pcapng_read_interface_statistics_block(fh, &bh, section_info, wblock, err, err_info))
+ return FALSE;
+ break;
+ case(BLOCK_TYPE_DSB):
+ if (!pcapng_read_decryption_secrets_block(fh, &bh, section_info, wblock, err, err_info))
+ return FALSE;
+ break;
+ case BLOCK_TYPE_SYSDIG_MI:
+ case BLOCK_TYPE_SYSDIG_PL_V1:
+ case BLOCK_TYPE_SYSDIG_FDL_V1:
+ case BLOCK_TYPE_SYSDIG_IL_V1:
+ case BLOCK_TYPE_SYSDIG_UL_V1:
+ case BLOCK_TYPE_SYSDIG_PL_V2:
+ case BLOCK_TYPE_SYSDIG_PL_V3:
+ case BLOCK_TYPE_SYSDIG_PL_V4:
+ case BLOCK_TYPE_SYSDIG_PL_V5:
+ case BLOCK_TYPE_SYSDIG_PL_V6:
+ case BLOCK_TYPE_SYSDIG_PL_V7:
+ case BLOCK_TYPE_SYSDIG_PL_V8:
+ case BLOCK_TYPE_SYSDIG_PL_V9:
+ case BLOCK_TYPE_SYSDIG_FDL_V2:
+ case BLOCK_TYPE_SYSDIG_IL_V2:
+ case BLOCK_TYPE_SYSDIG_UL_V2:
+ if (!pcapng_read_sysdig_meta_event_block(fh, &bh, wblock, err, err_info))
+ return FALSE;
+ break;
+ case(BLOCK_TYPE_CB_COPY):
+ case(BLOCK_TYPE_CB_NO_COPY):
+ if (!pcapng_read_custom_block(fh, &bh, section_info, wblock, err, err_info))
+ return FALSE;
+ break;
+ case(BLOCK_TYPE_SYSDIG_EVENT):
+ case(BLOCK_TYPE_SYSDIG_EVENT_V2):
+ case(BLOCK_TYPE_SYSDIG_EVENT_V2_LARGE):
+ /* case(BLOCK_TYPE_SYSDIG_EVF): */
+ if (!pcapng_read_sysdig_event_block(fh, &bh, section_info, wblock, err, err_info))
+ return FALSE;
+ break;
+ case(BLOCK_TYPE_SYSTEMD_JOURNAL_EXPORT):
+ if (!pcapng_read_systemd_journal_export_block(wth, fh, &bh, pn, wblock, err, err_info))
+ return FALSE;
+ break;
+ default:
+ ws_debug("Unknown block_type: 0x%08x (block ignored), block total length %d",
+ bh.block_type, bh.block_total_length);
+ if (!pcapng_read_unknown_block(fh, &bh, section_info, wblock, err, err_info))
+ return FALSE;
+ break;
+ }
+ }
+
+ /*
+ * Read and check the block trailer.
+ */
+ if (!pcapng_read_and_check_block_trailer(fh, &bh, section_info, err, err_info)) {
+ /* Not readable or not valid. */
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/* Process an IDB that we've just read. The contents of wblock are copied as needed. */
+static void
+pcapng_process_idb(wtap *wth, section_info_t *section_info,
+ wtapng_block_t *wblock)
+{
+ wtap_block_t int_data = wtap_block_create(WTAP_BLOCK_IF_ID_AND_INFO);
+ interface_info_t iface_info;
+ wtapng_if_descr_mandatory_t *if_descr_mand = (wtapng_if_descr_mandatory_t*)wtap_block_get_mandatory_data(int_data),
+ *wblock_if_descr_mand = (wtapng_if_descr_mandatory_t*)wtap_block_get_mandatory_data(wblock->block);
+ guint8 if_fcslen;
+
+ wtap_block_copy(int_data, wblock->block);
+
+ /* XXX if_tsoffset; opt 14 A 64 bits integer value that specifies an offset (in seconds)...*/
+ /* Interface statistics */
+ if_descr_mand->num_stat_entries = 0;
+ if_descr_mand->interface_statistics = NULL;
+
+ wtap_add_idb(wth, int_data);
+
+ iface_info.wtap_encap = wblock_if_descr_mand->wtap_encap;
+ iface_info.snap_len = wblock_if_descr_mand->snap_len;
+ iface_info.time_units_per_second = wblock_if_descr_mand->time_units_per_second;
+ iface_info.tsprecision = wblock_if_descr_mand->tsprecision;
+
+ /*
+ * Did we get an FCS length option?
+ */
+ if (wtap_block_get_uint8_option_value(wblock->block, OPT_IDB_FCSLEN,
+ &if_fcslen) == WTAP_OPTTYPE_SUCCESS) {
+ /*
+ * Yes.
+ */
+ iface_info.fcslen = if_fcslen;
+ } else {
+ /*
+ * No. Mark the FCS length as unknown.
+ */
+ iface_info.fcslen = -1;
+ }
+
+ /*
+ * Did we get a time stamp offset option?
+ */
+ if (wtap_block_get_int64_option_value(wblock->block, OPT_IDB_TSOFFSET,
+ &iface_info.tsoffset) == WTAP_OPTTYPE_SUCCESS) {
+ /*
+ * Yes.
+ *
+ * Remove the option, as the time stamps we provide will be
+ * absolute time stamps, with the offset added in, so it will
+ * appear as if there were no such option.
+ */
+ wtap_block_remove_option(wblock->block, OPT_IDB_TSOFFSET);
+ } else {
+ /*
+ * No. Default to 0, meahing that time stamps in the file are
+ * absolute time stamps.
+ */
+ iface_info.tsoffset = 0;
+ }
+
+ g_array_append_val(section_info->interfaces, iface_info);
+}
+
+/* Process an NRB that we have just read. */
+static void
+pcapng_process_nrb(wtap *wth, wtapng_block_t *wblock)
+{
+ wtapng_process_nrb(wth, wblock->block);
+
+ if (wth->nrbs == NULL) {
+ wth->nrbs = g_array_new(FALSE, FALSE, sizeof(wtap_block_t));
+ }
+ /* Store NRB such that it can be saved by the dumper. */
+ g_array_append_val(wth->nrbs, wblock->block);
+}
+
+/* Process a DSB that we have just read. */
+static void
+pcapng_process_dsb(wtap *wth, wtapng_block_t *wblock)
+{
+ wtapng_process_dsb(wth, wblock->block);
+
+ /* Store DSB such that it can be saved by the dumper. */
+ g_array_append_val(wth->dsbs, wblock->block);
+}
+
+/* Process a Sysdig meta event block that we have just read. */
+static void
+pcapng_process_sysdig_mev(wtap *wth, wtapng_block_t *wblock)
+{
+ // XXX add wtapng_process_sysdig_meb(wth, wblock->block);
+
+ /* Store meta event such that it can be saved by the dumper. */
+ g_array_append_val(wth->sysdig_meta_events, wblock->block);
+}
+
+static void
+pcapng_process_internal_block(wtap *wth, pcapng_t *pcapng, section_info_t *current_section, section_info_t new_section, wtapng_block_t *wblock, const gint64 *data_offset)
+{
+ wtap_block_t wtapng_if_descr;
+ wtap_block_t if_stats;
+ wtapng_if_stats_mandatory_t *if_stats_mand_block, *if_stats_mand;
+ wtapng_if_descr_mandatory_t *wtapng_if_descr_mand;
+
+ switch (wblock->type) {
+
+ case(BLOCK_TYPE_SHB):
+ ws_debug("another section header block");
+
+ /*
+ * Add this SHB to the table of SHBs.
+ */
+ g_array_append_val(wth->shb_hdrs, wblock->block);
+
+ /*
+ * Update the current section number, and add
+ * the updated section_info_t to the array of
+ * section_info_t's for this file.
+ */
+ pcapng->current_section_number++;
+ new_section.interfaces = g_array_new(FALSE, FALSE, sizeof(interface_info_t));
+ new_section.shb_off = *data_offset;
+ g_array_append_val(pcapng->sections, new_section);
+ break;
+
+ case(BLOCK_TYPE_IDB):
+ /* A new interface */
+ ws_debug("block type BLOCK_TYPE_IDB");
+ pcapng_process_idb(wth, current_section, wblock);
+ wtap_block_unref(wblock->block);
+ break;
+
+ case(BLOCK_TYPE_DSB):
+ /* Decryption secrets. */
+ ws_debug("block type BLOCK_TYPE_DSB");
+ pcapng_process_dsb(wth, wblock);
+ /* Do not free wblock->block, it is consumed by pcapng_process_dsb */
+ break;
+
+ case(BLOCK_TYPE_NRB):
+ /* More name resolution entries */
+ ws_debug("block type BLOCK_TYPE_NRB");
+ pcapng_process_nrb(wth, wblock);
+ /* Do not free wblock->block, it is consumed by pcapng_process_nrb */
+ break;
+
+ case(BLOCK_TYPE_ISB):
+ /*
+ * Another interface statistics report
+ *
+ * XXX - given that they're reports, we should be
+ * supplying them in read calls, and displaying them
+ * in the "packet" list, so you can see what the
+ * statistics were *at the time when the report was
+ * made*.
+ *
+ * The statistics from the *last* ISB could be displayed
+ * in the summary, but if there are packets after the
+ * last ISB, that could be misleading.
+ *
+ * If we only display them if that ISB has an isb_endtime
+ * option, which *should* only appear when capturing ended
+ * on that interface (so there should be no more packet
+ * blocks or ISBs for that interface after that point,
+ * that would be the best way of showing "summary"
+ * statistics.
+ */
+ ws_debug("block type BLOCK_TYPE_ISB");
+ if_stats_mand_block = (wtapng_if_stats_mandatory_t*)wtap_block_get_mandatory_data(wblock->block);
+ if (wth->interface_data->len <= if_stats_mand_block->interface_id) {
+ ws_debug("BLOCK_TYPE_ISB wblock.if_stats.interface_id %u >= number_of_interfaces",
+ if_stats_mand_block->interface_id);
+ } else {
+ /* Get the interface description */
+ wtapng_if_descr = g_array_index(wth->interface_data, wtap_block_t, if_stats_mand_block->interface_id);
+ wtapng_if_descr_mand = (wtapng_if_descr_mandatory_t*)wtap_block_get_mandatory_data(wtapng_if_descr);
+ if (wtapng_if_descr_mand->num_stat_entries == 0) {
+ /* First ISB found, no previous entry */
+ ws_debug("block type BLOCK_TYPE_ISB. First ISB found, no previous entry");
+ wtapng_if_descr_mand->interface_statistics = g_array_new(FALSE, FALSE, sizeof(wtap_block_t));
+ }
+
+ if_stats = wtap_block_create(WTAP_BLOCK_IF_STATISTICS);
+ if_stats_mand = (wtapng_if_stats_mandatory_t*)wtap_block_get_mandatory_data(if_stats);
+ if_stats_mand->interface_id = if_stats_mand_block->interface_id;
+ if_stats_mand->ts_high = if_stats_mand_block->ts_high;
+ if_stats_mand->ts_low = if_stats_mand_block->ts_low;
+
+ wtap_block_copy(if_stats, wblock->block);
+ g_array_append_val(wtapng_if_descr_mand->interface_statistics, if_stats);
+ wtapng_if_descr_mand->num_stat_entries++;
+ }
+ wtap_block_unref(wblock->block);
+ break;
+
+ case BLOCK_TYPE_SYSDIG_MI:
+ case BLOCK_TYPE_SYSDIG_PL_V1:
+ case BLOCK_TYPE_SYSDIG_FDL_V1:
+ case BLOCK_TYPE_SYSDIG_IL_V1:
+ case BLOCK_TYPE_SYSDIG_UL_V1:
+ case BLOCK_TYPE_SYSDIG_PL_V2:
+ case BLOCK_TYPE_SYSDIG_PL_V3:
+ case BLOCK_TYPE_SYSDIG_PL_V4:
+ case BLOCK_TYPE_SYSDIG_PL_V5:
+ case BLOCK_TYPE_SYSDIG_PL_V6:
+ case BLOCK_TYPE_SYSDIG_PL_V7:
+ case BLOCK_TYPE_SYSDIG_PL_V8:
+ case BLOCK_TYPE_SYSDIG_PL_V9:
+ case BLOCK_TYPE_SYSDIG_FDL_V2:
+ case BLOCK_TYPE_SYSDIG_IL_V2:
+ case BLOCK_TYPE_SYSDIG_UL_V2:
+ /* Decryption secrets. */
+ ws_debug("block type Sysdig meta event");
+ pcapng_process_sysdig_mev(wth, wblock);
+ /* Do not free wblock->block, it is consumed by pcapng_process_sysdig_meb */
+ break;
+
+ default:
+ /* XXX - improve handling of "unknown" blocks */
+ ws_debug("Unknown block type 0x%08x", wblock->type);
+ break;
+ }
+}
+
+/* classic wtap: open capture file */
+wtap_open_return_val
+pcapng_open(wtap *wth, int *err, gchar **err_info)
+{
+ wtapng_block_t wblock;
+ pcapng_t *pcapng;
+ pcapng_block_header_t bh;
+ gint64 saved_offset;
+ section_info_t first_section, new_section, *current_section;
+
+ ws_debug("opening file");
+ /*
+ * Read first block.
+ *
+ * First, try to read the block header.
+ */
+ if (!wtap_read_bytes_or_eof(wth->fh, &bh, sizeof bh, err, err_info)) {
+ ws_debug("wtap_read_bytes_or_eof() failed, err = %d.", *err);
+ if (*err == 0 || *err == WTAP_ERR_SHORT_READ) {
+ /*
+ * Short read or EOF.
+ *
+ * We're reading this as part of an open, so
+ * the file is too short to be a pcapng file.
+ */
+ *err = 0;
+ g_free(*err_info);
+ *err_info = NULL;
+ return WTAP_OPEN_NOT_MINE;
+ }
+ return WTAP_OPEN_ERROR;
+ }
+
+ /*
+ * If this is a pcapng file, the first block must be a
+ * Section Header Block.
+ */
+ if (bh.block_type != BLOCK_TYPE_SHB) {
+ /*
+ * Not an SHB, so this isn't a pcapng file.
+ *
+ * XXX - check for damage from transferring a file
+ * between Windows and UN*X as text rather than
+ * binary data?
+ */
+ ws_debug("first block type 0x%08x not SHB", bh.block_type);
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ ws_debug("got an SHB");
+
+ /*
+ * Now try to read the block body, filling in the section_info_t
+ * for the first section.
+ */
+ wblock.type = bh.block_type;
+ wblock.block = NULL;
+ /* we don't expect any packet blocks yet */
+ wblock.frame_buffer = NULL;
+ wblock.rec = NULL;
+
+ switch (pcapng_read_section_header_block(wth->fh, &bh, &first_section,
+ &wblock, err, err_info)) {
+ case PCAPNG_BLOCK_OK:
+ /* No problem */
+ break;
+
+ case PCAPNG_BLOCK_NOT_SHB:
+ /* This doesn't look like an SHB, so this isn't a pcapng file. */
+ wtap_block_unref(wblock.block);
+ *err = 0;
+ g_free(*err_info);
+ *err_info = NULL;
+ return WTAP_OPEN_NOT_MINE;
+
+ case PCAPNG_BLOCK_ERROR:
+ wtap_block_unref(wblock.block);
+ if (*err == WTAP_ERR_SHORT_READ) {
+ /*
+ * Short read.
+ *
+ * We're reading this as part of an open, so
+ * the file is too short to be a pcapng file.
+ */
+ *err = 0;
+ g_free(*err_info);
+ *err_info = NULL;
+ return WTAP_OPEN_NOT_MINE;
+ }
+ /* An I/O error. */
+ return WTAP_OPEN_ERROR;
+ }
+
+ /*
+ * Read and check the block trailer.
+ */
+ if (!pcapng_read_and_check_block_trailer(wth->fh, &bh, &first_section, err, err_info)) {
+ /* Not readable or not valid. */
+ wtap_block_unref(wblock.block);
+ return WTAP_OPEN_ERROR;
+ }
+
+ /*
+ * At this point, we've decided this is a pcapng file, not
+ * some other type of file, so we can't return WTAP_OPEN_NOT_MINE
+ * past this point.
+ *
+ * Copy the SHB that we just read to the first entry in the table of
+ * SHBs for this file.
+ */
+ wtap_block_copy(g_array_index(wth->shb_hdrs, wtap_block_t, 0), wblock.block);
+ wtap_block_unref(wblock.block);
+ wblock.block = NULL;
+
+ wth->file_encap = WTAP_ENCAP_NONE;
+ wth->snapshot_length = 0;
+ wth->file_tsprec = WTAP_TSPREC_UNKNOWN;
+ pcapng = g_new(pcapng_t, 1);
+ wth->priv = (void *)pcapng;
+ /*
+ * We're currently processing the first section; as this is written
+ * in C, that's section 0. :-)
+ */
+ pcapng->current_section_number = 0;
+
+ /*
+ * Create the array of interfaces for the first section.
+ */
+ first_section.interfaces = g_array_new(FALSE, FALSE, sizeof(interface_info_t));
+
+ /*
+ * The first section is at the very beginning of the file.
+ */
+ first_section.shb_off = 0;
+
+ /*
+ * Allocate the sections table with space reserved for the first
+ * section, and add that section.
+ */
+ pcapng->sections = g_array_sized_new(FALSE, FALSE, sizeof(section_info_t), 1);
+ g_array_append_val(pcapng->sections, first_section);
+
+ wth->subtype_read = pcapng_read;
+ wth->subtype_seek_read = pcapng_seek_read;
+ wth->subtype_close = pcapng_close;
+ wth->file_type_subtype = pcapng_file_type_subtype;
+
+ /* Always initialize the lists of Decryption Secret Blocks, Name
+ * Resolution Blocks, and Sysdig meta event blocks such that a
+ * wtap_dumper can refer to them right after opening the capture
+ * file. */
+ wth->dsbs = g_array_new(FALSE, FALSE, sizeof(wtap_block_t));
+ wth->nrbs = g_array_new(FALSE, FALSE, sizeof(wtap_block_t));
+ wth->sysdig_meta_events = g_array_new(FALSE, FALSE, sizeof(wtap_block_t));
+
+ /* Most other capture types (such as pcap) support a single link-layer
+ * type, indicated in the header, and don't support WTAP_ENCAP_PER_PACKET.
+ * Most programs that write such capture files want to know the link-layer
+ * type when initially opening the destination file, and (unlike Wireshark)
+ * don't want to read the entire source file to find all the link-layer
+ * types before writing (particularly if reading from a pipe or FIFO.)
+ *
+ * In support of this, read all the internally-processed, non packet
+ * blocks that appear before the first packet block (EPB or SPB).
+ *
+ * Note that such programs will still have issues when trying to read
+ * a pcapng that has a new link-layer type in an IDB in the middle of
+ * the file, as they will discover in the middle that no, they can't
+ * successfully write the output file as desired.
+ */
+ while (1) {
+ /* peek at next block */
+ /* Try to read the (next) block header */
+ saved_offset = file_tell(wth->fh);
+ if (!wtap_read_bytes_or_eof(wth->fh, &bh, sizeof bh, err, err_info)) {
+ if (*err == 0) {
+ /* EOF */
+ ws_debug("No more blocks available...");
+ break;
+ }
+ ws_debug("Check for more initial blocks, wtap_read_bytes_or_eof() failed, err = %d.",
+ *err);
+ return WTAP_OPEN_ERROR;
+ }
+
+ /* go back to where we were */
+ file_seek(wth->fh, saved_offset, SEEK_SET, err);
+
+ /*
+ * Get a pointer to the current section's section_info_t.
+ */
+ current_section = &g_array_index(pcapng->sections, section_info_t,
+ pcapng->current_section_number);
+
+ if (current_section->byte_swapped) {
+ bh.block_type = GUINT32_SWAP_LE_BE(bh.block_type);
+ }
+
+ ws_debug("Check for more initial internal blocks, block_type 0x%08x",
+ bh.block_type);
+
+ if (!get_block_type_internal(bh.block_type)) {
+ break; /* Next block has to be returned in pcap_read */
+ }
+ /* Note that some custom block types, unlike packet blocks,
+ * don't need to be preceded by an IDB and so theoretically
+ * we could skip past them here. However, then there's no good
+ * way to both later return those blocks in pcap_read() and
+ * ensure that we don't read and process the IDBs (and other
+ * internal block types) a second time.
+ *
+ * pcapng_read_systemd_journal_export_block() sets the file level
+ * link-layer type if it's still UNKNOWN. We could do the same here
+ * for it and possibly other types based on block type, even without
+ * reading them.
+ */
+ if (!pcapng_read_block(wth, wth->fh, pcapng, current_section,
+ &new_section, &wblock, err, err_info)) {
+ wtap_block_unref(wblock.block);
+ if (*err == 0) {
+ ws_debug("No more initial blocks available...");
+ break;
+ } else {
+ ws_debug("couldn't read block");
+ return WTAP_OPEN_ERROR;
+ }
+ }
+ pcapng_process_internal_block(wth, pcapng, current_section, new_section, &wblock, &saved_offset);
+ ws_debug("Read IDB number_of_interfaces %u, wtap_encap %i",
+ wth->interface_data->len, wth->file_encap);
+ }
+ return WTAP_OPEN_MINE;
+}
+
+/* classic wtap: read packet */
+static gboolean
+pcapng_read(wtap *wth, wtap_rec *rec, Buffer *buf, int *err,
+ gchar **err_info, gint64 *data_offset)
+{
+ pcapng_t *pcapng = (pcapng_t *)wth->priv;
+ section_info_t *current_section, new_section;
+ wtapng_block_t wblock;
+
+ wblock.frame_buffer = buf;
+ wblock.rec = rec;
+
+ /* read next block */
+ while (1) {
+ *data_offset = file_tell(wth->fh);
+ ws_noisy("data_offset is %" PRId64, *data_offset);
+
+ /*
+ * Get the section_info_t for the current section.
+ */
+ current_section = &g_array_index(pcapng->sections, section_info_t,
+ pcapng->current_section_number);
+
+ /*
+ * Read the next block.
+ */
+ if (!pcapng_read_block(wth, wth->fh, pcapng, current_section,
+ &new_section, &wblock, err, err_info)) {
+ ws_noisy("data_offset is finally %" PRId64, *data_offset);
+ ws_debug("couldn't read packet block");
+ wtap_block_unref(wblock.block);
+ return FALSE;
+ }
+
+ if (!wblock.internal) {
+ /*
+ * This is a block type we return to the caller to process.
+ */
+ ws_noisy("rec_type %u", wblock.rec->rec_type);
+ break;
+ }
+
+ /*
+ * This is a block type we process internally, rather than
+ * returning it for the caller to process.
+ */
+ pcapng_process_internal_block(wth, pcapng, current_section, new_section, &wblock, data_offset);
+ }
+
+ /*ws_debug("Read length: %u Packet length: %u", bytes_read, rec->rec_header.packet_header.caplen);*/
+ ws_noisy("data_offset is finally %" PRId64, *data_offset);
+
+ /* Provide the section number */
+ rec->presence_flags |= WTAP_HAS_SECTION_NUMBER;
+ rec->section_number = pcapng->current_section_number;
+
+ return TRUE;
+}
+
+/* classic wtap: seek to file position and read packet */
+static gboolean
+pcapng_seek_read(wtap *wth, gint64 seek_off,
+ wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info)
+{
+ pcapng_t *pcapng = (pcapng_t *)wth->priv;
+ section_info_t *section_info, new_section;
+ wtapng_block_t wblock;
+
+
+ /* seek to the right file position */
+ if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) < 0) {
+ return FALSE; /* Seek error */
+ }
+ ws_noisy("reading at offset %" PRIu64, seek_off);
+
+ /*
+ * Find the section_info_t for the section in which this block
+ * appears.
+ *
+ * First, make sure we have at least one section; if we don't, that's
+ * an internal error.
+ */
+ ws_assert(pcapng->sections->len >= 1);
+
+ /*
+ * Now scan backwards through the array to find the first section
+ * that begins at or before the offset of the block we're reading.
+ *
+ * Yes, that's O(n) in the number of blocks, but we're unlikely to
+ * have many blocks and pretty unlikely to have more than one.
+ */
+ guint section_number = pcapng->sections->len - 1;
+ for (;;) {
+ section_info = &g_array_index(pcapng->sections, section_info_t,
+ section_number);
+ if (section_info->shb_off <= seek_off)
+ break;
+
+ /*
+ * If that's section 0, something's wrong; that section should
+ * have an offset of 0.
+ */
+ ws_assert(section_number != 0);
+ section_number--;
+ }
+
+ wblock.frame_buffer = buf;
+ wblock.rec = rec;
+
+ /* read the block */
+ if (!pcapng_read_block(wth, wth->random_fh, pcapng, section_info,
+ &new_section, &wblock, err, err_info)) {
+ ws_debug("couldn't read packet block (err=%d).", *err);
+ wtap_block_unref(wblock.block);
+ return FALSE;
+ }
+
+ /* block must not be one we process internally rather than supplying */
+ if (wblock.internal) {
+ ws_debug("block type 0x%08x is not one we return",
+ wblock.type);
+ wtap_block_unref(wblock.block);
+ return FALSE;
+ }
+
+ wtap_block_unref(wblock.block);
+
+ /* Provide the section number */
+ rec->presence_flags |= WTAP_HAS_SECTION_NUMBER;
+ rec->section_number = section_number;
+
+ return TRUE;
+}
+
+/* classic wtap: close capture file */
+static void
+pcapng_close(wtap *wth)
+{
+ pcapng_t *pcapng = (pcapng_t *)wth->priv;
+
+ ws_debug("closing file");
+
+ /*
+ * Free up the interfaces tables for all the sections.
+ */
+ for (guint i = 0; i < pcapng->sections->len; i++) {
+ section_info_t *section_info = &g_array_index(pcapng->sections,
+ section_info_t, i);
+ g_array_free(section_info->interfaces, TRUE);
+ }
+ g_array_free(pcapng->sections, TRUE);
+}
+
+typedef guint32 (*compute_option_size_func)(wtap_block_t, guint, wtap_opttype_e, wtap_optval_t*);
+
+typedef struct compute_options_size_t
+{
+ guint32 size;
+ compute_option_size_func compute_option_size;
+} compute_options_size_t;
+
+static guint32 pcapng_compute_string_option_size(wtap_optval_t *optval)
+{
+ guint32 size = 0, pad;
+
+ size = (guint32)strlen(optval->stringval) & 0xffff;
+ if ((size % 4)) {
+ pad = 4 - (size % 4);
+ } else {
+ pad = 0;
+ }
+
+ size += pad;
+
+ return size;
+}
+
+#if 0
+static guint32 pcapng_compute_bytes_option_size(wtap_optval_t *optval)
+{
+ guint32 size = 0, pad;
+
+ size = (guint32)g_bytes_get_size(optval->byteval) & 0xffff;
+ if ((size % 4)) {
+ pad = 4 - (size % 4);
+ } else {
+ pad = 0;
+ }
+
+ size += pad;
+
+ return size;
+}
+#endif
+
+static guint32 pcapng_compute_if_filter_option_size(wtap_optval_t *optval)
+{
+ if_filter_opt_t* filter = &optval->if_filterval;
+ guint32 size;
+ guint32 pad;
+
+ if (filter->type == if_filter_pcap) {
+ size = (guint32)(strlen(filter->data.filter_str) + 1) & 0xffff;
+ } else if (filter->type == if_filter_bpf) {
+ size = (guint32)((filter->data.bpf_prog.bpf_prog_len * 8) + 1) & 0xffff;
+ } else {
+ /* Unknown type; don't write it */
+ size = 0;
+ }
+ if ((size % 4)) {
+ pad = 4 - (size % 4);
+ } else {
+ pad = 0;
+ }
+ size += pad;
+ return size;
+}
+
+static guint32 pcapng_compute_custom_option_size(wtap_optval_t *optval)
+{
+ size_t size, pad;
+
+ /* PEN */
+ size = sizeof(guint32);
+ switch (optval->custom_opt.pen) {
+ case PEN_NFLX:
+ /* NFLX type */
+ size += sizeof(guint32);
+ size += optval->custom_opt.data.nflx_data.custom_data_len;
+ break;
+ default:
+ size += optval->custom_opt.data.generic_data.custom_data_len;
+ break;
+ }
+ if (size > 65535) {
+ size = 65535;
+ }
+ if ((size % 4)) {
+ pad = 4 - (size % 4);
+ } else {
+ pad = 0;
+ }
+
+ size += pad;
+
+ return (guint32)size;
+}
+
+static guint32 pcapng_compute_packet_hash_option_size(wtap_optval_t *optval)
+{
+ packet_hash_opt_t* hash = &optval->packet_hash;
+ guint32 size;
+ guint32 pad;
+
+ switch (hash->type) {
+ case OPT_HASH_CRC32:
+ size = 4;
+ break;
+ case OPT_HASH_MD5:
+ size = 16;
+ break;
+ case OPT_HASH_SHA1:
+ size = 20;
+ break;
+ case OPT_HASH_TOEPLITZ:
+ size = 4;
+ break;
+ default:
+ /* 2COMP and XOR size not defined in standard (yet) */
+ size = hash->hash_bytes->len;
+ break;
+ }
+ if ((size % 4)) {
+ pad = 4 - (size % 4);
+ } else {
+ pad = 0;
+ }
+ size += pad;
+ return size;
+}
+
+static guint32 pcapng_compute_packet_verdict_option_size(wtap_optval_t *optval)
+{
+ packet_verdict_opt_t* verdict = &optval->packet_verdictval;
+ guint32 size;
+ guint32 pad;
+
+ switch (verdict->type) {
+
+ case packet_verdict_hardware:
+ size = verdict->data.verdict_bytes->len;
+ break;
+
+ case packet_verdict_linux_ebpf_tc:
+ size = 9;
+ break;
+
+ case packet_verdict_linux_ebpf_xdp:
+ size = 9;
+ break;
+
+ default:
+ size = 0;
+ break;
+ }
+ if ((size % 4)) {
+ pad = 4 - (size % 4);
+ } else {
+ pad = 0;
+ }
+ size += pad;
+ return size;
+}
+
+static gboolean
+compute_block_option_size(wtap_block_t block _U_, guint option_id, wtap_opttype_e option_type, wtap_optval_t *optval, void *user_data)
+{
+ compute_options_size_t* options_size = (compute_options_size_t*)user_data;
+ guint32 size = 0;
+
+ /*
+ * Process the option IDs that are the same for all block types here;
+ * call the block-type-specific compute_size function for others.
+ */
+ switch(option_id)
+ {
+ case OPT_COMMENT:
+ size = pcapng_compute_string_option_size(optval);
+ break;
+ case OPT_CUSTOM_STR_COPY:
+ case OPT_CUSTOM_BIN_COPY:
+ size = pcapng_compute_custom_option_size(optval);
+ break;
+ case OPT_CUSTOM_STR_NO_COPY:
+ case OPT_CUSTOM_BIN_NO_COPY:
+ /*
+ * Do not count these, as they're not supposed to be copied to
+ * new files.
+ *
+ * XXX - what if we're writing out a file that's *not* based on
+ * another file, so that we're *not* copying it from that file?
+ */
+ break;
+ default:
+ /* Block-type dependent; call the callback. */
+ size = (*options_size->compute_option_size)(block, option_id, option_type, optval);
+ break;
+ }
+
+ /*
+ * Are we writing this option?
+ */
+ if (size != 0) {
+ /*
+ * Yes. Add the size of the option header to the size of the
+ * option data.
+ */
+ options_size->size += 4;
+
+ /* Now add the size of the option value. */
+ options_size->size += size;
+
+ /* Add optional padding to 32 bits */
+ if ((size & 0x03) != 0)
+ {
+ options_size->size += 4 - (size & 0x03);
+ }
+ }
+ return TRUE; /* we always succeed */
+}
+
+static guint32
+compute_options_size(wtap_block_t block, compute_option_size_func compute_option_size)
+{
+ compute_options_size_t compute_options_size;
+
+ /*
+ * Compute the total size of all the options in the block.
+ * This always suceeds, so we don't check the return value.
+ */
+ compute_options_size.size = 0;
+ compute_options_size.compute_option_size = compute_option_size;
+ wtap_block_foreach_option(block, compute_block_option_size, &compute_options_size);
+
+ /* Are we writing any options? */
+ if (compute_options_size.size != 0) {
+ /* Yes, add the size of the End-of-options tag. */
+ compute_options_size.size += 4;
+ }
+ return compute_options_size.size;
+}
+
+static guint32 compute_shb_option_size(wtap_block_t block _U_, guint option_id, wtap_opttype_e option_type _U_, wtap_optval_t* optval)
+{
+ guint32 size;
+
+ switch(option_id)
+ {
+ case OPT_SHB_HARDWARE:
+ case OPT_SHB_OS:
+ case OPT_SHB_USERAPPL:
+ size = pcapng_compute_string_option_size(optval);
+ break;
+ default:
+ /* Unknown options - size by datatype? */
+ size = 0;
+ break;
+ }
+ return size;
+}
+
+typedef gboolean (*write_option_func)(wtap_dumper *, wtap_block_t, guint, wtap_opttype_e, wtap_optval_t*, int*);
+
+typedef struct write_options_t
+{
+ wtap_dumper *wdh;
+ int *err;
+ write_option_func write_option;
+}
+write_options_t;
+
+static gboolean pcapng_write_option_eofopt(wtap_dumper *wdh, int *err)
+{
+ struct pcapng_option_header option_hdr;
+
+ /* Write end of options */
+ option_hdr.type = OPT_EOFOPT;
+ option_hdr.value_length = 0;
+ if (!wtap_dump_file_write(wdh, &option_hdr, 4, err))
+ return FALSE;
+ return TRUE;
+}
+
+static gboolean pcapng_write_uint8_option(wtap_dumper *wdh, guint option_id, wtap_optval_t *optval, int *err)
+{
+ struct pcapng_option_header option_hdr;
+ const guint32 zero_pad = 0;
+
+ option_hdr.type = (guint16)option_id;
+ option_hdr.value_length = (guint16)1;
+ if (!wtap_dump_file_write(wdh, &option_hdr, 4, err))
+ return FALSE;
+
+ if (!wtap_dump_file_write(wdh, &optval->uint8val, 1, err))
+ return FALSE;
+
+ if (!wtap_dump_file_write(wdh, &zero_pad, 3, err))
+ return FALSE;
+
+ return TRUE;
+}
+
+static gboolean pcapng_write_uint32_option(wtap_dumper *wdh, guint option_id, wtap_optval_t *optval, int *err)
+{
+ struct pcapng_option_header option_hdr;
+
+ option_hdr.type = (guint16)option_id;
+ option_hdr.value_length = (guint16)4;
+ if (!wtap_dump_file_write(wdh, &option_hdr, 4, err))
+ return FALSE;
+
+ if (!wtap_dump_file_write(wdh, &optval->uint32val, 4, err))
+ return FALSE;
+
+ return TRUE;
+}
+
+static gboolean pcapng_write_uint64_option(wtap_dumper *wdh, guint option_id, wtap_optval_t *optval, int *err)
+{
+ struct pcapng_option_header option_hdr;
+
+ option_hdr.type = (guint16)option_id;
+ option_hdr.value_length = (guint16)8;
+ if (!wtap_dump_file_write(wdh, &option_hdr, 4, err))
+ return FALSE;
+
+ if (!wtap_dump_file_write(wdh, &optval->uint64val, 8, err))
+ return FALSE;
+
+ return TRUE;
+}
+
+static gboolean pcapng_write_timestamp_option(wtap_dumper *wdh, guint option_id, wtap_optval_t *optval, int *err)
+{
+ struct pcapng_option_header option_hdr;
+ guint32 high, low;
+
+ option_hdr.type = (guint16)option_id;
+ option_hdr.value_length = (guint16)8;
+ if (!wtap_dump_file_write(wdh, &option_hdr, 4, err))
+ return FALSE;
+
+ high = (guint32)(optval->uint64val >> 32);
+ low = (guint32)(optval->uint64val >> 0);
+ if (!wtap_dump_file_write(wdh, &high, 4, err))
+ return FALSE;
+ if (!wtap_dump_file_write(wdh, &low, 4, err))
+ return FALSE;
+
+ return TRUE;
+}
+
+static gboolean pcapng_write_string_option(wtap_dumper *wdh, guint option_id, wtap_optval_t *optval, int *err)
+{
+ struct pcapng_option_header option_hdr;
+ size_t size = strlen(optval->stringval);
+ const guint32 zero_pad = 0;
+ guint32 pad;
+
+ if (size == 0)
+ return TRUE;
+ if (size > 65535) {
+ /*
+ * Too big to fit in the option.
+ * Don't write anything.
+ *
+ * XXX - truncate it? Report an error?
+ */
+ return TRUE;
+ }
+
+ /* String options don't consider pad bytes part of the length */
+ option_hdr.type = (guint16)option_id;
+ option_hdr.value_length = (guint16)size;
+ if (!wtap_dump_file_write(wdh, &option_hdr, 4, err))
+ return FALSE;
+
+ if (!wtap_dump_file_write(wdh, optval->stringval, size, err))
+ return FALSE;
+
+ if ((size % 4)) {
+ pad = 4 - (size % 4);
+ } else {
+ pad = 0;
+ }
+
+ /* write padding (if any) */
+ if (pad != 0) {
+ if (!wtap_dump_file_write(wdh, &zero_pad, pad, err))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+#if 0
+static gboolean pcapng_write_bytes_option(wtap_dumper *wdh, guint option_id, wtap_optval_t *optval, int *err)
+{
+ struct pcapng_option_header option_hdr;
+ size_t size = g_bytes_get_size(optval->byteval);
+ const guint32 zero_pad = 0;
+ guint32 pad;
+
+ if (size == 0)
+ return TRUE;
+ if (size > 65535) {
+ /*
+ * Too big to fit in the option.
+ * Don't write anything.
+ *
+ * XXX - truncate it? Report an error?
+ */
+ return TRUE;
+ }
+
+ /* Bytes options don't consider pad bytes part of the length */
+ option_hdr.type = (guint16)option_id;
+ option_hdr.value_length = (guint16)size;
+ if (!wtap_dump_file_write(wdh, &option_hdr, 4, err))
+ return FALSE;
+
+ if (!wtap_dump_file_write(wdh, optval->stringval, size, err))
+ return FALSE;
+
+ if ((size % 4)) {
+ pad = 4 - (size % 4);
+ } else {
+ pad = 0;
+ }
+
+ /* write padding (if any) */
+ if (pad != 0) {
+ if (!wtap_dump_file_write(wdh, &zero_pad, pad, err))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean pcapng_write_ipv4_option(wtap_dumper *wdh, guint option_id, wtap_optval_t *optval, int *err)
+{
+ struct pcapng_option_header option_hdr;
+
+ option_hdr.type = (guint16)option_id;
+ option_hdr.value_length = (guint16)4;
+ if (!wtap_dump_file_write(wdh, &option_hdr, 4, err))
+ return FALSE;
+
+ if (!wtap_dump_file_write(wdh, &optval->ipv4val, 1, err))
+ return FALSE;
+
+ return TRUE;
+}
+
+static gboolean pcapng_write_ipv6_option(wtap_dumper *wdh, guint option_id, wtap_optval_t *optval, int *err)
+{
+ struct pcapng_option_header option_hdr;
+
+ option_hdr.type = (guint16)option_id;
+ option_hdr.value_length = (guint16)IPv6_ADDR_SIZE;
+ if (!wtap_dump_file_write(wdh, &option_hdr, 4, err))
+ return FALSE;
+
+ if (!wtap_dump_file_write(wdh, &optval->ipv6val.bytes, IPv6_ADDR_SIZE, err))
+ return FALSE;
+
+ return TRUE;
+}
+#endif
+
+static gboolean pcapng_write_if_filter_option(wtap_dumper *wdh, guint option_id, wtap_optval_t *optval, int *err)
+{
+ if_filter_opt_t* filter = &optval->if_filterval;
+ guint32 size, pad;
+ guint8 filter_type;
+ size_t filter_data_len;
+ struct pcapng_option_header option_hdr;
+ const guint32 zero_pad = 0;
+
+ switch (filter->type) {
+
+ case if_filter_pcap:
+ filter_type = 0; /* pcap filter string */
+ filter_data_len = strlen(filter->data.filter_str);
+ if (filter_data_len > 65534) {
+ /*
+ * Too big to fit in the option.
+ * Don't write anything.
+ *
+ * XXX - truncate it? Report an error?
+ */
+ return TRUE;
+ }
+ break;
+
+ case if_filter_bpf:
+ filter_type = 1; /* BPF filter program */
+ filter_data_len = filter->data.bpf_prog.bpf_prog_len*8;
+ if (filter_data_len > 65528) {
+ /*
+ * Too big to fit in the option. (The filter length
+ * must be a multiple of 8, as that's the length
+ * of a BPF instruction.) Don't write anything.
+ *
+ * XXX - truncate it? Report an error?
+ */
+ return TRUE;
+ }
+ break;
+
+ default:
+ /* Unknown filter type; don't write anything. */
+ return TRUE;
+ }
+ size = (guint32)(filter_data_len + 1);
+ if ((size % 4)) {
+ pad = 4 - (size % 4);
+ } else {
+ pad = 0;
+ }
+
+ option_hdr.type = option_id;
+ option_hdr.value_length = size;
+ if (!wtap_dump_file_write(wdh, &option_hdr, 4, err))
+ return FALSE;
+
+ /* Write the filter type */
+ if (!wtap_dump_file_write(wdh, &filter_type, 1, err))
+ return FALSE;
+
+ switch (filter->type) {
+
+ case if_filter_pcap:
+ /* Write the filter string */
+ if (!wtap_dump_file_write(wdh, filter->data.filter_str, filter_data_len, err))
+ return FALSE;
+ break;
+
+ case if_filter_bpf:
+ if (!wtap_dump_file_write(wdh, filter->data.bpf_prog.bpf_prog, filter_data_len, err))
+ return FALSE;
+ break;
+
+ default:
+ ws_assert_not_reached();
+ return TRUE;
+ }
+
+ /* write padding (if any) */
+ if (pad != 0) {
+ if (!wtap_dump_file_write(wdh, &zero_pad, pad, err))
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static gboolean pcapng_write_custom_option(wtap_dumper *wdh, guint option_id, wtap_optval_t *optval, int *err)
+{
+ struct pcapng_option_header option_hdr;
+ gsize pad;
+ gsize size;
+ const guint32 zero_pad = 0;
+ guint32 pen, type;
+ gboolean use_little_endian;
+
+ if ((option_id == OPT_CUSTOM_STR_NO_COPY) ||
+ (option_id == OPT_CUSTOM_BIN_NO_COPY))
+ return TRUE;
+ ws_debug("PEN %d", optval->custom_opt.pen);
+ switch (optval->custom_opt.pen) {
+ case PEN_NFLX:
+ size = sizeof(guint32) + sizeof(guint32) + optval->custom_opt.data.nflx_data.custom_data_len;
+ use_little_endian = optval->custom_opt.data.nflx_data.use_little_endian;
+ break;
+ default:
+ size = sizeof(guint32) + optval->custom_opt.data.generic_data.custom_data_len;
+ use_little_endian = false;
+ break;
+ }
+ ws_debug("use_little_endian %d", use_little_endian);
+ if (size > 65535) {
+ /*
+ * Too big to fit in the option.
+ * Don't write anything.
+ *
+ * XXX - truncate it? Report an error?
+ */
+ return TRUE;
+ }
+
+ /* write option header */
+ option_hdr.type = (guint16)option_id;
+ option_hdr.value_length = (guint16)size;
+ if (use_little_endian) {
+ option_hdr.type = GUINT16_TO_LE(option_hdr.type);
+ option_hdr.value_length = GUINT16_TO_LE(option_hdr.value_length);
+ }
+ if (!wtap_dump_file_write(wdh, &option_hdr, sizeof(struct pcapng_option_header), err))
+ return FALSE;
+
+ /* write PEN */
+ pen = optval->custom_opt.pen;
+ if (use_little_endian) {
+ pen = GUINT32_TO_LE(pen);
+ }
+ if (!wtap_dump_file_write(wdh, &pen, sizeof(guint32), err))
+ return FALSE;
+
+ switch (optval->custom_opt.pen) {
+ case PEN_NFLX:
+ /* write NFLX type */
+ type = GUINT32_TO_LE(optval->custom_opt.data.nflx_data.type);
+ ws_debug("type=%d", type);
+ if (!wtap_dump_file_write(wdh, &type, sizeof(guint32), err))
+ return FALSE;
+ /* write custom data */
+ if (!wtap_dump_file_write(wdh, optval->custom_opt.data.nflx_data.custom_data, optval->custom_opt.data.nflx_data.custom_data_len, err)) {
+ return FALSE;
+ }
+ break;
+ default:
+ /* write custom data */
+ if (!wtap_dump_file_write(wdh, optval->custom_opt.data.generic_data.custom_data, optval->custom_opt.data.generic_data.custom_data_len, err)) {
+ return FALSE;
+ }
+ break;
+ }
+
+ /* write padding (if any) */
+ if (size % 4 != 0) {
+ pad = 4 - (size % 4);
+ } else {
+ pad = 0;
+ }
+ if (pad != 0) {
+ if (!wtap_dump_file_write(wdh, &zero_pad, pad, err)) {
+ return FALSE;
+ }
+ }
+ ws_debug("Wrote custom option: type %u, length %u", option_hdr.type, option_hdr.value_length);
+
+ return TRUE;
+}
+
+static gboolean pcapng_write_packet_verdict_option(wtap_dumper *wdh, guint option_id, wtap_optval_t *optval, int *err)
+{
+ packet_verdict_opt_t* verdict = &optval->packet_verdictval;
+ struct pcapng_option_header option_hdr;
+ guint8 type;
+ size_t size;
+ const guint32 zero_pad = 0;
+ guint32 pad;
+
+ switch (verdict->type) {
+
+ case packet_verdict_hardware:
+ size = verdict->data.verdict_bytes->len;
+ if (size > 65535) {
+ /*
+ * Too big to fit in the option.
+ * Don't write anything.
+ *
+ * XXX - truncate it? Report an error?
+ */
+ return TRUE;
+ }
+ option_hdr.type = option_id;
+ option_hdr.value_length = (guint16)size;
+ if (!wtap_dump_file_write(wdh, &option_hdr, 4, err))
+ return FALSE;
+
+ type = packet_verdict_hardware;
+ if (!wtap_dump_file_write(wdh, &type, sizeof(guint8), err))
+ return FALSE;
+
+ if (!wtap_dump_file_write(wdh, verdict->data.verdict_bytes->data, size,
+ err))
+ return FALSE;
+ break;
+
+ case packet_verdict_linux_ebpf_tc:
+ size = 9;
+ option_hdr.type = option_id;
+ option_hdr.value_length = (guint16)size;
+ if (!wtap_dump_file_write(wdh, &option_hdr, 4, err))
+ return FALSE;
+
+ type = packet_verdict_linux_ebpf_tc;
+ if (!wtap_dump_file_write(wdh, &type, sizeof(guint8), err))
+ return FALSE;
+
+ if (!wtap_dump_file_write(wdh, &verdict->data.verdict_linux_ebpf_tc,
+ sizeof(guint64), err))
+ return FALSE;
+ break;
+
+ case packet_verdict_linux_ebpf_xdp:
+ size = 9;
+ option_hdr.type = option_id;
+ option_hdr.value_length = (guint16)size;
+ if (!wtap_dump_file_write(wdh, &option_hdr, 4, err))
+ return FALSE;
+
+ type = packet_verdict_linux_ebpf_xdp;
+ if (!wtap_dump_file_write(wdh, &type, sizeof(guint8), err))
+ return FALSE;
+
+ if (!wtap_dump_file_write(wdh, &verdict->data.verdict_linux_ebpf_xdp,
+ sizeof(guint64), err))
+ return FALSE;
+ break;
+
+ default:
+ /* Unknown - don't write it out. */
+ return TRUE;
+ }
+
+ /* write padding (if any) */
+ if ((size % 4)) {
+ pad = 4 - (size % 4);
+ if (!wtap_dump_file_write(wdh, &zero_pad, pad, err))
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static gboolean write_block_option(wtap_block_t block, guint option_id, wtap_opttype_e option_type _U_, wtap_optval_t *optval, void* user_data)
+{
+ write_options_t* options = (write_options_t*)user_data;
+
+ /*
+ * Process the option IDs that are the same for all block types here;
+ * call the block-type-specific compute_size function for others.
+ */
+ switch(option_id)
+ {
+ case OPT_COMMENT:
+ if (!pcapng_write_string_option(options->wdh, option_id, optval, options->err))
+ return FALSE;
+ break;
+ case OPT_CUSTOM_STR_COPY:
+ case OPT_CUSTOM_BIN_COPY:
+ if (!pcapng_write_custom_option(options->wdh, option_id, optval, options->err))
+ return FALSE;
+ break;
+ case OPT_CUSTOM_STR_NO_COPY:
+ case OPT_CUSTOM_BIN_NO_COPY:
+ /*
+ * Do not write these, as they're not supposed to be copied to
+ * new files.
+ *
+ * XXX - what if we're writing out a file that's *not* based on
+ * another file, so that we're *not* copying it from that file?
+ */
+ break;
+ default:
+ /* Block-type dependent; call the callback, if we have one. */
+ if (options->write_option != NULL &&
+ !(*options->write_option)(options->wdh, block, option_id, option_type, optval, options->err))
+ return FALSE;
+ break;
+ }
+ return TRUE;
+}
+
+static gboolean
+write_options(wtap_dumper *wdh, wtap_block_t block, write_option_func write_option, int *err)
+{
+ write_options_t options;
+
+ options.wdh = wdh;
+ options.err = err;
+ options.write_option = write_option;
+ if (!wtap_block_foreach_option(block, write_block_option, &options))
+ return FALSE;
+
+ /* Write end of options */
+ return pcapng_write_option_eofopt(wdh, err);
+}
+
+static gboolean write_wtap_shb_option(wtap_dumper *wdh, wtap_block_t block _U_, guint option_id, wtap_opttype_e option_type _U_, wtap_optval_t *optval, int *err)
+{
+ switch(option_id)
+ {
+ case OPT_SHB_HARDWARE:
+ case OPT_SHB_OS:
+ case OPT_SHB_USERAPPL:
+ if (!pcapng_write_string_option(wdh, option_id, optval, err))
+ return FALSE;
+ break;
+ default:
+ /* Unknown options - write by datatype? */
+ break;
+ }
+ return TRUE; /* success */
+}
+
+/* Write a section header block.
+ * If we don't have a section block header already, create a default
+ * one with no options.
+ */
+static gboolean
+pcapng_write_section_header_block(wtap_dumper *wdh, int *err)
+{
+ pcapng_block_header_t bh;
+ pcapng_section_header_block_t shb;
+ guint32 options_size;
+ wtap_block_t wdh_shb = NULL;
+
+ if (wdh->shb_hdrs && (wdh->shb_hdrs->len > 0)) {
+ wdh_shb = g_array_index(wdh->shb_hdrs, wtap_block_t, 0);
+ }
+
+ bh.block_total_length = (guint32)(sizeof(bh) + sizeof(shb) + 4);
+ options_size = 0;
+ if (wdh_shb) {
+ ws_debug("Have shb_hdr");
+
+ /* Compute size of all the options */
+ options_size = compute_options_size(wdh_shb, compute_shb_option_size);
+
+ bh.block_total_length += options_size;
+ }
+
+ ws_debug("Total len %u", bh.block_total_length);
+
+ /* write block header */
+ bh.block_type = BLOCK_TYPE_SHB;
+
+ if (!wtap_dump_file_write(wdh, &bh, sizeof bh, err))
+ return FALSE;
+
+ /* write block fixed content */
+ shb.magic = 0x1A2B3C4D;
+ shb.version_major = 1;
+ shb.version_minor = 0;
+ if (wdh_shb) {
+ wtapng_section_mandatory_t* section_data = (wtapng_section_mandatory_t*)wtap_block_get_mandatory_data(wdh_shb);
+ shb.section_length = section_data->section_length;
+ } else {
+ shb.section_length = -1;
+ }
+
+ if (!wtap_dump_file_write(wdh, &shb, sizeof shb, err))
+ return FALSE;
+
+ if (wdh_shb) {
+ /* Write options, if we have any */
+ if (options_size != 0) {
+ if (!write_options(wdh, wdh_shb, write_wtap_shb_option, err))
+ return FALSE;
+ }
+ }
+
+ /* write block footer */
+ if (!wtap_dump_file_write(wdh, &bh.block_total_length,
+ sizeof bh.block_total_length, err))
+ return FALSE;
+
+ return TRUE;
+}
+
+/* options defined in Section 2.5 (Options)
+ * Name Code Length Description
+ * opt_comment 1 variable A UTF-8 string containing a comment that is associated to the current block.
+ *
+ * Enhanced Packet Block options
+ * epb_flags 2 4 A flags word containing link-layer information. A complete specification of
+ * the allowed flags can be found in Appendix A (Packet Block Flags Word).
+ * epb_hash 3 variable This option contains a hash of the packet. The first byte specifies the hashing algorithm,
+ * while the following bytes contain the actual hash, whose size depends on the hashing algorithm,
+ * and hence from the value in the first bit. The hashing algorithm can be: 2s complement
+ * (algorithm byte = 0, size=XXX), XOR (algorithm byte = 1, size=XXX), CRC32 (algorithm byte = 2, size = 4),
+ * MD-5 (algorithm byte = 3, size=XXX), SHA-1 (algorithm byte = 4, size=XXX).
+ * The hash covers only the packet, not the header added by the capture driver:
+ * this gives the possibility to calculate it inside the network card.
+ * The hash allows easier comparison/merging of different capture files, and reliable data transfer between the
+ * data acquisition system and the capture library.
+ * epb_dropcount 4 8 A 64bit integer value specifying the number of packets lost (by the interface and the operating system)
+ * between this packet and the preceding one.
+ * epb_packetid 5 8 The epb_packetid option is a 64-bit unsigned integer that
+ * uniquely identifies the packet. If the same packet is seen
+ * by multiple interfaces and there is a way for the capture
+ * application to correlate them, the same epb_packetid value
+ * must be used. An example could be a router that captures
+ * packets on all its interfaces in both directions. When a
+ * packet hits interface A on ingress, an EPB entry gets
+ * created, TTL gets decremented, and right before it egresses
+ * on interface B another EPB entry gets created in the trace
+ * file. In this case, two packets are in the capture file,
+ * which are not identical but the epb_packetid can be used to
+ * correlate them.
+ * epb_queue 6 4 The epb_queue option is a 32-bit unsigned integer that
+ * identifies on which queue of the interface the specific
+ * packet was received.
+ * epb_verdict 7 variable The epb_verdict option stores a verdict of the packet. The
+ * verdict indicates what would be done with the packet after
+ * processing it. For example, a firewall could drop the
+ * packet. This verdict can be set by various components, i.e.
+ * Hardware, Linux's eBPF TC or XDP framework, etc. etc. The
+ * first octet specifies the verdict type, while the following
+ * octets contain the actual verdict data, whose size depends on
+ * the verdict type, and hence from the value in the first
+ * octet. The verdict type can be: Hardware (type octet = 0,
+ * size = variable), Linux_eBPF_TC (type octet = 1, size = 8
+ * (64-bit unsigned integer), value = TC_ACT_* as defined in the
+ * Linux pck_cls.h include), Linux_eBPF_XDP (type octet = 2,
+ * size = 8 (64-bit unsigned integer), value = xdp_action as
+ * defined in the Linux pbf.h include).
+ * opt_endofopt 0 0 It delimits the end of the optional fields. This block cannot be repeated within a given list of options.
+ */
+static guint32
+compute_epb_option_size(wtap_block_t block _U_, guint option_id, wtap_opttype_e option_type _U_, wtap_optval_t* optval)
+{
+ guint32 size;
+
+ switch(option_id)
+ {
+ case OPT_EPB_FLAGS:
+ size = 4;
+ break;
+ case OPT_EPB_DROPCOUNT:
+ size = 8;
+ break;
+ case OPT_EPB_PACKETID:
+ size = 8;
+ break;
+ case OPT_EPB_QUEUE:
+ size = 4;
+ break;
+ case OPT_EPB_VERDICT:
+ size = pcapng_compute_packet_verdict_option_size(optval);
+ break;
+ case OPT_EPB_HASH:
+ size = pcapng_compute_packet_hash_option_size(optval);
+ break;
+ default:
+ /* Unknown options - size by datatype? */
+ size = 0;
+ break;
+ }
+ return size;
+}
+
+static gboolean write_wtap_epb_option(wtap_dumper *wdh, wtap_block_t block _U_, guint option_id, wtap_opttype_e option_type _U_, wtap_optval_t *optval, int *err)
+{
+ switch(option_id)
+ {
+ case OPT_PKT_FLAGS:
+ if (!pcapng_write_uint32_option(wdh, OPT_EPB_FLAGS, optval, err))
+ return FALSE;
+ break;
+ case OPT_PKT_DROPCOUNT:
+ if (!pcapng_write_uint64_option(wdh, OPT_EPB_DROPCOUNT, optval, err))
+ return FALSE;
+ break;
+ case OPT_PKT_PACKETID:
+ if (!pcapng_write_uint64_option(wdh, OPT_EPB_PACKETID, optval, err))
+ return FALSE;
+ break;
+ case OPT_PKT_QUEUE:
+ if (!pcapng_write_uint32_option(wdh, OPT_EPB_QUEUE, optval, err))
+ return FALSE;
+ break;
+ case OPT_PKT_VERDICT:
+ if (!pcapng_write_packet_verdict_option(wdh, OPT_EPB_QUEUE, optval,
+ err))
+ break;
+ default:
+ /* Unknown options - write by datatype? */
+ break;
+ }
+ return TRUE; /* success */
+}
+
+static gboolean
+pcapng_write_enhanced_packet_block(wtap_dumper *wdh, const wtap_rec *rec,
+ const guint8 *pd, int *err, gchar **err_info)
+{
+ const union wtap_pseudo_header *pseudo_header = &rec->rec_header.packet_header.pseudo_header;
+ pcapng_block_header_t bh;
+ pcapng_enhanced_packet_block_t epb;
+ guint32 options_size = 0;
+ guint64 ts;
+ const guint32 zero_pad = 0;
+ guint32 pad_len;
+ guint32 phdr_len;
+ guint32 options_total_length = 0;
+ wtap_block_t int_data;
+ wtapng_if_descr_mandatory_t *int_data_mand;
+
+ /* Don't write anything we're not willing to read. */
+ if (rec->rec_header.packet_header.caplen > wtap_max_snaplen_for_encap(wdh->file_encap)) {
+ *err = WTAP_ERR_PACKET_TOO_LARGE;
+ return FALSE;
+ }
+
+ phdr_len = (guint32)pcap_get_phdr_size(rec->rec_header.packet_header.pkt_encap, pseudo_header);
+ if ((phdr_len + rec->rec_header.packet_header.caplen) % 4) {
+ pad_len = 4 - ((phdr_len + rec->rec_header.packet_header.caplen) % 4);
+ } else {
+ pad_len = 0;
+ }
+
+ if (rec->block != NULL) {
+ /* Compute size of all the options */
+ options_size = compute_options_size(rec->block, compute_epb_option_size);
+ }
+
+ /*
+ * Check the interface ID. Do this before writing the header,
+ * in case we need to add a new IDB.
+ */
+ if (rec->presence_flags & WTAP_HAS_INTERFACE_ID)
+ epb.interface_id = rec->rec_header.packet_header.interface_id;
+ else {
+ /*
+ * The source isn't sending us IDBs. See if we already have a
+ * matching interface, and use it if so.
+ */
+ for (epb.interface_id = 0; epb.interface_id < wdh->interface_data->len; ++epb.interface_id) {
+ int_data = g_array_index(wdh->interface_data, wtap_block_t,
+ epb.interface_id);
+ int_data_mand = (wtapng_if_descr_mandatory_t*)wtap_block_get_mandatory_data(int_data);
+ if (int_data_mand->wtap_encap == rec->rec_header.packet_header.pkt_encap) {
+ if (int_data_mand->tsprecision == rec->tsprec || (!(rec->presence_flags & WTAP_HAS_TS))) {
+ break;
+ }
+ }
+ }
+ if (epb.interface_id == wdh->interface_data->len) {
+ /*
+ * We don't have a matching IDB. Generate a new one
+ * and write it to the file.
+ */
+ int_data = wtap_rec_generate_idb(rec);
+ g_array_append_val(wdh->interface_data, int_data);
+ if (!pcapng_write_if_descr_block(wdh, int_data, err)) {
+ return FALSE;
+ }
+ }
+ }
+ if (epb.interface_id >= wdh->interface_data->len) {
+ /*
+ * Our caller is doing something bad.
+ */
+ *err = WTAP_ERR_INTERNAL;
+ *err_info = ws_strdup_printf("pcapng: epb.interface_id (%u) >= wdh->interface_data->len (%u)",
+ epb.interface_id, wdh->interface_data->len);
+ return FALSE;
+ }
+ int_data = g_array_index(wdh->interface_data, wtap_block_t,
+ epb.interface_id);
+ int_data_mand = (wtapng_if_descr_mandatory_t*)wtap_block_get_mandatory_data(int_data);
+ if (int_data_mand->wtap_encap != rec->rec_header.packet_header.pkt_encap) {
+ /*
+ * Our caller is doing something bad.
+ */
+ *err = WTAP_ERR_INTERNAL;
+ *err_info = ws_strdup_printf("pcapng: interface %u encap %d != packet encap %d",
+ epb.interface_id,
+ int_data_mand->wtap_encap,
+ rec->rec_header.packet_header.pkt_encap);
+ return FALSE;
+ }
+
+ /* write (enhanced) packet block header */
+ bh.block_type = BLOCK_TYPE_EPB;
+ bh.block_total_length = (guint32)sizeof(bh) + (guint32)sizeof(epb) + phdr_len + rec->rec_header.packet_header.caplen + pad_len + options_total_length + options_size + 4;
+
+ if (!wtap_dump_file_write(wdh, &bh, sizeof bh, err))
+ return FALSE;
+
+ /* write block fixed content */
+ /* Calculate the time stamp as a 64-bit integer. */
+ ts = ((guint64)rec->ts.secs) * int_data_mand->time_units_per_second +
+ (((guint64)rec->ts.nsecs) * int_data_mand->time_units_per_second) / 1000000000;
+ /*
+ * Split the 64-bit timestamp into two 32-bit pieces, using
+ * the time stamp resolution for the interface.
+ */
+ epb.timestamp_high = (guint32)(ts >> 32);
+ epb.timestamp_low = (guint32)ts;
+ epb.captured_len = rec->rec_header.packet_header.caplen + phdr_len;
+ epb.packet_len = rec->rec_header.packet_header.len + phdr_len;
+
+ if (!wtap_dump_file_write(wdh, &epb, sizeof epb, err))
+ return FALSE;
+
+ /* write pseudo header */
+ if (!pcap_write_phdr(wdh, rec->rec_header.packet_header.pkt_encap, pseudo_header, err)) {
+ return FALSE;
+ }
+
+ /* write packet data */
+ if (!wtap_dump_file_write(wdh, pd, rec->rec_header.packet_header.caplen, err))
+ return FALSE;
+
+ /* write padding (if any) */
+ if (pad_len != 0) {
+ if (!wtap_dump_file_write(wdh, &zero_pad, pad_len, err))
+ return FALSE;
+ }
+
+ /* Write options, if we have any */
+ if (options_size != 0) {
+ if (!write_options(wdh, rec->block, write_wtap_epb_option, err))
+ return FALSE;
+ }
+
+ /* write block footer */
+ if (!wtap_dump_file_write(wdh, &bh.block_total_length,
+ sizeof bh.block_total_length, err))
+ return FALSE;
+
+ return TRUE;
+}
+
+static gboolean
+pcapng_write_sysdig_event_block(wtap_dumper *wdh, const wtap_rec *rec,
+ const guint8 *pd, int *err)
+{
+ pcapng_block_header_t bh;
+ const guint32 zero_pad = 0;
+ guint32 pad_len;
+#if 0
+ gboolean have_options = FALSE;
+ struct pcapng_option option_hdr;
+ guint32 comment_len = 0, comment_pad_len = 0;
+#endif
+ guint32 options_total_length = 0;
+ guint16 cpu_id;
+ guint64 hdr_ts;
+ guint64 ts;
+ guint64 thread_id;
+ guint32 event_len;
+ guint16 event_type;
+
+ /* Don't write anything we're not willing to read. */
+ if (rec->rec_header.syscall_header.event_filelen > WTAP_MAX_PACKET_SIZE_STANDARD) {
+ *err = WTAP_ERR_PACKET_TOO_LARGE;
+ return FALSE;
+ }
+
+ if (rec->rec_header.syscall_header.event_filelen % 4) {
+ pad_len = 4 - (rec->rec_header.syscall_header.event_filelen % 4);
+ } else {
+ pad_len = 0;
+ }
+
+#if 0
+ /* Check if we should write comment option */
+ if (rec->opt_comment) {
+ have_options = TRUE;
+ comment_len = (guint32)strlen(rec->opt_comment) & 0xffff;
+ if ((comment_len % 4)) {
+ comment_pad_len = 4 - (comment_len % 4);
+ } else {
+ comment_pad_len = 0;
+ }
+ options_total_length = options_total_length + comment_len + comment_pad_len + 4 /* comment options tag */ ;
+ }
+ if (have_options) {
+ /* End-of options tag */
+ options_total_length += 4;
+ }
+#endif
+
+ /* write sysdig event block header */
+ bh.block_type = BLOCK_TYPE_SYSDIG_EVENT;
+ bh.block_total_length = (guint32)sizeof(bh) + SYSDIG_EVENT_HEADER_SIZE + rec->rec_header.syscall_header.event_filelen + pad_len + options_total_length + 4;
+
+ if (!wtap_dump_file_write(wdh, &bh, sizeof bh, err))
+ return FALSE;
+
+ /* Sysdig is always LE? */
+ cpu_id = GUINT16_TO_LE(rec->rec_header.syscall_header.cpu_id);
+ hdr_ts = (((guint64)rec->ts.secs) * 1000000000) + rec->ts.nsecs;
+ ts = GUINT64_TO_LE(hdr_ts);
+ thread_id = GUINT64_TO_LE(rec->rec_header.syscall_header.thread_id);
+ event_len = GUINT32_TO_LE(rec->rec_header.syscall_header.event_len);
+ event_type = GUINT16_TO_LE(rec->rec_header.syscall_header.event_type);
+
+ if (!wtap_dump_file_write(wdh, &cpu_id, sizeof cpu_id, err))
+ return FALSE;
+
+ if (!wtap_dump_file_write(wdh, &ts, sizeof ts, err))
+ return FALSE;
+
+ if (!wtap_dump_file_write(wdh, &thread_id, sizeof thread_id, err))
+ return FALSE;
+
+ if (!wtap_dump_file_write(wdh, &event_len, sizeof event_len, err))
+ return FALSE;
+
+ if (!wtap_dump_file_write(wdh, &event_type, sizeof event_type, err))
+ return FALSE;
+
+ /* write event data */
+ if (!wtap_dump_file_write(wdh, pd, rec->rec_header.syscall_header.event_filelen, err))
+ return FALSE;
+
+ /* write padding (if any) */
+ if (pad_len != 0) {
+ if (!wtap_dump_file_write(wdh, &zero_pad, pad_len, err))
+ return FALSE;
+ }
+
+ /* XXX Write comment? */
+
+ /* write block footer */
+ if (!wtap_dump_file_write(wdh, &bh.block_total_length,
+ sizeof bh.block_total_length, err))
+ return FALSE;
+
+ return TRUE;
+
+}
+
+static gboolean
+pcapng_write_systemd_journal_export_block(wtap_dumper *wdh, const wtap_rec *rec,
+ const guint8 *pd, int *err)
+{
+ pcapng_block_header_t bh;
+ const guint32 zero_pad = 0;
+ guint32 pad_len;
+
+ /* Don't write anything we're not willing to read. */
+ if (rec->rec_header.systemd_journal_export_header.record_len > WTAP_MAX_PACKET_SIZE_STANDARD) {
+ *err = WTAP_ERR_PACKET_TOO_LARGE;
+ return FALSE;
+ }
+
+ if (rec->rec_header.systemd_journal_export_header.record_len % 4) {
+ pad_len = 4 - (rec->rec_header.systemd_journal_export_header.record_len % 4);
+ } else {
+ pad_len = 0;
+ }
+
+ /* write systemd journal export block header */
+ bh.block_type = BLOCK_TYPE_SYSTEMD_JOURNAL_EXPORT;
+ bh.block_total_length = (guint32)sizeof(bh) + rec->rec_header.systemd_journal_export_header.record_len + pad_len + 4;
+
+ ws_debug("writing %u bytes, %u padded",
+ rec->rec_header.systemd_journal_export_header.record_len,
+ bh.block_total_length);
+
+ if (!wtap_dump_file_write(wdh, &bh, sizeof bh, err))
+ return FALSE;
+
+ /* write entry data */
+ if (!wtap_dump_file_write(wdh, pd, rec->rec_header.systemd_journal_export_header.record_len, err))
+ return FALSE;
+
+ /* write padding (if any) */
+ if (pad_len != 0) {
+ if (!wtap_dump_file_write(wdh, &zero_pad, pad_len, err))
+ return FALSE;
+ }
+
+ /* write block footer */
+ if (!wtap_dump_file_write(wdh, &bh.block_total_length,
+ sizeof bh.block_total_length, err))
+ return FALSE;
+
+ return TRUE;
+
+}
+
+static gboolean
+pcapng_write_custom_block(wtap_dumper *wdh, const wtap_rec *rec,
+ const guint8 *pd, int *err)
+{
+ pcapng_block_header_t bh;
+ pcapng_custom_block_t cb;
+ const guint32 zero_pad = 0;
+ guint32 pad_len;
+
+ /* Don't write anything we are not supposed to. */
+ if (!rec->rec_header.custom_block_header.copy_allowed) {
+ return TRUE;
+ }
+
+ /* Don't write anything we're not willing to read. */
+ if (rec->rec_header.custom_block_header.length > WTAP_MAX_PACKET_SIZE_STANDARD) {
+ *err = WTAP_ERR_PACKET_TOO_LARGE;
+ return FALSE;
+ }
+
+ if (rec->rec_header.custom_block_header.length % 4) {
+ pad_len = 4 - (rec->rec_header.custom_block_header.length % 4);
+ } else {
+ pad_len = 0;
+ }
+
+ /* write block header */
+ bh.block_type = BLOCK_TYPE_CB_COPY;
+ bh.block_total_length = (guint32)sizeof(bh) + (guint32)sizeof(cb) + rec->rec_header.custom_block_header.length + pad_len + 4;
+ ws_debug("writing %u bytes, %u padded, PEN %u",
+ rec->rec_header.custom_block_header.length,
+ bh.block_total_length, rec->rec_header.custom_block_header.pen);
+ if (!wtap_dump_file_write(wdh, &bh, sizeof bh, err)) {
+ return FALSE;
+ }
+
+ /* write custom block header */
+ cb.pen = rec->rec_header.custom_block_header.pen;
+ if (!wtap_dump_file_write(wdh, &cb, sizeof cb, err)) {
+ return FALSE;
+ }
+ ws_debug("wrote PEN = %u", cb.pen);
+
+ /* write custom data */
+ if (!wtap_dump_file_write(wdh, pd, rec->rec_header.custom_block_header.length, err)) {
+ return FALSE;
+ }
+
+ /* write padding (if any) */
+ if (pad_len > 0) {
+ if (!wtap_dump_file_write(wdh, &zero_pad, pad_len, err)) {
+ return FALSE;
+ }
+ }
+
+ /* write block footer */
+ if (!wtap_dump_file_write(wdh, &bh.block_total_length,
+ sizeof bh.block_total_length, err)) {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+pcapng_write_bblog_block(wtap_dumper *wdh, const wtap_rec *rec,
+ const guint8 *pd _U_, int *err)
+{
+ pcapng_block_header_t bh;
+ guint32 options_size = 0;
+ guint32 pen, skipped, type;
+
+ /* Compute size of all the options */
+ options_size = compute_options_size(rec->block, compute_epb_option_size);
+
+ /* write block header */
+ bh.block_type = BLOCK_TYPE_CB_COPY;
+ bh.block_total_length = (guint32)(sizeof(bh) + sizeof(guint32) + sizeof(guint32) + options_size + 4);
+ if (rec->rec_header.custom_block_header.custom_data_header.nflx_custom_data_header.type == BBLOG_TYPE_SKIPPED_BLOCK) {
+ bh.block_total_length += (guint32)sizeof(guint32);
+ }
+ ws_debug("writing %u bytes, type %u",
+ bh.block_total_length, rec->rec_header.custom_block_header.custom_data_header.nflx_custom_data_header.type);
+ if (!wtap_dump_file_write(wdh, &bh, sizeof(bh), err)) {
+ return FALSE;
+ }
+
+ /* write PEN */
+ pen = PEN_NFLX;
+ if (!wtap_dump_file_write(wdh, &pen, sizeof(guint32), err)) {
+ return FALSE;
+ }
+ ws_debug("wrote PEN = %u", pen);
+
+ /* write type */
+ type = GUINT32_TO_LE(rec->rec_header.custom_block_header.custom_data_header.nflx_custom_data_header.type);
+ if (!wtap_dump_file_write(wdh, &type, sizeof(guint32), err)) {
+ return FALSE;
+ }
+ ws_debug("wrote type = %u", rec->rec_header.custom_block_header.custom_data_header.nflx_custom_data_header.type);
+
+ if (rec->rec_header.custom_block_header.custom_data_header.nflx_custom_data_header.type == BBLOG_TYPE_SKIPPED_BLOCK) {
+ skipped = GUINT32_TO_LE(rec->rec_header.custom_block_header.custom_data_header.nflx_custom_data_header.skipped);
+ if (!wtap_dump_file_write(wdh, &skipped, sizeof(guint32), err)) {
+ return FALSE;
+ }
+ ws_debug("wrote skipped = %u", rec->rec_header.custom_block_header.custom_data_header.nflx_custom_data_header.skipped);
+ }
+
+ /* Write options, if we have any */
+ if (options_size != 0) {
+ /*
+ * This block type supports only comments and custom options,
+ * so it doesn't need a callback.
+ */
+ if (!write_options(wdh, rec->block, NULL, err))
+ return FALSE;
+ }
+
+ /* write block footer */
+ if (!wtap_dump_file_write(wdh, &bh.block_total_length,
+ sizeof bh.block_total_length, err)) {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+pcapng_write_decryption_secrets_block(wtap_dumper *wdh, wtap_block_t sdata, int *err)
+{
+ pcapng_block_header_t bh;
+ pcapng_decryption_secrets_block_t dsb;
+ wtapng_dsb_mandatory_t *mand_data = (wtapng_dsb_mandatory_t *)wtap_block_get_mandatory_data(sdata);
+ guint pad_len = (4 - (mand_data->secrets_len & 3)) & 3;
+
+ /* write block header */
+ bh.block_type = BLOCK_TYPE_DSB;
+ bh.block_total_length = MIN_DSB_SIZE + mand_data->secrets_len + pad_len;
+ ws_debug("Total len %u", bh.block_total_length);
+
+ if (!wtap_dump_file_write(wdh, &bh, sizeof bh, err))
+ return FALSE;
+
+ /* write block fixed content */
+ dsb.secrets_type = mand_data->secrets_type;
+ dsb.secrets_len = mand_data->secrets_len;
+ if (!wtap_dump_file_write(wdh, &dsb, sizeof dsb, err))
+ return FALSE;
+
+ if (!wtap_dump_file_write(wdh, mand_data->secrets_data, mand_data->secrets_len, err))
+ return FALSE;
+ if (pad_len) {
+ const guint32 zero_pad = 0;
+ if (!wtap_dump_file_write(wdh, &zero_pad, pad_len, err))
+ return FALSE;
+ }
+
+ /* write block footer */
+ if (!wtap_dump_file_write(wdh, &bh.block_total_length,
+ sizeof bh.block_total_length, err))
+ return FALSE;
+
+ return TRUE;
+}
+
+static bool
+pcapng_write_sysdig_meta_event_block(wtap_dumper *wdh, wtap_block_t mev_data, int *err)
+{
+ pcapng_block_header_t bh;
+ wtapng_sysdig_mev_mandatory_t *mand_data = (wtapng_sysdig_mev_mandatory_t *)wtap_block_get_mandatory_data(mev_data);
+ unsigned pad_len = (4 - (mand_data->mev_data_len & 3)) & 3;
+
+ /* write block header */
+ bh.block_type = mand_data->mev_type;
+ bh.block_total_length = MIN_BLOCK_SIZE + mand_data->mev_data_len + pad_len;
+ ws_debug("Sysdig mev total len %u", bh.block_total_length);
+
+ if (!wtap_dump_file_write(wdh, &bh, sizeof bh, err))
+ return false;
+
+ /* write block fixed content */
+ if (!wtap_dump_file_write(wdh, mand_data->mev_data, mand_data->mev_data_len, err))
+ return false;
+
+ if (pad_len) {
+ const uint32_t zero_pad = 0;
+ if (!wtap_dump_file_write(wdh, &zero_pad, pad_len, err))
+ return false;
+ }
+
+ /* write block footer */
+ if (!wtap_dump_file_write(wdh, &bh.block_total_length,
+ sizeof bh.block_total_length, err))
+ return false;
+
+ return true;
+}
+
+/*
+ * libpcap's maximum pcapng block size is currently 16MB.
+ *
+ * The maximum pcapng block size in macOS's private pcapng reading code
+ * is 1MB. (Yes, this means that a program using the standard pcap
+ * code to read pcapng files can handle bigger blocks than can programs
+ * using the private code, such as Apple's tcpdump, can handle.)
+ *
+ * The pcapng reading code here can handle NRBs of arbitrary size (less
+ * than 4GB, obviously), as they read each NRB record independently,
+ * rather than reading the entire block into memory.
+ *
+ * So, for now, we set the maximum NRB block size we write as 1 MB.
+ *
+ * (Yes, for the benefit of the fussy, "MB" is really "MiB".)
+ */
+
+#define NRES_BLOCK_MAX_SIZE (1024*1024)
+
+static guint32
+compute_nrb_option_size(wtap_block_t block _U_, guint option_id, wtap_opttype_e option_type _U_, wtap_optval_t* optval)
+{
+ guint32 size;
+
+ switch(option_id)
+ {
+ case OPT_NS_DNSNAME:
+ size = pcapng_compute_string_option_size(optval);
+ break;
+ case OPT_NS_DNSIP4ADDR:
+ size = 4;
+ break;
+ case OPT_NS_DNSIP6ADDR:
+ size = 16;
+ break;
+ default:
+ /* Unknown options - size by datatype? */
+ size = 0;
+ break;
+ }
+ return size;
+}
+
+static gboolean
+put_nrb_option(wtap_block_t block _U_, guint option_id, wtap_opttype_e option_type _U_, wtap_optval_t* optval, void* user_data)
+{
+ guint8 **opt_ptrp = (guint8 **)user_data;
+ guint32 size = 0;
+ struct pcapng_option_header option_hdr;
+ guint32 pad;
+
+ switch(option_id)
+ {
+ case OPT_COMMENT:
+ case OPT_NS_DNSNAME:
+ /* String options don't consider pad bytes part of the length */
+ size = (guint32)strlen(optval->stringval) & 0xffff;
+ option_hdr.type = (guint16)option_id;
+ option_hdr.value_length = (guint16)size;
+ memcpy(*opt_ptrp, &option_hdr, 4);
+ *opt_ptrp += 4;
+
+ memcpy(*opt_ptrp, optval->stringval, size);
+ *opt_ptrp += size;
+
+ if ((size % 4)) {
+ pad = 4 - (size % 4);
+ } else {
+ pad = 0;
+ }
+
+ /* put padding (if any) */
+ if (pad != 0) {
+ memset(*opt_ptrp, 0, pad);
+ *opt_ptrp += pad;
+ }
+ break;
+ case OPT_CUSTOM_STR_COPY:
+ case OPT_CUSTOM_BIN_COPY:
+ /* Custom options don't consider pad bytes part of the length */
+ size = (guint32)(optval->custom_opt.data.generic_data.custom_data_len + sizeof(guint32)) & 0xffff;
+ option_hdr.type = (guint16)option_id;
+ option_hdr.value_length = (guint16)size;
+ memcpy(*opt_ptrp, &option_hdr, 4);
+ *opt_ptrp += 4;
+
+ memcpy(*opt_ptrp, &optval->custom_opt.pen, sizeof(guint32));
+ *opt_ptrp += sizeof(guint32);
+
+ memcpy(*opt_ptrp, optval->custom_opt.data.generic_data.custom_data, optval->custom_opt.data.generic_data.custom_data_len);
+ *opt_ptrp += optval->custom_opt.data.generic_data.custom_data_len;
+
+ if ((size % 4)) {
+ pad = 4 - (size % 4);
+ } else {
+ pad = 0;
+ }
+
+ /* put padding (if any) */
+ if (pad != 0) {
+ memset(*opt_ptrp, 0, pad);
+ *opt_ptrp += pad;
+ }
+ break;
+ case OPT_NS_DNSIP4ADDR:
+ option_hdr.type = (guint16)option_id;
+ option_hdr.value_length = 4;
+ memcpy(*opt_ptrp, &option_hdr, 4);
+ *opt_ptrp += 4;
+
+ memcpy(*opt_ptrp, &optval->ipv4val, 4);
+ *opt_ptrp += 4;
+ break;
+ case OPT_NS_DNSIP6ADDR:
+ option_hdr.type = (guint16)option_id;
+ option_hdr.value_length = 16;
+ memcpy(*opt_ptrp, &option_hdr, 4);
+ *opt_ptrp += 4;
+
+ memcpy(*opt_ptrp, &optval->ipv6val, 16);
+ *opt_ptrp += 16;
+ break;
+ default:
+ /* Unknown options - size by datatype? */
+ break;
+ }
+ return TRUE; /* we always succeed */
+}
+
+static void
+put_nrb_options(wtap_dumper *wdh _U_, wtap_block_t nrb, guint8 *opt_ptr)
+{
+ struct pcapng_option option_hdr;
+
+ wtap_block_foreach_option(nrb, put_nrb_option, &opt_ptr);
+
+ /* Put end of options */
+ option_hdr.type = OPT_EOFOPT;
+ option_hdr.value_length = 0;
+ memcpy(opt_ptr, &option_hdr, 4);
+}
+
+static gboolean
+pcapng_write_name_resolution_block(wtap_dumper *wdh, wtap_block_t sdata, int *err)
+{
+ pcapng_block_header_t bh;
+ pcapng_name_resolution_block_t nrb;
+ wtapng_nrb_mandatory_t *mand_data = (wtapng_nrb_mandatory_t *)wtap_block_get_mandatory_data(sdata);
+ guint32 options_size;
+ size_t max_rec_data_size;
+ guint8 *block_data;
+ guint32 block_off;
+ size_t hostnamelen;
+ guint16 namelen;
+ guint32 tot_rec_len;
+ hashipv4_t *ipv4_hash_list_entry;
+ hashipv6_t *ipv6_hash_list_entry;
+ int i;
+
+ if (!mand_data) {
+ /*
+ * No name/address pairs to write.
+ * XXX - what if we have options?
+ */
+ return TRUE;
+ }
+
+ /* Calculate the space needed for options. */
+ options_size = compute_options_size(sdata, compute_nrb_option_size);
+
+ /*
+ * Make sure we can fit at least one maximum-sized record, plus
+ * an end-of-records record, plus the options, into a maximum-sized
+ * block.
+ *
+ * That requires that there be enough space for the block header
+ * (8 bytes), a maximum-sized record (2 bytes of record type, 2
+ * bytes of record value length, 65535 bytes of record value,
+ * and 1 byte of padding), an end-of-records record (4 bytes),
+ * the options (options_size bytes), and the block trailer (4
+ * bytes).
+ */
+ if (8 + 2 + 2 + 65535 + 1 + 4 + options_size + 4 > NRES_BLOCK_MAX_SIZE) {
+ /*
+ * XXX - we can't even fit the options in the largest NRB size
+ * we're willing to write and still have room enough for a
+ * maximum-sized record. Just discard the information for now.
+ */
+ return TRUE;
+ }
+
+ /*
+ * Allocate a buffer for the largest block we'll write.
+ */
+ block_data = (guint8 *)g_malloc(NRES_BLOCK_MAX_SIZE);
+
+ /*
+ * Calculate the maximum amount of record data we'll be able to
+ * fit into such a block, after taking into account the block header
+ * (8 bytes), the end-of-records record (4 bytes), the options
+ * (options_size bytes), and the block trailer (4 bytes).
+ */
+ max_rec_data_size = NRES_BLOCK_MAX_SIZE - (8 + 4 + options_size + 4);
+
+ block_off = 8; /* block type + block total length */
+ bh.block_type = BLOCK_TYPE_NRB;
+ bh.block_total_length = 12; /* block header + block trailer */
+
+ /*
+ * Write out the IPv4 resolved addresses, if any.
+ */
+ if (mand_data->ipv4_addr_list){
+ i = 0;
+ ipv4_hash_list_entry = (hashipv4_t *)g_list_nth_data(mand_data->ipv4_addr_list, i);
+ while(ipv4_hash_list_entry != NULL){
+
+ nrb.record_type = NRES_IP4RECORD;
+ hostnamelen = strlen(ipv4_hash_list_entry->name);
+ if (hostnamelen > (G_MAXUINT16 - 4) - 1) {
+ /*
+ * This won't fit in the largest possible NRB record;
+ * discard it.
+ */
+ i++;
+ ipv4_hash_list_entry = (hashipv4_t *)g_list_nth_data(mand_data->ipv4_addr_list, i);
+ continue;
+ }
+ namelen = (guint16)(hostnamelen + 1);
+ nrb.record_len = 4 + namelen; /* 4 bytes IPv4 address length */
+ /* 2 bytes record type, 2 bytes length field */
+ tot_rec_len = 4 + nrb.record_len + PADDING4(nrb.record_len);
+
+ if (block_off + tot_rec_len > max_rec_data_size) {
+ /*
+ * This record would overflow our maximum size for Name
+ * Resolution Blocks; write out all the records we created
+ * before it, and start a new NRB.
+ */
+
+ /* Append the end-of-records record */
+ memset(block_data + block_off, 0, 4);
+ block_off += 4;
+ bh.block_total_length += 4;
+
+ /*
+ * Put the options into the block.
+ */
+ put_nrb_options(wdh, sdata, block_data + block_off);
+ block_off += options_size;
+ bh.block_total_length += options_size;
+
+ /* Copy the block header. */
+ memcpy(block_data, &bh, sizeof(bh));
+
+ /* Copy the block trailer. */
+ memcpy(block_data + block_off, &bh.block_total_length, sizeof(bh.block_total_length));
+
+ ws_debug("Write bh.block_total_length bytes %d, block_off %u",
+ bh.block_total_length, block_off);
+
+ if (!wtap_dump_file_write(wdh, block_data, bh.block_total_length, err)) {
+ g_free(block_data);
+ return FALSE;
+ }
+
+ /*Start a new NRB */
+ block_off = 8; /* block type + block total length */
+ bh.block_type = BLOCK_TYPE_NRB;
+ bh.block_total_length = 12; /* block header + block trailer */
+ }
+
+ bh.block_total_length += tot_rec_len;
+ memcpy(block_data + block_off, &nrb, sizeof(nrb));
+ block_off += 4;
+ memcpy(block_data + block_off, &(ipv4_hash_list_entry->addr), 4);
+ block_off += 4;
+ memcpy(block_data + block_off, ipv4_hash_list_entry->name, namelen);
+ block_off += namelen;
+ memset(block_data + block_off, 0, PADDING4(namelen));
+ block_off += PADDING4(namelen);
+ ws_debug("added IPv4 record for %s", ipv4_hash_list_entry->name);
+
+ i++;
+ ipv4_hash_list_entry = (hashipv4_t *)g_list_nth_data(mand_data->ipv4_addr_list, i);
+ }
+ }
+
+ if (mand_data->ipv6_addr_list){
+ i = 0;
+ ipv6_hash_list_entry = (hashipv6_t *)g_list_nth_data(mand_data->ipv6_addr_list, i);
+ while(ipv6_hash_list_entry != NULL){
+
+ nrb.record_type = NRES_IP6RECORD;
+ hostnamelen = strlen(ipv6_hash_list_entry->name);
+ if (hostnamelen > (G_MAXUINT16 - 16) - 1) {
+ /*
+ * This won't fit in the largest possible NRB record;
+ * discard it.
+ */
+ i++;
+ ipv6_hash_list_entry = (hashipv6_t *)g_list_nth_data(mand_data->ipv6_addr_list, i);
+ continue;
+ }
+ namelen = (guint16)(hostnamelen + 1);
+ nrb.record_len = 16 + namelen; /* 16 bytes IPv6 address length */
+ /* 2 bytes record type, 2 bytes length field */
+ tot_rec_len = 4 + nrb.record_len + PADDING4(nrb.record_len);
+
+ if (block_off + tot_rec_len > max_rec_data_size) {
+ /*
+ * This record would overflow our maximum size for Name
+ * Resolution Blocks; write out all the records we created
+ * before it, and start a new NRB.
+ */
+
+ /* Append the end-of-records record */
+ memset(block_data + block_off, 0, 4);
+ block_off += 4;
+ bh.block_total_length += 4;
+
+ /*
+ * Put the options into the block.
+ */
+ put_nrb_options(wdh, sdata, block_data + block_off);
+ block_off += options_size;
+ bh.block_total_length += options_size;
+
+ /* Copy the block header. */
+ memcpy(block_data, &bh, sizeof(bh));
+
+ /* Copy the block trailer. */
+ memcpy(block_data + block_off, &bh.block_total_length, sizeof(bh.block_total_length));
+
+ ws_debug("write bh.block_total_length bytes %d, block_off %u",
+ bh.block_total_length, block_off);
+
+ if (!wtap_dump_file_write(wdh, block_data, bh.block_total_length, err)) {
+ g_free(block_data);
+ return FALSE;
+ }
+
+ /*Start a new NRB */
+ block_off = 8; /* block type + block total length */
+ bh.block_type = BLOCK_TYPE_NRB;
+ bh.block_total_length = 12; /* block header + block trailer */
+ }
+
+ bh.block_total_length += tot_rec_len;
+ memcpy(block_data + block_off, &nrb, sizeof(nrb));
+ block_off += 4;
+ memcpy(block_data + block_off, &(ipv6_hash_list_entry->addr), 16);
+ block_off += 16;
+ memcpy(block_data + block_off, ipv6_hash_list_entry->name, namelen);
+ block_off += namelen;
+ memset(block_data + block_off, 0, PADDING4(namelen));
+ block_off += PADDING4(namelen);
+ ws_debug("added IPv6 record for %s", ipv6_hash_list_entry->name);
+
+ i++;
+ ipv6_hash_list_entry = (hashipv6_t *)g_list_nth_data(mand_data->ipv6_addr_list, i);
+ }
+ }
+
+ /* Append the end-of-records record */
+ memset(block_data + block_off, 0, 4);
+ block_off += 4;
+ bh.block_total_length += 4;
+
+ /*
+ * Put the options into the block.
+ */
+ put_nrb_options(wdh, sdata, block_data + block_off);
+ block_off += options_size;
+ bh.block_total_length += options_size;
+
+ /* Copy the block header. */
+ memcpy(block_data, &bh, sizeof(bh));
+
+ /* Copy the block trailer. */
+ memcpy(block_data + block_off, &bh.block_total_length, sizeof(bh.block_total_length));
+
+ ws_debug("Write bh.block_total_length bytes %d, block_off %u",
+ bh.block_total_length, block_off);
+
+ if (!wtap_dump_file_write(wdh, block_data, bh.block_total_length, err)) {
+ g_free(block_data);
+ return FALSE;
+ }
+
+ g_free(block_data);
+
+ return TRUE;
+}
+
+static guint32 compute_isb_option_size(wtap_block_t block _U_, guint option_id, wtap_opttype_e option_type _U_, wtap_optval_t *optval _U_)
+{
+ guint32 size;
+
+ switch(option_id)
+ {
+ case OPT_ISB_STARTTIME:
+ case OPT_ISB_ENDTIME:
+ size = 8;
+ break;
+ case OPT_ISB_IFRECV:
+ case OPT_ISB_IFDROP:
+ case OPT_ISB_FILTERACCEPT:
+ case OPT_ISB_OSDROP:
+ case OPT_ISB_USRDELIV:
+ size = 8;
+ break;
+ default:
+ /* Unknown options - size by datatype? */
+ size = 0;
+ break;
+ }
+ return size;
+}
+
+static gboolean write_wtap_isb_option(wtap_dumper *wdh, wtap_block_t block _U_, guint option_id, wtap_opttype_e option_type _U_, wtap_optval_t *optval, int *err)
+{
+ switch(option_id)
+ {
+ case OPT_ISB_STARTTIME:
+ case OPT_ISB_ENDTIME:
+ if (!pcapng_write_timestamp_option(wdh, option_id, optval, err))
+ return FALSE;
+ break;
+ case OPT_ISB_IFRECV:
+ case OPT_ISB_IFDROP:
+ case OPT_ISB_FILTERACCEPT:
+ case OPT_ISB_OSDROP:
+ case OPT_ISB_USRDELIV:
+ if (!pcapng_write_uint64_option(wdh, option_id, optval, err))
+ return FALSE;
+ break;
+ default:
+ /* Unknown options - write by datatype? */
+ break;
+ }
+ return TRUE; /* success */
+}
+
+static gboolean
+pcapng_write_interface_statistics_block(wtap_dumper *wdh, wtap_block_t if_stats, int *err)
+{
+ pcapng_block_header_t bh;
+ pcapng_interface_statistics_block_t isb;
+ guint32 options_size;
+ wtapng_if_stats_mandatory_t* mand_data = (wtapng_if_stats_mandatory_t*)wtap_block_get_mandatory_data(if_stats);
+
+ ws_debug("entering function");
+
+ /* Compute size of all the options */
+ options_size = compute_options_size(if_stats, compute_isb_option_size);
+
+ /* write block header */
+ bh.block_type = BLOCK_TYPE_ISB;
+ bh.block_total_length = (guint32)(sizeof(bh) + sizeof(isb) + options_size + 4);
+ ws_debug("Total len %u", bh.block_total_length);
+
+ if (!wtap_dump_file_write(wdh, &bh, sizeof bh, err))
+ return FALSE;
+
+ /* write block fixed content */
+ isb.interface_id = mand_data->interface_id;
+ isb.timestamp_high = mand_data->ts_high;
+ isb.timestamp_low = mand_data->ts_low;
+
+ if (!wtap_dump_file_write(wdh, &isb, sizeof isb, err))
+ return FALSE;
+
+ /* Write options */
+ if (options_size != 0) {
+ if (!write_options(wdh, if_stats, write_wtap_isb_option, err))
+ return FALSE;
+ }
+
+ /* write block footer */
+ if (!wtap_dump_file_write(wdh, &bh.block_total_length,
+ sizeof bh.block_total_length, err))
+ return FALSE;
+ return TRUE;
+}
+
+static guint32 compute_idb_option_size(wtap_block_t block _U_, guint option_id, wtap_opttype_e option_type _U_, wtap_optval_t *optval)
+{
+ guint32 size;
+
+ switch(option_id)
+ {
+ case OPT_IDB_NAME:
+ case OPT_IDB_DESCRIPTION:
+ case OPT_IDB_OS:
+ case OPT_IDB_HARDWARE:
+ size = pcapng_compute_string_option_size(optval);
+ break;
+ case OPT_IDB_SPEED:
+ size = 8;
+ break;
+ case OPT_IDB_TSRESOL:
+ size = 1;
+ break;
+ case OPT_IDB_FILTER:
+ size = pcapng_compute_if_filter_option_size(optval);
+ break;
+ case OPT_IDB_FCSLEN:
+ size = 1;
+ break;
+ case OPT_IDB_TSOFFSET:
+ /*
+ * The time stamps handed to us when writing a file are
+ * absolute time staps, so the time stamp offset is
+ * zero.
+ *
+ * We do not adjust them when writing, so we should not
+ * write if_tsoffset options; that is interpreted as
+ * the offset is zero, i.e. the time stamps in the file
+ * are absolute.
+ */
+ size = 0;
+ break;
+ default:
+ /* Unknown options - size by datatype? */
+ size = 0;
+ break;
+ }
+ return size;
+}
+
+static gboolean write_wtap_idb_option(wtap_dumper *wdh, wtap_block_t block _U_, guint option_id, wtap_opttype_e option_type _U_, wtap_optval_t *optval, int *err)
+{
+ switch(option_id)
+ {
+ case OPT_IDB_NAME:
+ case OPT_IDB_DESCRIPTION:
+ case OPT_IDB_OS:
+ case OPT_IDB_HARDWARE:
+ if (!pcapng_write_string_option(wdh, option_id, optval, err))
+ return FALSE;
+ break;
+ case OPT_IDB_SPEED:
+ if (!pcapng_write_uint64_option(wdh, option_id, optval, err))
+ return FALSE;
+ break;
+ case OPT_IDB_TSRESOL:
+ if (!pcapng_write_uint8_option(wdh, option_id, optval, err))
+ return FALSE;
+ break;
+ case OPT_IDB_FILTER:
+ if (!pcapng_write_if_filter_option(wdh, option_id, optval, err))
+ return FALSE;
+ break;
+ case OPT_IDB_FCSLEN:
+ if (!pcapng_write_uint8_option(wdh, option_id, optval, err))
+ return FALSE;
+ break;
+ case OPT_IDB_TSOFFSET:
+ /*
+ * As noted above, we discard these.
+ */
+ break;
+ default:
+ /* Unknown options - size by datatype? */
+ break;
+ }
+ return TRUE;
+}
+
+static gboolean
+pcapng_write_if_descr_block(wtap_dumper *wdh, wtap_block_t int_data, int *err)
+{
+ pcapng_block_header_t bh;
+ pcapng_interface_description_block_t idb;
+ guint32 options_size;
+ wtapng_if_descr_mandatory_t* mand_data = (wtapng_if_descr_mandatory_t*)wtap_block_get_mandatory_data(int_data);
+ int link_type;
+
+ ws_debug("encap = %d (%s), snaplen = %d",
+ mand_data->wtap_encap,
+ wtap_encap_description(mand_data->wtap_encap),
+ mand_data->snap_len);
+
+ link_type = wtap_wtap_encap_to_pcap_encap(mand_data->wtap_encap);
+ if (link_type == -1) {
+ if (!pcapng_encap_is_ft_specific(mand_data->wtap_encap)) {
+ *err = WTAP_ERR_UNWRITABLE_ENCAP;
+ return FALSE;
+ }
+ }
+
+ /* Compute size of all the options */
+ options_size = compute_options_size(int_data, compute_idb_option_size);
+
+ /* write block header */
+ bh.block_type = BLOCK_TYPE_IDB;
+ bh.block_total_length = (guint32)(sizeof(bh) + sizeof(idb) + options_size + 4);
+ ws_debug("Total len %u", bh.block_total_length);
+
+ if (!wtap_dump_file_write(wdh, &bh, sizeof bh, err))
+ return FALSE;
+
+ /* write block fixed content */
+ idb.linktype = link_type;
+ idb.reserved = 0;
+ idb.snaplen = mand_data->snap_len;
+
+ if (!wtap_dump_file_write(wdh, &idb, sizeof idb, err))
+ return FALSE;
+
+ if (options_size != 0) {
+ /* Write options */
+ if (!write_options(wdh, int_data, write_wtap_idb_option, err))
+ return FALSE;
+ }
+
+ /* write block footer */
+ if (!wtap_dump_file_write(wdh, &bh.block_total_length,
+ sizeof bh.block_total_length, err))
+ return FALSE;
+
+ return TRUE;
+}
+
+static gboolean pcapng_add_idb(wtap_dumper *wdh, wtap_block_t idb,
+ int *err, gchar **err_info _U_)
+{
+ wtap_block_t idb_copy;
+
+ /*
+ * Add a copy of this IDB to our array of IDBs.
+ */
+ idb_copy = wtap_block_create(WTAP_BLOCK_IF_ID_AND_INFO);
+ wtap_block_copy(idb_copy, idb);
+ g_array_append_val(wdh->interface_data, idb_copy);
+
+ /*
+ * And write it to the output file.
+ */
+ return pcapng_write_if_descr_block(wdh, idb_copy, err);
+}
+
+static gboolean pcapng_write_internal_blocks(wtap_dumper *wdh, int *err)
+{
+
+ /* Write (optional) Decryption Secrets Blocks that were collected while
+ * reading packet blocks. */
+ if (wdh->dsbs_growing) {
+ for (guint i = wdh->dsbs_growing_written; i < wdh->dsbs_growing->len; i++) {
+ ws_debug("writing DSB %u", i);
+ wtap_block_t dsb = g_array_index(wdh->dsbs_growing, wtap_block_t, i);
+ if (!pcapng_write_decryption_secrets_block(wdh, dsb, err)) {
+ return FALSE;
+ }
+ ++wdh->dsbs_growing_written;
+ }
+ }
+
+ /* Write (optional) Sysdig Meta Event Blocks that were collected while
+ * reading packet blocks. */
+ if (wdh->sysdig_mev_growing) {
+ for (unsigned i = wdh->sysdig_mev_growing_written; i < wdh->sysdig_mev_growing->len; i++) {
+ ws_debug("writing Sysdig mev %u", i);
+ wtap_block_t mev = g_array_index(wdh->sysdig_mev_growing, wtap_block_t, i);
+ if (!pcapng_write_sysdig_meta_event_block(wdh, mev, err)) {
+ return false;
+ }
+ ++wdh->sysdig_mev_growing_written;
+ }
+ }
+
+ /* Write any hostname resolution info from wtap_dump_set_addrinfo_list() */
+ if (!wtap_addrinfo_list_empty(wdh->addrinfo_lists)) {
+ /*
+ * XXX: get_addrinfo_list() returns a list of all known and used
+ * resolved addresses, regardless of origin: existing NRBs, externally
+ * resolved, DNS packet data, a hosts file, and manual host resolution
+ * through the GUI. It does not include the source for each.
+ *
+ * If it did, we could instead create multiple NRBs, one for each
+ * server (as the options can only be included once per block.)
+ * Instead, we copy the options from the first already existing NRB
+ * (if there is one), since some of the name resolutions may be
+ * from that block.
+ */
+ wtap_block_t nrb;
+ if (wdh->nrbs_growing && wdh->nrbs_growing->len) {
+ nrb = wtap_block_make_copy(g_array_index(wdh->nrbs_growing, wtap_block_t, 0));
+ } else {
+ nrb = wtap_block_create(WTAP_BLOCK_NAME_RESOLUTION);
+ }
+ wtapng_nrb_mandatory_t *mand_data = (wtapng_nrb_mandatory_t *)wtap_block_get_mandatory_data(nrb);
+ mand_data->ipv4_addr_list = wdh->addrinfo_lists->ipv4_addr_list;
+ mand_data->ipv6_addr_list = wdh->addrinfo_lists->ipv6_addr_list;
+
+ if (!pcapng_write_name_resolution_block(wdh, nrb, err)) {
+ return FALSE;
+ }
+ mand_data->ipv4_addr_list = NULL;
+ mand_data->ipv6_addr_list = NULL;
+ wtap_block_unref(nrb);
+ g_list_free(wdh->addrinfo_lists->ipv4_addr_list);
+ wdh->addrinfo_lists->ipv4_addr_list = NULL;
+ g_list_free(wdh->addrinfo_lists->ipv6_addr_list);
+ wdh->addrinfo_lists->ipv6_addr_list = NULL;
+ /* Since the addrinfo lists include information from existing NRBs,
+ * avoid writing them to avoid duplication.
+ *
+ * XXX: Perhaps we don't want to include information from the NRBs
+ * in get_addrinfo_list at all, so that we could write existing
+ * NRBs as-is.
+ *
+ * This is still not well oriented for one-pass programs, where we
+ * don't have addrinfo_lists until we've already written the
+ * NRBs. We should not write both in such a situation. See bug 15502.
+ */
+ wtap_dump_discard_name_resolution(wdh);
+ }
+
+ /* Write (optional) Name Resolution Blocks that were collected while
+ * reading packet blocks. */
+ if (wdh->nrbs_growing) {
+ for (guint i = wdh->nrbs_growing_written; i < wdh->nrbs_growing->len; i++) {
+ wtap_block_t nrb = g_array_index(wdh->nrbs_growing, wtap_block_t, i);
+ if (!pcapng_write_name_resolution_block(wdh, nrb, err)) {
+ return FALSE;
+ }
+ ++wdh->nrbs_growing_written;
+ }
+ }
+
+ return TRUE;
+}
+
+static gboolean pcapng_dump(wtap_dumper *wdh,
+ const wtap_rec *rec,
+ const guint8 *pd, int *err, gchar **err_info)
+{
+#ifdef HAVE_PLUGINS
+ block_handler *handler;
+#endif
+
+ if (!pcapng_write_internal_blocks(wdh, err)) {
+ return FALSE;
+ }
+
+ ws_debug("encap = %d (%s) rec type = %u",
+ rec->rec_header.packet_header.pkt_encap,
+ wtap_encap_description(rec->rec_header.packet_header.pkt_encap),
+ rec->rec_type);
+
+ switch (rec->rec_type) {
+
+ case REC_TYPE_PACKET:
+ /*
+ * XXX - write a Simple Packet Block if there's no time
+ * stamp or other information that doesn't appear in an
+ * SPB?
+ */
+ if (!pcapng_write_enhanced_packet_block(wdh, rec, pd, err,
+ err_info)) {
+ return FALSE;
+ }
+ break;
+
+ case REC_TYPE_FT_SPECIFIC_EVENT:
+ case REC_TYPE_FT_SPECIFIC_REPORT:
+#ifdef HAVE_PLUGINS
+ /*
+ * Do we have a handler for this block type?
+ */
+ if (block_handlers != NULL &&
+ (handler = (block_handler *)g_hash_table_lookup(block_handlers,
+ GUINT_TO_POINTER(rec->rec_header.ft_specific_header.record_type))) != NULL) {
+ /* Yes. Call it to write out this record. */
+ if (!handler->writer(wdh, rec, pd, err))
+ return FALSE;
+ } else
+#endif
+ {
+ /* No. */
+ *err = WTAP_ERR_UNWRITABLE_REC_TYPE;
+ return FALSE;
+ }
+ break;
+
+ case REC_TYPE_SYSCALL:
+ if (!pcapng_write_sysdig_event_block(wdh, rec, pd, err)) {
+ return FALSE;
+ }
+ break;
+
+ case REC_TYPE_SYSTEMD_JOURNAL_EXPORT:
+ if (!pcapng_write_systemd_journal_export_block(wdh, rec, pd, err)) {
+ return FALSE;
+ }
+ break;
+
+ case REC_TYPE_CUSTOM_BLOCK:
+ switch (rec->rec_header.custom_block_header.pen) {
+ case PEN_NFLX:
+ if (!pcapng_write_bblog_block(wdh, rec, pd, err)) {
+ return FALSE;
+ }
+ break;
+ default:
+ if (!pcapng_write_custom_block(wdh, rec, pd, err)) {
+ return FALSE;
+ }
+ break;
+ }
+ break;
+
+ default:
+ /* We don't support writing this record type. */
+ *err = WTAP_ERR_UNWRITABLE_REC_TYPE;
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/* Finish writing to a dump file.
+ Returns TRUE on success, FALSE on failure. */
+static gboolean pcapng_dump_finish(wtap_dumper *wdh, int *err,
+ gchar **err_info _U_)
+{
+ guint i, j;
+
+ /* Flush any hostname resolution or decryption secrets info we may have */
+ if (!pcapng_write_internal_blocks(wdh, err)) {
+ return FALSE;
+ }
+
+ for (i = 0; i < wdh->interface_data->len; i++) {
+
+ /* Get the interface description */
+ wtap_block_t int_data;
+ wtapng_if_descr_mandatory_t *int_data_mand;
+
+ int_data = g_array_index(wdh->interface_data, wtap_block_t, i);
+ int_data_mand = (wtapng_if_descr_mandatory_t*)wtap_block_get_mandatory_data(int_data);
+
+ for (j = 0; j < int_data_mand->num_stat_entries; j++) {
+ wtap_block_t if_stats;
+
+ if_stats = g_array_index(int_data_mand->interface_statistics, wtap_block_t, j);
+ ws_debug("write ISB for interface %u",
+ ((wtapng_if_stats_mandatory_t*)wtap_block_get_mandatory_data(if_stats))->interface_id);
+ if (!pcapng_write_interface_statistics_block(wdh, if_stats, err)) {
+ return FALSE;
+ }
+ }
+ }
+
+ ws_debug("leaving function");
+ return TRUE;
+}
+
+/* Returns TRUE on success, FALSE on failure; sets "*err" to an error code on
+ failure */
+static gboolean
+pcapng_dump_open(wtap_dumper *wdh, int *err, gchar **err_info _U_)
+{
+ guint i;
+
+ ws_debug("entering function");
+ /* This is a pcapng file */
+ wdh->subtype_add_idb = pcapng_add_idb;
+ wdh->subtype_write = pcapng_dump;
+ wdh->subtype_finish = pcapng_dump_finish;
+
+ /* write the section header block */
+ if (!pcapng_write_section_header_block(wdh, err)) {
+ return FALSE;
+ }
+ ws_debug("wrote section header block.");
+
+ /* Write the Interface description blocks */
+ ws_debug("Number of IDBs to write (number of interfaces) %u",
+ wdh->interface_data->len);
+
+ for (i = 0; i < wdh->interface_data->len; i++) {
+
+ /* Get the interface description */
+ wtap_block_t idb;
+
+ idb = g_array_index(wdh->interface_data, wtap_block_t, i);
+
+ if (!pcapng_write_if_descr_block(wdh, idb, err)) {
+ return FALSE;
+ }
+
+ }
+
+ /* Write (optional) fixed Decryption Secrets Blocks. */
+ if (wdh->dsbs_initial) {
+ for (i = 0; i < wdh->dsbs_initial->len; i++) {
+ wtap_block_t dsb = g_array_index(wdh->dsbs_initial, wtap_block_t, i);
+ if (!pcapng_write_decryption_secrets_block(wdh, dsb, err)) {
+ return FALSE;
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+/* Returns 0 if we could write the specified encapsulation type,
+ an error indication otherwise. */
+static int pcapng_dump_can_write_encap(int wtap_encap)
+{
+ ws_debug("encap = %d (%s)",
+ wtap_encap,
+ wtap_encap_description(wtap_encap));
+
+ /* Per-packet encapsulation is supported. */
+ if (wtap_encap == WTAP_ENCAP_PER_PACKET)
+ return 0;
+
+ /* No encapsulation type (yet) is supported. */
+ if (wtap_encap == WTAP_ENCAP_NONE)
+ return 0;
+
+ /* Is it a filetype-specific encapsulation that we support? */
+ if (pcapng_encap_is_ft_specific(wtap_encap)) {
+ return 0;
+ }
+
+ /* Make sure we can figure out this DLT type */
+ if (wtap_wtap_encap_to_pcap_encap(wtap_encap) == -1)
+ return WTAP_ERR_UNWRITABLE_ENCAP;
+
+ return 0;
+}
+
+/*
+ * Returns TRUE if the specified encapsulation type is filetype-specific
+ * and one that we support.
+ */
+gboolean pcapng_encap_is_ft_specific(int encap)
+{
+ switch (encap) {
+ case WTAP_ENCAP_SYSTEMD_JOURNAL:
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/*
+ * pcapng supports several block types, and supports more than one
+ * of them.
+ *
+ * It also supports comments for many block types, as well as other
+ * option types.
+ */
+
+/* Options for section blocks. */
+static const struct supported_option_type section_block_options_supported[] = {
+ { OPT_COMMENT, MULTIPLE_OPTIONS_SUPPORTED },
+ { OPT_CUSTOM_STR_COPY, MULTIPLE_OPTIONS_SUPPORTED },
+ { OPT_CUSTOM_BIN_COPY, MULTIPLE_OPTIONS_SUPPORTED },
+ { OPT_CUSTOM_STR_NO_COPY, MULTIPLE_OPTIONS_SUPPORTED },
+ { OPT_CUSTOM_BIN_NO_COPY, MULTIPLE_OPTIONS_SUPPORTED },
+ { OPT_SHB_HARDWARE, ONE_OPTION_SUPPORTED },
+ { OPT_SHB_USERAPPL, ONE_OPTION_SUPPORTED }
+};
+
+/* Options for interface blocks. */
+static const struct supported_option_type interface_block_options_supported[] = {
+ { OPT_COMMENT, MULTIPLE_OPTIONS_SUPPORTED },
+ { OPT_CUSTOM_STR_COPY, MULTIPLE_OPTIONS_SUPPORTED },
+ { OPT_CUSTOM_BIN_COPY, MULTIPLE_OPTIONS_SUPPORTED },
+ { OPT_CUSTOM_STR_NO_COPY, MULTIPLE_OPTIONS_SUPPORTED },
+ { OPT_CUSTOM_BIN_NO_COPY, MULTIPLE_OPTIONS_SUPPORTED },
+ { OPT_IDB_NAME, ONE_OPTION_SUPPORTED },
+ { OPT_IDB_DESCRIPTION, ONE_OPTION_SUPPORTED },
+ { OPT_IDB_IP4ADDR, MULTIPLE_OPTIONS_SUPPORTED },
+ { OPT_IDB_IP6ADDR, MULTIPLE_OPTIONS_SUPPORTED },
+ { OPT_IDB_MACADDR, ONE_OPTION_SUPPORTED },
+ { OPT_IDB_EUIADDR, ONE_OPTION_SUPPORTED },
+ { OPT_IDB_SPEED, ONE_OPTION_SUPPORTED },
+ { OPT_IDB_TSRESOL, ONE_OPTION_SUPPORTED },
+ { OPT_IDB_TZONE, ONE_OPTION_SUPPORTED },
+ { OPT_IDB_FILTER, ONE_OPTION_SUPPORTED },
+ { OPT_IDB_OS, ONE_OPTION_SUPPORTED },
+ { OPT_IDB_FCSLEN, ONE_OPTION_SUPPORTED },
+ { OPT_IDB_TSOFFSET, ONE_OPTION_SUPPORTED },
+ { OPT_IDB_HARDWARE, ONE_OPTION_SUPPORTED }
+};
+
+/* Options for name resolution blocks. */
+static const struct supported_option_type name_resolution_block_options_supported[] = {
+ { OPT_COMMENT, MULTIPLE_OPTIONS_SUPPORTED },
+ { OPT_CUSTOM_STR_COPY, MULTIPLE_OPTIONS_SUPPORTED },
+ { OPT_CUSTOM_BIN_COPY, MULTIPLE_OPTIONS_SUPPORTED },
+ { OPT_CUSTOM_STR_NO_COPY, MULTIPLE_OPTIONS_SUPPORTED },
+ { OPT_CUSTOM_BIN_NO_COPY, MULTIPLE_OPTIONS_SUPPORTED },
+ { OPT_NS_DNSNAME, ONE_OPTION_SUPPORTED },
+ { OPT_NS_DNSIP4ADDR, ONE_OPTION_SUPPORTED },
+ { OPT_NS_DNSIP6ADDR, ONE_OPTION_SUPPORTED }
+};
+
+/* Options for interface statistics blocks. */
+static const struct supported_option_type interface_statistics_block_options_supported[] = {
+ { OPT_COMMENT, MULTIPLE_OPTIONS_SUPPORTED },
+ { OPT_CUSTOM_STR_COPY, MULTIPLE_OPTIONS_SUPPORTED },
+ { OPT_CUSTOM_BIN_COPY, MULTIPLE_OPTIONS_SUPPORTED },
+ { OPT_CUSTOM_STR_NO_COPY, MULTIPLE_OPTIONS_SUPPORTED },
+ { OPT_CUSTOM_BIN_NO_COPY, MULTIPLE_OPTIONS_SUPPORTED },
+ { OPT_ISB_STARTTIME, ONE_OPTION_SUPPORTED },
+ { OPT_ISB_ENDTIME, ONE_OPTION_SUPPORTED },
+ { OPT_ISB_IFRECV, ONE_OPTION_SUPPORTED },
+ { OPT_ISB_IFDROP, ONE_OPTION_SUPPORTED },
+ { OPT_ISB_FILTERACCEPT, ONE_OPTION_SUPPORTED },
+ { OPT_ISB_OSDROP, ONE_OPTION_SUPPORTED },
+ { OPT_ISB_USRDELIV, ONE_OPTION_SUPPORTED }
+};
+
+/* Options for decryption secrets blocks. */
+static const struct supported_option_type decryption_secrets_block_options_supported[] = {
+ { OPT_COMMENT, MULTIPLE_OPTIONS_SUPPORTED },
+ { OPT_CUSTOM_STR_COPY, MULTIPLE_OPTIONS_SUPPORTED },
+ { OPT_CUSTOM_BIN_COPY, MULTIPLE_OPTIONS_SUPPORTED },
+ { OPT_CUSTOM_STR_NO_COPY, MULTIPLE_OPTIONS_SUPPORTED },
+ { OPT_CUSTOM_BIN_NO_COPY, MULTIPLE_OPTIONS_SUPPORTED }
+};
+
+/* Options for Sysdig meta event blocks. */
+static const struct supported_option_type sysdig_meta_events_block_options_supported[] = {
+ { OPT_COMMENT, MULTIPLE_OPTIONS_SUPPORTED },
+ { OPT_CUSTOM_STR_COPY, MULTIPLE_OPTIONS_SUPPORTED },
+ { OPT_CUSTOM_BIN_COPY, MULTIPLE_OPTIONS_SUPPORTED },
+ { OPT_CUSTOM_STR_NO_COPY, MULTIPLE_OPTIONS_SUPPORTED },
+ { OPT_CUSTOM_BIN_NO_COPY, MULTIPLE_OPTIONS_SUPPORTED }
+};
+
+/* Options for packet blocks. */
+static const struct supported_option_type packet_block_options_supported[] = {
+ { OPT_COMMENT, MULTIPLE_OPTIONS_SUPPORTED },
+ { OPT_PKT_FLAGS, ONE_OPTION_SUPPORTED },
+ { OPT_PKT_DROPCOUNT, ONE_OPTION_SUPPORTED },
+ { OPT_PKT_PACKETID, ONE_OPTION_SUPPORTED },
+ { OPT_PKT_QUEUE, ONE_OPTION_SUPPORTED },
+ { OPT_PKT_HASH, MULTIPLE_OPTIONS_SUPPORTED },
+ { OPT_PKT_VERDICT, MULTIPLE_OPTIONS_SUPPORTED },
+ { OPT_CUSTOM_STR_COPY, MULTIPLE_OPTIONS_SUPPORTED },
+ { OPT_CUSTOM_BIN_COPY, MULTIPLE_OPTIONS_SUPPORTED },
+ { OPT_CUSTOM_STR_NO_COPY, MULTIPLE_OPTIONS_SUPPORTED },
+ { OPT_CUSTOM_BIN_NO_COPY, MULTIPLE_OPTIONS_SUPPORTED }
+};
+
+/* Options for file-type-sepcific reports. */
+static const struct supported_option_type ft_specific_report_block_options_supported[] = {
+ { OPT_COMMENT, MULTIPLE_OPTIONS_SUPPORTED },
+ { OPT_CUSTOM_STR_COPY, MULTIPLE_OPTIONS_SUPPORTED },
+ { OPT_CUSTOM_BIN_COPY, MULTIPLE_OPTIONS_SUPPORTED },
+ { OPT_CUSTOM_STR_NO_COPY, MULTIPLE_OPTIONS_SUPPORTED },
+ { OPT_CUSTOM_BIN_NO_COPY, MULTIPLE_OPTIONS_SUPPORTED }
+};
+
+/* Options for file-type-sepcific event. */
+static const struct supported_option_type ft_specific_event_block_options_supported[] = {
+ { OPT_COMMENT, MULTIPLE_OPTIONS_SUPPORTED },
+ { OPT_CUSTOM_STR_COPY, MULTIPLE_OPTIONS_SUPPORTED },
+ { OPT_CUSTOM_BIN_COPY, MULTIPLE_OPTIONS_SUPPORTED },
+ { OPT_CUSTOM_STR_NO_COPY, MULTIPLE_OPTIONS_SUPPORTED },
+ { OPT_CUSTOM_BIN_NO_COPY, MULTIPLE_OPTIONS_SUPPORTED }
+};
+
+/* Options for systemd journal entry. */
+static const struct supported_option_type systemd_journal_export_block_options_supported[] = {
+ { OPT_COMMENT, MULTIPLE_OPTIONS_SUPPORTED },
+ { OPT_CUSTOM_STR_COPY, MULTIPLE_OPTIONS_SUPPORTED },
+ { OPT_CUSTOM_BIN_COPY, MULTIPLE_OPTIONS_SUPPORTED },
+ { OPT_CUSTOM_STR_NO_COPY, MULTIPLE_OPTIONS_SUPPORTED },
+ { OPT_CUSTOM_BIN_NO_COPY, MULTIPLE_OPTIONS_SUPPORTED }
+};
+
+static const struct supported_block_type pcapng_blocks_supported[] = {
+ /* Multiple sections. */
+ { WTAP_BLOCK_SECTION, MULTIPLE_BLOCKS_SUPPORTED, OPTION_TYPES_SUPPORTED(section_block_options_supported) },
+
+ /* Multiple interfaces. */
+ { WTAP_BLOCK_IF_ID_AND_INFO, MULTIPLE_BLOCKS_SUPPORTED, OPTION_TYPES_SUPPORTED(interface_block_options_supported) },
+
+ /* Multiple blocks of name resolution information */
+ { WTAP_BLOCK_NAME_RESOLUTION, MULTIPLE_BLOCKS_SUPPORTED, OPTION_TYPES_SUPPORTED(name_resolution_block_options_supported) },
+
+ /* Multiple blocks of interface statistics. */
+ { WTAP_BLOCK_IF_STATISTICS, MULTIPLE_BLOCKS_SUPPORTED, OPTION_TYPES_SUPPORTED(interface_statistics_block_options_supported) },
+
+ /* Multiple blocks of decryption secrets. */
+ { WTAP_BLOCK_DECRYPTION_SECRETS, MULTIPLE_BLOCKS_SUPPORTED, OPTION_TYPES_SUPPORTED(decryption_secrets_block_options_supported) },
+
+ /* Multiple blocks of decryption secrets. */
+ { WTAP_BLOCK_SYSDIG_META_EVENT, MULTIPLE_BLOCKS_SUPPORTED, OPTION_TYPES_SUPPORTED(sysdig_meta_events_block_options_supported) },
+
+ /* And, obviously, multiple packets. */
+ { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, OPTION_TYPES_SUPPORTED(packet_block_options_supported) },
+
+ /* Multiple file-type specific reports (including local ones). */
+ { WTAP_BLOCK_FT_SPECIFIC_REPORT, MULTIPLE_BLOCKS_SUPPORTED, OPTION_TYPES_SUPPORTED(ft_specific_report_block_options_supported) },
+
+ /* Multiple file-type specific events (including local ones). */
+ { WTAP_BLOCK_FT_SPECIFIC_EVENT, MULTIPLE_BLOCKS_SUPPORTED, OPTION_TYPES_SUPPORTED(ft_specific_event_block_options_supported) },
+
+ /* Multiple systemd journal export records. */
+ { WTAP_BLOCK_SYSTEMD_JOURNAL_EXPORT, MULTIPLE_BLOCKS_SUPPORTED, OPTION_TYPES_SUPPORTED(systemd_journal_export_block_options_supported) },
+
+ /* Multiple custom blocks. */
+ { WTAP_BLOCK_CUSTOM, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED },
+};
+
+static const struct file_type_subtype_info pcapng_info = {
+ "Wireshark/... - pcapng", "pcapng", "pcapng", "ntar",
+ FALSE, BLOCKS_SUPPORTED(pcapng_blocks_supported),
+ pcapng_dump_can_write_encap, pcapng_dump_open, NULL
+};
+
+void register_pcapng(void)
+{
+ pcapng_file_type_subtype = wtap_register_file_type_subtype(&pcapng_info);
+
+ wtap_register_backwards_compatibility_lua_name("PCAPNG",
+ pcapng_file_type_subtype);
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * vi: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */
diff --git a/wiretap/pcapng.h b/wiretap/pcapng.h
new file mode 100644
index 00000000..3e5735d9
--- /dev/null
+++ b/wiretap/pcapng.h
@@ -0,0 +1,77 @@
+/** @file
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __W_PCAPNG_H__
+#define __W_PCAPNG_H__
+
+#include <glib.h>
+#include "wtap.h"
+#include "ws_symbol_export.h"
+
+#define PCAPNG_MAGIC 0x1A2B3C4D
+#define PCAPNG_SWAPPED_MAGIC 0x4D3C2B1A
+
+#define PCAPNG_MAJOR_VERSION 1
+#define PCAPNG_MINOR_VERSION 0
+
+/* pcapng: common block header file encoding for every block type */
+typedef struct pcapng_block_header_s {
+ guint32 block_type;
+ guint32 block_total_length;
+ /* x bytes block_body */
+ /* guint32 block_total_length */
+} pcapng_block_header_t;
+
+/* pcapng: section header block file encoding */
+typedef struct pcapng_section_header_block_s {
+ /* pcapng_block_header_t */
+ guint32 magic;
+ guint16 version_major;
+ guint16 version_minor;
+ guint64 section_length; /* might be -1 for unknown */
+ /* ... Options ... */
+} pcapng_section_header_block_t;
+
+/* pcapng: interface description block file encoding */
+typedef struct pcapng_interface_description_block_s {
+ guint16 linktype;
+ guint16 reserved;
+ guint32 snaplen;
+ /* ... Options ... */
+} pcapng_interface_description_block_t;
+
+/* pcapng: interface statistics block file encoding */
+typedef struct pcapng_interface_statistics_block_s {
+ guint32 interface_id;
+ guint32 timestamp_high;
+ guint32 timestamp_low;
+ /* ... Options ... */
+} pcapng_interface_statistics_block_t;
+
+/* pcapng: Decryption Secrets Block file encoding */
+typedef struct pcapng_decryption_secrets_block_s {
+ guint32 secrets_type; /* Secrets Type, see secrets-types.h */
+ guint32 secrets_len; /* Size of variable-length secrets data. */
+ /* x bytes Secrets Data. */
+ /* ... Options ... */
+} pcapng_decryption_secrets_block_t;
+
+struct pcapng_option_header {
+ guint16 type;
+ guint16 value_length;
+};
+
+/*
+ * Minimum IDB size = minimum block size + size of fixed length portion of IDB.
+ */
+#define MIN_IDB_SIZE ((guint32)(MIN_BLOCK_SIZE + sizeof(pcapng_interface_description_block_t)))
+#define MIN_DSB_SIZE ((guint32)(MIN_BLOCK_SIZE + sizeof(pcapng_decryption_secrets_block_t)))
+
+wtap_open_return_val pcapng_open(wtap *wth, int *err, gchar **err_info);
+
+#endif
diff --git a/wiretap/pcapng_module.h b/wiretap/pcapng_module.h
new file mode 100644
index 00000000..e4ff9463
--- /dev/null
+++ b/wiretap/pcapng_module.h
@@ -0,0 +1,211 @@
+/** @file
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __PCAP_MODULE_H__
+#define __PCAP_MODULE_H__
+
+/*
+ * These are the officially registered block types, from the pcapng
+ * specification.
+ *
+ * XXX - Dear Sysdig People: please add your blocks to the spec!
+ */
+#define BLOCK_TYPE_SHB 0x0A0D0D0A /* Section Header Block */
+#define BLOCK_TYPE_IDB 0x00000001 /* Interface Description Block */
+#define BLOCK_TYPE_PB 0x00000002 /* Packet Block (obsolete) */
+#define BLOCK_TYPE_SPB 0x00000003 /* Simple Packet Block */
+#define BLOCK_TYPE_NRB 0x00000004 /* Name Resolution Block */
+#define BLOCK_TYPE_ISB 0x00000005 /* Interface Statistics Block */
+#define BLOCK_TYPE_EPB 0x00000006 /* Enhanced Packet Block */
+#define BLOCK_TYPE_IRIG_TS 0x00000007 /* IRIG Timestamp Block */
+#define BLOCK_TYPE_ARINC_429 0x00000008 /* ARINC 429 in AFDX Encapsulation Information Block */
+#define BLOCK_TYPE_SYSTEMD_JOURNAL_EXPORT 0x00000009 /* systemd journal entry */
+#define BLOCK_TYPE_DSB 0x0000000A /* Decryption Secrets Block */
+#define BLOCK_TYPE_SYSDIG_MI 0x00000201 /* Sysdig Machine Info Block */
+#define BLOCK_TYPE_SYSDIG_PL_V1 0x00000202 /* Sysdig Process List Block */
+#define BLOCK_TYPE_SYSDIG_FDL_V1 0x00000203 /* Sysdig File Descriptor List Block */
+#define BLOCK_TYPE_SYSDIG_EVENT 0x00000204 /* Sysdig Event Block */
+#define BLOCK_TYPE_SYSDIG_IL_V1 0x00000205 /* Sysdig Interface List Block */
+#define BLOCK_TYPE_SYSDIG_UL_V1 0x00000206 /* Sysdig User List Block */
+#define BLOCK_TYPE_SYSDIG_PL_V2 0x00000207 /* Sysdig Process List Block version 2 */
+#define BLOCK_TYPE_SYSDIG_EVF 0x00000208 /* Sysdig Event Block with flags */
+#define BLOCK_TYPE_SYSDIG_PL_V3 0x00000209 /* Sysdig Process List Block version 3 */
+#define BLOCK_TYPE_SYSDIG_PL_V4 0x00000210 /* Sysdig Process List Block version 4 */
+#define BLOCK_TYPE_SYSDIG_PL_V5 0x00000211 /* Sysdig Process List Block version 5 */
+#define BLOCK_TYPE_SYSDIG_PL_V6 0x00000212 /* Sysdig Process List Block version 6 */
+#define BLOCK_TYPE_SYSDIG_PL_V7 0x00000213 /* Sysdig Process List Block version 7 */
+#define BLOCK_TYPE_SYSDIG_PL_V8 0x00000214 /* Sysdig Process List Block version 8 */
+#define BLOCK_TYPE_SYSDIG_PL_V9 0x00000215 /* Sysdig Process List Block version 9 */
+#define BLOCK_TYPE_SYSDIG_EVENT_V2 0x00000216 /* Sysdig Event Block version 2 */
+#define BLOCK_TYPE_SYSDIG_EVF_V2 0x00000217 /* Sysdig Event Block with flags version 2 */
+#define BLOCK_TYPE_SYSDIG_FDL_V2 0x00000218 /* Sysdig File Descriptor List Block */
+#define BLOCK_TYPE_SYSDIG_IL_V2 0x00000219 /* Sysdig Interface List Block version 2 */
+#define BLOCK_TYPE_SYSDIG_UL_V2 0x00000220 /* Sysdig User List Block version 2 */
+#define BLOCK_TYPE_SYSDIG_EVENT_V2_LARGE 0x00000221 /* Sysdig Event Block version 2 with large payload */
+#define BLOCK_TYPE_SYSDIG_EVF_V2_LARGE 0x00000222 /* Sysdig Event Block with flags version 2 with large payload */
+#define BLOCK_TYPE_CB_COPY 0x00000BAD /* Custom Block which can be copied */
+#define BLOCK_TYPE_CB_NO_COPY 0x40000BAD /* Custom Block which should not be copied */
+
+/* TODO: the following are not yet well defined in the draft spec,
+ * and do not yet have block type values assigned to them:
+ * Compression Block
+ * Encryption Block
+ * Fixed Length Block
+ * Directory Block
+ * Traffic Statistics and Monitoring Blocks
+ * Event/Security Block
+ */
+
+/* Block data to be passed between functions during reading */
+typedef struct wtapng_block_s {
+ guint32 type; /* block_type as defined by pcapng */
+ gboolean internal; /* TRUE if this block type shouldn't be returned from pcapng_read() */
+ wtap_block_t block;
+ wtap_rec *rec;
+ Buffer *frame_buffer;
+} wtapng_block_t;
+
+/* Section data in private struct */
+/*
+ * XXX - there needs to be a more general way to implement the Netflix
+ * BBLog blocks and options.
+ */
+typedef struct section_info_t {
+ gboolean byte_swapped; /**< TRUE if this section is not in our byte order */
+ guint16 version_major; /**< Major version number of this section */
+ guint16 version_minor; /**< Minor version number of this section */
+ GArray *interfaces; /**< Interfaces found in this section */
+ gint64 shb_off; /**< File offset of the SHB for this section */
+ guint32 bblog_version; /**< BBLog: version used */
+ guint64 bblog_offset_tv_sec; /**< BBLog: UTC offset */
+ guint64 bblog_offset_tv_usec;
+} section_info_t;
+
+/*
+ * Reader and writer routines for pcapng block types.
+ */
+typedef gboolean (*block_reader)(FILE_T fh, guint32 block_read,
+ gboolean byte_swapped,
+ wtapng_block_t *wblock,
+ int *err, gchar **err_info);
+typedef gboolean (*block_writer)(wtap_dumper *wdh, const wtap_rec *rec,
+ const guint8 *pd, int *err);
+
+/*
+ * Register a handler for a pcapng block type.
+ */
+WS_DLL_PUBLIC
+void register_pcapng_block_type_handler(guint block_type, block_reader reader,
+ block_writer writer);
+
+/*
+ * Handler routines for pcapng option type.
+ */
+typedef gboolean (*option_parser)(wtap_block_t block,
+ gboolean byte_swapped,
+ guint option_length,
+ const guint8 *option_content,
+ int *err, gchar **err_info);
+typedef guint32 (*option_sizer)(guint option_id, wtap_optval_t *optval);
+typedef gboolean (*option_writer)(wtap_dumper *wdh, guint option_id,
+ wtap_optval_t *optval, int *err);
+
+/*
+ * Register a handler for a pcapng option code for a particular block
+ * type.
+ */
+WS_DLL_PUBLIC
+void register_pcapng_option_handler(guint block_type, guint option_code,
+ option_parser parser,
+ option_sizer sizer,
+ option_writer writer);
+
+/*
+ * Byte order of the options within a block.
+ *
+ * This is usually the byte order of the section, but, for options
+ * within a Custom Block, it needs to be a specified byte order,
+ * or a byte order indicated by data in the Custom Data (stored in
+ * a fashion that doesn't require knowing the byte order of the
+ * Custom Data, as it's also the byte order of the Custom Data
+ * itself), so that programs ignorant of the format of a given
+ * type of Custom Block can still read a block from one file and
+ * write it to another, even if the host doing the writing has
+ * a byte order different from the host that previously wrote
+ * the file.
+ */
+typedef enum {
+ OPT_SECTION_BYTE_ORDER, /* byte order of this section */
+ OPT_BIG_ENDIAN, /* as it says */
+ OPT_LITTLE_ENDIAN /* ditto */
+} pcapng_opt_byte_order_e;
+
+/*
+ * Process the options section of a block. process_option points to
+ * a routine that processes all the block-specific options, i.e.
+ * options other than the end-of-options, comment, and custom
+ * options.
+ */
+WS_DLL_PUBLIC
+gboolean pcapng_process_options(FILE_T fh, wtapng_block_t *wblock,
+ section_info_t *section_info,
+ guint opt_cont_buf_len,
+ gboolean (*process_option)(wtapng_block_t *,
+ const section_info_t *,
+ guint16, guint16,
+ const guint8 *,
+ int *, gchar **),
+ pcapng_opt_byte_order_e byte_order,
+ int *err, gchar **err_info);
+
+/*
+ * Helper routines to process options with types used in more than one
+ * block type.
+ */
+WS_DLL_PUBLIC
+void pcapng_process_uint8_option(wtapng_block_t *wblock,
+ guint16 option_code, guint16 option_length,
+ const guint8 *option_content);
+
+WS_DLL_PUBLIC
+void pcapng_process_uint32_option(wtapng_block_t *wblock,
+ const section_info_t *section_info,
+ pcapng_opt_byte_order_e byte_order,
+ guint16 option_code, guint16 option_length,
+ const guint8 *option_content);
+
+WS_DLL_PUBLIC
+void pcapng_process_timestamp_option(wtapng_block_t *wblock,
+ const section_info_t *section_info,
+ pcapng_opt_byte_order_e byte_order,
+ guint16 option_code, guint16 option_length,
+ const guint8 *option_content);
+
+WS_DLL_PUBLIC
+void pcapng_process_uint64_option(wtapng_block_t *wblock,
+ const section_info_t *section_info,
+ pcapng_opt_byte_order_e byte_order,
+ guint16 option_code, guint16 option_length,
+ const guint8 *option_content);
+
+WS_DLL_PUBLIC
+void pcapng_process_int64_option(wtapng_block_t *wblock,
+ const section_info_t *section_info,
+ pcapng_opt_byte_order_e byte_order,
+ guint16 option_code, guint16 option_length,
+ const guint8 *option_content);
+
+WS_DLL_PUBLIC
+void pcapng_process_string_option(wtapng_block_t *wblock, guint16 option_code,
+ guint16 option_length, const guint8 *option_content);
+
+WS_DLL_PUBLIC
+void pcapng_process_bytes_option(wtapng_block_t *wblock, guint16 option_code,
+ guint16 option_length, const guint8 *option_content);
+
+#endif /* __PCAP_MODULE_H__ */
diff --git a/wiretap/peekclassic.c b/wiretap/peekclassic.c
new file mode 100644
index 00000000..18b926dd
--- /dev/null
+++ b/wiretap/peekclassic.c
@@ -0,0 +1,746 @@
+/* peekclassic.c
+ * Routines for opening files in what Savvius (formerly WildPackets) calls
+ * the classic file format in the description of their "PeekRdr Sample
+ * Application" (C++ source code to read their capture files, downloading
+ * of which requires a maintenance contract, so it's not free as in beer
+ * and probably not as in speech, either).
+ *
+ * As that description says, it's used by AiroPeek and AiroPeek NX prior
+ * to 2.0, EtherPeek prior to 6.0, and EtherPeek NX prior to 3.0. It
+ * was probably also used by TokenPeek.
+ *
+ * This handles versions 5, 6, and 7 of that format (the format version
+ * number is what appears in the file, and is distinct from the application
+ * version number).
+ *
+ * Copyright (c) 2001, Daniel Thompson <d.thompson@gmx.net>
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "config.h"
+#include <string.h>
+
+#include <wsutil/epochs.h>
+#include <wsutil/802_11-utils.h>
+#include <wsutil/ws_assert.h>
+
+#include "wtap-int.h"
+#include "file_wrappers.h"
+#include "peekclassic.h"
+
+/* CREDITS
+ *
+ * This file decoder could not have been written without examining how
+ * tcptrace (http://www.tcptrace.org/) handles EtherPeek files.
+ */
+
+/* master header */
+typedef struct peekclassic_master_header {
+ guint8 version;
+ guint8 status;
+} peekclassic_master_header_t;
+#define PEEKCLASSIC_MASTER_HDR_SIZE 2
+
+/* secondary header (V5,V6,V7) */
+typedef struct peekclassic_v567_header {
+ guint32 filelength;
+ guint32 numPackets;
+ guint32 timeDate;
+ guint32 timeStart;
+ guint32 timeStop;
+ guint32 mediaType; /* Media Type Ethernet=0 Token Ring = 1 */
+ guint32 physMedium; /* Physical Medium native=0 802.1=1 */
+ guint32 appVers; /* App Version Number Maj.Min.Bug.Build */
+ guint32 linkSpeed; /* Link Speed Bits/sec */
+ guint32 reserved[3];
+} peekclassic_v567_header_t;
+#define PEEKCLASSIC_V567_HDR_SIZE 48
+
+/* full header */
+typedef struct peekclassic_header {
+ peekclassic_master_header_t master;
+ union {
+ peekclassic_v567_header_t v567;
+ } secondary;
+} peekclassic_header_t;
+
+/*
+ * Packet header (V5, V6).
+ *
+ * NOTE: the time stamp, although it's a 32-bit number, is only aligned
+ * on a 16-bit boundary. (Does this date back to 68K Macs? The 68000
+ * only required 16-bit alignment of 32-bit quantities, as did the 68010,
+ * and the 68020/68030/68040 required no alignment.)
+ *
+ * As such, we cannot declare this as a C structure, as compilers on
+ * most platforms will put 2 bytes of padding before the time stamp to
+ * align it on a 32-bit boundary.
+ *
+ * So, instead, we #define numbers as the offsets of the fields.
+ */
+#define PEEKCLASSIC_V56_LENGTH_OFFSET 0
+#define PEEKCLASSIC_V56_SLICE_LENGTH_OFFSET 2
+#define PEEKCLASSIC_V56_FLAGS_OFFSET 4
+#define PEEKCLASSIC_V56_STATUS_OFFSET 5
+#define PEEKCLASSIC_V56_TIMESTAMP_OFFSET 6
+#define PEEKCLASSIC_V56_DESTNUM_OFFSET 10
+#define PEEKCLASSIC_V56_SRCNUM_OFFSET 12
+#define PEEKCLASSIC_V56_PROTONUM_OFFSET 14
+#define PEEKCLASSIC_V56_PROTOSTR_OFFSET 16
+#define PEEKCLASSIC_V56_FILTERNUM_OFFSET 24
+#define PEEKCLASSIC_V56_PKT_SIZE 26
+
+/* 64-bit time in micro seconds from the (Mac) epoch */
+typedef struct peekclassic_utime {
+ guint32 upper;
+ guint32 lower;
+} peekclassic_utime;
+
+/*
+ * Packet header (V7).
+ *
+ * This doesn't have the same alignment problem, but we do it with
+ * #defines anyway.
+ */
+#define PEEKCLASSIC_V7_PROTONUM_OFFSET 0
+#define PEEKCLASSIC_V7_LENGTH_OFFSET 2
+#define PEEKCLASSIC_V7_SLICE_LENGTH_OFFSET 4
+#define PEEKCLASSIC_V7_FLAGS_OFFSET 6
+#define PEEKCLASSIC_V7_STATUS_OFFSET 7
+#define PEEKCLASSIC_V7_TIMESTAMP_OFFSET 8
+#define PEEKCLASSIC_V7_PKT_SIZE 16
+
+/*
+ * Flag bits.
+ */
+#define FLAGS_CONTROL_FRAME 0x01 /* Frame is a control frame */
+#define FLAGS_HAS_CRC_ERROR 0x02 /* Frame has a CRC error */
+#define FLAGS_HAS_FRAME_ERROR 0x04 /* Frame has a frame error */
+#define FLAGS_ROUTE_INFO 0x08 /* Frame has token ring routing information */
+#define FLAGS_FRAME_TOO_LONG 0x10 /* Frame too long */
+#define FLAGS_FRAME_TOO_SHORT 0x20 /* Frame too short (runt) */
+#define FLAGS_TRIGGER 0x40 /* Trigger packet (?) */
+#define FLAGS_SNAP 0x80 /* SNAP packet (SNAP header?) */
+
+/*
+ * Status bits.
+ */
+#define STATUS_SELECTED 0x01 /* Selected (in the *Peek GUI?) */
+#define STATUS_TRUNCATED 0x02 /* Truncated (?) */
+#define STATUS_APPLEPEEK 0x10 /* ApplePeek packet (?) */
+#define STATUS_SLICED 0x20 /* Sliced (cut short by snaplen?) */
+#define STATUS_HIDDEN 0x80 /* Hidden (in the *Peek GUI?) */
+
+typedef struct {
+ time_t reference_time;
+} peekclassic_t;
+
+static gboolean peekclassic_read_v7(wtap *wth, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info, gint64 *data_offset);
+static gboolean peekclassic_seek_read_v7(wtap *wth, gint64 seek_off,
+ wtap_rec *rec, Buffer *buf, int *err, gchar **err_info);
+static int peekclassic_read_packet_v7(wtap *wth, FILE_T fh,
+ wtap_rec *rec, Buffer *buf, int *err, gchar **err_info);
+static gboolean peekclassic_read_v56(wtap *wth, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info, gint64 *data_offset);
+static gboolean peekclassic_seek_read_v56(wtap *wth, gint64 seek_off,
+ wtap_rec *rec, Buffer *buf, int *err, gchar **err_info);
+static gboolean peekclassic_read_packet_v56(wtap *wth, FILE_T fh,
+ wtap_rec *rec, Buffer *buf, int *err, gchar **err_info);
+
+static int peekclassic_v56_file_type_subtype = -1;
+static int peekclassic_v7_file_type_subtype = -1;
+
+void register_peekclassic(void);
+
+wtap_open_return_val peekclassic_open(wtap *wth, int *err, gchar **err_info)
+{
+ peekclassic_header_t ep_hdr;
+ time_t reference_time;
+ int file_encap;
+ peekclassic_t *peekclassic;
+
+ /* Peek classic files do not start with a magic value large enough
+ * to be unique; hence we use the following algorithm to determine
+ * the type of an unknown file:
+ * - populate the master header and reject file if there is no match
+ * - populate the secondary header and check that the reserved space
+ * is zero, and check some other fields; this isn't perfect,
+ * and we may have to add more checks at some point.
+ */
+ ws_assert(sizeof(ep_hdr.master) == PEEKCLASSIC_MASTER_HDR_SIZE);
+ if (!wtap_read_bytes(wth->fh, &ep_hdr.master,
+ (int)sizeof(ep_hdr.master), err, err_info)) {
+ if (*err != WTAP_ERR_SHORT_READ)
+ return WTAP_OPEN_ERROR;
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ /*
+ * It appears that EtherHelp (a free application from WildPackets
+ * that did blind capture, saving to a file, so that you could
+ * give the resulting file to somebody with EtherPeek) saved
+ * captures in EtherPeek format except that it ORed the 0x80
+ * bit on in the version number.
+ *
+ * We therefore strip off the 0x80 bit in the version number.
+ * Perhaps there's some reason to care whether the capture
+ * came from EtherHelp; if we discover one, we should check
+ * that bit.
+ */
+ ep_hdr.master.version &= ~0x80;
+
+ /* switch on the file version */
+ switch (ep_hdr.master.version) {
+
+ case 5:
+ case 6:
+ case 7:
+ /* get the secondary header */
+ ws_assert(sizeof(ep_hdr.secondary.v567) ==
+ PEEKCLASSIC_V567_HDR_SIZE);
+ if (!wtap_read_bytes(wth->fh, &ep_hdr.secondary.v567,
+ (int)sizeof(ep_hdr.secondary.v567), err, err_info)) {
+ if (*err != WTAP_ERR_SHORT_READ)
+ return WTAP_OPEN_ERROR;
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ if ((0 != ep_hdr.secondary.v567.reserved[0]) ||
+ (0 != ep_hdr.secondary.v567.reserved[1]) ||
+ (0 != ep_hdr.secondary.v567.reserved[2])) {
+ /* still unknown */
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ /*
+ * Check the mediaType and physMedium fields.
+ * We assume it's not a Peek classic file if
+ * these aren't values we know, rather than
+ * reporting them as invalid Peek classic files,
+ * as, given the lack of a magic number, we need
+ * all the checks we can get.
+ */
+ ep_hdr.secondary.v567.mediaType =
+ g_ntohl(ep_hdr.secondary.v567.mediaType);
+ ep_hdr.secondary.v567.physMedium =
+ g_ntohl(ep_hdr.secondary.v567.physMedium);
+
+ switch (ep_hdr.secondary.v567.physMedium) {
+
+ case 0:
+ /*
+ * "Native" format, presumably meaning
+ * Ethernet or Token Ring.
+ */
+ switch (ep_hdr.secondary.v567.mediaType) {
+
+ case 0:
+ file_encap = WTAP_ENCAP_ETHERNET;
+ break;
+
+ case 1:
+ file_encap = WTAP_ENCAP_TOKEN_RING;
+ break;
+
+ default:
+ /*
+ * Assume this isn't a Peek classic file.
+ */
+ return WTAP_OPEN_NOT_MINE;
+ }
+ break;
+
+ case 1:
+ switch (ep_hdr.secondary.v567.mediaType) {
+
+ case 0:
+ /*
+ * 802.11, with a private header giving
+ * some radio information. Presumably
+ * this is from AiroPeek.
+ */
+ file_encap = WTAP_ENCAP_IEEE_802_11_WITH_RADIO;
+ break;
+
+ default:
+ /*
+ * Assume this isn't a Peek classic file.
+ */
+ return WTAP_OPEN_NOT_MINE;
+ }
+ break;
+
+ default:
+ /*
+ * Assume this isn't a Peek classic file.
+ */
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+
+ /*
+ * Assume this is a V5, V6 or V7 Peek classic file, and
+ * byte swap the rest of the fields in the secondary header.
+ *
+ * XXX - we could check the file length if the file were
+ * uncompressed, but it might be compressed.
+ */
+ ep_hdr.secondary.v567.filelength =
+ g_ntohl(ep_hdr.secondary.v567.filelength);
+ ep_hdr.secondary.v567.numPackets =
+ g_ntohl(ep_hdr.secondary.v567.numPackets);
+ ep_hdr.secondary.v567.timeDate =
+ g_ntohl(ep_hdr.secondary.v567.timeDate);
+ ep_hdr.secondary.v567.timeStart =
+ g_ntohl(ep_hdr.secondary.v567.timeStart);
+ ep_hdr.secondary.v567.timeStop =
+ g_ntohl(ep_hdr.secondary.v567.timeStop);
+ ep_hdr.secondary.v567.appVers =
+ g_ntohl(ep_hdr.secondary.v567.appVers);
+ ep_hdr.secondary.v567.linkSpeed =
+ g_ntohl(ep_hdr.secondary.v567.linkSpeed);
+
+ /* Get the reference time as a time_t */
+ reference_time = ep_hdr.secondary.v567.timeDate - EPOCH_DELTA_1904_01_01_00_00_00_UTC;
+ break;
+
+ default:
+ /*
+ * Assume this isn't a Peek classic file.
+ */
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ /*
+ * This is a Peek classic file.
+ *
+ * At this point we have recognised the file type and have populated
+ * the whole ep_hdr structure in host byte order.
+ */
+ peekclassic = g_new(peekclassic_t, 1);
+ wth->priv = (void *)peekclassic;
+ peekclassic->reference_time = reference_time;
+ wth->file_encap = file_encap;
+ switch (ep_hdr.master.version) {
+
+ case 5:
+ case 6:
+ wth->file_type_subtype = peekclassic_v56_file_type_subtype;
+ wth->subtype_read = peekclassic_read_v56;
+ wth->subtype_seek_read = peekclassic_seek_read_v56;
+ break;
+
+ case 7:
+ wth->file_type_subtype = peekclassic_v7_file_type_subtype;
+ wth->subtype_read = peekclassic_read_v7;
+ wth->subtype_seek_read = peekclassic_seek_read_v7;
+ break;
+
+ default:
+ /* this is impossible */
+ ws_assert_not_reached();
+ }
+
+ wth->snapshot_length = 0; /* not available in header */
+ wth->file_tsprec = WTAP_TSPREC_USEC;
+
+ /*
+ * Add an IDB; we don't know how many interfaces were
+ * involved, so we just say one interface, about which
+ * we only know the link-layer type, snapshot length,
+ * and time stamp resolution.
+ */
+ wtap_add_generated_idb(wth);
+
+ return WTAP_OPEN_MINE;
+}
+
+static gboolean peekclassic_read_v7(wtap *wth, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info, gint64 *data_offset)
+{
+ int sliceLength;
+
+ *data_offset = file_tell(wth->fh);
+
+ /* Read the packet. */
+ sliceLength = peekclassic_read_packet_v7(wth, wth->fh, rec, buf,
+ err, err_info);
+ if (sliceLength < 0)
+ return FALSE;
+
+ /* Skip extra ignored data at the end of the packet. */
+ if ((guint32)sliceLength > rec->rec_header.packet_header.caplen) {
+ if (!wtap_read_bytes(wth->fh, NULL, sliceLength - rec->rec_header.packet_header.caplen,
+ err, err_info))
+ return FALSE;
+ }
+
+ /* Records are padded to an even length, so if the slice length
+ is odd, read the padding byte. */
+ if (sliceLength & 0x01) {
+ if (!wtap_read_bytes(wth->fh, NULL, 1, err, err_info))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean peekclassic_seek_read_v7(wtap *wth, gint64 seek_off,
+ wtap_rec *rec, Buffer *buf, int *err, gchar **err_info)
+{
+ if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
+ return FALSE;
+
+ /* Read the packet. */
+ if (peekclassic_read_packet_v7(wth, wth->random_fh, rec, buf,
+ err, err_info) == -1) {
+ if (*err == 0)
+ *err = WTAP_ERR_SHORT_READ;
+ return FALSE;
+ }
+ return TRUE;
+}
+
+#define RADIO_INFO_SIZE 4
+
+static int peekclassic_read_packet_v7(wtap *wth, FILE_T fh,
+ wtap_rec *rec, Buffer *buf, int *err, gchar **err_info)
+{
+ guint8 ep_pkt[PEEKCLASSIC_V7_PKT_SIZE];
+#if 0
+ guint16 protoNum;
+#endif
+ guint16 length;
+ guint16 sliceLength;
+ guint8 flags;
+ guint8 status;
+ guint64 timestamp;
+ time_t tsecs;
+ guint32 tusecs;
+ guint32 pack_flags;
+ guint8 radio_info[RADIO_INFO_SIZE];
+
+ if (!wtap_read_bytes_or_eof(fh, ep_pkt, sizeof(ep_pkt), err, err_info))
+ return -1;
+
+ /* Extract the fields from the packet */
+#if 0
+ protoNum = pntoh16(&ep_pkt[PEEKCLASSIC_V7_PROTONUM_OFFSET]);
+#endif
+ length = pntoh16(&ep_pkt[PEEKCLASSIC_V7_LENGTH_OFFSET]);
+ sliceLength = pntoh16(&ep_pkt[PEEKCLASSIC_V7_SLICE_LENGTH_OFFSET]);
+ flags = ep_pkt[PEEKCLASSIC_V7_FLAGS_OFFSET];
+ status = ep_pkt[PEEKCLASSIC_V7_STATUS_OFFSET];
+ timestamp = pntoh64(&ep_pkt[PEEKCLASSIC_V7_TIMESTAMP_OFFSET]);
+
+ /* force sliceLength to be the actual length of the packet */
+ if (0 == sliceLength) {
+ sliceLength = length;
+ }
+ /*
+ * The maximum value of sliceLength and length are 65535, which
+ * are less than WTAP_MAX_PACKET_SIZE_STANDARD will ever be, so we don't
+ * need to check them.
+ */
+
+ /* fill in packet header values */
+ rec->rec_type = REC_TYPE_PACKET;
+ rec->block = wtap_block_create(WTAP_BLOCK_PACKET);
+ rec->presence_flags = WTAP_HAS_TS|WTAP_HAS_CAP_LEN;
+ tsecs = (time_t) (timestamp/1000000);
+ tusecs = (guint32) (timestamp - tsecs*1000000);
+ rec->ts.secs = tsecs - EPOCH_DELTA_1904_01_01_00_00_00_UTC;
+ rec->ts.nsecs = tusecs * 1000;
+ rec->rec_header.packet_header.len = length;
+ rec->rec_header.packet_header.caplen = sliceLength;
+ pack_flags = 0;
+ if (flags & FLAGS_HAS_CRC_ERROR)
+ pack_flags |= PACK_FLAGS_CRC_ERROR;
+ if (flags & FLAGS_FRAME_TOO_LONG)
+ pack_flags |= PACK_FLAGS_PACKET_TOO_LONG;
+ if (flags & FLAGS_FRAME_TOO_SHORT)
+ pack_flags |= PACK_FLAGS_PACKET_TOO_SHORT;
+ wtap_block_add_uint32_option(rec->block, OPT_PKT_FLAGS, pack_flags);
+
+ switch (wth->file_encap) {
+
+ case WTAP_ENCAP_IEEE_802_11_WITH_RADIO:
+ memset(&rec->rec_header.packet_header.pseudo_header.ieee_802_11, 0, sizeof(rec->rec_header.packet_header.pseudo_header.ieee_802_11));
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.fcs_len = 0; /* no FCS */
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.decrypted = FALSE;
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.datapad = FALSE;
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy = PHDR_802_11_PHY_UNKNOWN;
+
+ /*
+ * Now process the radio information pseudo-header.
+ * It's a 4-byte pseudo-header, consisting of:
+ *
+ * 1 byte of data rate, in units of 500 kb/s;
+ *
+ * 1 byte of channel number;
+ *
+ * 1 byte of signal strength as a percentage of
+ * the maximum, i.e. (RXVECTOR RSSI/RXVECTOR RSSI_Max)*100,
+ * or, at least, that's what I infer it is, given what
+ * the WildPackets note "Converting Signal Strength
+ * Percentage to dBm Values" says (it also says that
+ * the conversion the percentage to a dBm value is
+ * an adapter-dependent process, so, as we don't know
+ * what type of adapter was used to do the capture,
+ * we can't do the conversion);
+ *
+ * 1 byte of unknown content (padding?).
+ */
+ if (rec->rec_header.packet_header.len < RADIO_INFO_SIZE || rec->rec_header.packet_header.caplen < RADIO_INFO_SIZE) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("peekclassic: 802.11 packet has length < 4");
+ return -1;
+ }
+ rec->rec_header.packet_header.len -= RADIO_INFO_SIZE;
+ rec->rec_header.packet_header.caplen -= RADIO_INFO_SIZE;
+ sliceLength -= RADIO_INFO_SIZE;
+
+ /* read the pseudo-header */
+ if (!wtap_read_bytes(fh, radio_info, RADIO_INFO_SIZE, err, err_info))
+ return -1;
+
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.has_data_rate = TRUE;
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.data_rate = radio_info[0];
+
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.has_channel = TRUE;
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.channel = radio_info[1];
+
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.has_signal_percent = TRUE;
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.signal_percent = radio_info[2];
+
+ /*
+ * We don't know they PHY, but we do have the data rate;
+ * try to guess it based on the data rate and channel.
+ */
+ if (RATE_IS_DSSS(rec->rec_header.packet_header.pseudo_header.ieee_802_11.data_rate)) {
+ /* 11b */
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy = PHDR_802_11_PHY_11B;
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy_info.info_11b.has_short_preamble = FALSE;
+ } else if (RATE_IS_OFDM(rec->rec_header.packet_header.pseudo_header.ieee_802_11.data_rate)) {
+ /* 11a or 11g, depending on the band. */
+ if (CHAN_IS_BG(rec->rec_header.packet_header.pseudo_header.ieee_802_11.channel)) {
+ /* 11g */
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy = PHDR_802_11_PHY_11G;
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy_info.info_11g.has_mode = FALSE;
+ } else {
+ /* 11a */
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy = PHDR_802_11_PHY_11A;
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy_info.info_11a.has_channel_type = FALSE;
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.phy_info.info_11a.has_turbo_type = FALSE;
+ }
+ }
+
+ /*
+ * The last 4 bytes appear to be random data - the length
+ * might include the FCS - so we reduce the length by 4.
+ *
+ * Or maybe this is just the same kind of random 4 bytes
+ * of junk at the end you get in Wireless Sniffer
+ * captures.
+ */
+ if (rec->rec_header.packet_header.len < 4 || rec->rec_header.packet_header.caplen < 4) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("peekclassic: 802.11 packet has length < 8");
+ return -1;
+ }
+ rec->rec_header.packet_header.len -= 4;
+ rec->rec_header.packet_header.caplen -= 4;
+ break;
+
+ case WTAP_ENCAP_ETHERNET:
+ /* XXX - it appears that if the low-order bit of
+ "status" is 0, there's an FCS in this frame,
+ and if it's 1, there's 4 bytes of 0. */
+ rec->rec_header.packet_header.pseudo_header.eth.fcs_len = (status & 0x01) ? 0 : 4;
+ break;
+ }
+
+ /* read the packet data */
+ if (!wtap_read_packet_bytes(fh, buf, rec->rec_header.packet_header.caplen, err, err_info))
+ return -1;
+
+ return sliceLength;
+}
+
+static gboolean peekclassic_read_v56(wtap *wth, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info, gint64 *data_offset)
+{
+ *data_offset = file_tell(wth->fh);
+
+ /* read the packet */
+ if (!peekclassic_read_packet_v56(wth, wth->fh, rec, buf,
+ err, err_info))
+ return FALSE;
+
+ /*
+ * XXX - is the captured packet data padded to a multiple
+ * of 2 bytes?
+ */
+ return TRUE;
+}
+
+static gboolean peekclassic_seek_read_v56(wtap *wth, gint64 seek_off,
+ wtap_rec *rec, Buffer *buf, int *err, gchar **err_info)
+{
+ if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
+ return FALSE;
+
+ /* read the packet */
+ if (!peekclassic_read_packet_v56(wth, wth->random_fh, rec, buf,
+ err, err_info)) {
+ if (*err == 0)
+ *err = WTAP_ERR_SHORT_READ;
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static gboolean peekclassic_read_packet_v56(wtap *wth, FILE_T fh,
+ wtap_rec *rec, Buffer *buf, int *err, gchar **err_info)
+{
+ peekclassic_t *peekclassic = (peekclassic_t *)wth->priv;
+ guint8 ep_pkt[PEEKCLASSIC_V56_PKT_SIZE];
+ guint16 length;
+ guint16 sliceLength;
+ guint8 flags;
+#if 0
+ guint8 status;
+#endif
+ guint32 timestamp;
+#if 0
+ guint16 destNum;
+ guint16 srcNum;
+#endif
+#if 0
+ guint16 protoNum;
+ char protoStr[8];
+#endif
+ guint32 pack_flags;
+
+ if (!wtap_read_bytes_or_eof(fh, ep_pkt, sizeof(ep_pkt), err, err_info))
+ return FALSE;
+
+ /* Extract the fields from the packet */
+ length = pntoh16(&ep_pkt[PEEKCLASSIC_V56_LENGTH_OFFSET]);
+ sliceLength = pntoh16(&ep_pkt[PEEKCLASSIC_V56_SLICE_LENGTH_OFFSET]);
+ flags = ep_pkt[PEEKCLASSIC_V56_FLAGS_OFFSET];
+#if 0
+ status = ep_pkt[PEEKCLASSIC_V56_STATUS_OFFSET];
+#endif
+ timestamp = pntoh32(&ep_pkt[PEEKCLASSIC_V56_TIMESTAMP_OFFSET]);
+#if 0
+ destNum = pntoh16(&ep_pkt[PEEKCLASSIC_V56_DESTNUM_OFFSET]);
+ srcNum = pntoh16(&ep_pkt[PEEKCLASSIC_V56_SRCNUM_OFFSET]);
+ protoNum = pntoh16(&ep_pkt[PEEKCLASSIC_V56_PROTONUM_OFFSET]);
+ memcpy(protoStr, &ep_pkt[PEEKCLASSIC_V56_PROTOSTR_OFFSET],
+ sizeof protoStr);
+#endif
+
+ /*
+ * XXX - is the captured packet data padded to a multiple
+ * of 2 bytes?
+ */
+
+ /* force sliceLength to be the actual length of the packet */
+ if (0 == sliceLength) {
+ sliceLength = length;
+ }
+ /*
+ * The maximum value of sliceLength and length are 65535, which
+ * are less than WTAP_MAX_PACKET_SIZE_STANDARD will ever be, so we don't
+ * need to check them.
+ */
+
+ /* fill in packet header values */
+ rec->rec_type = REC_TYPE_PACKET;
+ rec->block = wtap_block_create(WTAP_BLOCK_PACKET);
+ rec->presence_flags = WTAP_HAS_TS|WTAP_HAS_CAP_LEN;
+ /* timestamp is in milliseconds since reference_time */
+ rec->ts.secs = peekclassic->reference_time + (timestamp / 1000);
+ rec->ts.nsecs = 1000 * (timestamp % 1000) * 1000;
+ rec->rec_header.packet_header.len = length;
+ rec->rec_header.packet_header.caplen = sliceLength;
+ pack_flags = 0;
+ if (flags & FLAGS_HAS_CRC_ERROR)
+ pack_flags |= PACK_FLAGS_CRC_ERROR;
+ if (flags & FLAGS_FRAME_TOO_LONG)
+ pack_flags |= PACK_FLAGS_PACKET_TOO_LONG;
+ if (flags & FLAGS_FRAME_TOO_SHORT)
+ pack_flags |= PACK_FLAGS_PACKET_TOO_SHORT;
+ wtap_block_add_uint32_option(rec->block, OPT_PKT_FLAGS, pack_flags);
+
+ switch (wth->file_encap) {
+
+ case WTAP_ENCAP_ETHERNET:
+ /* We assume there's no FCS in this frame. */
+ rec->rec_header.packet_header.pseudo_header.eth.fcs_len = 0;
+ break;
+ }
+
+ /* read the packet data */
+ return wtap_read_packet_bytes(fh, buf, sliceLength, err, err_info);
+}
+
+static const struct supported_block_type peekclassic_v56_blocks_supported[] = {
+ /*
+ * We support packet blocks, with no comments or other options.
+ */
+ { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
+};
+
+static const struct file_type_subtype_info peekclassic_v56_info = {
+ "Savvius classic (V5 and V6)", "peekclassic56", "pkt", "tpc;apc;wpz",
+ FALSE, BLOCKS_SUPPORTED(peekclassic_v56_blocks_supported),
+ NULL, NULL, NULL
+};
+
+static const struct supported_block_type peekclassic_v7_blocks_supported[] = {
+ /*
+ * We support packet blocks, with no comments or other options.
+ */
+ { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
+};
+
+static const struct file_type_subtype_info peekclassic_v7_info = {
+ "Savvius classic (V7)", "peekclassic7", "pkt", "tpc;apc;wpz",
+ FALSE, BLOCKS_SUPPORTED(peekclassic_v7_blocks_supported),
+ NULL, NULL, NULL
+};
+
+void register_peekclassic(void)
+{
+ peekclassic_v56_file_type_subtype = wtap_register_file_type_subtype(&peekclassic_v56_info);
+ peekclassic_v7_file_type_subtype = wtap_register_file_type_subtype(&peekclassic_v7_info);
+
+ /*
+ * Register names for backwards compatibility with the
+ * wtap_filetypes table in Lua.
+ */
+ wtap_register_backwards_compatibility_lua_name("PEEKCLASSIC_V56",
+ peekclassic_v56_file_type_subtype);
+ wtap_register_backwards_compatibility_lua_name("PEEKCLASSIC_V7",
+ peekclassic_v7_file_type_subtype);
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/wiretap/peekclassic.h b/wiretap/peekclassic.h
new file mode 100644
index 00000000..36a98226
--- /dev/null
+++ b/wiretap/peekclassic.h
@@ -0,0 +1,19 @@
+/** @file
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ */
+
+#ifndef __W_PEEKCLASSIC_H__
+#define __W_PEEKCLASSIC_H__
+
+#include <glib.h>
+#include "wtap.h"
+#include "ws_symbol_export.h"
+
+wtap_open_return_val peekclassic_open(wtap *wth, int *err, gchar **err_info);
+
+#endif
diff --git a/wiretap/peektagged.c b/wiretap/peektagged.c
new file mode 100644
index 00000000..acef3463
--- /dev/null
+++ b/wiretap/peektagged.c
@@ -0,0 +1,968 @@
+/* peektagged.c
+ * Routines for opening files in what Savvius (formerly WildPackets) calls
+ * the tagged file format in the description of their "PeekRdr Sample
+ * Application" (C++ source code to read their capture files, downloading
+ * of which requires a maintenance contract, so it's not free as in beer
+ * and probably not as in speech, either).
+ *
+ * As that description says, it's used by AiroPeek and AiroPeek NX 2.0
+ * and later, EtherPeek 6.0 and later, EtherPeek NX 3.0 and later,
+ * EtherPeek VX 1.0 and later, GigaPeek NX 1.0 and later, Omni3 1.0
+ * and later (both OmniPeek and the Remote Engine), and WANPeek NX
+ * 1.0 and later. They also say it'll be used by future Savvius
+ * products.
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "config.h"
+#include <string.h>
+#include <stdlib.h>
+#include "wtap-int.h"
+#include "file_wrappers.h"
+#include "peektagged.h"
+#include <wsutil/802_11-utils.h>
+
+/* CREDITS
+ *
+ * This file decoder could not have been written without examining
+ * http://www.varsanofiev.com/inside/airopeekv9.htm, the help from
+ * Martin Regner and Guy Harris, and the etherpeek.c file (as it
+ * was called before renaming it to peekclassic.c).
+ */
+
+/*
+ * Section header.
+ *
+ * A Peek tagged file consists of multiple sections, each of which begins
+ * with a header in the following format.
+ *
+ * The section ID is a 4-character string saying what type of section
+ * it is. The section length is a little-endian field giving the
+ * length of the section, in bytes, including the section header
+ * itself. The other field of the section header is a little-endian
+ * constant that always appears to be 0x00000200.
+ *
+ * Files we've seen have the following sections, in order:
+ *
+ * "\177vers" - version information. The contents are XML, giving
+ * the file format version and application version information.
+ *
+ * "sess" - capture session information. The contents are XML, giving
+ * various information about the capture session.
+ *
+ * "pkts" - captured packets. The contents are binary records, one for
+ * each packet, with the record being a list of tagged values followed
+ * by the raw packet data.
+ */
+typedef struct peektagged_section_header {
+ gint8 section_id[4]; /* string identifying the section */
+ guint32 section_len; /* little-endian section length */
+ guint32 section_const; /* little-endian 0x00000200 */
+} peektagged_section_header_t;
+
+/*
+ * Network subtype values.
+ *
+ * XXX - do different network subtype values for 802.11 indicate different
+ * network adapter types, with some adapters supplying the FCS and others
+ * not supplying the FCS?
+ */
+#define PEEKTAGGED_NST_ETHERNET 0
+#define PEEKTAGGED_NST_802_11 1 /* 802.11 with 0's at the end */
+#define PEEKTAGGED_NST_802_11_2 2 /* 802.11 with 0's at the end */
+#define PEEKTAGGED_NST_802_11_WITH_FCS 3 /* 802.11 with FCS at the end */
+
+/* tags for fields in packet header */
+#define TAG_PEEKTAGGED_LENGTH 0x0000
+#define TAG_PEEKTAGGED_TIMESTAMP_LOWER 0x0001
+#define TAG_PEEKTAGGED_TIMESTAMP_UPPER 0x0002
+#define TAG_PEEKTAGGED_FLAGS_AND_STATUS 0x0003 /* upper 24 bits unused? */
+#define TAG_PEEKTAGGED_CHANNEL 0x0004
+#define TAG_PEEKTAGGED_DATA_RATE_OR_MCS_INDEX 0x0005
+#define TAG_PEEKTAGGED_SIGNAL_PERC 0x0006
+#define TAG_PEEKTAGGED_SIGNAL_DBM 0x0007
+#define TAG_PEEKTAGGED_NOISE_PERC 0x0008
+#define TAG_PEEKTAGGED_NOISE_DBM 0x0009
+#define TAG_PEEKTAGGED_UNKNOWN_0x000A 0x000A
+#define TAG_PEEKTAGGED_CENTER_FREQUENCY 0x000D /* Frequency */
+#define TAG_PEEKTAGGED_UNKNOWN_0x000E 0x000E /* "Band"? */
+#define TAG_PEEKTAGGED_UNKNOWN_0x000F 0x000F /* antenna 2 signal dBm? */
+#define TAG_PEEKTAGGED_UNKNOWN_0x0010 0x0010 /* antenna 3 signal dBm? */
+#define TAG_PEEKTAGGED_UNKNOWN_0x0011 0x0011 /* antenna 4 signal dBm? */
+#define TAG_PEEKTAGGED_UNKNOWN_0x0012 0x0012 /* antenna 2 noise dBm? */
+#define TAG_PEEKTAGGED_UNKNOWN_0x0013 0x0013 /* antenna 3 noise dBm? */
+#define TAG_PEEKTAGGED_UNKNOWN_0x0014 0x0014 /* antenna 4 noise dBm? */
+#define TAG_PEEKTAGGED_EXT_FLAGS 0x0015 /* Extended flags for 802.11n and beyond */
+
+#define TAG_PEEKTAGGED_SLICE_LENGTH 0xffff
+
+/*
+ * Flags.
+ *
+ * We're assuming here that the "remote Peek" flags from bug 9586 are
+ * the same as the "Peek tagged" flags.
+ *
+ * Are these the same as in "Peek classic"? The first three are.
+ */
+#define FLAGS_CONTROL_FRAME 0x01 /* Frame is a control frame */
+#define FLAGS_HAS_CRC_ERROR 0x02 /* Frame has a CRC error */
+#define FLAGS_HAS_FRAME_ERROR 0x04 /* Frame has a frame error */
+
+/*
+ * Status.
+ *
+ * Is this in the next 8 bits of the "flags and status" field?
+ */
+#define STATUS_PROTECTED 0x0400 /* Frame is protected (encrypted) */
+#define STATUS_DECRYPT_ERROR 0x0800 /* Error decrypting protected frame */
+#define STATUS_SHORT_PREAMBLE 0x4000 /* Short preamble */
+
+/*
+ * Extended flags.
+ *
+ * Some determined from bug 10637, some determined from bug 9586,
+ * and the ones present in both agree, so we're assuming that
+ * the "remote Peek" protocol and the "Peek tagged" file format
+ * use the same bits (which wouldn't be too surprising, as they
+ * both come from Wildpackets).
+ */
+#define EXT_FLAG_20_MHZ_LOWER 0x00000001
+#define EXT_FLAG_20_MHZ_UPPER 0x00000002
+#define EXT_FLAG_40_MHZ 0x00000004
+#define EXT_FLAGS_BANDWIDTH 0x00000007
+#define EXT_FLAG_HALF_GI 0x00000008
+#define EXT_FLAG_FULL_GI 0x00000010
+#define EXT_FLAGS_GI 0x00000018
+#define EXT_FLAG_AMPDU 0x00000020
+#define EXT_FLAG_AMSDU 0x00000040
+#define EXT_FLAG_802_11ac 0x00000080
+#define EXT_FLAG_MCS_INDEX_USED 0x00000100
+
+/* 64-bit time in nanoseconds from the (Windows FILETIME) epoch */
+typedef struct peektagged_utime {
+ guint32 upper;
+ guint32 lower;
+} peektagged_utime;
+
+typedef struct {
+ gboolean has_fcs;
+} peektagged_t;
+
+static gboolean peektagged_read(wtap *wth, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info, gint64 *data_offset);
+static gboolean peektagged_seek_read(wtap *wth, gint64 seek_off,
+ wtap_rec *rec, Buffer *buf, int *err, gchar **err_info);
+
+static int peektagged_file_type_subtype = -1;
+
+void register_peektagged(void);
+
+static int wtap_file_read_pattern (wtap *wth, const char *pattern, int *err,
+ gchar **err_info)
+{
+ int c;
+ const char *cp;
+
+ cp = pattern;
+ while (*cp)
+ {
+ c = file_getc(wth->fh);
+ if (c == EOF)
+ {
+ *err = file_error(wth->fh, err_info);
+ if (*err != 0 && *err != WTAP_ERR_SHORT_READ)
+ return -1; /* error */
+ return 0; /* EOF */
+ }
+ if (c == *cp)
+ cp++;
+ else
+ {
+ if (c == pattern[0])
+ cp = &pattern[1];
+ else
+ cp = pattern;
+ }
+ }
+ return (*cp == '\0' ? 1 : 0);
+}
+
+
+static int wtap_file_read_till_separator (wtap *wth, char *buffer, int buflen,
+ const char *separators, int *err,
+ gchar **err_info)
+{
+ int c;
+ char *cp;
+ int i;
+
+ for (cp = buffer, i = 0; i < buflen; i++, cp++)
+ {
+ c = file_getc(wth->fh);
+ if (c == EOF)
+ {
+ *err = file_error(wth->fh, err_info);
+ if (*err != 0 && *err != WTAP_ERR_SHORT_READ)
+ return -1; /* error */
+ return 0; /* EOF */
+ }
+ if (strchr (separators, c) != NULL)
+ {
+ *cp = '\0';
+ break;
+ }
+ else
+ *cp = c;
+ }
+ return i;
+}
+
+
+static int wtap_file_read_number (wtap *wth, guint32 *num, int *err,
+ gchar **err_info)
+{
+ int ret;
+ char str_num[12];
+ unsigned long value;
+ char *p;
+
+ ret = wtap_file_read_till_separator (wth, str_num, sizeof (str_num)-1, "<",
+ err, err_info);
+ if (ret == 0 || ret == -1) {
+ /* 0 means EOF, which means "not a valid Peek tagged file";
+ -1 means error, and "err" has been set. */
+ return ret;
+ }
+ value = strtoul (str_num, &p, 10);
+ if (p == str_num || value > G_MAXUINT32)
+ return 0;
+ *num = (guint32)value;
+ return 1;
+}
+
+
+wtap_open_return_val peektagged_open(wtap *wth, int *err, gchar **err_info)
+{
+ peektagged_section_header_t ap_hdr;
+ int ret;
+ guint32 fileVersion = 0;
+ guint32 mediaType;
+ guint32 mediaSubType = 0;
+ int file_encap;
+ static const int peektagged_encap[] = {
+ WTAP_ENCAP_ETHERNET,
+ WTAP_ENCAP_IEEE_802_11_WITH_RADIO,
+ WTAP_ENCAP_IEEE_802_11_WITH_RADIO,
+ WTAP_ENCAP_IEEE_802_11_WITH_RADIO
+ };
+ #define NUM_PEEKTAGGED_ENCAPS (sizeof peektagged_encap / sizeof peektagged_encap[0])
+ peektagged_t *peektagged;
+
+ if (!wtap_read_bytes(wth->fh, &ap_hdr, (int)sizeof(ap_hdr), err, err_info)) {
+ if (*err != WTAP_ERR_SHORT_READ)
+ return WTAP_OPEN_ERROR;
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ if (memcmp (ap_hdr.section_id, "\177ver", sizeof(ap_hdr.section_id)) != 0)
+ return WTAP_OPEN_NOT_MINE; /* doesn't begin with a "\177ver" section */
+
+ /*
+ * XXX - we should get the length of the "\177ver" section, check
+ * that it's followed by a little-endian 0x00000200, and then,
+ * when reading the XML, make sure we don't go past the end of
+ * that section, and skip to the end of that section when
+ * we have the file version (and possibly check to make sure all
+ * tags are properly opened and closed).
+ */
+ ret = wtap_file_read_pattern (wth, "<FileVersion>", err, err_info);
+ if (ret == -1)
+ return WTAP_OPEN_ERROR;
+ if (ret == 0) {
+ /* 0 means EOF, which means "not a valid Peek tagged file" */
+ return WTAP_OPEN_NOT_MINE;
+ }
+ ret = wtap_file_read_number (wth, &fileVersion, err, err_info);
+ if (ret == -1)
+ return WTAP_OPEN_ERROR;
+ if (ret == 0) {
+ /* 0 means EOF, which means "not a valid Peek tagged file" */
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ /* If we got this far, we assume it's a Peek tagged file. */
+ if (fileVersion != 9) {
+ /* We only support version 9. */
+ *err = WTAP_ERR_UNSUPPORTED;
+ *err_info = ws_strdup_printf("peektagged: version %u unsupported",
+ fileVersion);
+ return WTAP_OPEN_ERROR;
+ }
+
+ /*
+ * XXX - once we've skipped the "\177ver" section, we should
+ * check for a "sess" section and fail if we don't see it.
+ * Then we should get the length of the "sess" section, check
+ * that it's followed by a little-endian 0x00000200, and then,
+ * when reading the XML, make sure we don't go past the end of
+ * that section, and skip to the end of the section when
+ * we have the file version (and possibly check to make sure all
+ * tags are properly opened and closed).
+ */
+ ret = wtap_file_read_pattern (wth, "<MediaType>", err, err_info);
+ if (ret == -1)
+ return WTAP_OPEN_ERROR;
+ if (ret == 0) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("peektagged: <MediaType> tag not found");
+ return WTAP_OPEN_ERROR;
+ }
+ /* XXX - this appears to be 0 in both the EtherPeek and AiroPeek
+ files we've seen; should we require it to be 0? */
+ ret = wtap_file_read_number (wth, &mediaType, err, err_info);
+ if (ret == -1)
+ return WTAP_OPEN_ERROR;
+ if (ret == 0) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("peektagged: <MediaType> value not found");
+ return WTAP_OPEN_ERROR;
+ }
+
+ ret = wtap_file_read_pattern (wth, "<MediaSubType>", err, err_info);
+ if (ret == -1)
+ return WTAP_OPEN_ERROR;
+ if (ret == 0) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("peektagged: <MediaSubType> tag not found");
+ return WTAP_OPEN_ERROR;
+ }
+ ret = wtap_file_read_number (wth, &mediaSubType, err, err_info);
+ if (ret == -1)
+ return WTAP_OPEN_ERROR;
+ if (ret == 0) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("peektagged: <MediaSubType> value not found");
+ return WTAP_OPEN_ERROR;
+ }
+ if (mediaSubType >= NUM_PEEKTAGGED_ENCAPS
+ || peektagged_encap[mediaSubType] == WTAP_ENCAP_UNKNOWN) {
+ *err = WTAP_ERR_UNSUPPORTED;
+ *err_info = ws_strdup_printf("peektagged: network type %u unknown or unsupported",
+ mediaSubType);
+ return WTAP_OPEN_ERROR;
+ }
+
+ ret = wtap_file_read_pattern (wth, "pkts", err, err_info);
+ if (ret == -1)
+ return WTAP_OPEN_ERROR;
+ if (ret == 0) {
+ *err = WTAP_ERR_SHORT_READ;
+ return WTAP_OPEN_ERROR;
+ }
+
+ /* skip 8 zero bytes */
+ if (!wtap_read_bytes (wth->fh, NULL, 8, err, err_info)) {
+ return WTAP_OPEN_ERROR;
+ }
+
+ /*
+ * This is an Peek tagged file.
+ */
+ file_encap = peektagged_encap[mediaSubType];
+
+ wth->file_type_subtype = peektagged_file_type_subtype;
+ wth->file_encap = file_encap;
+ wth->subtype_read = peektagged_read;
+ wth->subtype_seek_read = peektagged_seek_read;
+ wth->file_tsprec = WTAP_TSPREC_NSEC;
+
+ peektagged = g_new(peektagged_t, 1);
+ wth->priv = (void *)peektagged;
+ switch (mediaSubType) {
+
+ case PEEKTAGGED_NST_ETHERNET:
+ case PEEKTAGGED_NST_802_11:
+ case PEEKTAGGED_NST_802_11_2:
+ peektagged->has_fcs = FALSE;
+ break;
+
+ case PEEKTAGGED_NST_802_11_WITH_FCS:
+ peektagged->has_fcs = TRUE;
+ break;
+ }
+
+ wth->snapshot_length = 0; /* not available in header */
+
+ /*
+ * Add an IDB; we don't know how many interfaces were involved,
+ * so we just say one interface, about which we only know
+ * the link-layer type, snapshot length, and time stamp
+ * resolution.
+ */
+ wtap_add_generated_idb(wth);
+
+ return WTAP_OPEN_MINE;
+}
+
+/*
+ * Read the packet.
+ *
+ * XXX - we should supply the additional radio information;
+ * the pseudo-header should probably be supplied in a fashion
+ * similar to the radiotap radio header, so that the 802.11
+ * dissector can determine which, if any, information items
+ * are present.
+ */
+static int
+peektagged_read_packet(wtap *wth, FILE_T fh, wtap_rec *rec,
+ Buffer *buf, int *err, gchar **err_info)
+{
+ peektagged_t *peektagged = (peektagged_t *)wth->priv;
+ gboolean read_a_tag = FALSE;
+ guint8 tag_value[6];
+ guint16 tag;
+ gboolean saw_length = FALSE;
+ guint32 length = 0;
+ guint32 sliceLength = 0;
+ gboolean saw_timestamp_lower = FALSE;
+ gboolean saw_timestamp_upper = FALSE;
+ gboolean saw_flags_and_status = FALSE;
+ peektagged_utime timestamp;
+ guint32 flags_and_status = 0;
+ guint32 ext_flags = 0;
+ gboolean saw_data_rate_or_mcs_index = FALSE;
+ guint32 data_rate_or_mcs_index = 0;
+ gint channel;
+ guint frequency;
+ struct ieee_802_11_phdr ieee_802_11 = {0};
+ guint i;
+ int skip_len = 0;
+ guint64 t;
+
+ timestamp.upper = 0;
+ timestamp.lower = 0;
+ ieee_802_11.fcs_len = -1; /* Unknown */
+ ieee_802_11.decrypted = FALSE;
+ ieee_802_11.datapad = FALSE;
+ ieee_802_11.phy = PHDR_802_11_PHY_UNKNOWN;
+
+ /* Extract the fields from the packet header */
+ do {
+ /* Get the tag and value.
+ XXX - this assumes all values are 4 bytes long. */
+ if (!wtap_read_bytes_or_eof(fh, tag_value, sizeof tag_value, err, err_info)) {
+ if (*err == 0) {
+ /*
+ * Short read if we've read something already;
+ * just an EOF if we haven't.
+ */
+ if (read_a_tag)
+ *err = WTAP_ERR_SHORT_READ;
+ }
+ return -1;
+ }
+ read_a_tag = TRUE;
+ tag = pletoh16(&tag_value[0]);
+ switch (tag) {
+
+ case TAG_PEEKTAGGED_LENGTH:
+ if (saw_length) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("peektagged: record has two length fields");
+ return -1;
+ }
+ length = pletoh32(&tag_value[2]);
+ saw_length = TRUE;
+ break;
+
+ case TAG_PEEKTAGGED_TIMESTAMP_LOWER:
+ if (saw_timestamp_lower) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("peektagged: record has two timestamp-lower fields");
+ return -1;
+ }
+ timestamp.lower = pletoh32(&tag_value[2]);
+ saw_timestamp_lower = TRUE;
+ break;
+
+ case TAG_PEEKTAGGED_TIMESTAMP_UPPER:
+ if (saw_timestamp_upper) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("peektagged: record has two timestamp-upper fields");
+ return -1;
+ }
+ timestamp.upper = pletoh32(&tag_value[2]);
+ saw_timestamp_upper = TRUE;
+ break;
+
+ case TAG_PEEKTAGGED_FLAGS_AND_STATUS:
+ saw_flags_and_status = TRUE;
+ flags_and_status = pletoh32(&tag_value[2]);
+ break;
+
+ case TAG_PEEKTAGGED_CHANNEL:
+ ieee_802_11.has_channel = TRUE;
+ ieee_802_11.channel = pletoh32(&tag_value[2]);
+ break;
+
+ case TAG_PEEKTAGGED_DATA_RATE_OR_MCS_INDEX:
+ data_rate_or_mcs_index = pletoh32(&tag_value[2]);
+ saw_data_rate_or_mcs_index = TRUE;
+ break;
+
+ case TAG_PEEKTAGGED_SIGNAL_PERC:
+ ieee_802_11.has_signal_percent = TRUE;
+ ieee_802_11.signal_percent = pletoh32(&tag_value[2]);
+ break;
+
+ case TAG_PEEKTAGGED_SIGNAL_DBM:
+ ieee_802_11.has_signal_dbm = TRUE;
+ ieee_802_11.signal_dbm = pletoh32(&tag_value[2]);
+ break;
+
+ case TAG_PEEKTAGGED_NOISE_PERC:
+ ieee_802_11.has_noise_percent = TRUE;
+ ieee_802_11.noise_percent = pletoh32(&tag_value[2]);
+ break;
+
+ case TAG_PEEKTAGGED_NOISE_DBM:
+ ieee_802_11.has_noise_dbm = TRUE;
+ ieee_802_11.noise_dbm = pletoh32(&tag_value[2]);
+ break;
+
+ case TAG_PEEKTAGGED_UNKNOWN_0x000A:
+ /*
+ * XXX - seen in some 802.11 captures.
+ * Always seems to have the value 0 or 5.
+ */
+ break;
+
+ case TAG_PEEKTAGGED_CENTER_FREQUENCY:
+ /* XXX - also seen in an EtherPeek capture; value unknown */
+ ieee_802_11.has_frequency = TRUE;
+ ieee_802_11.frequency = pletoh32(&tag_value[2]);
+ break;
+
+ case TAG_PEEKTAGGED_UNKNOWN_0x000E:
+ /*
+ * XXX - seen in some 802.11 captures.
+ * Usually has the value 4, but, in some packets, has the
+ * values 6 or 302.
+ *
+ * Is this the mysterious "band" field that shows up in
+ * some "Peek remote" protocol captures, with values in
+ * the 30x or 40x ranges? It's not always associated
+ * with the "extended flags" tag for HT/VHT information,
+ * so it's probably not 11n/11ac-specific. Values other
+ * than 4 appear, in my captures, only in packets with
+ * the "extended flags" tag. 302 appeared in a packet
+ * with EXT_FLAG_MCS_INDEX_USED; 6 appeared in packets
+ * without EXT_FLAG_MCS_INDEX_USED.
+ */
+ break;
+
+ case TAG_PEEKTAGGED_UNKNOWN_0x000F:
+ /*
+ * XXX - seen in some 802.11 captures; dB or dBm value?
+ * Multiple antennas?
+ */
+ break;
+
+ case TAG_PEEKTAGGED_UNKNOWN_0x0010:
+ /*
+ * XXX - seen in some 802.11 captures; dB or dBm value?
+ * Multiple antennas?
+ */
+ break;
+
+ case TAG_PEEKTAGGED_UNKNOWN_0x0011:
+ /*
+ * XXX - seen in some 802.11 captures; dB or dBm value?
+ * Multiple antennas?
+ */
+ break;
+
+ case TAG_PEEKTAGGED_UNKNOWN_0x0012:
+ /*
+ * XXX - seen in some 802.11 captures; dB or dBm value?
+ * Multiple antennas?
+ */
+ break;
+
+ case TAG_PEEKTAGGED_UNKNOWN_0x0013:
+ /*
+ * XXX - seen in some 802.11 captures; dB or dBm value?
+ * Multiple antennas?
+ */
+ break;
+
+ case TAG_PEEKTAGGED_UNKNOWN_0x0014:
+ /*
+ * XXX - seen in some 802.11 captures; dB or dBm value?
+ * Multiple antennas?
+ */
+ break;
+
+ case TAG_PEEKTAGGED_EXT_FLAGS:
+ /*
+ * We assume this is present for HT and VHT frames and absent
+ * for other frames.
+ */
+ ext_flags = pletoh32(&tag_value[2]);
+ if (ext_flags & EXT_FLAG_802_11ac) {
+ ieee_802_11.phy = PHDR_802_11_PHY_11AC;
+ /*
+ * XXX - this probably has only one user, so only
+ * one MCS index and only one NSS, but where's the
+ * NSS?
+ */
+ for (i = 0; i < 4; i++)
+ ieee_802_11.phy_info.info_11ac.nss[i] = 0;
+
+ switch (ext_flags & EXT_FLAGS_GI) {
+
+ case EXT_FLAG_HALF_GI:
+ ieee_802_11.phy_info.info_11ac.has_short_gi = TRUE;
+ ieee_802_11.phy_info.info_11ac.short_gi = 1;
+ break;
+
+ case EXT_FLAG_FULL_GI:
+ ieee_802_11.phy_info.info_11ac.has_short_gi = TRUE;
+ ieee_802_11.phy_info.info_11ac.short_gi = 0;
+ break;
+
+ default:
+ /* Mutually exclusive flags set or nothing set */
+ break;
+ }
+ } else {
+ ieee_802_11.phy = PHDR_802_11_PHY_11N;
+ switch (ext_flags & EXT_FLAGS_BANDWIDTH) {
+
+ case 0:
+ ieee_802_11.phy_info.info_11n.has_bandwidth = TRUE;
+ ieee_802_11.phy_info.info_11n.bandwidth = PHDR_802_11_BANDWIDTH_20_MHZ;
+ break;
+
+ case EXT_FLAG_20_MHZ_LOWER:
+ ieee_802_11.phy_info.info_11n.has_bandwidth = TRUE;
+ ieee_802_11.phy_info.info_11n.bandwidth = PHDR_802_11_BANDWIDTH_20_20L;
+ break;
+
+ case EXT_FLAG_20_MHZ_UPPER:
+ ieee_802_11.phy_info.info_11n.has_bandwidth = TRUE;
+ ieee_802_11.phy_info.info_11n.bandwidth = PHDR_802_11_BANDWIDTH_20_20U;
+ break;
+
+ case EXT_FLAG_40_MHZ:
+ ieee_802_11.phy_info.info_11n.has_bandwidth = TRUE;
+ ieee_802_11.phy_info.info_11n.bandwidth = PHDR_802_11_BANDWIDTH_40_MHZ;
+ break;
+
+ default:
+ /* Mutually exclusive flags set */
+ break;
+ }
+
+ switch (ext_flags & EXT_FLAGS_GI) {
+
+ case EXT_FLAG_HALF_GI:
+ ieee_802_11.phy_info.info_11n.has_short_gi = TRUE;
+ ieee_802_11.phy_info.info_11n.short_gi = 1;
+ break;
+
+ case EXT_FLAG_FULL_GI:
+ ieee_802_11.phy_info.info_11n.has_short_gi = TRUE;
+ ieee_802_11.phy_info.info_11n.short_gi = 0;
+ break;
+
+ default:
+ /* Mutually exclusive flags set or nothing set */
+ break;
+ }
+ }
+ break;
+
+ case TAG_PEEKTAGGED_SLICE_LENGTH:
+ sliceLength = pletoh32(&tag_value[2]);
+ break;
+
+ default:
+ break;
+ }
+ } while (tag != TAG_PEEKTAGGED_SLICE_LENGTH); /* last tag */
+
+ if (!saw_length) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("peektagged: record has no length field");
+ return -1;
+ }
+ if (!saw_timestamp_lower) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("peektagged: record has no timestamp-lower field");
+ return -1;
+ }
+ if (!saw_timestamp_upper) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("peektagged: record has no timestamp-upper field");
+ return -1;
+ }
+
+ /*
+ * If sliceLength is 0, force it to be the actual length of the packet.
+ */
+ if (sliceLength == 0)
+ sliceLength = length;
+
+ if (sliceLength > WTAP_MAX_PACKET_SIZE_STANDARD) {
+ /*
+ * Probably a corrupt capture file; don't blow up trying
+ * to allocate space for an immensely-large packet.
+ */
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("peektagged: File has %u-byte packet, bigger than maximum of %u",
+ sliceLength, WTAP_MAX_PACKET_SIZE_STANDARD);
+ return -1;
+ }
+
+ rec->rec_type = REC_TYPE_PACKET;
+ rec->block = wtap_block_create(WTAP_BLOCK_PACKET);
+ rec->presence_flags = WTAP_HAS_TS|WTAP_HAS_CAP_LEN;
+ rec->rec_header.packet_header.len = length;
+ rec->rec_header.packet_header.caplen = sliceLength;
+ if (saw_flags_and_status) {
+ guint32 flags = 0;
+ if (flags_and_status & FLAGS_HAS_CRC_ERROR)
+ flags |= PACK_FLAGS_CRC_ERROR;
+ wtap_block_add_uint32_option(rec->block, OPT_PKT_FLAGS, flags);
+ }
+
+ /* calculate and fill in packet time stamp */
+ t = (((guint64) timestamp.upper) << 32) + timestamp.lower;
+ if (!nsfiletime_to_nstime(&rec->ts, t)) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("peektagged: time stamp outside supported range");
+ return -1;
+ }
+
+ switch (wth->file_encap) {
+
+ case WTAP_ENCAP_IEEE_802_11_WITH_RADIO:
+ if (saw_data_rate_or_mcs_index) {
+ if (ext_flags & EXT_FLAG_MCS_INDEX_USED) {
+ /*
+ * It's an MCS index.
+ *
+ * XXX - what about 11ac?
+ */
+ if (!(ext_flags & EXT_FLAG_802_11ac)) {
+ ieee_802_11.phy_info.info_11n.has_mcs_index = TRUE;
+ ieee_802_11.phy_info.info_11n.mcs_index = data_rate_or_mcs_index;
+ }
+ } else {
+ /* It's a data rate. */
+ ieee_802_11.has_data_rate = TRUE;
+ ieee_802_11.data_rate = data_rate_or_mcs_index;
+ if (ieee_802_11.phy == PHDR_802_11_PHY_UNKNOWN) {
+ /*
+ * We don't know they PHY; try to guess it based
+ * on the data rate and channel/center frequency.
+ */
+ if (RATE_IS_DSSS(ieee_802_11.data_rate)) {
+ /* 11b */
+ ieee_802_11.phy = PHDR_802_11_PHY_11B;
+ if (saw_flags_and_status) {
+ ieee_802_11.phy_info.info_11b.has_short_preamble = TRUE;
+ ieee_802_11.phy_info.info_11b.short_preamble =
+ (flags_and_status & STATUS_SHORT_PREAMBLE) ? TRUE : FALSE;;
+ } else
+ ieee_802_11.phy_info.info_11b.has_short_preamble = FALSE;
+ } else if (RATE_IS_OFDM(ieee_802_11.data_rate)) {
+ /* 11a or 11g, depending on the band. */
+ if (ieee_802_11.has_channel) {
+ if (CHAN_IS_BG(ieee_802_11.channel)) {
+ /* 11g */
+ ieee_802_11.phy = PHDR_802_11_PHY_11G;
+ } else {
+ /* 11a */
+ ieee_802_11.phy = PHDR_802_11_PHY_11A;
+ }
+ } else if (ieee_802_11.has_frequency) {
+ if (FREQ_IS_BG(ieee_802_11.frequency)) {
+ /* 11g */
+ ieee_802_11.phy = PHDR_802_11_PHY_11G;
+ } else {
+ /* 11a */
+ ieee_802_11.phy = PHDR_802_11_PHY_11A;
+ }
+ }
+ if (ieee_802_11.phy == PHDR_802_11_PHY_11G) {
+ /* Set 11g metadata */
+ ieee_802_11.phy_info.info_11g.has_mode = FALSE;
+ } else if (ieee_802_11.phy == PHDR_802_11_PHY_11A) {
+ /* Set 11a metadata */
+ ieee_802_11.phy_info.info_11a.has_channel_type = FALSE;
+ ieee_802_11.phy_info.info_11a.has_turbo_type = FALSE;
+ }
+ /* Otherwise we don't know the PHY */
+ }
+ }
+ }
+ }
+ if (ieee_802_11.has_frequency && !ieee_802_11.has_channel) {
+ /* Frequency, but no channel; try to calculate the channel. */
+ channel = ieee80211_mhz_to_chan(ieee_802_11.frequency);
+ if (channel != -1) {
+ ieee_802_11.has_channel = TRUE;
+ ieee_802_11.channel = channel;
+ }
+ } else if (ieee_802_11.has_channel && !ieee_802_11.has_frequency) {
+ /*
+ * If it's 11 legacy DHSS, 11b, or 11g, it's 2.4 GHz,
+ * so we can calculate the frequency.
+ *
+ * If it's 11a, it's 5 GHz, so we can calculate the
+ * frequency.
+ */
+ switch (ieee_802_11.phy) {
+
+ case PHDR_802_11_PHY_11_DSSS:
+ case PHDR_802_11_PHY_11B:
+ case PHDR_802_11_PHY_11G:
+ frequency = ieee80211_chan_to_mhz(ieee_802_11.channel, TRUE);
+ break;
+
+ case PHDR_802_11_PHY_11A:
+ frequency = ieee80211_chan_to_mhz(ieee_802_11.channel, FALSE);
+ break;
+
+ default:
+ /* We don't know the band. */
+ frequency = 0;
+ break;
+ }
+ if (frequency != 0) {
+ ieee_802_11.has_frequency = TRUE;
+ ieee_802_11.frequency = frequency;
+ }
+ }
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11 = ieee_802_11;
+ if (peektagged->has_fcs)
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.fcs_len = 4;
+ else {
+ if (rec->rec_header.packet_header.len < 4 || rec->rec_header.packet_header.caplen < 4) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("peektagged: 802.11 packet has length < 4");
+ return FALSE;
+ }
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.fcs_len = 0;
+ rec->rec_header.packet_header.len -= 4;
+ rec->rec_header.packet_header.caplen -= 4;
+ skip_len = 4;
+ }
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.decrypted = FALSE;
+ rec->rec_header.packet_header.pseudo_header.ieee_802_11.datapad = FALSE;
+ break;
+
+ case WTAP_ENCAP_ETHERNET:
+ /*
+ * The last 4 bytes appear to be 0 in the captures I've seen;
+ * are there any captures where it's an FCS?
+ */
+ if (rec->rec_header.packet_header.len < 4 || rec->rec_header.packet_header.caplen < 4) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("peektagged: Ethernet packet has length < 4");
+ return FALSE;
+ }
+ rec->rec_header.packet_header.pseudo_header.eth.fcs_len = 0;
+ rec->rec_header.packet_header.len -= 4;
+ rec->rec_header.packet_header.caplen -= 4;
+ skip_len = 4;
+ break;
+ }
+
+ /* Read the packet data. */
+ if (!wtap_read_packet_bytes(fh, buf, rec->rec_header.packet_header.caplen, err, err_info))
+ return -1;
+
+ return skip_len;
+}
+
+static gboolean peektagged_read(wtap *wth, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info, gint64 *data_offset)
+{
+ int skip_len;
+
+ *data_offset = file_tell(wth->fh);
+
+ /* Read the packet. */
+ skip_len = peektagged_read_packet(wth, wth->fh, rec, buf, err, err_info);
+ if (skip_len == -1)
+ return FALSE;
+
+ if (skip_len != 0) {
+ /* Skip extra junk at the end of the packet data. */
+ if (!wtap_read_bytes(wth->fh, NULL, skip_len, err, err_info))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+peektagged_seek_read(wtap *wth, gint64 seek_off,
+ wtap_rec *rec, Buffer *buf, int *err, gchar **err_info)
+{
+ if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
+ return FALSE;
+
+ /* Read the packet. */
+ if (peektagged_read_packet(wth, wth->random_fh, rec, buf, err, err_info) == -1) {
+ if (*err == 0)
+ *err = WTAP_ERR_SHORT_READ;
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static const struct supported_block_type peektagged_blocks_supported[] = {
+ /*
+ * We support packet blocks, with no comments or other options.
+ */
+ { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
+};
+
+static const struct file_type_subtype_info peektagged_info = {
+ "Savvius tagged", "peektagged", "pkt", "tpc;apc;wpz",
+ FALSE, BLOCKS_SUPPORTED(peektagged_blocks_supported),
+ NULL, NULL, NULL
+};
+
+void register_peektagged(void)
+{
+ peektagged_file_type_subtype = wtap_register_file_type_subtype(&peektagged_info);
+
+ /*
+ * Register name for backwards compatibility with the
+ * wtap_filetypes table in Lua.
+ */
+ wtap_register_backwards_compatibility_lua_name("PEEKTAGGED",
+ peektagged_file_type_subtype);
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * vi: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */
diff --git a/wiretap/peektagged.h b/wiretap/peektagged.h
new file mode 100644
index 00000000..04451ef1
--- /dev/null
+++ b/wiretap/peektagged.h
@@ -0,0 +1,16 @@
+/** @file
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __W_PEEKTAGGED_H__
+#define __W_PEEKTAGGED_H__
+#include <glib.h>
+#include "ws_symbol_export.h"
+
+wtap_open_return_val peektagged_open(wtap *wth, int *err, gchar **err_info);
+
+#endif
diff --git a/wiretap/pppdump.c b/wiretap/pppdump.c
new file mode 100644
index 00000000..15eda4fc
--- /dev/null
+++ b/wiretap/pppdump.c
@@ -0,0 +1,838 @@
+/* pppdump.c
+ *
+ * Copyright (c) 2000 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "config.h"
+#include "wtap-int.h"
+#include "pppdump.h"
+#include "file_wrappers.h"
+
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+
+#include <wsutil/ws_assert.h>
+
+/*
+pppdump records
+Daniel Thompson (STMicroelectronics) <daniel.thompson@st.com>
+
++------+
+| 0x07 | Reset time
++------+------+------+------+
+| t3 | t2 | t1 | t0 | t = time_t
++------+------+------+------+
+
++------+
+| 0x06 | Time step (short)
++------+
+| ts | ts = time step (tenths of seconds)
++------+
+
++------+
+| 0x05 | Time step (long)
++------+------+------+------+
+| ts3 | ts2 | ts1 | ts0 | ts = time step (tenths of seconds)
++------+------+------+------+
+
++------+
+| 0x04 | Receive deliminator (not seen in practice)
++------+
+
++------+
+| 0x03 | Send deliminator (not seen in practice)
++------+
+
++------+
+| 0x02 | Received data
++------+------+
+| n1 | n0 | n = number of bytes following
++------+------+
+| data |
+| |
+
++------+
+| 0x01 | Sent data
++------+------+
+| n1 | n0 | n = number of bytes following
++------+------+
+| data |
+| |
+*/
+
+#define PPPD_SENT_DATA 0x01
+#define PPPD_RECV_DATA 0x02
+#define PPPD_SEND_DELIM 0x03
+#define PPPD_RECV_DELIM 0x04
+#define PPPD_TIME_STEP_LONG 0x05
+#define PPPD_TIME_STEP_SHORT 0x06
+#define PPPD_RESET_TIME 0x07
+
+/* this buffer must be at least (2*PPPD_MTU) + sizeof(ppp_header) +
+ * sizeof(lcp_header) + sizeof(ipcp_header). PPPD_MTU is *very* rarely
+ * larger than 1500 so this value is fine.
+ *
+ * It's less than WTAP_MAX_PACKET_SIZE_STANDARD, so we don't have to worry about
+ * too-large packets.
+ */
+#define PPPD_BUF_SIZE 8192
+
+typedef enum {
+ DIRECTION_SENT,
+ DIRECTION_RECV
+} direction_enum;
+
+static gboolean pppdump_read(wtap *wth, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info, gint64 *data_offset);
+static gboolean pppdump_seek_read(wtap *wth, gint64 seek_off,
+ wtap_rec *rec, Buffer *buf, int *err, gchar **err_info);
+
+static int pppdump_file_type_subtype = -1;
+
+void register_pppdump(void);
+
+/*
+ * Information saved about a packet, during the initial sequential pass
+ * through the file, to allow us to later re-read it when randomly
+ * reading packets.
+ *
+ * "offset" is the offset in the file of the first data chunk containing data
+ * from that packet; note that it may also contain data from previous
+ * packets.
+ *
+ * "num_bytes_to_skip" is the number of bytes from previous packets in that
+ * first data chunk.
+ *
+ * "dir" is the direction of the packet.
+ */
+typedef struct {
+ gint64 offset;
+ gint64 num_bytes_to_skip;
+ direction_enum dir;
+} pkt_id;
+
+/*
+ * Information about a packet currently being processed. There is one of
+ * these for the sent packet being processed and one of these for the
+ * received packet being processed, as we could be in the middle of
+ * processing both a received packet and a sent packet.
+ *
+ * "dir" is the direction of the packet.
+ *
+ * "cnt" is the number of bytes of packet data we've accumulated.
+ *
+ * "esc" is TRUE if the next byte we see is escaped (and thus must be XORed
+ * with 0x20 before saving it), FALSE otherwise.
+ *
+ * "buf" is a buffer containing the packet data we've accumulated.
+ *
+ * "id_offset" is the offset in the file of the first data chunk
+ * containing data from the packet we're processing.
+ *
+ * "sd_offset" is the offset in the file of the first data byte from
+ * the packet we're processing - which isn't necessarily right after
+ * the header of the first data chunk, as we may already have assembled
+ * packets from that chunk.
+ *
+ * "cd_offset" is the offset in the file of the current data chunk we're
+ * processing.
+ */
+typedef struct {
+ direction_enum dir;
+ int cnt;
+ gboolean esc;
+ guint8 buf[PPPD_BUF_SIZE];
+ gint64 id_offset;
+ gint64 sd_offset;
+ gint64 cd_offset;
+} pkt_t;
+
+/*
+ * This keeps state used while processing records.
+ *
+ * "timestamp" is the seconds portion of the current time stamp value,
+ * as updated from PPPD_RESET_TIME, PPPD_TIME_STEP_LONG, and
+ * PPPD_TIME_STEP_SHORT records. "tenths" is the tenths-of-seconds
+ * portion.
+ *
+ * "spkt" and "rpkt" are "pkt_t" structures for the sent and received
+ * packets we're currently working on.
+ *
+ * "offset" is the current offset in the file.
+ *
+ * "num_bytes" and "pkt" are information saved when we finish accumulating
+ * the data for a packet, if the data chunk we're working on still has more
+ * data in it:
+ *
+ * "num_bytes" is the number of bytes of additional data remaining
+ * in the chunk after we've finished accumulating the data for the
+ * packet.
+ *
+ * "pkt" is the "pkt_t" for the type of packet the data chunk is for
+ * (sent or received packet).
+ *
+ * "seek_state" is another state structure used while processing records
+ * when doing a seek-and-read. (That structure doesn't itself have a
+ * "seek_state" structure.)
+ *
+ * "pids" is a GPtrArray of pointers to "pkt_id" structures for all the
+ * packets we've seen during the initial sequential pass, to allow us to
+ * later retrieve them with random accesses.
+ *
+ * "pkt_cnt" is the number of packets we've seen up to this point in the
+ * sequential pass.
+ */
+typedef struct _pppdump_t {
+ time_t timestamp;
+ guint tenths;
+ pkt_t spkt;
+ pkt_t rpkt;
+ gint64 offset;
+ int num_bytes;
+ pkt_t *pkt;
+ struct _pppdump_t *seek_state;
+ GPtrArray *pids;
+ guint pkt_cnt;
+} pppdump_t;
+
+static int
+process_data(pppdump_t *state, FILE_T fh, pkt_t *pkt, int n, guint8 *pd,
+ int *err, gchar **err_info, pkt_id *pid);
+
+static gboolean
+collate(pppdump_t *state, FILE_T fh, int *err, gchar **err_info, guint8 *pd,
+ int *num_bytes, direction_enum *direction, pkt_id *pid,
+ gint64 num_bytes_to_skip);
+
+static void
+pppdump_close(wtap *wth);
+
+static void
+init_state(pppdump_t *state)
+{
+
+ state->num_bytes = 0;
+ state->pkt = NULL;
+
+ state->spkt.dir = DIRECTION_SENT;
+ state->spkt.cnt = 0;
+ state->spkt.esc = FALSE;
+ state->spkt.id_offset = 0;
+ state->spkt.sd_offset = 0;
+ state->spkt.cd_offset = 0;
+
+ state->rpkt.dir = DIRECTION_RECV;
+ state->rpkt.cnt = 0;
+ state->rpkt.esc = FALSE;
+ state->rpkt.id_offset = 0;
+ state->rpkt.sd_offset = 0;
+ state->rpkt.cd_offset = 0;
+
+ state->seek_state = NULL;
+ state->offset = 0x100000; /* to detect errors during development */
+}
+
+
+wtap_open_return_val
+pppdump_open(wtap *wth, int *err, gchar **err_info)
+{
+ guint8 buffer[6]; /* Looking for: 0x07 t3 t2 t1 t0 ID */
+ pppdump_t *state;
+
+ /* There is no file header, only packet records. Fortunately for us,
+ * timestamp records are separated from packet records, so we should
+ * find an "initial time stamp" (i.e., a "reset time" record, or
+ * record type 0x07) at the beginning of the file. We'll check for
+ * that, plus a valid record following the 0x07 and the four bytes
+ * representing the timestamp.
+ */
+
+ if (!wtap_read_bytes(wth->fh, buffer, sizeof(buffer),
+ err, err_info)) {
+ if (*err != WTAP_ERR_SHORT_READ)
+ return WTAP_OPEN_ERROR;
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ if (buffer[0] == PPPD_RESET_TIME &&
+ (buffer[5] == PPPD_SENT_DATA ||
+ buffer[5] == PPPD_RECV_DATA ||
+ buffer[5] == PPPD_TIME_STEP_LONG ||
+ buffer[5] == PPPD_TIME_STEP_SHORT ||
+ buffer[5] == PPPD_RESET_TIME)) {
+
+ goto my_file_type;
+ }
+ else {
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ my_file_type:
+
+ if (file_seek(wth->fh, 5, SEEK_SET, err) == -1)
+ return WTAP_OPEN_ERROR;
+
+ state = g_new(pppdump_t, 1);
+ wth->priv = (void *)state;
+ state->timestamp = pntoh32(&buffer[1]);
+ state->tenths = 0;
+
+ init_state(state);
+
+ state->offset = 5;
+ wth->file_encap = WTAP_ENCAP_PPP_WITH_PHDR;
+ wth->file_type_subtype = pppdump_file_type_subtype;
+
+ wth->snapshot_length = PPPD_BUF_SIZE; /* just guessing */
+ wth->subtype_read = pppdump_read;
+ wth->subtype_seek_read = pppdump_seek_read;
+ wth->subtype_close = pppdump_close;
+ wth->file_tsprec = WTAP_TSPREC_100_MSEC;
+
+ state->seek_state = g_new(pppdump_t,1);
+
+ /* If we have a random stream open, we're going to be reading
+ the file randomly; set up a GPtrArray of pointers to
+ information about how to retrieve the data for each packet. */
+ if (wth->random_fh != NULL)
+ state->pids = g_ptr_array_new();
+ else
+ state->pids = NULL;
+ state->pkt_cnt = 0;
+
+ /*
+ * Add an IDB; we don't know how many interfaces were
+ * involved, so we just say one interface, about which
+ * we only know the link-layer type, snapshot length,
+ * and time stamp resolution.
+ */
+ wtap_add_generated_idb(wth);
+
+ return WTAP_OPEN_MINE;
+}
+
+/* Set part of the struct wtap_rec. */
+static void
+pppdump_set_phdr(wtap_rec *rec, int num_bytes,
+ direction_enum direction)
+{
+ rec->rec_type = REC_TYPE_PACKET;
+ rec->block = wtap_block_create(WTAP_BLOCK_PACKET);
+ rec->rec_header.packet_header.len = num_bytes;
+ rec->rec_header.packet_header.caplen = num_bytes;
+ rec->rec_header.packet_header.pkt_encap = WTAP_ENCAP_PPP_WITH_PHDR;
+
+ rec->rec_header.packet_header.pseudo_header.p2p.sent = (direction == DIRECTION_SENT ? TRUE : FALSE);
+}
+
+/* Find the next packet and parse it; called from wtap_read(). */
+static gboolean
+pppdump_read(wtap *wth, wtap_rec *rec, Buffer *buf, int *err, gchar **err_info,
+ gint64 *data_offset)
+{
+ int num_bytes;
+ direction_enum direction;
+ pppdump_t *state;
+ pkt_id *pid;
+
+ state = (pppdump_t *)wth->priv;
+
+ /* If we have a random stream open, allocate a structure to hold
+ the information needed to read this packet's data again. */
+ if (wth->random_fh != NULL) {
+ pid = g_new(pkt_id, 1);
+ if (!pid) {
+ *err = errno; /* assume a malloc failed and set "errno" */
+ return FALSE;
+ }
+ pid->offset = 0;
+ } else
+ pid = NULL; /* sequential only */
+
+ ws_buffer_assure_space(buf, PPPD_BUF_SIZE);
+ if (!collate(state, wth->fh, err, err_info, ws_buffer_start_ptr(buf),
+ &num_bytes, &direction, pid, 0)) {
+ g_free(pid);
+ return FALSE;
+ }
+
+ if (pid != NULL)
+ pid->dir = direction;
+
+ if (pid != NULL)
+ g_ptr_array_add(state->pids, pid);
+ /* The user's data_offset is not really an offset, but a packet number. */
+ *data_offset = state->pkt_cnt;
+ state->pkt_cnt++;
+
+ pppdump_set_phdr(rec, num_bytes, direction);
+ rec->presence_flags = WTAP_HAS_TS;
+ rec->ts.secs = state->timestamp;
+ rec->ts.nsecs = state->tenths * 100000000;
+
+ return TRUE;
+}
+
+/* Returns number of bytes copied for record, -1 if failure.
+ *
+ * This is modeled after pppdump.c, the utility to parse pppd log files; it
+ * comes with the ppp distribution.
+ */
+static int
+process_data(pppdump_t *state, FILE_T fh, pkt_t *pkt, int n, guint8 *pd,
+ int *err, gchar **err_info, pkt_id *pid)
+{
+ int c;
+ int num_bytes = n;
+ int num_written;
+
+ for (; num_bytes > 0; --num_bytes) {
+ c = file_getc(fh);
+ if (c == EOF) {
+ *err = file_error(fh, err_info);
+ if (*err == 0) {
+ *err = WTAP_ERR_SHORT_READ;
+ }
+ return -1;
+ }
+ state->offset++;
+ switch (c) {
+ case 0x7e:
+ /*
+ * Flag Sequence for RFC 1662 HDLC-like
+ * framing.
+ *
+ * As this is a raw trace of octets going
+ * over the wire, and that might include
+ * the login sequence, there is no
+ * guarantee that *only* PPP traffic
+ * appears in this file, so there is no
+ * guarantee that the first 0x7e we see is
+ * a start flag sequence, and therefore we
+ * cannot safely ignore all characters up
+ * to the first 0x7e, and therefore we
+ * might end up with some bogus PPP
+ * packets.
+ */
+ if (pkt->cnt > 0) {
+ /*
+ * We've seen stuff before this,
+ * so this is the end of a frame.
+ * Make a frame out of that stuff.
+ */
+ pkt->esc = FALSE;
+
+ num_written = pkt->cnt;
+ pkt->cnt = 0;
+ if (num_written <= 0) {
+ return 0;
+ }
+
+ if (num_written > PPPD_BUF_SIZE) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("pppdump: File has %u-byte packet, bigger than maximum of %u",
+ num_written, PPPD_BUF_SIZE);
+ return -1;
+ }
+
+ memcpy(pd, pkt->buf, num_written);
+
+ /*
+ * Remember the offset of the
+ * first record containing data
+ * for this packet, and how far
+ * into that record to skip to
+ * get to the beginning of the
+ * data for this packet; the number
+ * of bytes to skip into that record
+ * is the file offset of the first
+ * byte of this packet minus the
+ * file offset of the first byte of
+ * this record, minus 3 bytes for the
+ * header of this record (which, if
+ * we re-read this record, we will
+ * process, not skip).
+ */
+ if (pid) {
+ pid->offset = pkt->id_offset;
+ pid->num_bytes_to_skip =
+ pkt->sd_offset - pkt->id_offset - 3;
+ ws_assert(pid->num_bytes_to_skip >= 0);
+ }
+
+ num_bytes--;
+ if (num_bytes > 0) {
+ /*
+ * There's more data in this
+ * record.
+ * Set the initial data offset
+ * for the next packet.
+ */
+ pkt->id_offset = pkt->cd_offset;
+ pkt->sd_offset = state->offset;
+ } else {
+ /*
+ * There is no more data in
+ * this record.
+ * Thus, we don't have the
+ * initial data offset for
+ * the next packet.
+ */
+ pkt->id_offset = 0;
+ pkt->sd_offset = 0;
+ }
+ state->num_bytes = num_bytes;
+ state->pkt = pkt;
+ return num_written;
+ }
+ break;
+
+ case 0x7d:
+ /*
+ * Control Escape octet for octet-stuffed
+ * RFC 1662 HDLC-like framing.
+ */
+ if (!pkt->esc) {
+ /*
+ * Control Escape not preceded by
+ * Control Escape; discard it
+ * but XOR the next octet with
+ * 0x20.
+ */
+ pkt->esc = TRUE;
+ break;
+ }
+ /*
+ * Control Escape preceded by Control Escape;
+ * treat it as an ordinary character,
+ * by falling through.
+ */
+
+ /* FALL THROUGH */
+ default:
+ if (pkt->esc) {
+ /*
+ * This character was preceded by
+ * Control Escape, so XOR it with
+ * 0x20, as per RFC 1662's octet-
+ * stuffed framing, and clear
+ * the flag saying that the
+ * character should be escaped.
+ */
+ c ^= 0x20;
+ pkt->esc = FALSE;
+ }
+
+ if (pkt->cnt >= PPPD_BUF_SIZE) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("pppdump: File has %u-byte packet, bigger than maximum of %u",
+ pkt->cnt - 1, PPPD_BUF_SIZE);
+ return -1;
+ }
+ pkt->buf[pkt->cnt++] = c;
+ break;
+ }
+ }
+
+ /* we could have run out of bytes to read */
+ return 0;
+}
+
+/* Returns TRUE if packet data copied, FALSE if error occurred or EOF (no more records). */
+static gboolean
+collate(pppdump_t* state, FILE_T fh, int *err, gchar **err_info, guint8 *pd,
+ int *num_bytes, direction_enum *direction, pkt_id *pid,
+ gint64 num_bytes_to_skip)
+{
+ int id;
+ pkt_t *pkt = NULL;
+ int byte0, byte1;
+ int n, num_written = 0;
+ gint64 start_offset;
+ guint32 time_long;
+ guint8 time_short;
+
+ /*
+ * Process any data left over in the current record when doing
+ * sequential processing.
+ */
+ if (state->num_bytes > 0) {
+ ws_assert(num_bytes_to_skip == 0);
+ pkt = state->pkt;
+ num_written = process_data(state, fh, pkt, state->num_bytes,
+ pd, err, err_info, pid);
+
+ if (num_written < 0) {
+ return FALSE;
+ }
+ else if (num_written > 0) {
+ *num_bytes = num_written;
+ *direction = pkt->dir;
+ return TRUE;
+ }
+ /* if 0 bytes written, keep processing */
+ } else {
+ /*
+ * We didn't have any data left over, so the packet will
+ * start at the beginning of a record.
+ */
+ if (pid)
+ pid->num_bytes_to_skip = 0;
+ }
+
+ /*
+ * That didn't get all the data for this packet, so process
+ * subsequent records.
+ */
+ start_offset = state->offset;
+ while ((id = file_getc(fh)) != EOF) {
+ state->offset++;
+ switch (id) {
+ case PPPD_SENT_DATA:
+ case PPPD_RECV_DATA:
+ pkt = id == PPPD_SENT_DATA ? &state->spkt : &state->rpkt;
+
+ /*
+ * Save the offset of the beginning of
+ * the current record.
+ */
+ pkt->cd_offset = state->offset - 1;
+
+ /*
+ * Get the length of the record.
+ */
+ byte0 = file_getc(fh);
+ if (byte0 == EOF)
+ goto done;
+ state->offset++;
+ byte1 = file_getc(fh);
+ if (byte1 == EOF)
+ goto done;
+ state->offset++;
+ n = (byte0 << 8) | byte1;
+
+ if (pkt->id_offset == 0) {
+ /*
+ * We don't have the initial data
+ * offset for this packet, which
+ * means this is the first
+ * data record for that packet.
+ * Save the offset of the
+ * beginning of that record and
+ * the offset of the first data
+ * byte in the packet, which is
+ * the first data byte in the
+ * record.
+ */
+ pkt->id_offset = pkt->cd_offset;
+ pkt->sd_offset = state->offset;
+ }
+
+ if (n == 0)
+ continue;
+
+ ws_assert(num_bytes_to_skip < n);
+ while (num_bytes_to_skip) {
+ if (file_getc(fh) == EOF)
+ goto done;
+ state->offset++;
+ num_bytes_to_skip--;
+ n--;
+ }
+ num_written = process_data(state, fh, pkt, n,
+ pd, err, err_info, pid);
+
+ if (num_written < 0) {
+ return FALSE;
+ }
+ else if (num_written > 0) {
+ *num_bytes = num_written;
+ *direction = pkt->dir;
+ return TRUE;
+ }
+ /* if 0 bytes written, keep looping */
+ break;
+
+ case PPPD_SEND_DELIM:
+ case PPPD_RECV_DELIM:
+ /* What can we do? */
+ break;
+
+ case PPPD_RESET_TIME:
+ if (!wtap_read_bytes(fh, &time_long, sizeof(guint32), err, err_info))
+ return FALSE;
+ state->offset += sizeof(guint32);
+ state->timestamp = pntoh32(&time_long);
+ state->tenths = 0;
+ break;
+
+ case PPPD_TIME_STEP_LONG:
+ if (!wtap_read_bytes(fh, &time_long, sizeof(guint32), err, err_info))
+ return FALSE;
+ state->offset += sizeof(guint32);
+ state->tenths += pntoh32(&time_long);
+
+ if (state->tenths >= 10) {
+ state->timestamp += state->tenths / 10;
+ state->tenths = state->tenths % 10;
+ }
+
+ break;
+
+ case PPPD_TIME_STEP_SHORT:
+ if (!wtap_read_bytes(fh, &time_short, sizeof(guint8), err, err_info))
+ return FALSE;
+ state->offset += sizeof(guint8);
+ state->tenths += time_short;
+
+ if (state->tenths >= 10) {
+ state->timestamp += state->tenths / 10;
+ state->tenths = state->tenths % 10;
+ }
+
+ break;
+
+ default:
+ /* XXX - bad file */
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("pppdump: bad ID byte 0x%02x", id);
+ return FALSE;
+ }
+
+ }
+
+done:
+ *err = file_error(fh, err_info);
+ if (*err == 0) {
+ if (state->offset != start_offset) {
+ /*
+ * We read at least one byte, so we were working
+ * on a record; an EOF means that record was
+ * cut short.
+ */
+ *err = WTAP_ERR_SHORT_READ;
+ }
+ }
+ return FALSE;
+}
+
+
+
+/* Used to read packets in random-access fashion */
+static gboolean
+pppdump_seek_read(wtap *wth,
+ gint64 seek_off,
+ wtap_rec *rec,
+ Buffer *buf,
+ int *err,
+ gchar **err_info)
+{
+ int num_bytes;
+ guint8 *pd;
+ direction_enum direction;
+ pppdump_t *state;
+ pkt_id *pid;
+ gint64 num_bytes_to_skip;
+
+ state = (pppdump_t *)wth->priv;
+
+ pid = (pkt_id *)g_ptr_array_index(state->pids, seek_off);
+ if (!pid) {
+ *err = WTAP_ERR_BAD_FILE; /* XXX - better error? */
+ *err_info = g_strdup("pppdump: PID not found for record");
+ return FALSE;
+ }
+
+ if (file_seek(wth->random_fh, pid->offset, SEEK_SET, err) == -1)
+ return FALSE;
+
+ init_state(state->seek_state);
+ state->seek_state->offset = pid->offset;
+
+ ws_buffer_assure_space(buf, PPPD_BUF_SIZE);
+ pd = ws_buffer_start_ptr(buf);
+
+ /*
+ * We'll start reading at the first record containing data from
+ * this packet; however, that doesn't mean "collate()" will
+ * stop only when we've read that packet, as there might be
+ * data for packets going in the other direction as well, and
+ * we might finish processing one of those packets before we
+ * finish processing the packet we're reading.
+ *
+ * Therefore, we keep reading until we get a packet that's
+ * going in the direction we want.
+ */
+ num_bytes_to_skip = pid->num_bytes_to_skip;
+ do {
+ if (!collate(state->seek_state, wth->random_fh, err, err_info,
+ pd, &num_bytes, &direction, NULL, num_bytes_to_skip))
+ return FALSE;
+ num_bytes_to_skip = 0;
+ } while (direction != pid->dir);
+
+ pppdump_set_phdr(rec, num_bytes, pid->dir);
+
+ return TRUE;
+}
+
+static void
+pppdump_close(wtap *wth)
+{
+ pppdump_t *state;
+
+ state = (pppdump_t *)wth->priv;
+
+ if (state->seek_state) { /* should always be TRUE */
+ g_free(state->seek_state);
+ }
+
+ if (state->pids) {
+ unsigned int i;
+ for (i = 0; i < g_ptr_array_len(state->pids); i++) {
+ g_free(g_ptr_array_index(state->pids, i));
+ }
+ g_ptr_array_free(state->pids, TRUE);
+ }
+}
+
+static const struct supported_block_type pppdump_blocks_supported[] = {
+ /*
+ * We support packet blocks, with no comments or other options.
+ */
+ { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
+};
+
+static const struct file_type_subtype_info pppdump_info = {
+ "pppd log (pppdump format)", "pppd", NULL, NULL,
+ FALSE, BLOCKS_SUPPORTED(pppdump_blocks_supported),
+ NULL, NULL, NULL
+};
+
+void register_pppdump(void)
+{
+ pppdump_file_type_subtype = wtap_register_file_type_subtype(&pppdump_info);
+
+ /*
+ * Register name for backwards compatibility with the
+ * wtap_filetypes table in Lua.
+ */
+ wtap_register_backwards_compatibility_lua_name("PPPDUMP",
+ pppdump_file_type_subtype);
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/wiretap/pppdump.h b/wiretap/pppdump.h
new file mode 100644
index 00000000..a0a022fe
--- /dev/null
+++ b/wiretap/pppdump.h
@@ -0,0 +1,18 @@
+/** @file
+ *
+ * Copyright (c) 2000 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ */
+
+#ifndef __PPPDUMP_H__
+#define __PPPDUMP_H__
+
+#include <glib.h>
+#include "wtap.h"
+#include "ws_symbol_export.h"
+
+wtap_open_return_val pppdump_open(wtap *wth, int *err, gchar **err_info);
+
+#endif
diff --git a/wiretap/radcom.c b/wiretap/radcom.c
new file mode 100644
index 00000000..6e6c5963
--- /dev/null
+++ b/wiretap/radcom.c
@@ -0,0 +1,411 @@
+/* radcom.c
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include "wtap-int.h"
+#include "file_wrappers.h"
+#include "radcom.h"
+
+struct frame_date {
+ guint16 year;
+ guint8 month;
+ guint8 day;
+ guint32 sec; /* seconds since midnight */
+ guint32 usec;
+};
+
+struct unaligned_frame_date {
+ char year[2];
+ char month;
+ char day;
+ char sec[4]; /* seconds since midnight */
+ char usec[4];
+};
+
+/* Found at the beginning of the file. Bytes 2 and 3 (D2:00) seem to be
+ * different in some captures */
+static const guint8 radcom_magic[8] = {
+ 0x42, 0xD2, 0x00, 0x34, 0x12, 0x66, 0x22, 0x88
+};
+
+static const guint8 encap_magic[4] = {
+ 0x00, 0x42, 0x43, 0x09
+};
+
+static const guint8 active_time_magic[11] = {
+ 'A', 'c', 't', 'i', 'v', 'e', ' ', 'T', 'i', 'm', 'e'
+};
+
+/* RADCOM record header - followed by frame data (perhaps including FCS).
+
+ "data_length" appears to be the length of packet data following
+ the record header. It's 0 in the last record.
+
+ "length" appears to be the amount of captured packet data, and
+ "real_length" might be the actual length of the frame on the wire -
+ in some captures, it's the same as "length", and, in others,
+ it's greater than "length". In the last record, however, those
+ may have bogus values (or is that some kind of trailer record?).
+
+ "xxx" appears to be all-zero in all but the last record in one
+ capture; if so, perhaps this indicates that the last record is,
+ in fact, a trailer of some sort, and some field in the header
+ is a record type. */
+struct radcomrec_hdr {
+ char xxx[4]; /* unknown */
+ char data_length[2]; /* packet length? */
+ char xxy[5]; /* unknown */
+ struct unaligned_frame_date date; /* date/time stamp of packet */
+ char real_length[2]; /* actual length of packet */
+ char length[2]; /* captured length of packet */
+ char xxz[2]; /* unknown */
+ char dce; /* DCE/DTE flag (and other flags?) */
+ char xxw[9]; /* unknown */
+};
+
+static gboolean radcom_read(wtap *wth, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info, gint64 *data_offset);
+static gboolean radcom_seek_read(wtap *wth, gint64 seek_off,
+ wtap_rec *rec, Buffer *buf, int *err, gchar **err_info);
+static gboolean radcom_read_rec(wtap *wth, FILE_T fh, wtap_rec *rec,
+ Buffer *buf, int *err, gchar **err_info);
+
+static int radcom_file_type_subtype = -1;
+
+void register_radcom(void);
+
+wtap_open_return_val radcom_open(wtap *wth, int *err, gchar **err_info)
+{
+ guint8 r_magic[8], t_magic[11], search_encap[7];
+ struct frame_date start_date;
+#if 0
+ guint32 sec;
+ struct tm tm;
+#endif
+
+ /* Read in the string that should be at the start of a RADCOM file */
+ if (!wtap_read_bytes(wth->fh, r_magic, 8, err, err_info)) {
+ if (*err != WTAP_ERR_SHORT_READ)
+ return WTAP_OPEN_ERROR;
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ /* XXX: bytes 2 and 3 of the "magic" header seem to be different in some
+ * captures. We force them to our standard value so that the test
+ * succeeds (until we find if they have a special meaning, perhaps a
+ * version number ?) */
+ r_magic[1] = 0xD2;
+ r_magic[2] = 0x00;
+ if (memcmp(r_magic, radcom_magic, 8) != 0) {
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ /* Look for the "Active Time" string. The "frame_date" structure should
+ * be located 32 bytes before the beginning of this string */
+ if (!wtap_read_bytes(wth->fh, t_magic, 11, err, err_info)) {
+ if (*err != WTAP_ERR_SHORT_READ)
+ return WTAP_OPEN_ERROR;
+ return WTAP_OPEN_NOT_MINE;
+ }
+ while (memcmp(t_magic, active_time_magic, 11) != 0)
+ {
+ if (file_seek(wth->fh, -10, SEEK_CUR, err) == -1)
+ return WTAP_OPEN_ERROR;
+ if (!wtap_read_bytes(wth->fh, t_magic, 11, err, err_info)) {
+ if (*err != WTAP_ERR_SHORT_READ)
+ return WTAP_OPEN_ERROR;
+ return WTAP_OPEN_NOT_MINE;
+ }
+ }
+ if (file_seek(wth->fh, -43, SEEK_CUR, err) == -1)
+ return WTAP_OPEN_ERROR;
+
+ /* Get capture start time */
+ if (!wtap_read_bytes(wth->fh, &start_date, sizeof(struct frame_date),
+ err, err_info)) {
+ if (*err != WTAP_ERR_SHORT_READ)
+ return WTAP_OPEN_ERROR;
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ /* So what time is this? */
+ if (!wtap_read_bytes(wth->fh, NULL, sizeof(struct frame_date),
+ err, err_info)) {
+ if (*err != WTAP_ERR_SHORT_READ)
+ return WTAP_OPEN_ERROR;
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ for (;;) {
+ if (!wtap_read_bytes(wth->fh, search_encap, 4,
+ err, err_info)) {
+ if (*err != WTAP_ERR_SHORT_READ)
+ return WTAP_OPEN_ERROR;
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ if (memcmp(encap_magic, search_encap, 4) == 0)
+ break;
+
+ /*
+ * OK, that's not it, go forward 1 byte - reading
+ * the magic moved us forward 4 bytes, so seeking
+ * backward 3 bytes moves forward 1 byte - and
+ * try the 4 bytes at that offset.
+ */
+ if (file_seek(wth->fh, -3, SEEK_CUR, err) == -1)
+ return WTAP_OPEN_ERROR;
+ }
+ if (!wtap_read_bytes(wth->fh, NULL, 12, err, err_info)) {
+ if (*err != WTAP_ERR_SHORT_READ)
+ return WTAP_OPEN_ERROR;
+ return WTAP_OPEN_NOT_MINE;
+ }
+ if (!wtap_read_bytes(wth->fh, search_encap, 4, err, err_info)) {
+ if (*err != WTAP_ERR_SHORT_READ)
+ return WTAP_OPEN_ERROR;
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ /* This is a radcom file */
+ wth->file_type_subtype = radcom_file_type_subtype;
+ wth->subtype_read = radcom_read;
+ wth->subtype_seek_read = radcom_seek_read;
+ wth->snapshot_length = 0; /* not available in header, only in frame */
+ wth->file_tsprec = WTAP_TSPREC_USEC;
+
+#if 0
+ tm.tm_year = pletoh16(&start_date.year)-1900;
+ tm.tm_mon = start_date.month-1;
+ tm.tm_mday = start_date.day;
+ sec = pletoh32(&start_date.sec);
+ tm.tm_hour = sec/3600;
+ tm.tm_min = (sec%3600)/60;
+ tm.tm_sec = sec%60;
+ tm.tm_isdst = -1;
+#endif
+
+ if (memcmp(search_encap, "LAPB", 4) == 0)
+ wth->file_encap = WTAP_ENCAP_LAPB;
+ else if (memcmp(search_encap, "Ethe", 4) == 0)
+ wth->file_encap = WTAP_ENCAP_ETHERNET;
+ else if (memcmp(search_encap, "ATM/", 4) == 0)
+ wth->file_encap = WTAP_ENCAP_ATM_RFC1483;
+ else {
+ *err = WTAP_ERR_UNSUPPORTED;
+ *err_info = ws_strdup_printf("radcom: network type \"%.4s\" unknown", search_encap);
+ return WTAP_OPEN_ERROR;
+ }
+
+#if 0
+ if (!wtap_read_bytes(wth->fh, &next_date, sizeof(struct frame_date),
+ err, err_info))
+ return WTAP_OPEN_ERROR;
+
+ while (memcmp(&start_date, &next_date, 4)) {
+ if (file_seek(wth->fh, 1-sizeof(struct frame_date), SEEK_CUR, err) == -1)
+ return WTAP_OPEN_ERROR;
+ if (!wtap_read_bytes(wth->fh, &next_date, sizeof(struct frame_date),
+ err, err_info))
+ return WTAP_OPEN_ERROR;
+ }
+#endif
+
+ if (wth->file_encap == WTAP_ENCAP_ETHERNET) {
+ if (!wtap_read_bytes(wth->fh, NULL, 294, err, err_info))
+ return WTAP_OPEN_ERROR;
+ } else if (wth->file_encap == WTAP_ENCAP_LAPB) {
+ if (!wtap_read_bytes(wth->fh, NULL, 297, err, err_info))
+ return WTAP_OPEN_ERROR;
+ } else if (wth->file_encap == WTAP_ENCAP_ATM_RFC1483) {
+ if (!wtap_read_bytes(wth->fh, NULL, 504, err, err_info))
+ return WTAP_OPEN_ERROR;
+ }
+
+ /*
+ * Add an IDB; we don't know how many interfaces were involved,
+ * so we just say one interface, about which we only know
+ * the link-layer type, snapshot length, and time stamp
+ * resolution.
+ */
+ wtap_add_generated_idb(wth);
+
+ return WTAP_OPEN_MINE;
+}
+
+/* Read the next packet */
+static gboolean radcom_read(wtap *wth, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info, gint64 *data_offset)
+{
+ char fcs[2];
+
+ *data_offset = file_tell(wth->fh);
+
+ /* Read record. */
+ if (!radcom_read_rec(wth, wth->fh, rec, buf, err, err_info)) {
+ /* Read error or EOF */
+ return FALSE;
+ }
+
+ if (wth->file_encap == WTAP_ENCAP_LAPB) {
+ /* Read the FCS.
+ XXX - should we have some way of indicating the
+ presence and size of an FCS to our caller?
+ That'd let us handle other file types as well. */
+ if (!wtap_read_bytes(wth->fh, &fcs, sizeof fcs, err, err_info))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+radcom_seek_read(wtap *wth, gint64 seek_off,
+ wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info)
+{
+ if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
+ return FALSE;
+
+ /* Read record. */
+ if (!radcom_read_rec(wth, wth->random_fh, rec, buf, err,
+ err_info)) {
+ /* Read error or EOF */
+ if (*err == 0) {
+ /* EOF means "short read" in random-access mode */
+ *err = WTAP_ERR_SHORT_READ;
+ }
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static gboolean
+radcom_read_rec(wtap *wth, FILE_T fh, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info)
+{
+ struct radcomrec_hdr hdr;
+ guint16 data_length, real_length, length;
+ guint32 sec;
+ struct tm tm;
+ guint8 atmhdr[8];
+
+ if (!wtap_read_bytes_or_eof(fh, &hdr, sizeof hdr, err, err_info))
+ return FALSE;
+
+ data_length = pletoh16(&hdr.data_length);
+ if (data_length == 0) {
+ /*
+ * The last record appears to have 0 in its "data_length"
+ * field, but non-zero values in other fields, so we
+ * check for that and treat it as an EOF indication.
+ */
+ *err = 0;
+ return FALSE;
+ }
+ length = pletoh16(&hdr.length);
+ real_length = pletoh16(&hdr.real_length);
+ /*
+ * The maximum value of length is 65535, which is less than
+ * WTAP_MAX_PACKET_SIZE_STANDARD will ever be, so we don't need to check
+ * it.
+ */
+
+ rec->rec_type = REC_TYPE_PACKET;
+ rec->block = wtap_block_create(WTAP_BLOCK_PACKET);
+ rec->presence_flags = WTAP_HAS_TS|WTAP_HAS_CAP_LEN;
+
+ tm.tm_year = pletoh16(&hdr.date.year)-1900;
+ tm.tm_mon = (hdr.date.month&0x0f)-1;
+ tm.tm_mday = hdr.date.day;
+ sec = pletoh32(&hdr.date.sec);
+ tm.tm_hour = sec/3600;
+ tm.tm_min = (sec%3600)/60;
+ tm.tm_sec = sec%60;
+ tm.tm_isdst = -1;
+ rec->ts.secs = mktime(&tm);
+ rec->ts.nsecs = pletoh32(&hdr.date.usec) * 1000;
+
+ switch (wth->file_encap) {
+
+ case WTAP_ENCAP_ETHERNET:
+ /* XXX - is there an FCS? */
+ rec->rec_header.packet_header.pseudo_header.eth.fcs_len = -1;
+ break;
+
+ case WTAP_ENCAP_LAPB:
+ rec->rec_header.packet_header.pseudo_header.dte_dce.flags = (hdr.dce & 0x1) ?
+ 0x00 : FROM_DCE;
+ length -= 2; /* FCS */
+ real_length -= 2;
+ break;
+
+ case WTAP_ENCAP_ATM_RFC1483:
+ /*
+ * XXX - is this stuff a pseudo-header?
+ * The direction appears to be in the "hdr.dce" field.
+ */
+ if (!wtap_read_bytes(fh, atmhdr, sizeof atmhdr, err,
+ err_info))
+ return FALSE; /* Read error */
+ length -= 8;
+ real_length -= 8;
+ break;
+ }
+
+ rec->rec_header.packet_header.len = real_length;
+ rec->rec_header.packet_header.caplen = length;
+
+ /*
+ * Read the packet data.
+ */
+ if (!wtap_read_packet_bytes(fh, buf, length, err, err_info))
+ return FALSE; /* Read error */
+
+ return TRUE;
+}
+
+static const struct supported_block_type radcom_blocks_supported[] = {
+ /*
+ * We support packet blocks, with no comments or other options.
+ */
+ { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
+};
+
+static const struct file_type_subtype_info radcom_info = {
+ "RADCOM WAN/LAN analyzer", "radcom", NULL, NULL,
+ FALSE, BLOCKS_SUPPORTED(radcom_blocks_supported),
+ NULL, NULL, NULL
+};
+
+void register_radcom(void)
+{
+ radcom_file_type_subtype = wtap_register_file_type_subtype(&radcom_info);
+
+ /*
+ * Register name for backwards compatibility with the
+ * wtap_filetypes table in Lua.
+ */
+ wtap_register_backwards_compatibility_lua_name("RADCOM",
+ radcom_file_type_subtype);
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/wiretap/radcom.h b/wiretap/radcom.h
new file mode 100644
index 00000000..f45a5931
--- /dev/null
+++ b/wiretap/radcom.h
@@ -0,0 +1,19 @@
+/** @file
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ */
+
+#ifndef __RADCOM_H__
+#define __RADCOM_H__
+
+#include <glib.h>
+#include "wtap.h"
+#include "ws_symbol_export.h"
+
+wtap_open_return_val radcom_open(wtap *wth, int *err, gchar **err_info);
+
+#endif
diff --git a/wiretap/required_file_handlers.h b/wiretap/required_file_handlers.h
new file mode 100644
index 00000000..ae07de14
--- /dev/null
+++ b/wiretap/required_file_handlers.h
@@ -0,0 +1,34 @@
+/** @file
+ *
+ * Functions and variables defined by required file handlers (pcap,
+ * nanosecond pcap, pcapng).
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __REQUIRED_FILE_HANDLERS_H__
+#define __REQUIRED_FILE_HANDLERS_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/*
+ * These are for use within libwiretap only; they are not exported.
+ */
+extern void register_pcap(void);
+extern void register_pcapng(void);
+
+extern int pcap_file_type_subtype; /* regular pcap */
+extern int pcap_nsec_file_type_subtype; /* pcap with nanosecond resolution */
+extern int pcapng_file_type_subtype; /* pcapng */
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __REQUIRED_FILE_HANDLERS_H__ */
diff --git a/wiretap/rfc7468.c b/wiretap/rfc7468.c
new file mode 100644
index 00000000..1c143273
--- /dev/null
+++ b/wiretap/rfc7468.c
@@ -0,0 +1,221 @@
+/* rfc7468.c
+ *
+ * Implements loading of files in the format specified by RFC 7468.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "config.h"
+
+#include "rfc7468.h"
+
+#include "file_wrappers.h"
+#include "wtap-int.h"
+
+#include <wsutil/buffer.h>
+
+#include <glib.h>
+
+#include <string.h>
+
+static int rfc7468_file_type_subtype = -1;
+
+void register_rfc7468(void);
+
+enum line_type {
+ LINE_TYPE_PREEB,
+ LINE_TYPE_POSTEB,
+ LINE_TYPE_OTHER,
+};
+
+const char PREEB_BEGIN[] = "-----BEGIN ";
+#define PREEB_BEGIN_LEN (sizeof PREEB_BEGIN - 1)
+const char POSTEB_BEGIN[] = "-----END ";
+#define POSTEB_BEGIN_LEN (sizeof POSTEB_BEGIN - 1)
+
+static gboolean rfc7468_read_line(FILE_T fh, enum line_type *line_type, Buffer *buf,
+ int* err, gchar** err_info)
+{
+ /* Make the chunk size large enough that most lines can fit in a single chunk.
+ Strict RFC 7468 syntax only allows up to 64 characters per line, but we provide
+ some leeway to accommodate nonconformant producers and explanatory text.
+ The 3 extra bytes are for the trailing CR+LF and NUL terminator. */
+ char line_chunk[128 + 3];
+ char *line_chunk_end;
+
+ if (!(line_chunk_end = file_getsp(line_chunk, sizeof line_chunk, fh))) {
+ *err = file_error(fh, err_info);
+ return FALSE;
+ }
+
+ // First chunk determines the line type.
+ if (memcmp(line_chunk, PREEB_BEGIN, PREEB_BEGIN_LEN) == 0)
+ *line_type = LINE_TYPE_PREEB;
+ else if (memcmp(line_chunk, POSTEB_BEGIN, POSTEB_BEGIN_LEN) == 0)
+ *line_type = LINE_TYPE_POSTEB;
+ else
+ *line_type = LINE_TYPE_OTHER;
+
+ for (;;) {
+ gsize line_chunk_len = line_chunk_end - line_chunk;
+ if (line_chunk_len > G_MAXINT - ws_buffer_length(buf)) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup_printf(
+ "File contains an encoding larger than the maximum of %d bytes",
+ G_MAXINT);
+ return FALSE;
+ }
+
+ ws_buffer_append(buf, line_chunk, line_chunk_len);
+
+ if (line_chunk_end[-1] == '\n' || file_eof(fh))
+ break;
+
+ if (!(line_chunk_end = file_getsp(line_chunk, sizeof line_chunk, fh))) {
+ *err = file_error(fh, err_info);
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+static gboolean rfc7468_read_impl(FILE_T fh, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info)
+{
+ ws_buffer_clean(buf);
+
+ gboolean saw_preeb = FALSE;
+
+ for (;;) {
+ enum line_type line_type;
+
+ if (!rfc7468_read_line(fh, &line_type, buf, err, err_info)) {
+ if (*err != 0 || !saw_preeb) return FALSE;
+
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("Missing post-encapsulation boundary at end of file");
+ return FALSE;
+ }
+
+ if (saw_preeb) {
+ if (line_type == LINE_TYPE_POSTEB) break;
+ } else {
+ if (line_type == LINE_TYPE_PREEB) saw_preeb = TRUE;
+ }
+ }
+
+ rec->rec_type = REC_TYPE_PACKET;
+ rec->presence_flags = 0;
+ rec->ts.secs = 0;
+ rec->ts.nsecs = 0;
+ rec->rec_header.packet_header.caplen = (guint32)ws_buffer_length(buf);
+ rec->rec_header.packet_header.len = (guint32)ws_buffer_length(buf);
+
+ return TRUE;
+}
+
+static gboolean rfc7468_read(wtap *wth, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info, gint64 *data_offset)
+{
+ *data_offset = file_tell(wth->fh);
+
+ return rfc7468_read_impl(wth->fh, rec, buf, err, err_info);
+}
+
+static gboolean rfc7468_seek_read(wtap *wth, gint64 seek_off, wtap_rec *rec,
+ Buffer *buf, int *err, gchar **err_info)
+{
+ if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) < 0)
+ return FALSE;
+
+ return rfc7468_read_impl(wth->random_fh, rec, buf, err, err_info);
+}
+
+wtap_open_return_val rfc7468_open(wtap *wth, int *err, gchar **err_info)
+{
+ /* To detect whether this file matches our format, we need to find the
+ first pre-encapsulation boundary, which may be located anywhere in the file,
+ since it may be preceded by explanatory text. However, we don't want to
+ read the entire file to find it, since the file may be huge, and detection
+ needs to be fast. Therefore, we'll assume that if the boundary exists,
+ it's located within a small initial chunk of the file. The size of
+ the chunk was chosen arbitrarily. */
+ char initial_chunk[2048];
+ int initial_chunk_size = file_read(&initial_chunk, sizeof initial_chunk, wth->fh);
+
+ if (initial_chunk_size < 0) {
+ *err = file_error(wth->fh, err_info);
+ return WTAP_OPEN_ERROR;
+ }
+
+ char *chunk_end_ptr = initial_chunk + initial_chunk_size;
+
+ // Try to find a line that starts with PREEB_BEGIN in the initial chunk.
+ for (char *line_ptr = initial_chunk; ; ) {
+ if ((unsigned)(chunk_end_ptr - line_ptr) < PREEB_BEGIN_LEN)
+ return WTAP_OPEN_NOT_MINE;
+
+ if (memcmp(line_ptr, PREEB_BEGIN, PREEB_BEGIN_LEN) == 0)
+ break;
+
+ // Try next line.
+ char *lf_ptr = memchr(line_ptr, '\n', chunk_end_ptr - line_ptr);
+ if (!lf_ptr)
+ return WTAP_OPEN_NOT_MINE;
+ line_ptr = lf_ptr + 1;
+ }
+
+ if (file_seek(wth->fh, 0, SEEK_SET, err) == -1)
+ return WTAP_OPEN_ERROR;
+
+ wth->file_type_subtype = rfc7468_file_type_subtype;
+ wth->file_encap = WTAP_ENCAP_RFC7468;
+
+ wth->snapshot_length = 0;
+ wth->file_tsprec = WTAP_TSPREC_SEC;
+
+ wth->subtype_read = rfc7468_read;
+ wth->subtype_seek_read = rfc7468_seek_read;
+
+ return WTAP_OPEN_MINE;
+}
+
+static const struct supported_block_type rfc7468_blocks_supported[] = {
+ /*
+ * We provide one "packet" for each encoded structure in the file,
+ * and don't support any options.
+ */
+ { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
+};
+
+static const struct file_type_subtype_info rfc7468_info = {
+ "RFC 7468 files", "rfc7468", NULL, NULL,
+ FALSE, BLOCKS_SUPPORTED(rfc7468_blocks_supported),
+ NULL, NULL, NULL
+};
+
+void register_rfc7468(void)
+{
+ rfc7468_file_type_subtype = wtap_register_file_type_subtype(&rfc7468_info);
+
+ /*
+ * Register name for backwards compatibility with the
+ * wtap_filetypes table in Lua.
+ */
+ wtap_register_backwards_compatibility_lua_name("RFC7468",
+ rfc7468_file_type_subtype);
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local Variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * vi: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */
diff --git a/wiretap/rfc7468.h b/wiretap/rfc7468.h
new file mode 100644
index 00000000..0a3d55f9
--- /dev/null
+++ b/wiretap/rfc7468.h
@@ -0,0 +1,27 @@
+/** @file
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __PEM_H__
+#define __PEM_H__
+
+#include <glib.h>
+#include "wtap.h"
+
+wtap_open_return_val rfc7468_open(wtap *wth, int *err, gchar **err_info);
+
+#endif
+
+/*
+* Editor modelines - https://www.wireshark.org/tools/modelines.html
+*
+* Local Variables:
+* c-basic-offset: 4
+* tab-width: 8
+* indent-tabs-mode: nil
+* End:
+*
+* vi: set shiftwidth=4 tabstop=8 expandtab:
+* :indentSize=4:tabSize=8:noTabs=true:
+*/
diff --git a/wiretap/rtpdump.c b/wiretap/rtpdump.c
new file mode 100644
index 00000000..cd18b28a
--- /dev/null
+++ b/wiretap/rtpdump.c
@@ -0,0 +1,356 @@
+/* rtpdump.c
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * Support for RTPDump file format
+ * Copyright (c) 2023 by David Perry <boolean263@protonmail.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+/* The rtpdump file format is the "dump" format as generated by rtpdump from
+ * <https://github.com/irtlab/rtptools>. It starts with an ASCII text header:
+ *
+ * #!rtpplay1.0 source_address/port\n
+ *
+ * followed by a binary header:
+ *
+ * typedef struct {
+ * struct timeval start; // start of recording (GMT)
+ * uint32_t source; // network source (multicast)
+ * uint16_t port; // UDP port
+ * } RD_hdr_t;
+ *
+ * Note that the SAME source address and port are included twice in
+ * this header, as seen here:
+ * <https://github.com/irtlab/rtptools/blob/9356740cb0c/rtpdump.c#L189>
+ *
+ * Wireshark can also generate rtpdump files, but it uses DIFFERENT addresses
+ * and ports in the text vs binary headers. See rtp_write_header() in
+ * ui/tap-rtp-common.c -- Wireshark puts the destination address and port
+ * in the text header, but the source address and port in the binary header.
+ *
+ * Wireshark may also generate the file with an IPv6 address in the text
+ * header, whereas rtpdump only supports IPv4 here. The binary header
+ * can't hold an IPv6 address without fully breaking compatibility with
+ * the rtptools project, so Wireshark truncates the address.
+ *
+ * Either way, each packet which follows is a RTP or RTCP packet of the form
+ *
+ * typedef struct {
+ * uint16_t length; // length of original packet, including header
+ * uint16_t plen; // actual header+payload length for RTP, 0 for RTCP
+ * uint32_t offset; // ms since the start of recording
+ * } RD_packet_t;
+ *
+ * followed by length bytes of packet data.
+ */
+
+#include "config.h"
+#include <wtap-int.h>
+#include <file_wrappers.h>
+#include <wsutil/exported_pdu_tlvs.h>
+#include <wsutil/inet_addr.h>
+#include <wsutil/nstime.h>
+#include <wsutil/strtoi.h>
+#include <wsutil/wslog.h>
+#include "rtpdump.h"
+#include <string.h>
+
+/* NB. I've included the version string in the magic for stronger identification.
+ * If we add/change version support, we'll also need to edit:
+ * - wiretap/mime_file.c
+ * - resources/freedesktop/org.wireshark.Wireshark-mime.xml
+ * - epan/dissectors/file-rtpdump.c
+ */
+#define RTP_MAGIC "#!rtpplay1.0 "
+#define RTP_MAGIC_LEN 13
+
+/* Reasonable maximum length for the RTP header (after the magic):
+ * - WS_INET6_ADDRSTRLEN characters for a IPv6 address
+ * - 1 for a slash
+ * - 5 characters for a port number
+ * - 1 character for a newline
+ * - 4 bytes for each of start seconds, start useconds, IPv4 address
+ * - 2 bytes for each of port, padding
+ * and 2 bytes of fudge factor, just in case.
+ */
+#define RTP_HEADER_MAX_LEN 25+WS_INET6_ADDRSTRLEN
+
+/* Reasonable buffer size for holding the Exported PDU headers:
+ * - 8 bytes for the port type header
+ * - 8 bytes for one port
+ * - 4+EXP_PDU_TAG_IPV6_LEN for one IPv6 address
+ * (same space would hold 2 IPv4 addresses with room to spare)
+ */
+#define RTP_BUFFER_INIT_LEN 20+EXP_PDU_TAG_IPV6_LEN
+
+static gboolean
+rtpdump_read(wtap *wth, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info,
+ gint64 *data_offset);
+
+static gboolean
+rtpdump_seek_read(wtap *wth, gint64 seek_off,
+ wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info);
+
+static void
+rtpdump_close(wtap *wth);
+
+static int rtpdump_file_type_subtype = -1;
+
+typedef union ip_addr_u {
+ ws_in4_addr ipv4;
+ ws_in6_addr ipv6;
+} ip_addr_t;
+
+typedef struct rtpdump_priv_s {
+ Buffer epdu_headers;
+ nstime_t start_time;
+} rtpdump_priv_t;
+
+void register_rtpdump(void);
+
+wtap_open_return_val
+rtpdump_open(wtap *wth, int *err, char **err_info)
+{
+ guint8 buf_magic[RTP_MAGIC_LEN];
+ char ch = '\0';
+ rtpdump_priv_t *priv = NULL;
+ ip_addr_t txt_addr;
+ ws_in4_addr bin_addr;
+ guint16 txt_port = 0;
+ guint16 bin_port = 0;
+ GString *header_str = NULL;
+ gboolean is_ipv6 = FALSE;
+ gboolean got_ip = FALSE;
+ nstime_t start_time = NSTIME_INIT_ZERO;
+ Buffer *buf;
+
+ if (!wtap_read_bytes(wth->fh, buf_magic, RTP_MAGIC_LEN, err, err_info)) {
+ return (*err == WTAP_ERR_SHORT_READ)
+ ? WTAP_OPEN_NOT_MINE
+ : WTAP_OPEN_ERROR;
+ }
+ if (strncmp(buf_magic, RTP_MAGIC, RTP_MAGIC_LEN) != 0) {
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ /* Parse the text header for an IP and port. */
+ header_str = g_string_sized_new(RTP_HEADER_MAX_LEN);
+ do {
+ if (!wtap_read_bytes(wth->fh, &ch, 1, err, err_info)) {
+ g_string_free(header_str, TRUE);
+ return (*err == WTAP_ERR_SHORT_READ)
+ ? WTAP_OPEN_NOT_MINE
+ : WTAP_OPEN_ERROR;
+ }
+
+ if (ch == '/') {
+ /* Everything up to now should be an IP address */
+ if (ws_inet_pton4(header_str->str, &txt_addr.ipv4)) {
+ is_ipv6 = FALSE;
+ }
+ else if (ws_inet_pton6(header_str->str, &txt_addr.ipv6)) {
+ is_ipv6 = TRUE;
+ }
+ else {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup("rtpdump: bad IP in header text");
+ g_string_free(header_str, TRUE);
+ return WTAP_OPEN_ERROR;
+ }
+
+ got_ip = TRUE;
+ g_string_truncate(header_str, 0);
+ }
+ else if (ch == '\n') {
+ if (!got_ip) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup("rtpdump: no IP in header text");
+ g_string_free(header_str, TRUE);
+ return WTAP_OPEN_ERROR;
+ }
+ if (!ws_strtou16(header_str->str, NULL, &txt_port)) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup("rtpdump: bad port in header text");
+ g_string_free(header_str, TRUE);
+ return WTAP_OPEN_ERROR;
+ }
+ break;
+ }
+ else if (g_ascii_isprint(ch)) {
+ g_string_append_c(header_str, ch);
+ }
+ else {
+ g_string_free(header_str, TRUE);
+ return WTAP_OPEN_NOT_MINE;
+ }
+ } while (ch != '\n');
+
+ g_string_free(header_str, TRUE);
+
+ if (!got_ip || txt_port == 0) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup("rtpdump: bad header text");
+ return WTAP_OPEN_ERROR;
+ }
+
+ /* Whether generated by rtpdump or Wireshark, the binary header
+ * has the source IP and port. If the text header has an IPv6 address,
+ * this address was likely truncated from an IPv6 address as well
+ * and is likely inaccurate, so we will ignore it.
+ */
+
+#define FAIL G_STMT_START { \
+ return (*err == WTAP_ERR_SHORT_READ) \
+ ? WTAP_OPEN_NOT_MINE \
+ : WTAP_OPEN_ERROR; \
+} G_STMT_END
+
+ if (!wtap_read_bytes(wth->fh, &start_time.secs, 4, err, err_info)) FAIL;
+ start_time.secs = g_ntohl(start_time.secs);
+
+ if (!wtap_read_bytes(wth->fh, &start_time.nsecs, 4, err, err_info)) FAIL;
+ start_time.nsecs = g_ntohl(start_time.nsecs) * 1000;
+
+ if (!wtap_read_bytes(wth->fh, &bin_addr, 4, err, err_info)) FAIL;
+ if (!wtap_read_bytes(wth->fh, &bin_port, 2, err, err_info)) FAIL;
+ bin_port = g_ntohs(bin_port);
+
+ /* Finally, padding */
+ if (!wtap_read_bytes(wth->fh, NULL, 2, err, err_info)) FAIL;
+
+#undef FAIL
+ /* If we made it this far, we have all the info we need to generate
+ * most of the Exported PDU headers for every packet in this stream.
+ */
+ priv = g_new0(rtpdump_priv_t, 1);
+ priv->start_time = start_time;
+ buf = &priv->epdu_headers; /* shorthand */
+ ws_buffer_init(buf, RTP_BUFFER_INIT_LEN);
+ wtap_buffer_append_epdu_uint(buf, EXP_PDU_TAG_PORT_TYPE, EXP_PDU_PT_UDP);
+ if (is_ipv6) {
+ /* File must be generated by Wireshark. Text address is IPv6 destination,
+ * binary address is invalid and ignored here.
+ */
+ wtap_buffer_append_epdu_tag(buf, EXP_PDU_TAG_IPV6_DST, (const guint8 *)&txt_addr.ipv6, EXP_PDU_TAG_IPV6_LEN);
+ wtap_buffer_append_epdu_uint(buf, EXP_PDU_TAG_DST_PORT, txt_port);
+ }
+ else if (txt_addr.ipv4 == bin_addr && txt_port == bin_port) {
+ /* File must be generated by rtpdump. Both addresses are IPv4 source. */
+ wtap_buffer_append_epdu_tag(buf, EXP_PDU_TAG_IPV4_SRC, (const guint8 *)&bin_addr, EXP_PDU_TAG_IPV4_LEN);
+ wtap_buffer_append_epdu_uint(buf, EXP_PDU_TAG_SRC_PORT, bin_port);
+ }
+ else {
+ /* File must be generated by Wireshark. Text is IPv4 destination,
+ * binary is IPv4 source.
+ */
+ wtap_buffer_append_epdu_tag(buf, EXP_PDU_TAG_IPV4_DST, (const guint8 *)&txt_addr.ipv4, EXP_PDU_TAG_IPV4_LEN);
+ wtap_buffer_append_epdu_uint(buf, EXP_PDU_TAG_DST_PORT, txt_port);
+
+ wtap_buffer_append_epdu_tag(buf, EXP_PDU_TAG_IPV4_SRC, (const guint8 *)&bin_addr, EXP_PDU_TAG_IPV4_LEN);
+ wtap_buffer_append_epdu_uint(buf, EXP_PDU_TAG_SRC_PORT, bin_port);
+ }
+
+ wth->priv = (void *)priv;
+ wth->subtype_close = rtpdump_close;
+ wth->subtype_read = rtpdump_read;
+ wth->subtype_seek_read = rtpdump_seek_read;
+ wth->file_type_subtype = rtpdump_file_type_subtype;
+ wth->file_encap = WTAP_ENCAP_WIRESHARK_UPPER_PDU;
+ /* Starting timestamp has microsecond precision, but delta time
+ * between packets is only milliseconds.
+ */
+ wth->file_tsprec = WTAP_TSPREC_MSEC;
+
+ return WTAP_OPEN_MINE;
+}
+
+static gboolean
+rtpdump_read_packet(wtap *wth, FILE_T fh, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info)
+{
+ rtpdump_priv_t *priv = (rtpdump_priv_t *)wth->priv;
+ nstime_t ts = NSTIME_INIT_ZERO;
+ guint32 epdu_len = 0; /* length of the Exported PDU headers we add */
+ const guint8 hdr_len = 8; /* Header comprised of the following 3 fields: */
+ guint16 length; /* length of packet, including this header (may
+ be smaller than plen if not whole packet recorded) */
+ guint16 plen; /* actual header+payload length for RTP, 0 for RTCP */
+ guint32 offset; /* milliseconds since the start of recording */
+
+ if (!wtap_read_bytes_or_eof(fh, (void *)&length, 2, err, err_info)) return FALSE;
+ length = g_ntohs(length);
+ if (!wtap_read_bytes(fh, (void *)&plen, 2, err, err_info)) return FALSE;
+ plen = g_ntohs(plen);
+ if (!wtap_read_bytes(fh, (void *)&offset, 4, err, err_info)) return FALSE;
+ offset = g_ntohl(offset);
+
+ /* Set length to remaining length of packet data */
+ length -= hdr_len;
+
+ ws_buffer_append_buffer(buf, &priv->epdu_headers);
+ if (plen == 0) {
+ /* RTCP sample */
+ plen = length;
+ wtap_buffer_append_epdu_string(buf, EXP_PDU_TAG_DISSECTOR_NAME, "rtcp");
+ }
+ else {
+ /* RTP sample */
+ wtap_buffer_append_epdu_string(buf, EXP_PDU_TAG_DISSECTOR_NAME, "rtp");
+ }
+ epdu_len = wtap_buffer_append_epdu_end(buf);
+
+ /* Offset is milliseconds since the start of recording */
+ ts.secs = offset / 1000;
+ ts.nsecs = (offset % 1000) * 1000000;
+ nstime_sum(&rec->ts, &priv->start_time, &ts);
+ rec->presence_flags |= WTAP_HAS_TS | WTAP_HAS_CAP_LEN;
+ rec->rec_header.packet_header.caplen = epdu_len + plen;
+ rec->rec_header.packet_header.len = epdu_len + length;
+ rec->rec_type = REC_TYPE_PACKET;
+
+ return wtap_read_packet_bytes(fh, buf, length, err, err_info);
+}
+
+static gboolean
+rtpdump_read(wtap *wth, wtap_rec *rec, Buffer *buf, int *err, gchar **err_info,
+ gint64 *data_offset)
+{
+ *data_offset = file_tell(wth->fh);
+ return rtpdump_read_packet(wth, wth->fh, rec, buf, err, err_info);
+}
+
+static gboolean
+rtpdump_seek_read(wtap *wth, gint64 seek_off, wtap_rec *rec,
+ Buffer *buf, int *err, gchar **err_info)
+{
+ if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
+ return FALSE;
+ return rtpdump_read_packet(wth, wth->random_fh, rec, buf, err, err_info);
+}
+
+static void
+rtpdump_close(wtap *wth)
+{
+ rtpdump_priv_t *priv = (rtpdump_priv_t *)wth->priv;
+ ws_buffer_free(&priv->epdu_headers);
+}
+
+static const struct supported_block_type rtpdump_blocks_supported[] = {
+ /* We support packet blocks, with no comments or other options. */
+ { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
+};
+
+static const struct file_type_subtype_info rtpdump_info = {
+ "RTPDump stream file", "rtpdump", "rtp", "rtpdump",
+ FALSE, BLOCKS_SUPPORTED(rtpdump_blocks_supported),
+ NULL, NULL, NULL
+};
+
+void register_rtpdump(void)
+{
+ rtpdump_file_type_subtype = wtap_register_file_type_subtype(&rtpdump_info);
+}
diff --git a/wiretap/rtpdump.h b/wiretap/rtpdump.h
new file mode 100644
index 00000000..5245c8c7
--- /dev/null
+++ b/wiretap/rtpdump.h
@@ -0,0 +1,20 @@
+/** @file
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * Support for RTPDump file format
+ * Copyright (c) 2023 by David Perry <boolean263@protonmail.com
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef RTPDUMP_H__
+#define RTPDUMP_H__
+
+#include <wiretap/wtap.h>
+
+wtap_open_return_val
+rtpdump_open(wtap *wth, int *err, char **err_info);
+
+#endif /* RTPDUMP_H__ */
diff --git a/wiretap/ruby_marshal.c b/wiretap/ruby_marshal.c
new file mode 100644
index 00000000..026f7b65
--- /dev/null
+++ b/wiretap/ruby_marshal.c
@@ -0,0 +1,123 @@
+/* ruby_marshal.c
+ *
+ * Routines for reading a binary file containing a ruby marshal object
+ *
+ * Copyright 2018, Dario Lombardo <lomato@gmail.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include "wtap-int.h"
+#include "file_wrappers.h"
+
+#include "ruby_marshal.h"
+
+static int ruby_marshal_file_type_subtype = -1;
+
+void register_ruby_marshal(void);
+
+static gboolean is_ruby_marshal(const guint8* filebuf)
+{
+ if (filebuf[0] != RUBY_MARSHAL_MAJOR)
+ return FALSE;
+ if (filebuf[1] != RUBY_MARSHAL_MINOR)
+ return FALSE;
+ switch (filebuf[2]) {
+ case '0':
+ case 'T':
+ case 'F':
+ case 'i':
+ case ':':
+ case '"':
+ case 'I':
+ case '[':
+ case '{':
+ case 'f':
+ case 'c':
+ case 'm':
+ case 'S':
+ case '/':
+ case 'o':
+ case 'C':
+ case 'e':
+ case ';':
+ case '@':
+ return TRUE;
+ break;
+ default:
+ return FALSE;
+ }
+}
+
+wtap_open_return_val ruby_marshal_open(wtap *wth, int *err, gchar **err_info)
+{
+ /* The size of this buffer should match the expectations of is_ruby_marshal */
+ guint8 filebuf[3];
+ int bytes_read;
+
+ bytes_read = file_read(filebuf, sizeof(filebuf), wth->fh);
+ if (bytes_read < 0) {
+ /* Read error. */
+ *err = file_error(wth->fh, err_info);
+ return WTAP_OPEN_ERROR;
+ }
+
+ if (bytes_read != sizeof(filebuf) || !is_ruby_marshal(filebuf)) {
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ if (file_seek(wth->fh, 0, SEEK_SET, err) == -1) {
+ return WTAP_OPEN_ERROR;
+ }
+
+ wth->file_type_subtype = ruby_marshal_file_type_subtype;
+ wth->file_encap = WTAP_ENCAP_RUBY_MARSHAL;
+ wth->file_tsprec = WTAP_TSPREC_SEC;
+ wth->subtype_read = wtap_full_file_read;
+ wth->subtype_seek_read = wtap_full_file_seek_read;
+ wth->snapshot_length = 0;
+
+ return WTAP_OPEN_MINE;
+}
+
+static const struct supported_block_type ruby_marshal_blocks_supported[] = {
+ /*
+ * We support packet blocks, with no comments or other options.
+ */
+ { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
+};
+
+static const struct file_type_subtype_info ruby_marshal_info = {
+ "Ruby marshal files", "ruby_marshal", NULL, NULL,
+ FALSE, BLOCKS_SUPPORTED(ruby_marshal_blocks_supported),
+ NULL, NULL, NULL
+};
+
+void register_ruby_marshal(void)
+{
+ ruby_marshal_file_type_subtype = wtap_register_file_type_subtype(&ruby_marshal_info);
+
+ /*
+ * Register name for backwards compatibility with the
+ * wtap_filetypes table in Lua.
+ */
+ wtap_register_backwards_compatibility_lua_name("RUBY_MARSHAL",
+ ruby_marshal_file_type_subtype);
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * vi: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */
diff --git a/wiretap/ruby_marshal.h b/wiretap/ruby_marshal.h
new file mode 100644
index 00000000..e89d3bf7
--- /dev/null
+++ b/wiretap/ruby_marshal.h
@@ -0,0 +1,35 @@
+/** @file
+ *
+ * Copyright 2018, Dario Lombardo <lomato@gmail.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ */
+
+#ifndef __RUBY_MARSHAL_H__
+#define __RUBY_MARSHAL_H__
+
+#include <glib.h>
+
+#include "wtap.h"
+
+// Current Ruby Marshal library version
+#define RUBY_MARSHAL_MAJOR 4
+#define RUBY_MARSHAL_MINOR 8
+
+wtap_open_return_val ruby_marshal_open(wtap *wth, int *err, gchar **err_info);
+
+#endif
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * vi: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */
diff --git a/wiretap/secrets-types.h b/wiretap/secrets-types.h
new file mode 100644
index 00000000..7e44a736
--- /dev/null
+++ b/wiretap/secrets-types.h
@@ -0,0 +1,24 @@
+/** @file
+ *
+ * Identifiers used by Decryption Secrets Blocks (DSB).
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __SECRETS_TYPES_H__
+#define __SECRETS_TYPES_H__
+
+/*
+ * Type describing the format of the opaque secrets value in a pcapng DSB.
+ */
+#define SECRETS_TYPE_TLS 0x544c534b /* TLS Key Log */
+#define SECRETS_TYPE_SSH 0x5353484b /* SSH Key Log */
+#define SECRETS_TYPE_WIREGUARD 0x57474b4c /* WireGuard Key Log */
+#define SECRETS_TYPE_ZIGBEE_NWK_KEY 0x5a4e574b /* Zigbee NWK Key */
+#define SECRETS_TYPE_ZIGBEE_APS_KEY 0x5a415053 /* Zigbee APS Key */
+
+#endif /* __SECRETS_TYPES_H__ */
diff --git a/wiretap/snoop.c b/wiretap/snoop.c
new file mode 100644
index 00000000..8621be6e
--- /dev/null
+++ b/wiretap/snoop.c
@@ -0,0 +1,1013 @@
+/* snoop.c
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "config.h"
+#include <string.h>
+#include "wtap-int.h"
+#include "file_wrappers.h"
+#include "atm.h"
+#include "snoop.h"
+#include <wsutil/802_11-utils.h>
+#include <wsutil/ws_roundup.h>
+
+/* See RFC 1761 for a description of the "snoop" file format. */
+
+typedef struct {
+ gboolean is_shomiti;
+} snoop_t;
+
+/* Magic number in "snoop" files. */
+static const char snoop_magic[] = {
+ 's', 'n', 'o', 'o', 'p', '\0', '\0', '\0'
+};
+
+/* "snoop" file header (minus magic number). */
+struct snoop_hdr {
+ guint32 version; /* version number (should be 2) */
+ guint32 network; /* network type */
+};
+
+/* "snoop" record header. */
+struct snooprec_hdr {
+ guint32 orig_len; /* actual length of packet */
+ guint32 incl_len; /* number of octets captured in file */
+ guint32 rec_len; /* length of record */
+ guint32 cum_drops; /* cumulative number of dropped packets */
+ guint32 ts_sec; /* timestamp seconds */
+ guint32 ts_usec; /* timestamp microseconds */
+};
+
+/*
+ * The link-layer header on ATM packets.
+ */
+struct snoop_atm_hdr {
+ guint8 flags; /* destination and traffic type */
+ guint8 vpi; /* VPI */
+ guint16 vci; /* VCI */
+};
+
+/*
+ * Extra information stuffed into the padding in Shomiti/Finisar Surveyor
+ * captures.
+ */
+struct shomiti_trailer {
+ guint16 phy_rx_length; /* length on the wire, including FCS? */
+ guint16 phy_rx_status; /* status flags */
+ guint32 ts_40_ns_lsb; /* 40 ns time stamp, low-order bytes? */
+ guint32 ts_40_ns_msb; /* 40 ns time stamp, low-order bytes? */
+ gint32 frame_id; /* "FrameID"? */
+};
+
+/*
+ * phy_rx_status flags.
+ */
+#define RX_STATUS_OVERFLOW 0x8000 /* overflow error */
+#define RX_STATUS_BAD_CRC 0x4000 /* CRC error */
+#define RX_STATUS_DRIBBLE_NIBBLE 0x2000 /* dribble/nibble bits? */
+#define RX_STATUS_SHORT_FRAME 0x1000 /* frame < 64 bytes */
+#define RX_STATUS_OVERSIZE_FRAME 0x0800 /* frame > 1518 bytes */
+#define RX_STATUS_GOOD_FRAME 0x0400 /* frame OK */
+#define RX_STATUS_N12_BYTES_RECEIVED 0x0200 /* first 12 bytes of frame received? */
+#define RX_STATUS_RXABORT 0x0100 /* RXABORT during reception */
+#define RX_STATUS_FIFO_ERROR 0x0080 /* receive FIFO error */
+#define RX_STATUS_TRIGGERED 0x0001 /* frame did trigger */
+
+static gboolean snoop_read(wtap *wth, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info, gint64 *data_offset);
+static gboolean snoop_seek_read(wtap *wth, gint64 seek_off,
+ wtap_rec *rec, Buffer *buf, int *err, gchar **err_info);
+static int snoop_read_packet(wtap *wth, FILE_T fh, wtap_rec *rec,
+ Buffer *buf, int *err, gchar **err_info);
+static gboolean snoop_read_atm_pseudoheader(FILE_T fh,
+ union wtap_pseudo_header *pseudo_header, int *err, gchar **err_info);
+static gboolean snoop_read_shomiti_wireless_pseudoheader(FILE_T fh,
+ union wtap_pseudo_header *pseudo_header, int *err, gchar **err_info,
+ int *header_size);
+static gboolean snoop_dump(wtap_dumper *wdh, const wtap_rec *rec,
+ const guint8 *pd, int *err, gchar **err_info);
+
+static int snoop_file_type_subtype = -1;
+static int shomiti_file_type_subtype = -1;
+
+void register_snoop(void);
+
+/*
+ * See
+ *
+ * https://pubs.opengroup.org/onlinepubs/9638599/apdxf.htm
+ *
+ * for the "dlpi.h" header file specified by The Open Group, which lists
+ * the DL_ values for various protocols; Solaris 7 uses the same values.
+ *
+ * See
+ *
+ * https://www.iana.org/assignments/snoop-datalink-types
+ *
+ * for the IETF list of snoop datalink types.
+ *
+ * The page at
+ *
+ * http://mrpink.lerc.nasa.gov/118x/support.html
+ *
+ * had links to modified versions of "tcpdump" and "libpcap" for SUNatm
+ * DLPI support; they suggested that the 3.0 version of SUNatm uses those
+ * values. The Wayback Machine archived that page, but not the stuff
+ * to which it linked, unfortunately.
+ *
+ * It also has a link to "convert.c", which is a program to convert files
+ * from the format written by the "atmsnoop" program that comes with the
+ * SunATM package to regular "snoop" format, claims that "SunATM 2.1 claimed
+ * to be DL_FDDI (don't ask why). SunATM 3.0 claims to be DL_IPATM, which
+ * is 0x12".
+ *
+ * It also says that "ATM Mac header is 12 bytes long.", and seems to imply
+ * that in an "atmsnoop" file, the header contains 2 bytes (direction and
+ * VPI?), 2 bytes of VCI, 6 bytes of something, and 2 bytes of Ethernet
+ * type; if those 6 bytes are 2 bytes of DSAP, 2 bytes of LSAP, 1 byte
+ * of LLC control, and 3 bytes of SNAP OUI, that'd mean that an ATM
+ * pseudo-header in an "atmsnoop" file is probably 1 byte of direction,
+ * 1 byte of VPI, and 2 bytes of VCI.
+ *
+ * The aforementioned page also has a link to some capture files from
+ * "atmsnoop"; this version of "snoop.c" appears to be able to read them.
+ *
+ * Source to an "atmdump" package, which includes a modified version of
+ * "libpcap" to handle SunATM DLPI and an ATM driver for FreeBSD, and
+ * also includes "atmdump", which is a modified "tcpdump", was available
+ * at
+ *
+ * ftp://ftp.cs.ndsu.nodak.edu/pub/freebsd/atm/atm-bpf.tgz
+ *
+ * (the host name is no longer valid) and that code also indicated that
+ * DL_IPATM is used, and that an ATM packet handed up from the Sun driver
+ * for the Sun SBus ATM card on Solaris 2.5.1 has 1 byte of direction,
+ * 1 byte of VPI, 2 bytes of VCI, and then the ATM PDU, and suggests that
+ * the direction flag is 0x80 for "transmitted" (presumably meaning
+ * DTE->DCE) and presumably not 0x80 for "received" (presumably meaning
+ * DCE->DTE). That code was used as the basis for the SunATM support in
+ * later versions of libpcap and tcpdump, and it worked at the time the
+ * development was done with the SunATM code on the system on which the
+ * development was done.
+ *
+ * In fact, the "direction" byte appears to have some other stuff, perhaps
+ * a traffic type, in the lower 7 bits, with the 8th bit indicating the
+ * direction. That appears to be the case.
+ *
+ * I don't know what the encapsulation of any of the other types is, so I
+ * leave them all as WTAP_ENCAP_UNKNOWN, except for those for which Brian
+ * Ginsbach has supplied information about the way UNICOS/mp uses them.
+ * I also don't know whether "snoop" can handle any of them (it presumably
+ * can't handle ATM, otherwise Sun wouldn't have supplied "atmsnoop"; even
+ * if it can't, this may be useful reference information for anybody doing
+ * code to use DLPI to do raw packet captures on those network types.
+ *
+ * https://web.archive.org/web/20010906213807/http://www.shomiti.com/support/TNCapFileFormat.htm
+ *
+ * gives information on Shomiti's mutant flavor of snoop. For some unknown
+ * reason, they decided not to just Go With The DLPI Flow, and instead used
+ * the types unspecified in RFC 1461 for their own nefarious purposes, such
+ * as distinguishing 10MB from 100MB from 1000MB Ethernet and distinguishing
+ * 4MB from 16MB Token Ring, and distinguishing both of them from the
+ * "Shomiti" versions of same.
+ */
+wtap_open_return_val snoop_open(wtap *wth, int *err, gchar **err_info)
+{
+ char magic[sizeof snoop_magic];
+ struct snoop_hdr hdr;
+ struct snooprec_hdr rec_hdr;
+ guint padbytes;
+ gboolean is_shomiti;
+ static const int snoop_encap[] = {
+ WTAP_ENCAP_ETHERNET, /* IEEE 802.3 */
+ WTAP_ENCAP_UNKNOWN, /* IEEE 802.4 Token Bus */
+ WTAP_ENCAP_TOKEN_RING,
+ WTAP_ENCAP_UNKNOWN, /* IEEE 802.6 Metro Net */
+ WTAP_ENCAP_ETHERNET,
+ WTAP_ENCAP_UNKNOWN, /* HDLC */
+ WTAP_ENCAP_UNKNOWN, /* Character Synchronous, e.g. bisync */
+ WTAP_ENCAP_UNKNOWN, /* IBM Channel-to-Channel */
+ WTAP_ENCAP_FDDI_BITSWAPPED,
+ WTAP_ENCAP_NULL, /* Other */
+ WTAP_ENCAP_UNKNOWN, /* Frame Relay LAPF */
+ WTAP_ENCAP_UNKNOWN, /* Multi-protocol over Frame Relay */
+ WTAP_ENCAP_UNKNOWN, /* Character Async (e.g., SLIP and PPP?) */
+ WTAP_ENCAP_UNKNOWN, /* X.25 Classical IP */
+ WTAP_ENCAP_NULL, /* software loopback */
+ WTAP_ENCAP_UNKNOWN, /* not defined in "dlpi.h" */
+ WTAP_ENCAP_IP_OVER_FC, /* Fibre Channel */
+ WTAP_ENCAP_UNKNOWN, /* ATM */
+ WTAP_ENCAP_ATM_PDUS, /* ATM Classical IP */
+ WTAP_ENCAP_UNKNOWN, /* X.25 LAPB */
+ WTAP_ENCAP_UNKNOWN, /* ISDN */
+ WTAP_ENCAP_UNKNOWN, /* HIPPI */
+ WTAP_ENCAP_UNKNOWN, /* 100VG-AnyLAN Ethernet */
+ WTAP_ENCAP_UNKNOWN, /* 100VG-AnyLAN Token Ring */
+ WTAP_ENCAP_UNKNOWN, /* "ISO 8802/3 and Ethernet" */
+ WTAP_ENCAP_UNKNOWN, /* 100BaseT (but that's just Ethernet) */
+ WTAP_ENCAP_IP_OVER_IB_SNOOP, /* Infiniband */
+ };
+ #define NUM_SNOOP_ENCAPS (sizeof snoop_encap / sizeof snoop_encap[0])
+ #define SNOOP_PRIVATE_BIT 0x80000000
+ static const int snoop_private_encap[] = {
+ WTAP_ENCAP_UNKNOWN, /* Not Used */
+ WTAP_ENCAP_UNKNOWN, /* IPv4 Tunnel Link */
+ WTAP_ENCAP_UNKNOWN, /* IPv6 Tunnel Link */
+ WTAP_ENCAP_UNKNOWN, /* Virtual network interface */
+ WTAP_ENCAP_UNKNOWN, /* IEEE 802.11 */
+ WTAP_ENCAP_IPNET, /* ipnet(7D) link */
+ WTAP_ENCAP_UNKNOWN, /* IPMP stub interface */
+ WTAP_ENCAP_UNKNOWN, /* 6to4 Tunnel Link */
+ };
+ #define NUM_SNOOP_PRIVATE_ENCAPS (sizeof snoop_private_encap / sizeof snoop_private_encap[0])
+ static const int shomiti_encap[] = {
+ WTAP_ENCAP_ETHERNET, /* IEEE 802.3 */
+ WTAP_ENCAP_UNKNOWN, /* IEEE 802.4 Token Bus */
+ WTAP_ENCAP_TOKEN_RING,
+ WTAP_ENCAP_UNKNOWN, /* IEEE 802.6 Metro Net */
+ WTAP_ENCAP_ETHERNET,
+ WTAP_ENCAP_UNKNOWN, /* HDLC */
+ WTAP_ENCAP_UNKNOWN, /* Character Synchronous, e.g. bisync */
+ WTAP_ENCAP_UNKNOWN, /* IBM Channel-to-Channel */
+ WTAP_ENCAP_FDDI_BITSWAPPED,
+ WTAP_ENCAP_UNKNOWN, /* Other */
+ WTAP_ENCAP_ETHERNET, /* Fast Ethernet */
+ WTAP_ENCAP_TOKEN_RING, /* 4MB 802.5 token ring */
+ WTAP_ENCAP_ETHERNET, /* Gigabit Ethernet */
+ WTAP_ENCAP_TOKEN_RING, /* "IEEE 802.5 Shomiti" */
+ WTAP_ENCAP_TOKEN_RING, /* "4MB IEEE 802.5 Shomiti" */
+ WTAP_ENCAP_UNKNOWN, /* Other */
+ WTAP_ENCAP_UNKNOWN, /* Other */
+ WTAP_ENCAP_UNKNOWN, /* Other */
+ WTAP_ENCAP_IEEE_802_11_WITH_RADIO, /* IEEE 802.11 with Radio Header */
+ WTAP_ENCAP_ETHERNET, /* 10 Gigabit Ethernet */
+ };
+ #define NUM_SHOMITI_ENCAPS (sizeof shomiti_encap / sizeof shomiti_encap[0])
+ int file_encap;
+ gint64 saved_offset;
+ snoop_t *snoop;
+
+ /* Read in the string that should be at the start of a "snoop" file */
+ if (!wtap_read_bytes(wth->fh, magic, sizeof magic, err, err_info)) {
+ if (*err != WTAP_ERR_SHORT_READ)
+ return WTAP_OPEN_ERROR;
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ if (memcmp(magic, snoop_magic, sizeof snoop_magic) != 0) {
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ /* Read the rest of the header. */
+ if (!wtap_read_bytes(wth->fh, &hdr, sizeof hdr, err, err_info))
+ return WTAP_OPEN_ERROR;
+
+ /*
+ * Make sure it's a version we support.
+ */
+ hdr.version = g_ntohl(hdr.version);
+ switch (hdr.version) {
+
+ case 2: /* Solaris 2.x and later snoop, and Shomiti
+ Surveyor prior to 3.0, or 3.0 and later
+ with NDIS card */
+ case 3: /* Surveyor 3.0 and later, with Shomiti CMM2 hardware */
+ case 4: /* Surveyor 3.0 and later, with Shomiti GAM hardware */
+ case 5: /* Surveyor 3.0 and later, with Shomiti THG hardware */
+ break;
+
+ default:
+ *err = WTAP_ERR_UNSUPPORTED;
+ *err_info = ws_strdup_printf("snoop: version %u unsupported", hdr.version);
+ return WTAP_OPEN_ERROR;
+ }
+
+ /*
+ * Oh, this is lovely.
+ *
+ * I suppose Shomiti could give a bunch of lawyerly noise about
+ * how "well, RFC 1761 said they were unassigned, and that's
+ * the standard, not the DLPI header file, so it's perfectly OK
+ * for us to use them, blah blah blah", but it's still irritating
+ * as hell that they used the unassigned-in-RFC-1761 values for
+ * their own purposes - especially given that Sun also used
+ * one of them in atmsnoop.
+ *
+ * We can't determine whether it's a Shomiti capture based on
+ * the version number, as, according to their documentation on
+ * their capture file format, Shomiti uses a version number of 2
+ * if the data "was captured using an NDIS card", which presumably
+ * means "captured with an ordinary boring network card via NDIS"
+ * as opposed to "captured with our whizzo special capture
+ * hardware".
+ *
+ * The only way I can see to determine that is to check how much
+ * padding there is in the first packet - if there's enough
+ * padding for a Shomiti trailer, it's probably a Shomiti
+ * capture, and otherwise, it's probably from Snoop.
+ */
+
+ /*
+ * Start out assuming it's not a Shomiti capture.
+ */
+ is_shomiti = FALSE;
+
+ /* Read first record header. */
+ saved_offset = file_tell(wth->fh);
+ if (!wtap_read_bytes_or_eof(wth->fh, &rec_hdr, sizeof rec_hdr, err, err_info)) {
+ if (*err != 0)
+ return WTAP_OPEN_ERROR;
+
+ /*
+ * The file ends after the record header, which means this
+ * is a capture with no packets.
+ *
+ * We assume it's a snoop file; the actual type of file is
+ * irrelevant, as there are no records in it, and thus no
+ * extra information if it's a Shomiti capture, and no
+ * link-layer headers whose type we have to know, and no
+ * Ethernet frames that might have an FCS.
+ */
+ } else {
+ /*
+ * Compute the number of bytes of padding in the
+ * record. If it's at least the size of a Shomiti
+ * trailer record, we assume this is a Shomiti
+ * capture. (Some atmsnoop captures appear
+ * to have 4 bytes of padding, and at least one
+ * snoop capture appears to have 6 bytes of padding;
+ * the Shomiti header is larger than either of those.)
+ */
+ if (g_ntohl(rec_hdr.rec_len) >
+ (sizeof rec_hdr + g_ntohl(rec_hdr.incl_len))) {
+ /*
+ * Well, we have padding; how much?
+ */
+ padbytes = g_ntohl(rec_hdr.rec_len) -
+ ((guint)sizeof rec_hdr + g_ntohl(rec_hdr.incl_len));
+
+ /*
+ * Is it at least the size of a Shomiti trailer?
+ */
+ is_shomiti =
+ (padbytes >= sizeof (struct shomiti_trailer));
+ }
+ }
+
+ /*
+ * Seek back to the beginning of the first record.
+ */
+ if (file_seek(wth->fh, saved_offset, SEEK_SET, err) == -1)
+ return WTAP_OPEN_ERROR;
+
+ hdr.network = g_ntohl(hdr.network);
+ if (is_shomiti) {
+ if (hdr.network >= NUM_SHOMITI_ENCAPS
+ || shomiti_encap[hdr.network] == WTAP_ENCAP_UNKNOWN) {
+ *err = WTAP_ERR_UNSUPPORTED;
+ *err_info = ws_strdup_printf("snoop: Shomiti network type %u unknown or unsupported",
+ hdr.network);
+ return WTAP_OPEN_ERROR;
+ }
+ file_encap = shomiti_encap[hdr.network];
+ } else if (hdr.network & SNOOP_PRIVATE_BIT) {
+ if ((hdr.network^SNOOP_PRIVATE_BIT) >= NUM_SNOOP_PRIVATE_ENCAPS
+ || snoop_private_encap[hdr.network^SNOOP_PRIVATE_BIT] == WTAP_ENCAP_UNKNOWN) {
+ *err = WTAP_ERR_UNSUPPORTED;
+ *err_info = ws_strdup_printf("snoop: private network type %u unknown or unsupported",
+ hdr.network);
+ return WTAP_OPEN_ERROR;
+ }
+ file_encap = snoop_private_encap[hdr.network^SNOOP_PRIVATE_BIT];
+ } else {
+ if (hdr.network >= NUM_SNOOP_ENCAPS
+ || snoop_encap[hdr.network] == WTAP_ENCAP_UNKNOWN) {
+ *err = WTAP_ERR_UNSUPPORTED;
+ *err_info = ws_strdup_printf("snoop: network type %u unknown or unsupported",
+ hdr.network);
+ return WTAP_OPEN_ERROR;
+ }
+ file_encap = snoop_encap[hdr.network];
+ }
+
+ /*
+ * We don't currently use the extra information in Shomiti
+ * records, so we use the same routines to read snoop and
+ * Shomiti files.
+ */
+ wth->file_type_subtype = is_shomiti ? shomiti_file_type_subtype : snoop_file_type_subtype;
+ snoop = g_new0(snoop_t, 1);
+ wth->priv = (void *)snoop;
+ wth->subtype_read = snoop_read;
+ wth->subtype_seek_read = snoop_seek_read;
+ wth->file_encap = file_encap;
+ wth->snapshot_length = 0; /* not available in header */
+ wth->file_tsprec = WTAP_TSPREC_USEC;
+ snoop->is_shomiti = is_shomiti;
+
+ /*
+ * Add an IDB; we don't know how many interfaces were
+ * involved, so we just say one interface, about which
+ * we only know the link-layer type, snapshot length,
+ * and time stamp resolution.
+ */
+ wtap_add_generated_idb(wth);
+
+ return WTAP_OPEN_MINE;
+}
+
+/*
+ * XXX - pad[3] is the length of the header, not including
+ * the length of the pad field; is it a 1-byte field, a 2-byte
+ * field with pad[2] usually being 0, a 3-byte field with
+ * pad[1] and pad[2] usually being 0, or a 4-byte field?
+ *
+ * If it's not a 4-byte field, is there anything significant
+ * in the other bytes?
+ *
+ * Can the header length ever be less than 8, so that not
+ * all the fields following pad are present?
+ *
+ * What's in undecrypt? In captures I've seen, undecrypt[0]
+ * is usually 0x00 but sometimes 0x02 or 0x06, and undecrypt[1]
+ * is either 0x00 or 0x02.
+ *
+ * What's in preamble? In captures I've seen, it's 0x00.
+ *
+ * What's in code? In captures I've seen, it's 0x01 or 0x03.
+ *
+ * If the header is longer than 8 bytes, what are the other fields?
+ */
+typedef struct {
+ guint8 pad[4];
+ guint8 undecrypt[2];
+ guint8 rate;
+ guint8 preamble;
+ guint8 code;
+ guint8 signal;
+ guint8 qual;
+ guint8 channel;
+} shomiti_wireless_header;
+
+
+/* Read the next packet */
+static gboolean snoop_read(wtap *wth, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info, gint64 *data_offset)
+{
+ int padbytes;
+
+ *data_offset = file_tell(wth->fh);
+
+ padbytes = snoop_read_packet(wth, wth->fh, rec, buf, err, err_info);
+ if (padbytes == -1)
+ return FALSE;
+
+ /*
+ * Skip over the padding, if any.
+ */
+ if (padbytes != 0) {
+ if (!wtap_read_bytes(wth->fh, NULL, padbytes, err, err_info))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+snoop_seek_read(wtap *wth, gint64 seek_off,
+ wtap_rec *rec, Buffer *buf, int *err, gchar **err_info)
+{
+ if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
+ return FALSE;
+
+ if (snoop_read_packet(wth, wth->random_fh, rec, buf, err, err_info) == -1) {
+ if (*err == 0)
+ *err = WTAP_ERR_SHORT_READ;
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static int
+snoop_read_packet(wtap *wth, FILE_T fh, wtap_rec *rec,
+ Buffer *buf, int *err, gchar **err_info)
+{
+ snoop_t *snoop = (snoop_t *)wth->priv;
+ struct snooprec_hdr hdr;
+ guint32 rec_size;
+ guint32 packet_size;
+ guint32 orig_size;
+ int header_size;
+
+ /* Read record header. */
+ if (!wtap_read_bytes_or_eof(fh, &hdr, sizeof hdr, err, err_info))
+ return -1;
+
+ rec_size = g_ntohl(hdr.rec_len);
+ orig_size = g_ntohl(hdr.orig_len);
+ packet_size = g_ntohl(hdr.incl_len);
+ if (orig_size > WTAP_MAX_PACKET_SIZE_STANDARD) {
+ /*
+ * Probably a corrupt capture file; don't blow up trying
+ * to allocate space for an immensely-large packet.
+ */
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("snoop: File has %u-byte original length, bigger than maximum of %u",
+ orig_size, WTAP_MAX_PACKET_SIZE_STANDARD);
+ return -1;
+ }
+ if (packet_size > WTAP_MAX_PACKET_SIZE_STANDARD) {
+ /*
+ * Probably a corrupt capture file; don't blow up trying
+ * to allocate space for an immensely-large packet.
+ */
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("snoop: File has %u-byte packet, bigger than maximum of %u",
+ packet_size, WTAP_MAX_PACKET_SIZE_STANDARD);
+ return -1;
+ }
+ if (packet_size > rec_size) {
+ /*
+ * Probably a corrupt capture file.
+ */
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("snoop: File has %u-byte packet, bigger than record size %u",
+ packet_size, rec_size);
+ return -1;
+ }
+
+ switch (wth->file_encap) {
+
+ case WTAP_ENCAP_ATM_PDUS:
+ /*
+ * This is an ATM packet, so the first four bytes are
+ * the direction of the packet (transmit/receive), the
+ * VPI, and the VCI; read them and generate the
+ * pseudo-header from them.
+ */
+ if (packet_size < sizeof (struct snoop_atm_hdr)) {
+ /*
+ * Uh-oh, the packet isn't big enough to even
+ * have a pseudo-header.
+ */
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("snoop: atmsnoop file has a %u-byte packet, too small to have even an ATM pseudo-header",
+ packet_size);
+ return -1;
+ }
+ if (!snoop_read_atm_pseudoheader(fh, &rec->rec_header.packet_header.pseudo_header,
+ err, err_info))
+ return -1; /* Read error */
+
+ /*
+ * Don't count the pseudo-header as part of the packet.
+ */
+ rec_size -= (guint32)sizeof (struct snoop_atm_hdr);
+ orig_size -= (guint32)sizeof (struct snoop_atm_hdr);
+ packet_size -= (guint32)sizeof (struct snoop_atm_hdr);
+ break;
+
+ case WTAP_ENCAP_ETHERNET:
+ /*
+ * If this is a snoop file, we assume there's no FCS in
+ * this frame; if this is a Shomit file, we assume there
+ * is. (XXX - or should we treat it a "maybe"?)
+ */
+ if (snoop->is_shomiti)
+ rec->rec_header.packet_header.pseudo_header.eth.fcs_len = 4;
+ else
+ rec->rec_header.packet_header.pseudo_header.eth.fcs_len = 0;
+ break;
+
+ case WTAP_ENCAP_IEEE_802_11_WITH_RADIO:
+ if (packet_size < sizeof (shomiti_wireless_header)) {
+ /*
+ * Uh-oh, the packet isn't big enough to even
+ * have a pseudo-header.
+ */
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("snoop: Shomiti wireless file has a %u-byte packet, too small to have even a wireless pseudo-header",
+ packet_size);
+ return -1;
+ }
+ if (!snoop_read_shomiti_wireless_pseudoheader(fh,
+ &rec->rec_header.packet_header.pseudo_header, err, err_info, &header_size))
+ return -1; /* Read error */
+
+ /*
+ * Don't count the pseudo-header as part of the packet.
+ */
+ rec_size -= header_size;
+ orig_size -= header_size;
+ packet_size -= header_size;
+ break;
+ }
+
+ rec->rec_type = REC_TYPE_PACKET;
+ rec->block = wtap_block_create(WTAP_BLOCK_PACKET);
+ rec->presence_flags = WTAP_HAS_TS|WTAP_HAS_CAP_LEN;
+ rec->ts.secs = g_ntohl(hdr.ts_sec);
+ rec->ts.nsecs = g_ntohl(hdr.ts_usec) * 1000;
+ rec->rec_header.packet_header.caplen = packet_size;
+ rec->rec_header.packet_header.len = orig_size;
+
+ if (rec_size < (sizeof hdr + packet_size)) {
+ /*
+ * What, *negative* padding? Bogus.
+ */
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("snoop: File has %u-byte record with packet size of %u",
+ rec_size, packet_size);
+ return -1;
+ }
+
+ /*
+ * Read the packet data.
+ */
+ if (!wtap_read_packet_bytes(fh, buf, packet_size, err, err_info))
+ return -1; /* failed */
+
+ /*
+ * If this is ATM LANE traffic, try to guess what type of LANE
+ * traffic it is based on the packet contents.
+ */
+ if (wth->file_encap == WTAP_ENCAP_ATM_PDUS &&
+ rec->rec_header.packet_header.pseudo_header.atm.type == TRAF_LANE) {
+ atm_guess_lane_type(rec, ws_buffer_start_ptr(buf));
+ }
+
+ return rec_size - ((guint)sizeof hdr + packet_size);
+}
+
+static gboolean
+snoop_read_atm_pseudoheader(FILE_T fh, union wtap_pseudo_header *pseudo_header,
+ int *err, gchar **err_info)
+{
+ struct snoop_atm_hdr atm_phdr;
+ guint8 vpi;
+ guint16 vci;
+
+ if (!wtap_read_bytes(fh, &atm_phdr, sizeof atm_phdr, err, err_info))
+ return FALSE;
+
+ vpi = atm_phdr.vpi;
+ vci = pntoh16(&atm_phdr.vci);
+
+ /*
+ * The lower 4 bits of the first byte of the header indicate
+ * the type of traffic, as per the "atmioctl.h" header in
+ * SunATM.
+ */
+ switch (atm_phdr.flags & 0x0F) {
+
+ case 0x01: /* LANE */
+ pseudo_header->atm.aal = AAL_5;
+ pseudo_header->atm.type = TRAF_LANE;
+ break;
+
+ case 0x02: /* RFC 1483 LLC multiplexed traffic */
+ pseudo_header->atm.aal = AAL_5;
+ pseudo_header->atm.type = TRAF_LLCMX;
+ break;
+
+ case 0x05: /* ILMI */
+ pseudo_header->atm.aal = AAL_5;
+ pseudo_header->atm.type = TRAF_ILMI;
+ break;
+
+ case 0x06: /* Signalling AAL */
+ pseudo_header->atm.aal = AAL_SIGNALLING;
+ pseudo_header->atm.type = TRAF_UNKNOWN;
+ break;
+
+ case 0x03: /* MARS (RFC 2022) */
+ pseudo_header->atm.aal = AAL_5;
+ pseudo_header->atm.type = TRAF_UNKNOWN;
+ break;
+
+ case 0x04: /* IFMP (Ipsilon Flow Management Protocol; see RFC 1954) */
+ pseudo_header->atm.aal = AAL_5;
+ pseudo_header->atm.type = TRAF_UNKNOWN; /* XXX - TRAF_IPSILON? */
+ break;
+
+ default:
+ /*
+ * Assume it's AAL5, unless it's VPI 0 and VCI 5, in which
+ * case assume it's AAL_SIGNALLING; we know nothing more
+ * about it.
+ *
+ * XXX - is this necessary? Or are we guaranteed that
+ * all signalling traffic has a type of 0x06?
+ *
+ * XXX - is this guaranteed to be AAL5? Or, if the type is
+ * 0x00 ("raw"), might it be non-AAL5 traffic?
+ */
+ if (vpi == 0 && vci == 5)
+ pseudo_header->atm.aal = AAL_SIGNALLING;
+ else
+ pseudo_header->atm.aal = AAL_5;
+ pseudo_header->atm.type = TRAF_UNKNOWN;
+ break;
+ }
+ pseudo_header->atm.subtype = TRAF_ST_UNKNOWN;
+
+ pseudo_header->atm.vpi = vpi;
+ pseudo_header->atm.vci = vci;
+ pseudo_header->atm.channel = (atm_phdr.flags & 0x80) ? 0 : 1;
+
+ /* We don't have this information */
+ pseudo_header->atm.flags = 0;
+ pseudo_header->atm.cells = 0;
+ pseudo_header->atm.aal5t_u2u = 0;
+ pseudo_header->atm.aal5t_len = 0;
+ pseudo_header->atm.aal5t_chksum = 0;
+
+ return TRUE;
+}
+
+static gboolean
+snoop_read_shomiti_wireless_pseudoheader(FILE_T fh,
+ union wtap_pseudo_header *pseudo_header, int *err, gchar **err_info,
+ int *header_size)
+{
+ shomiti_wireless_header whdr;
+ int rsize;
+
+ if (!wtap_read_bytes(fh, &whdr, sizeof whdr, err, err_info))
+ return FALSE;
+
+ /* the 4th byte of the pad is actually a header length,
+ * we've already read 8 bytes of it, and it must never
+ * be less than 8.
+ *
+ * XXX - presumably that means that the header length
+ * doesn't include the length field, as we've read
+ * 12 bytes total.
+ */
+ if (whdr.pad[3] < 8) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("snoop: Header length in Surveyor record is %u, less than minimum of 8",
+ whdr.pad[3]);
+ return FALSE;
+ }
+ /* Skip the header. */
+ rsize = ((int) whdr.pad[3]) - 8;
+ if (!wtap_read_bytes(fh, NULL, rsize, err, err_info))
+ return FALSE;
+
+ memset(&pseudo_header->ieee_802_11, 0, sizeof(pseudo_header->ieee_802_11));
+ pseudo_header->ieee_802_11.fcs_len = 4;
+ pseudo_header->ieee_802_11.decrypted = FALSE;
+ pseudo_header->ieee_802_11.datapad = FALSE;
+ pseudo_header->ieee_802_11.phy = PHDR_802_11_PHY_UNKNOWN;
+ pseudo_header->ieee_802_11.has_channel = TRUE;
+ pseudo_header->ieee_802_11.channel = whdr.channel;
+ pseudo_header->ieee_802_11.has_data_rate = TRUE;
+ pseudo_header->ieee_802_11.data_rate = whdr.rate;
+ pseudo_header->ieee_802_11.has_signal_percent = TRUE;
+ pseudo_header->ieee_802_11.signal_percent = whdr.signal;
+
+ /*
+ * We don't know they PHY, but we do have the data rate;
+ * try to guess the PHY based on the data rate and channel.
+ */
+ if (RATE_IS_DSSS(pseudo_header->ieee_802_11.data_rate)) {
+ /* 11b */
+ pseudo_header->ieee_802_11.phy = PHDR_802_11_PHY_11B;
+ pseudo_header->ieee_802_11.phy_info.info_11b.has_short_preamble = FALSE;
+ } else if (RATE_IS_OFDM(pseudo_header->ieee_802_11.data_rate)) {
+ /* 11a or 11g, depending on the band. */
+ if (CHAN_IS_BG(pseudo_header->ieee_802_11.channel)) {
+ /* 11g */
+ pseudo_header->ieee_802_11.phy = PHDR_802_11_PHY_11G;
+ pseudo_header->ieee_802_11.phy_info.info_11g.has_mode = FALSE;
+ } else {
+ /* 11a */
+ pseudo_header->ieee_802_11.phy = PHDR_802_11_PHY_11A;
+ pseudo_header->ieee_802_11.phy_info.info_11a.has_channel_type = FALSE;
+ pseudo_header->ieee_802_11.phy_info.info_11a.has_turbo_type = FALSE;
+ }
+ }
+
+ /* add back the header and don't forget the pad as well */
+ *header_size = rsize + 8 + 4;
+
+ return TRUE;
+}
+
+static const int wtap_encap[] = {
+ -1, /* WTAP_ENCAP_UNKNOWN -> unsupported */
+ 0x04, /* WTAP_ENCAP_ETHERNET -> DL_ETHER */
+ 0x02, /* WTAP_ENCAP_TOKEN_RING -> DL_TPR */
+ -1, /* WTAP_ENCAP_SLIP -> unsupported */
+ -1, /* WTAP_ENCAP_PPP -> unsupported */
+ 0x08, /* WTAP_ENCAP_FDDI -> DL_FDDI */
+ 0x08, /* WTAP_ENCAP_FDDI_BITSWAPPED -> DL_FDDI */
+ -1, /* WTAP_ENCAP_RAW_IP -> unsupported */
+ -1, /* WTAP_ENCAP_ARCNET -> unsupported */
+ -1, /* WTAP_ENCAP_ARCNET_LINUX -> unsupported */
+ -1, /* WTAP_ENCAP_ATM_RFC1483 -> unsupported */
+ -1, /* WTAP_ENCAP_LINUX_ATM_CLIP -> unsupported */
+ -1, /* WTAP_ENCAP_LAPB -> unsupported*/
+ 0x12, /* WTAP_ENCAP_ATM_PDUS -> DL_IPATM */
+};
+#define NUM_WTAP_ENCAPS (sizeof wtap_encap / sizeof wtap_encap[0])
+
+/* Returns 0 if we could write the specified encapsulation type,
+ an error indication otherwise. */
+static int snoop_dump_can_write_encap(int encap)
+{
+ /* Per-packet encapsulations aren't supported. */
+ if (encap == WTAP_ENCAP_PER_PACKET)
+ return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
+
+ if (encap < 0 || (unsigned)encap >= NUM_WTAP_ENCAPS || wtap_encap[encap] == -1)
+ return WTAP_ERR_UNWRITABLE_ENCAP;
+
+ return 0;
+}
+
+/* Returns TRUE on success, FALSE on failure; sets "*err" to an error code on
+ failure */
+static gboolean snoop_dump_open(wtap_dumper *wdh, int *err, gchar **err_info _U_)
+{
+ struct snoop_hdr file_hdr;
+
+ /* This is a snoop file */
+ wdh->subtype_write = snoop_dump;
+
+ /* Write the file header. */
+ if (!wtap_dump_file_write(wdh, &snoop_magic, sizeof snoop_magic, err))
+ return FALSE;
+
+ /* current "snoop" format is 2 */
+ file_hdr.version = g_htonl(2);
+ file_hdr.network = g_htonl(wtap_encap[wdh->file_encap]);
+ if (!wtap_dump_file_write(wdh, &file_hdr, sizeof file_hdr, err))
+ return FALSE;
+
+ return TRUE;
+}
+
+/* Write a record for a packet to a dump file.
+ Returns TRUE on success, FALSE on failure. */
+static gboolean snoop_dump(wtap_dumper *wdh,
+ const wtap_rec *rec,
+ const guint8 *pd, int *err, gchar **err_info _U_)
+{
+ const union wtap_pseudo_header *pseudo_header = &rec->rec_header.packet_header.pseudo_header;
+ struct snooprec_hdr rec_hdr;
+ int reclen;
+ guint padlen;
+ static const char zeroes[4] = {0};
+ struct snoop_atm_hdr atm_hdr;
+ int atm_hdrsize;
+
+ /* We can only write packet records. */
+ if (rec->rec_type != REC_TYPE_PACKET) {
+ *err = WTAP_ERR_UNWRITABLE_REC_TYPE;
+ return FALSE;
+ }
+
+ /*
+ * Make sure this packet doesn't have a link-layer type that
+ * differs from the one for the file.
+ */
+ if (wdh->file_encap != rec->rec_header.packet_header.pkt_encap) {
+ *err = WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
+ return FALSE;
+ }
+
+ if (wdh->file_encap == WTAP_ENCAP_ATM_PDUS)
+ atm_hdrsize = sizeof (struct snoop_atm_hdr);
+ else
+ atm_hdrsize = 0;
+
+ /* Record length = header length plus data length... */
+ reclen = (int)sizeof rec_hdr + rec->rec_header.packet_header.caplen + atm_hdrsize;
+
+
+ /* ... plus enough bytes to pad it to a 4-byte boundary. */
+ padlen = WS_ROUNDUP_4(reclen) - reclen;
+ reclen += padlen;
+
+ /* Don't write anything we're not willing to read. */
+ if (rec->rec_header.packet_header.caplen + atm_hdrsize > WTAP_MAX_PACKET_SIZE_STANDARD) {
+ *err = WTAP_ERR_PACKET_TOO_LARGE;
+ return FALSE;
+ }
+
+ rec_hdr.orig_len = g_htonl(rec->rec_header.packet_header.len + atm_hdrsize);
+ rec_hdr.incl_len = g_htonl(rec->rec_header.packet_header.caplen + atm_hdrsize);
+ rec_hdr.rec_len = g_htonl(reclen);
+ rec_hdr.cum_drops = 0;
+ rec_hdr.ts_sec = g_htonl(rec->ts.secs);
+ rec_hdr.ts_usec = g_htonl(rec->ts.nsecs / 1000);
+ if (!wtap_dump_file_write(wdh, &rec_hdr, sizeof rec_hdr, err))
+ return FALSE;
+
+ if (wdh->file_encap == WTAP_ENCAP_ATM_PDUS) {
+ /*
+ * Write the ATM header.
+ */
+ atm_hdr.flags =
+ (pseudo_header->atm.channel == 0) ? 0x80 : 0x00;
+ switch (pseudo_header->atm.aal) {
+
+ case AAL_SIGNALLING:
+ /* Signalling AAL */
+ atm_hdr.flags |= 0x06;
+ break;
+
+ case AAL_5:
+ switch (pseudo_header->atm.type) {
+
+ case TRAF_LANE:
+ /* LANE */
+ atm_hdr.flags |= 0x01;
+ break;
+
+ case TRAF_LLCMX:
+ /* RFC 1483 LLC multiplexed traffic */
+ atm_hdr.flags |= 0x02;
+ break;
+
+ case TRAF_ILMI:
+ /* ILMI */
+ atm_hdr.flags |= 0x05;
+ break;
+ }
+ break;
+ }
+ atm_hdr.vpi = (guint8) pseudo_header->atm.vpi;
+ atm_hdr.vci = g_htons(pseudo_header->atm.vci);
+ if (!wtap_dump_file_write(wdh, &atm_hdr, sizeof atm_hdr, err))
+ return FALSE;
+ }
+
+ if (!wtap_dump_file_write(wdh, pd, rec->rec_header.packet_header.caplen, err))
+ return FALSE;
+
+ /* Now write the padding. */
+ if (!wtap_dump_file_write(wdh, zeroes, padlen, err))
+ return FALSE;
+ return TRUE;
+}
+
+static const struct supported_block_type snoop_blocks_supported[] = {
+ /*
+ * We support packet blocks, with no comments or other options.
+ */
+ { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
+};
+
+static const struct file_type_subtype_info snoop_info = {
+ "Sun snoop", "snoop", "snoop", "cap",
+ FALSE, BLOCKS_SUPPORTED(snoop_blocks_supported),
+ snoop_dump_can_write_encap, snoop_dump_open, NULL
+};
+
+static const struct supported_block_type shomiti_blocks_supported[] = {
+ /*
+ * We support packet blocks, with no comments or other options.
+ */
+ { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
+};
+
+static const struct file_type_subtype_info shomiti_info = {
+ "Shomiti/Finisar Surveyor", "shomiti", "cap", NULL,
+ FALSE, BLOCKS_SUPPORTED(shomiti_blocks_supported),
+ NULL, NULL, NULL
+};
+
+void register_snoop(void)
+{
+ snoop_file_type_subtype = wtap_register_file_type_subtype(&snoop_info);
+ shomiti_file_type_subtype = wtap_register_file_type_subtype(&shomiti_info);
+
+ /*
+ * Register names for backwards compatibility with the
+ * wtap_filetypes table in Lua.
+ */
+ wtap_register_backwards_compatibility_lua_name("SNOOP",
+ snoop_file_type_subtype);
+ wtap_register_backwards_compatibility_lua_name("SHOMITI",
+ shomiti_file_type_subtype);
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/wiretap/snoop.h b/wiretap/snoop.h
new file mode 100644
index 00000000..746764d9
--- /dev/null
+++ b/wiretap/snoop.h
@@ -0,0 +1,18 @@
+/** @file
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __W_SNOOP_H__
+#define __W_SNOOP_H__
+
+#include <glib.h>
+#include "wtap.h"
+#include "ws_symbol_export.h"
+
+wtap_open_return_val snoop_open(wtap *wth, int *err, gchar **err_info);
+
+#endif
diff --git a/wiretap/socketcan.h b/wiretap/socketcan.h
new file mode 100644
index 00000000..f864ca3f
--- /dev/null
+++ b/wiretap/socketcan.h
@@ -0,0 +1,38 @@
+/** @file
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * Support for Busmaster log file format
+ * Copyright (c) 2019 by Maksim Salau <maksim.salau@gmail.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef SOCKETCAN_H__
+#define SOCKETCAN_H__
+
+#include <gmodule.h>
+
+#define CAN_MAX_DLEN 8
+#define CANFD_MAX_DLEN 64
+
+typedef struct can_frame {
+ guint32 can_id; /* 32 bit CAN_ID + EFF/RTR/ERR flags */
+ guint8 can_dlc; /* frame payload length in byte (0 .. CAN_MAX_DLEN) */
+ guint8 __pad; /* padding */
+ guint8 __res0; /* reserved / padding */
+ guint8 __res1; /* reserved / padding */
+ guint8 data[CAN_MAX_DLEN];
+} can_frame_t;
+
+typedef struct canfd_frame {
+ guint32 can_id; /* 32 bit CAN_ID + EFF flag */
+ guint8 len; /* frame payload length in byte */
+ guint8 flags; /* additional flags for CAN FD */
+ guint8 __res0; /* reserved / padding */
+ guint8 __res1; /* reserved / padding */
+ guint8 data[CANFD_MAX_DLEN];
+} canfd_frame_t;
+
+#endif /* SOCKETCAN_H__ */
diff --git a/wiretap/stanag4607.c b/wiretap/stanag4607.c
new file mode 100644
index 00000000..0b2f2bff
--- /dev/null
+++ b/wiretap/stanag4607.c
@@ -0,0 +1,246 @@
+/* stanag4607.c
+ *
+ * STANAG 4607 file reading
+ *
+ * http://www.nato.int/structur/AC/224/standard/4607/4607e_JAS_ED3.pdf
+ * That is now missing from that site, but is available on the Wayback
+ * Machine:
+ *
+ * https://web.archive.org/web/20130223054955/http://www.nato.int/structur/AC/224/standard/4607/4607.htm
+ *
+ * https://nso.nato.int/nso/zPublic/ap/aedp-7(2).pdf
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "config.h"
+
+#include "wtap-int.h"
+#include "file_wrappers.h"
+#include <wsutil/buffer.h>
+#include "stanag4607.h"
+
+typedef struct {
+ time_t base_secs;
+} stanag4607_t;
+
+#define PKT_HDR_SIZE 32 /* size of a packet header */
+#define SEG_HDR_SIZE 5 /* size of a segment header */
+
+static int stanag4607_file_type_subtype = -1;
+
+void register_stanag4607(void);
+
+static gboolean is_valid_id(guint16 version_id)
+{
+#define VERSION_21 0x3231
+#define VERSION_30 0x3330
+ if ((version_id != VERSION_21) &&
+ (version_id != VERSION_30))
+ /* Not a stanag4607 file */
+ return FALSE;
+ return TRUE;
+}
+
+static gboolean stanag4607_read_file(wtap *wth, FILE_T fh, wtap_rec *rec,
+ Buffer *buf, int *err, gchar **err_info)
+{
+ stanag4607_t *stanag4607 = (stanag4607_t *)wth->priv;
+ guint32 millisecs, secs, nsecs;
+ gint64 offset = 0;
+ guint8 stanag_pkt_hdr[PKT_HDR_SIZE+SEG_HDR_SIZE];
+ guint32 packet_size;
+
+ *err = 0;
+
+ /* Combined packet header and segment header */
+ if (!wtap_read_bytes_or_eof(fh, stanag_pkt_hdr, sizeof stanag_pkt_hdr, err, err_info))
+ return FALSE;
+ offset += sizeof stanag_pkt_hdr;
+
+ if (!is_valid_id(pntoh16(&stanag_pkt_hdr[0]))) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("Bad version number");
+ return FALSE;
+ }
+
+ rec->rec_type = REC_TYPE_PACKET;
+ rec->block = wtap_block_create(WTAP_BLOCK_PACKET);
+
+ /* The next 4 bytes are the packet length */
+ packet_size = pntoh32(&stanag_pkt_hdr[2]);
+ if (packet_size > WTAP_MAX_PACKET_SIZE_STANDARD) {
+ /*
+ * Probably a corrupt capture file; don't blow up trying
+ * to allocate space for an immensely-large packet.
+ */
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("stanag4607: File has %" PRIu32 "d-byte packet, "
+ "bigger than maximum of %u", packet_size, WTAP_MAX_PACKET_SIZE_STANDARD);
+ return FALSE;
+ }
+ if (packet_size < PKT_HDR_SIZE+SEG_HDR_SIZE) {
+ /*
+ * Probably a corrupt capture file; don't, for example, loop
+ * infinitely if the size is zero.
+ */
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("stanag4607: File has %" PRIu32 "d-byte packet, "
+ "smaller than minimum of %u", packet_size, PKT_HDR_SIZE+SEG_HDR_SIZE);
+ return FALSE;
+ }
+ rec->rec_header.packet_header.caplen = packet_size;
+ rec->rec_header.packet_header.len = packet_size;
+
+ /* Sadly, the header doesn't contain times; but some segments do */
+ /* So, get the segment header, which is just past the 32-byte header. */
+ rec->presence_flags = WTAP_HAS_TS;
+
+ /* If no time specified, it's the last baseline time */
+ rec->ts.secs = stanag4607->base_secs;
+ rec->ts.nsecs = 0;
+ millisecs = 0;
+
+#define MISSION_SEGMENT 1
+#define DWELL_SEGMENT 2
+#define JOB_DEFINITION_SEGMENT 5
+#define PLATFORM_LOCATION_SEGMENT 13
+ if (MISSION_SEGMENT == stanag_pkt_hdr[32]) {
+ guint8 mseg[39];
+ struct tm tm;
+
+ if (!wtap_read_bytes(fh, &mseg, sizeof mseg, err, err_info))
+ return FALSE;
+ offset += sizeof mseg;
+
+ tm.tm_year = pntoh16(&mseg[35]) - 1900;
+ tm.tm_mon = mseg[37] - 1;
+ tm.tm_mday = mseg[38];
+ tm.tm_hour = 0;
+ tm.tm_min = 0;
+ tm.tm_sec = 0;
+ tm.tm_isdst = -1;
+ stanag4607->base_secs = mktime(&tm);
+ rec->ts.secs = stanag4607->base_secs;
+ }
+ else if (PLATFORM_LOCATION_SEGMENT == stanag_pkt_hdr[32]) {
+ if (!wtap_read_bytes(fh, &millisecs, sizeof millisecs, err, err_info))
+ return FALSE;
+ offset += sizeof millisecs;
+ millisecs = g_ntohl(millisecs);
+ }
+ else if (DWELL_SEGMENT == stanag_pkt_hdr[32]) {
+ guint8 dseg[19];
+ if (!wtap_read_bytes(fh, &dseg, sizeof dseg, err, err_info))
+ return FALSE;
+ offset += sizeof dseg;
+ millisecs = pntoh32(&dseg[15]);
+ }
+ if (0 != millisecs) {
+ secs = millisecs/1000;
+ nsecs = (millisecs - 1000 * secs) * 1000000;
+ rec->ts.secs = stanag4607->base_secs + secs;
+ rec->ts.nsecs = nsecs;
+ }
+
+ /* wind back to the start of the packet ... */
+ if (file_seek(fh, - offset, SEEK_CUR, err) == -1)
+ return FALSE;
+
+ return wtap_read_packet_bytes(fh, buf, packet_size, err, err_info);
+}
+
+static gboolean stanag4607_read(wtap *wth, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info, gint64 *data_offset)
+{
+ *data_offset = file_tell(wth->fh);
+
+ return stanag4607_read_file(wth, wth->fh, rec, buf, err, err_info);
+}
+
+static gboolean stanag4607_seek_read(wtap *wth, gint64 seek_off,
+ wtap_rec *rec,
+ Buffer *buf, int *err, gchar **err_info)
+{
+ if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
+ return FALSE;
+
+ return stanag4607_read_file(wth, wth->random_fh, rec, buf, err, err_info);
+}
+
+wtap_open_return_val stanag4607_open(wtap *wth, int *err, gchar **err_info)
+{
+ guint16 version_id;
+ stanag4607_t *stanag4607;
+
+ if (!wtap_read_bytes(wth->fh, &version_id, sizeof version_id, err, err_info))
+ return (*err != WTAP_ERR_SHORT_READ) ? WTAP_OPEN_ERROR : WTAP_OPEN_NOT_MINE;
+
+ if (!is_valid_id(GUINT16_TO_BE(version_id)))
+ /* Not a stanag4607 file */
+ return WTAP_OPEN_NOT_MINE;
+
+ /* seek back to the start of the file */
+ if (file_seek(wth->fh, 0, SEEK_SET, err) == -1)
+ return WTAP_OPEN_ERROR;
+
+ wth->file_type_subtype = stanag4607_file_type_subtype;
+ wth->file_encap = WTAP_ENCAP_STANAG_4607;
+ wth->snapshot_length = 0; /* not known */
+
+ stanag4607 = g_new(stanag4607_t, 1);
+ wth->priv = (void *)stanag4607;
+ stanag4607->base_secs = 0; /* unknown as of yet */
+
+ wth->subtype_read = stanag4607_read;
+ wth->subtype_seek_read = stanag4607_seek_read;
+ wth->file_tsprec = WTAP_TSPREC_MSEC;
+
+ /*
+ * Add an IDB; we don't know how many interfaces were
+ * involved, so we just say one interface, about which
+ * we only know the link-layer type, snapshot length,
+ * and time stamp resolution.
+ */
+ wtap_add_generated_idb(wth);
+
+ return WTAP_OPEN_MINE;
+}
+
+static const struct supported_block_type stanag4607_blocks_supported[] = {
+ /*
+ * We support packet blocks, with no comments or other options.
+ */
+ { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
+};
+
+static const struct file_type_subtype_info stanag4607_info = {
+ "STANAG 4607 Format", "stanag4607", NULL, NULL,
+ FALSE, BLOCKS_SUPPORTED(stanag4607_blocks_supported),
+ NULL, NULL, NULL
+};
+
+void register_stanag4607(void)
+{
+ stanag4607_file_type_subtype = wtap_register_file_type_subtype(&stanag4607_info);
+
+ /*
+ * Register name for backwards compatibility with the
+ * wtap_filetypes table in Lua.
+ */
+ wtap_register_backwards_compatibility_lua_name("STANAG_4607",
+ stanag4607_file_type_subtype);
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local Variables:
+ * c-basic-offset: 2
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * vi: set shiftwidth=2 tabstop=8 expandtab:
+ * :indentSize=2:tabSize=8:noTabs=true:
+ */
diff --git a/wiretap/stanag4607.h b/wiretap/stanag4607.h
new file mode 100644
index 00000000..1d322b88
--- /dev/null
+++ b/wiretap/stanag4607.h
@@ -0,0 +1,18 @@
+/** @file
+ *
+ * STANAG 4607 file reading
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ */
+
+#ifndef __STANAG_4607_H__
+#define __STANAG_4607_H__
+
+#include <glib.h>
+#include <wiretap/wtap.h>
+#include "ws_symbol_export.h"
+
+wtap_open_return_val stanag4607_open(wtap *wth, int *err, gchar **err_info);
+
+#endif
diff --git a/wiretap/systemd_journal.c b/wiretap/systemd_journal.c
new file mode 100644
index 00000000..1cce5e59
--- /dev/null
+++ b/wiretap/systemd_journal.c
@@ -0,0 +1,276 @@
+/* systemd_journal.c
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include "wtap-int.h"
+#include "pcapng_module.h"
+#include "file_wrappers.h"
+#include "systemd_journal.h"
+
+// To do:
+// - Request a pcap encapsulation type.
+// - Should we add separate types for binary, plain, and JSON or add a metadata header?
+
+// Systemd journals are stored in the following formats:
+// Journal File Format (native binary): https://www.freedesktop.org/wiki/Software/systemd/journal-files/
+// Journal Export Format: https://www.freedesktop.org/wiki/Software/systemd/export/
+// Journal JSON format: https://www.freedesktop.org/wiki/Software/systemd/json/
+// This reads Journal Export Format files but could be extended to support
+// the binary and JSON formats.
+
+// Example data:
+// __CURSOR=s=1d56bab64d414960b9907ab0cc7f7c62;i=2;b=1497926e8b4b4d3ca6a5805e157fa73c;m=5d0ae5;t=56f2f5b66ce6f;x=20cb01e28bb496a8
+// __REALTIME_TIMESTAMP=1529624071163503
+// __MONOTONIC_TIMESTAMP=6097637
+// _BOOT_ID=1497926e8b4b4d3ca6a5805e157fa73c
+// PRIORITY=6
+// _MACHINE_ID=62c342838a6e436dacea041aa4b5064b
+// _HOSTNAME=example.wireshark.org
+// _SOURCE_MONOTONIC_TIMESTAMP=0
+// _TRANSPORT=kernel
+// SYSLOG_FACILITY=0
+// SYSLOG_IDENTIFIER=kernel
+// MESSAGE=Initializing cgroup subsys cpuset
+
+static gboolean systemd_journal_read(wtap *wth, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info, gint64 *data_offset);
+static gboolean systemd_journal_seek_read(wtap *wth, gint64 seek_off,
+ wtap_rec *rec, Buffer *buf, int *err, gchar **err_info);
+static gboolean systemd_journal_read_export_entry(FILE_T fh, wtap_rec *rec,
+ Buffer *buf, int *err, gchar **err_info);
+
+// The Journal Export Format specification doesn't place limits on entry
+// lengths or lines per entry. We do.
+#define MAX_EXPORT_ENTRY_LENGTH WTAP_MAX_PACKET_SIZE_STANDARD
+#define MAX_EXPORT_ENTRY_LINES 100
+
+// Strictly speaking, we only need __REALTIME_TIMESTAMP= since we use
+// that to set the packet timestamp. According to
+// https://www.freedesktop.org/software/systemd/man/systemd.journal-fields.html
+// __CURSOR= and __MONOTONIC_TIMESTAMP= should be present as well, so
+// check for them order to improve our heuristics.
+#define FLD__CURSOR "__CURSOR="
+#define FLD__REALTIME_TIMESTAMP "__REALTIME_TIMESTAMP="
+#define FLD__MONOTONIC_TIMESTAMP "__MONOTONIC_TIMESTAMP="
+
+static int systemd_journal_file_type_subtype = -1;
+
+void register_systemd_journal(void);
+
+wtap_open_return_val systemd_journal_open(wtap *wth, int *err _U_, gchar **err_info _U_)
+{
+ gchar *entry_buff = (gchar*) g_malloc(MAX_EXPORT_ENTRY_LENGTH);
+ gchar *entry_line = NULL;
+ gboolean got_cursor = FALSE;
+ gboolean got_rt_ts = FALSE;
+ gboolean got_mt_ts = FALSE;
+ int line_num;
+
+ errno = 0;
+ for (line_num = 0; line_num < MAX_EXPORT_ENTRY_LINES; line_num++) {
+ entry_line = file_gets(entry_buff, MAX_EXPORT_ENTRY_LENGTH, wth->fh);
+ if (!entry_line) {
+ break;
+ }
+ if (entry_line[0] == '\n') {
+ break;
+ } else if (strncmp(entry_line, FLD__CURSOR, strlen(FLD__CURSOR)) == 0) {
+ got_cursor = TRUE;
+ } else if (strncmp(entry_line, FLD__REALTIME_TIMESTAMP, strlen(FLD__REALTIME_TIMESTAMP)) == 0) {
+ got_rt_ts = TRUE;
+ } else if (strncmp(entry_line, FLD__MONOTONIC_TIMESTAMP, strlen(FLD__MONOTONIC_TIMESTAMP)) == 0) {
+ got_mt_ts = TRUE;
+ }
+ }
+ g_free(entry_buff);
+
+ if (file_seek(wth->fh, 0, SEEK_SET, err) == -1) {
+ return WTAP_OPEN_ERROR;
+ }
+
+ if (!got_cursor || !got_rt_ts || !got_mt_ts) {
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ wth->file_type_subtype = systemd_journal_file_type_subtype;
+ wth->subtype_read = systemd_journal_read;
+ wth->subtype_seek_read = systemd_journal_seek_read;
+ wth->file_encap = WTAP_ENCAP_SYSTEMD_JOURNAL;
+ wth->file_tsprec = WTAP_TSPREC_USEC;
+
+ /*
+ * Add an IDB; we don't know how many interfaces were
+ * involved, so we just say one interface, about which
+ * we only know the link-layer type, snapshot length,
+ * and time stamp resolution.
+ */
+ wtap_add_generated_idb(wth);
+
+ return WTAP_OPEN_MINE;
+}
+
+/* Read the next packet */
+static gboolean systemd_journal_read(wtap *wth, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info, gint64 *data_offset)
+{
+ *data_offset = file_tell(wth->fh);
+
+ /* Read record. */
+ if (!systemd_journal_read_export_entry(wth->fh, rec, buf, err, err_info)) {
+ /* Read error or EOF */
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+ static gboolean
+systemd_journal_seek_read(wtap *wth, gint64 seek_off,
+ wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info)
+{
+ if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
+ return FALSE;
+
+ /* Read record. */
+ if (!systemd_journal_read_export_entry(wth->random_fh, rec, buf, err, err_info)) {
+ /* Read error or EOF */
+ if (*err == 0) {
+ /* EOF means "short read" in random-access mode */
+ *err = WTAP_ERR_SHORT_READ;
+ }
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static gboolean
+systemd_journal_read_export_entry(FILE_T fh, wtap_rec *rec, Buffer *buf, int *err, gchar **err_info)
+{
+ size_t fld_end = 0;
+ gchar *buf_ptr;
+ gchar *entry_line = NULL;
+ gboolean got_cursor = FALSE;
+ gboolean got_rt_ts = FALSE;
+ gboolean got_mt_ts = FALSE;
+ gboolean got_double_newline = FALSE;
+ int line_num;
+ size_t rt_ts_len = strlen(FLD__REALTIME_TIMESTAMP);
+
+ ws_buffer_assure_space(buf, MAX_EXPORT_ENTRY_LENGTH);
+ buf_ptr = (gchar *) ws_buffer_start_ptr(buf);
+
+ for (line_num = 0; line_num < MAX_EXPORT_ENTRY_LINES; line_num++) {
+ entry_line = file_gets(buf_ptr + fld_end, MAX_EXPORT_ENTRY_LENGTH - (int) fld_end, fh);
+ if (!entry_line) {
+ break;
+ }
+ fld_end += strlen(entry_line);
+ if (entry_line[0] == '\n') {
+ got_double_newline = TRUE;
+ break;
+ } else if (strncmp(entry_line, FLD__CURSOR, strlen(FLD__CURSOR)) == 0) {
+ got_cursor = TRUE;
+ } else if (strncmp(entry_line, FLD__REALTIME_TIMESTAMP, rt_ts_len) == 0) {
+ errno = 0;
+ unsigned long rt_ts = strtoul(entry_line+rt_ts_len, NULL, 10);
+ if (!errno) {
+ rec->ts.secs = (time_t) rt_ts / 1000000;
+ rec->ts.nsecs = (rt_ts % 1000000) * 1000;
+ rec->tsprec = WTAP_TSPREC_USEC;
+ got_rt_ts = TRUE;
+ }
+ } else if (strncmp(entry_line, FLD__MONOTONIC_TIMESTAMP, strlen(FLD__MONOTONIC_TIMESTAMP)) == 0) {
+ got_mt_ts = TRUE;
+ } else if (!strstr(entry_line, "=")) {
+ // Start of binary data.
+ if (fld_end >= MAX_EXPORT_ENTRY_LENGTH - 8) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("systemd: binary length too long");
+ return FALSE;
+ }
+ guint64 data_len, le_data_len;
+ if (!wtap_read_bytes(fh, &le_data_len, 8, err, err_info)) {
+ return FALSE;
+ }
+ memcpy(buf_ptr + fld_end, &le_data_len, 8);
+ fld_end += 8;
+ data_len = pletoh64(&le_data_len);
+ if (data_len < 1 || data_len - 1 >= MAX_EXPORT_ENTRY_LENGTH - fld_end) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("systemd: binary data too long");
+ return FALSE;
+ }
+ // Data + trailing \n
+ if (!wtap_read_bytes(fh, buf_ptr + fld_end, (unsigned) data_len + 1, err, err_info)) {
+ return FALSE;
+ }
+ fld_end += (size_t) data_len + 1;
+ }
+ if (MAX_EXPORT_ENTRY_LENGTH < fld_end + 2) { // \n\0
+ break;
+ }
+ }
+
+ if (!got_cursor || !got_rt_ts || !got_mt_ts) {
+ return FALSE;
+ }
+
+ if (!got_double_newline && !file_eof(fh)) {
+ return FALSE;
+ }
+
+ rec->rec_type = REC_TYPE_SYSTEMD_JOURNAL_EXPORT;
+ rec->block = wtap_block_create(WTAP_BLOCK_SYSTEMD_JOURNAL_EXPORT);
+ rec->presence_flags = WTAP_HAS_TS|WTAP_HAS_CAP_LEN;
+ rec->rec_header.systemd_journal_export_header.record_len = (guint32) fld_end;
+
+ return TRUE;
+}
+
+static const struct supported_block_type systemd_journal_blocks_supported[] = {
+ /*
+ * We support systemd journal blocks, with no comments or other options.
+ */
+ { WTAP_BLOCK_SYSTEMD_JOURNAL_EXPORT, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
+};
+
+static const struct file_type_subtype_info systemd_journal_info = {
+ "systemd journal export", "systemd_journal", NULL, NULL,
+ FALSE, BLOCKS_SUPPORTED(systemd_journal_blocks_supported),
+ NULL, NULL, NULL
+};
+
+void register_systemd_journal(void)
+{
+ systemd_journal_file_type_subtype = wtap_register_file_type_subtype(&systemd_journal_info);
+
+ /*
+ * Register name for backwards compatibility with the
+ * wtap_filetypes table in Lua.
+ */
+ wtap_register_backwards_compatibility_lua_name("SYSTEMD_JOURNAL",
+ systemd_journal_file_type_subtype);
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * vi: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */
diff --git a/wiretap/systemd_journal.h b/wiretap/systemd_journal.h
new file mode 100644
index 00000000..0a34c66c
--- /dev/null
+++ b/wiretap/systemd_journal.h
@@ -0,0 +1,19 @@
+/** @file
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ */
+
+#ifndef __SYSTEMD_JOURNAL_H__
+#define __SYSTEMD_JOURNAL_H__
+
+#include <glib.h>
+#include "wtap.h"
+#include "ws_symbol_export.h"
+
+wtap_open_return_val systemd_journal_open(wtap *wth, int *err, gchar **err_info);
+
+#endif // __SYSTEMD_JOURNAL_H__
diff --git a/wiretap/tnef.c b/wiretap/tnef.c
new file mode 100644
index 00000000..a7b17c3a
--- /dev/null
+++ b/wiretap/tnef.c
@@ -0,0 +1,83 @@
+/* tnef.c
+ *
+ * Transport-Neutral Encapsulation Format (TNEF) file reading
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "config.h"
+
+#include "wtap-int.h"
+#include "file_wrappers.h"
+#include <wsutil/buffer.h>
+#include "tnef.h"
+
+static int tnef_file_type_subtype = -1;
+
+void register_tnef(void);
+
+wtap_open_return_val tnef_open(wtap *wth, int *err, gchar **err_info)
+{
+ guint32 magic;
+
+ if (!wtap_read_bytes(wth->fh, &magic, sizeof magic, err, err_info))
+ return (*err != WTAP_ERR_SHORT_READ) ? WTAP_OPEN_ERROR : WTAP_OPEN_NOT_MINE;
+
+ if (GUINT32_TO_LE(magic) != TNEF_SIGNATURE)
+ /* Not a tnef file */
+ return WTAP_OPEN_NOT_MINE;
+
+ /* seek back to the start of the file */
+ if (file_seek(wth->fh, 0, SEEK_SET, err) == -1)
+ return WTAP_OPEN_ERROR;
+
+ wth->file_type_subtype = tnef_file_type_subtype;
+ wth->file_encap = WTAP_ENCAP_TNEF;
+ wth->snapshot_length = 0;
+
+ wth->subtype_read = wtap_full_file_read;
+ wth->subtype_seek_read = wtap_full_file_seek_read;
+ wth->file_tsprec = WTAP_TSPREC_SEC;
+
+ return WTAP_OPEN_MINE;
+}
+
+static const struct supported_block_type tnef_blocks_supported[] = {
+ /*
+ * This is a file format that we dissect, so we provide only one
+ * "packet" with the file's contents, and don't support any
+ * options.
+ */
+ { WTAP_BLOCK_PACKET, ONE_BLOCK_SUPPORTED, NO_OPTIONS_SUPPORTED }
+};
+
+static const struct file_type_subtype_info tnef_info = {
+ "Transport-Neutral Encapsulation Format", "tnef", NULL, NULL,
+ FALSE, BLOCKS_SUPPORTED(tnef_blocks_supported),
+ NULL, NULL, NULL
+};
+
+void register_tnef(void)
+{
+ tnef_file_type_subtype = wtap_register_file_type_subtype(&tnef_info);
+
+ /*
+ * Register name for backwards compatibility with the
+ * wtap_filetypes table in Lua.
+ */
+ wtap_register_backwards_compatibility_lua_name("TNEF",
+ tnef_file_type_subtype);
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local Variables:
+ * c-basic-offset: 2
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * vi: set shiftwidth=2 tabstop=8 expandtab:
+ * :indentSize=2:tabSize=8:noTabs=true:
+ */
diff --git a/wiretap/tnef.h b/wiretap/tnef.h
new file mode 100644
index 00000000..21873d14
--- /dev/null
+++ b/wiretap/tnef.h
@@ -0,0 +1,20 @@
+/** @file
+ *
+ * Transport-Neutral Encapsulation Format (TNEF) file reading
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ */
+
+#ifndef __TNEF_H__
+#define __TNEF_H__
+
+#include <glib.h>
+#include <wiretap/wtap.h>
+#include "ws_symbol_export.h"
+
+#define TNEF_SIGNATURE 0x223E9F78
+
+wtap_open_return_val tnef_open(wtap *wth, int *err, gchar **err_info);
+
+#endif
diff --git a/wiretap/toshiba.c b/wiretap/toshiba.c
new file mode 100644
index 00000000..7c08d329
--- /dev/null
+++ b/wiretap/toshiba.c
@@ -0,0 +1,469 @@
+/* toshiba.c
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "config.h"
+#include "wtap-int.h"
+#include "toshiba.h"
+#include "file_wrappers.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+/* This module reads the output of the 'snoop' command in the Toshiba
+ * TR-600 and TR-650 "Compact" ISDN Routers. You can telnet to the
+ * router and run 'snoop' on the different channels, and at different
+ * detail levels. Be sure to choose the 'dump' level to get the hex dump.
+ * The 'snoop' command has nothing to do with the Solaris 'snoop'
+ * command, except that they both capture packets.
+ */
+
+/*
+ Example 'snoop' output data:
+
+Script started on Thu Sep 9 21:48:49 1999
+]0;gram@nirvana:/tmp$ telnet 10.0.0.254
+Trying 10.0.0.254...
+Connected to 10.0.0.254.
+Escape character is '^]'.
+
+
+TR-600(tr600) System Console
+
+Login:admin
+Password:*******
+*--------------------------------------------------------*
+| T O S H I B A T R - 6 0 0 |
+| < Compact Router > |
+| V1.02.02 |
+| |
+| (C) Copyright TOSHIBA Corp. 1997 All rights reserved. |
+*--------------------------------------------------------*
+
+tr600>snoop dump b1
+ Trace start?(on/off/dump/dtl)->dump
+ IP Address?->b1
+B1 Port Filetering
+Trace start(Dump Mode)...
+
+tr600>[No.1] 00:00:09.14 B1:1 Tx 207.193.26.136->151.164.1.8 DNS SPORT=1028 LEN=38 CHKSUM=4FD4 ID=2390 Query RD QCNT=1 pow.zing.org?
+OFFSET 0001-0203-0405-0607-0809-0A0B-0C0D-0E0F 0123456789ABCDEF LEN=67
+0000 : FF03 003D C000 0008 2145 0000 3A12 6500 ...=....!E..:.e.
+0010 : 003F 11E6 58CF C11A 8897 A401 0804 0400 .?..X...........
+0020 : 3500 264F D409 5601 0000 0100 0000 0000 5.&O..V.........
+0030 : 0003 706F 7704 7A69 6E67 036F 7267 0000 ..pow.zing.org..
+0040 : 0100 01 ...
+
+[No.2] 00:00:09.25 B1:1 Rx 151.164.1.8->207.193.26.136 DNS DPORT=1028 LEN=193 CHKSUM=3E06 ID=2390 Answer RD RA QCNT=1 pow.zing.org? ANCNT=1 pow.zing.org=206.57.36.90 TTL=2652
+OFFSET 0001-0203-0405-0607-0809-0A0B-0C0D-0E0F 0123456789ABCDEF LEN=222
+0000 : FF03 003D C000 0013 2145 0000 D590 9340 ...=....!E.....@
+0010 : 00F7 116F 8E97 A401 08CF C11A 8800 3504 ...o..........5.
+0020 : 0400 C13E 0609 5681 8000 0100 0100 0300 ...>..V.........
+0030 : 0303 706F 7704 7A69 6E67 036F 7267 0000 ..pow.zing.org..
+0040 : 0100 01C0 0C00 0100 0100 000A 5C00 04CE ............\...
+0050 : 3924 5A04 5A49 4E47 036F 7267 0000 0200 9$Z.ZING.org....
+0060 : 0100 016F 5B00 0D03 4841 4E03 5449 5703 ...o[...HAN.TIW.
+0070 : 4E45 5400 C02E 0002 0001 0001 6F5B 0006 NET.........o[..
+0080 : 034E 5331 C02E C02E 0002 0001 0001 6F5B .NS1..........o[
+0090 : 001C 0854 414C 4945 5349 4E0D 434F 4E46 ...TALIESIN.CONF
+00A0 : 4142 554C 4154 494F 4E03 434F 4D00 C042 ABULATION.COM..B
+00B0 : 0001 0001 0001 51EC 0004 CE39 2406 C05B ......Q....9$..[
+00C0 : 0001 0001 0001 6F5B 0004 CE39 245A C06D ......o[...9$Z.m
+00D0 : 0001 0001 0001 4521 0004 187C 1F01 ......E!...|..
+
+ */
+
+/* Magic text to check for toshiba-ness of file */
+static const char toshiba_hdr_magic[] =
+{ 'T', ' ', 'O', ' ', 'S', ' ', 'H', ' ', 'I', ' ', 'B', ' ', 'A' };
+#define TOSHIBA_HDR_MAGIC_SIZE (sizeof toshiba_hdr_magic / sizeof toshiba_hdr_magic[0])
+
+/* Magic text for start of packet */
+static const char toshiba_rec_magic[] = { '[', 'N', 'o', '.' };
+#define TOSHIBA_REC_MAGIC_SIZE (sizeof toshiba_rec_magic / sizeof toshiba_rec_magic[0])
+
+static gboolean toshiba_read(wtap *wth, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info, gint64 *data_offset);
+static gboolean toshiba_seek_read(wtap *wth, gint64 seek_off,
+ wtap_rec *rec, Buffer *buf, int *err, gchar **err_info);
+static gboolean parse_single_hex_dump_line(char* rec, guint8 *buf,
+ guint byte_offset);
+static gboolean parse_toshiba_packet(FILE_T fh, wtap_rec *rec,
+ Buffer *buf, int *err, gchar **err_info);
+
+static int toshiba_file_type_subtype = -1;
+
+void register_toshiba(void);
+
+/* Seeks to the beginning of the next packet, and returns the
+ byte offset. Returns -1 on failure, and sets "*err" to the error
+ and "*err_info" to null or an additional error string. */
+static gint64 toshiba_seek_next_packet(wtap *wth, int *err, gchar **err_info)
+{
+ int byte;
+ guint level = 0;
+ gint64 cur_off;
+
+ while ((byte = file_getc(wth->fh)) != EOF) {
+ if (byte == toshiba_rec_magic[level]) {
+ level++;
+ if (level >= TOSHIBA_REC_MAGIC_SIZE) {
+ /* note: we're leaving file pointer right after the magic characters */
+ cur_off = file_tell(wth->fh);
+ if (cur_off == -1) {
+ /* Error. */
+ *err = file_error(wth->fh, err_info);
+ return -1;
+ }
+ return cur_off + 1;
+ }
+ } else {
+ level = 0;
+ }
+ }
+ /* EOF or error. */
+ *err = file_error(wth->fh, err_info);
+ return -1;
+}
+
+#define TOSHIBA_HEADER_LINES_TO_CHECK 200
+#define TOSHIBA_LINE_LENGTH 240
+
+/* Look through the first part of a file to see if this is
+ * a Toshiba trace file.
+ *
+ * Returns TRUE if it is, FALSE if it isn't or if we get an I/O error;
+ * if we get an I/O error, "*err" will be set to a non-zero value and
+ * "*err_info" will be set to null or an additional error string.
+ */
+static gboolean toshiba_check_file_type(wtap *wth, int *err, gchar **err_info)
+{
+ char buf[TOSHIBA_LINE_LENGTH];
+ guint i, reclen, level, line;
+ char byte;
+
+ buf[TOSHIBA_LINE_LENGTH-1] = 0;
+
+ for (line = 0; line < TOSHIBA_HEADER_LINES_TO_CHECK; line++) {
+ if (file_gets(buf, TOSHIBA_LINE_LENGTH, wth->fh) == NULL) {
+ /* EOF or error. */
+ *err = file_error(wth->fh, err_info);
+ return FALSE;
+ }
+
+ reclen = (guint) strlen(buf);
+ if (reclen < TOSHIBA_HDR_MAGIC_SIZE) {
+ continue;
+ }
+
+ level = 0;
+ for (i = 0; i < reclen; i++) {
+ byte = buf[i];
+ if (byte == toshiba_hdr_magic[level]) {
+ level++;
+ if (level >= TOSHIBA_HDR_MAGIC_SIZE) {
+ return TRUE;
+ }
+ }
+ else {
+ level = 0;
+ }
+ }
+ }
+ *err = 0;
+ return FALSE;
+}
+
+
+wtap_open_return_val toshiba_open(wtap *wth, int *err, gchar **err_info)
+{
+ /* Look for Toshiba header */
+ if (!toshiba_check_file_type(wth, err, err_info)) {
+ if (*err != 0 && *err != WTAP_ERR_SHORT_READ)
+ return WTAP_OPEN_ERROR;
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ wth->file_encap = WTAP_ENCAP_PER_PACKET;
+ wth->file_type_subtype = toshiba_file_type_subtype;
+ wth->snapshot_length = 0; /* not known */
+ wth->subtype_read = toshiba_read;
+ wth->subtype_seek_read = toshiba_seek_read;
+ wth->file_tsprec = WTAP_TSPREC_10_MSEC;
+
+ return WTAP_OPEN_MINE;
+}
+
+/* Find the next packet and parse it; called from wtap_read(). */
+static gboolean toshiba_read(wtap *wth, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info, gint64 *data_offset)
+{
+ gint64 offset;
+
+ /* Find the next packet */
+ offset = toshiba_seek_next_packet(wth, err, err_info);
+ if (offset < 1)
+ return FALSE;
+ *data_offset = offset;
+
+ /* Parse the packet */
+ return parse_toshiba_packet(wth->fh, rec, buf, err, err_info);
+}
+
+/* Used to read packets in random-access fashion */
+static gboolean
+toshiba_seek_read(wtap *wth, gint64 seek_off,
+ wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info)
+{
+ if (file_seek(wth->random_fh, seek_off - 1, SEEK_SET, err) == -1)
+ return FALSE;
+
+ if (!parse_toshiba_packet(wth->random_fh, rec, buf, err, err_info)) {
+ if (*err == 0)
+ *err = WTAP_ERR_SHORT_READ;
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/* Parses a packet. */
+static gboolean
+parse_toshiba_packet(FILE_T fh, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info)
+{
+ union wtap_pseudo_header *pseudo_header = &rec->rec_header.packet_header.pseudo_header;
+ char line[TOSHIBA_LINE_LENGTH];
+ int num_items_scanned;
+ int pkt_len, pktnum, hr, min, sec, csec;
+ char channel[10], direction[10];
+ int i, hex_lines;
+ guint8 *pd;
+
+ /* Our file pointer should be on the line containing the
+ * summary information for a packet. Read in that line and
+ * extract the useful information
+ */
+ if (file_gets(line, TOSHIBA_LINE_LENGTH, fh) == NULL) {
+ *err = file_error(fh, err_info);
+ if (*err == 0) {
+ *err = WTAP_ERR_SHORT_READ;
+ }
+ return FALSE;
+ }
+
+ /* Find text in line after "[No.". Limit the length of the
+ * two strings since we have fixed buffers for channel[] and
+ * direction[] */
+ num_items_scanned = sscanf(line, "%9d] %2d:%2d:%2d.%9d %9s %9s",
+ &pktnum, &hr, &min, &sec, &csec, channel, direction);
+
+ if (num_items_scanned != 7) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("toshiba: record header isn't valid");
+ return FALSE;
+ }
+
+ /* Scan lines until we find the OFFSET line. In a "telnet" trace,
+ * this will be the next line. But if you save your telnet session
+ * to a file from within a Windows-based telnet client, it may
+ * put in line breaks at 80 columns (or however big your "telnet" box
+ * is). CRT (a Windows telnet app from VanDyke) does this.
+ * Here we assume that 80 columns will be the minimum size, and that
+ * the OFFSET line is not broken in the middle. It's the previous
+ * line that is normally long and can thus be broken at column 80.
+ */
+ do {
+ if (file_gets(line, TOSHIBA_LINE_LENGTH, fh) == NULL) {
+ *err = file_error(fh, err_info);
+ if (*err == 0) {
+ *err = WTAP_ERR_SHORT_READ;
+ }
+ return FALSE;
+ }
+
+ /* Check for "OFFSET 0001-0203" at beginning of line */
+ line[16] = '\0';
+
+ } while (strcmp(line, "OFFSET 0001-0203") != 0);
+
+ num_items_scanned = sscanf(line+64, "LEN=%9d", &pkt_len);
+ if (num_items_scanned != 1) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("toshiba: OFFSET line doesn't have valid LEN item");
+ return FALSE;
+ }
+ if (pkt_len < 0) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("toshiba: packet header has a negative packet length");
+ return FALSE;
+ }
+ if ((guint)pkt_len > WTAP_MAX_PACKET_SIZE_STANDARD) {
+ /*
+ * Probably a corrupt capture file; don't blow up trying
+ * to allocate space for an immensely-large packet.
+ */
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("toshiba: File has %u-byte packet, bigger than maximum of %u",
+ (guint)pkt_len, WTAP_MAX_PACKET_SIZE_STANDARD);
+ return FALSE;
+ }
+
+ rec->rec_type = REC_TYPE_PACKET;
+ rec->block = wtap_block_create(WTAP_BLOCK_PACKET);
+ rec->presence_flags = WTAP_HAS_TS|WTAP_HAS_CAP_LEN;
+ rec->ts.secs = hr * 3600 + min * 60 + sec;
+ rec->ts.nsecs = csec * 10000000;
+ rec->rec_header.packet_header.caplen = pkt_len;
+ rec->rec_header.packet_header.len = pkt_len;
+
+ switch (channel[0]) {
+ case 'B':
+ rec->rec_header.packet_header.pkt_encap = WTAP_ENCAP_ISDN;
+ pseudo_header->isdn.uton = (direction[0] == 'T');
+ pseudo_header->isdn.channel = (guint8)
+ strtol(&channel[1], NULL, 10);
+ break;
+
+ case 'D':
+ rec->rec_header.packet_header.pkt_encap = WTAP_ENCAP_ISDN;
+ pseudo_header->isdn.uton = (direction[0] == 'T');
+ pseudo_header->isdn.channel = 0;
+ break;
+
+ default:
+ rec->rec_header.packet_header.pkt_encap = WTAP_ENCAP_ETHERNET;
+ /* XXX - is there an FCS in the frame? */
+ pseudo_header->eth.fcs_len = -1;
+ break;
+ }
+
+ /* Make sure we have enough room for the packet */
+ ws_buffer_assure_space(buf, pkt_len);
+ pd = ws_buffer_start_ptr(buf);
+
+ /* Calculate the number of hex dump lines, each
+ * containing 16 bytes of data */
+ hex_lines = pkt_len / 16 + ((pkt_len % 16) ? 1 : 0);
+
+ for (i = 0; i < hex_lines; i++) {
+ if (file_gets(line, TOSHIBA_LINE_LENGTH, fh) == NULL) {
+ *err = file_error(fh, err_info);
+ if (*err == 0) {
+ *err = WTAP_ERR_SHORT_READ;
+ }
+ return FALSE;
+ }
+ if (!parse_single_hex_dump_line(line, pd, i * 16)) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("toshiba: hex dump not valid");
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+/*
+ 1 2 3 4
+0123456789012345678901234567890123456789012345
+0000 : FF03 003D C000 0008 2145 0000 3A12 6500 ...=....!E..:.e.
+0010 : 003F 11E6 58CF C11A 8897 A401 0804 0400 .?..X...........
+0020 : 0100 01 ...
+*/
+
+#define START_POS 7
+#define HEX_LENGTH ((8 * 4) + 7) /* eight clumps of 4 bytes with 7 inner spaces */
+
+/* Take a string representing one line from a hex dump and converts the
+ * text to binary data. We check the printed offset with the offset
+ * we are passed to validate the record. We place the bytes in the buffer
+ * at the specified offset.
+ *
+ * In the process, we're going to write all over the string.
+ *
+ * Returns TRUE if good hex dump, FALSE if bad.
+ */
+static gboolean
+parse_single_hex_dump_line(char* rec, guint8 *buf, guint byte_offset) {
+
+ int pos, i;
+ char *s;
+ unsigned long value;
+ guint16 word_value;
+
+ /* Get the byte_offset directly from the record */
+ rec[4] = '\0';
+ s = rec;
+ value = strtoul(s, NULL, 16);
+
+ if (value != byte_offset) {
+ return FALSE;
+ }
+
+ /* Go through the substring representing the values and:
+ * 1. Replace any spaces with '0's
+ * 2. Place \0's every 5 bytes (to terminate the string)
+ *
+ * Then read the eight sets of hex bytes
+ */
+
+ for (pos = START_POS; pos < START_POS + HEX_LENGTH; pos++) {
+ if (rec[pos] == ' ') {
+ rec[pos] = '0';
+ }
+ }
+
+ pos = START_POS;
+ for (i = 0; i < 8; i++) {
+ rec[pos+4] = '\0';
+
+ word_value = (guint16) strtoul(&rec[pos], NULL, 16);
+ buf[byte_offset + i * 2 + 0] = (guint8) (word_value >> 8);
+ buf[byte_offset + i * 2 + 1] = (guint8) (word_value & 0x00ff);
+ pos += 5;
+ }
+
+ return TRUE;
+}
+
+static const struct supported_block_type toshiba_blocks_supported[] = {
+ /*
+ * We support packet blocks, with no comments or other options.
+ */
+ { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
+};
+
+static const struct file_type_subtype_info toshiba_info = {
+ "Toshiba Compact ISDN Router snoop", "toshiba", "txt", NULL,
+ FALSE, BLOCKS_SUPPORTED(toshiba_blocks_supported),
+ NULL, NULL, NULL
+};
+
+void register_toshiba(void)
+{
+ toshiba_file_type_subtype = wtap_register_file_type_subtype(&toshiba_info);
+
+ /*
+ * Register name for backwards compatibility with the
+ * wtap_filetypes table in Lua.
+ */
+ wtap_register_backwards_compatibility_lua_name("TOSHIBA",
+ toshiba_file_type_subtype);
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/wiretap/toshiba.h b/wiretap/toshiba.h
new file mode 100644
index 00000000..9fe96df9
--- /dev/null
+++ b/wiretap/toshiba.h
@@ -0,0 +1,18 @@
+/** @file
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __W_TOSHIBA_H__
+#define __W_TOSHIBA_H__
+
+#include <glib.h>
+#include "wtap.h"
+#include "ws_symbol_export.h"
+
+wtap_open_return_val toshiba_open(wtap *wth, int *err, gchar **err_info);
+
+#endif
diff --git a/wiretap/visual.c b/wiretap/visual.c
new file mode 100644
index 00000000..684a6a2e
--- /dev/null
+++ b/wiretap/visual.c
@@ -0,0 +1,910 @@
+/* visual.c
+ * File read and write routines for Visual Networks cap files.
+ * Copyright (c) 2001, Tom Nisbet tnisbet@visualnetworks.com
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "config.h"
+#include <string.h>
+#include "wtap-int.h"
+#include "file_wrappers.h"
+#include "visual.h"
+
+/*
+ * A Visual Networks traffic capture file contains three sections. The
+ * first is a 192 octet file header. This is followed by the captured
+ * packet header, and for ATM captures, there is an additional atm packet header.
+ * The data follows the packet header. The last section is the packet index block.
+ * The index block contains one 4 octet pointer for each captured packet.
+ * The first packet index is (4 * num_pkts) octets from the end of the file
+ * and the last index is in the last four octets of the file.
+ *
+ * All integer and time values are stored in little-endian format, except for
+ * the ATM Packet Header, which is stored in network byte order.
+ *
+ * [ File Header ]
+ *
+ *
+ * [ Packet Header 1 ] [(opt) ATM Packet Header] [ Data ]
+ * ...
+ * [ Packet Header n ] [(opt) ATM Packet Header] [ Data ]
+ *
+ *
+ * [ Index Block 1 ] ... [ Index Block n ]
+ */
+
+/* Capture file header, INCLUDING the magic number, is 192 bytes. */
+#define CAPTUREFILE_HEADER_SIZE 192
+
+/* Magic number for Visual Networks traffic capture files. */
+static const char visual_magic[] = {
+ 5, 'V', 'N', 'F'
+};
+
+
+/* Visual File Header (minus magic number). */
+/* This structure is used to extract information */
+struct visual_file_hdr
+{
+ guint32 num_pkts; /* Number of packets in the file */
+ guint32 start_time; /* Capture start time in PC format */
+ guint16 media_type; /* IANA ifType of packet source */
+ guint16 max_length; /* Max allowable stored packet length */
+ guint16 file_flags; /* File type flags */
+ /* Bit 0 indicates indexes present */
+ guint16 file_version; /* Version number of this file format */
+ guint32 media_speed; /* ifSpeed of packet source in bits/sec. */
+ guint16 media_param; /* Media-specific extra parameter. */
+ char RESERVED_[102]; /* MUST BE ALL ZEROS FOR FUTURE COMPATIBILITY */
+ char description[64]; /* File description (null terminated) */
+};
+
+
+/* Packet status bits */
+#define PS_LONG 0x01
+#define PS_SHORT 0x02
+#define PS_ERRORED 0x04
+#define PS_1ST_AFTER_DROP 0x08
+#define PS_APPROX_ORDER 0x10
+#define PS_SENT 0x40
+#define PS_ABORTED 0x80
+
+/* Visual Packet Header */
+/* This structure is used to extract information */
+struct visual_pkt_hdr
+{
+ guint32 ts_delta; /* Time stamp - msecs since start of capture */
+ guint16 orig_len; /* Actual length of packet */
+ guint16 incl_len; /* Number of octets captured in file */
+ guint32 status; /* Packet status flags (media specific) */
+ guint8 encap_hint; /* Encapsulation type hint */
+ guint8 encap_skip; /* Number of bytes to skip before decoding */
+ char RESERVED_[6]; /* RESERVED - must be zero */
+};
+
+/* Optional Visual ATM Packet Header */
+/* This structure is used to extract information */
+struct visual_atm_hdr
+{
+ guint16 vpi; /* 4 bits of zeros; 12 bits of ATM VPI */
+ guint16 vci; /* ATM VCI */
+ guint8 info; /* 4 bits version; 3 bits unused-zero; 1 bit direction */
+ guint8 category; /* indicates type of traffic. 4 bits of status + 4 bits of type */
+ guint16 cell_count; /* number of cells that make up this pdu */
+ guint32 data_length; /* PDU data length for AAL-5 PDUs, all others - cellcount * 48 */
+ guint32 ts_secs; /* seonds value of sysUpTime when the last cell of this PDU was captured */
+ guint32 ts_nsec; /* nanoseonds value of sysUpTime when the last cell of this PDU was captured */
+
+};
+
+/* visual_atm_hdr info bit definitions */
+#define FROM_NETWORK 0x01
+#define ATM_VER_MASK 0xf0 /* Not currently displayed */
+
+/* visual_atm_hdr category definitions */
+/* High nibble - not currently displayed */
+#define VN_INCOMPLETE 0x40
+#define VN_BAD_CRC 0x80
+#define VN_CAT_STAT_MASK 0xf0
+/* Low nibble */
+#define VN_UNKNOWN 0x00
+#define VN_AAL1 0x01
+#define VN_AAL2 0x02
+#define VN_AAL34 0x03
+#define VN_O191 0x04
+#define VN_AAL5 0x05
+#define VN_OAM 0x0a
+#define VN_RM 0x0b
+#define VN_IDLE 0x0c
+#define VN_CAT_TYPE_MASK 0x0f
+
+
+/* Additional information for reading Visual files */
+struct visual_read_info
+{
+ guint32 num_pkts; /* Number of pkts in the file */
+ guint32 current_pkt; /* Next packet to be read */
+ time_t start_time; /* Capture start time in seconds */
+};
+
+
+/* Additional information for writing Visual files */
+struct visual_write_info
+{
+ guint32 start_time; /* Capture start time in seconds */
+ int index_table_index; /* Index of the next index entry */
+ int index_table_size; /* Allocated size of the index table */
+ guint32 * index_table; /* File offsets for the packets */
+ guint32 next_offset; /* Offset of next packet */
+};
+
+
+/* Local functions to handle file reads and writes */
+static gboolean visual_read(wtap *wth, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info, gint64 *data_offset);
+static gboolean visual_seek_read(wtap *wth, gint64 seek_off,
+ wtap_rec *rec, Buffer *buf, int *err, gchar **err_info);
+static gboolean visual_read_packet(wtap *wth, FILE_T fh,
+ wtap_rec *rec, Buffer *buf, int *err, gchar **err_info);
+static gboolean visual_dump(wtap_dumper *wdh, const wtap_rec *rec,
+ const guint8 *pd, int *err, gchar **err_info);
+static gboolean visual_dump_finish(wtap_dumper *wdh, int *err,
+ gchar **err_info);
+static void visual_dump_free(wtap_dumper *wdh);
+
+static int visual_file_type_subtype = -1;
+
+void register_visual(void);
+
+
+/* Open a file for reading */
+wtap_open_return_val visual_open(wtap *wth, int *err, gchar **err_info)
+{
+ char magic[sizeof visual_magic];
+ struct visual_file_hdr vfile_hdr;
+ struct visual_read_info * visual;
+ int encap;
+
+ /* Check the magic string at the start of the file */
+ if (!wtap_read_bytes(wth->fh, magic, sizeof magic, err, err_info))
+ {
+ if (*err != WTAP_ERR_SHORT_READ)
+ return WTAP_OPEN_ERROR;
+ return WTAP_OPEN_NOT_MINE;
+ }
+ if (memcmp(magic, visual_magic, sizeof visual_magic) != 0)
+ {
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ /* Read the rest of the file header. */
+ if (!wtap_read_bytes(wth->fh, &vfile_hdr, sizeof vfile_hdr, err, err_info))
+ {
+ return WTAP_OPEN_ERROR;
+ }
+
+ /* Verify the file version is known */
+ vfile_hdr.file_version = pletoh16(&vfile_hdr.file_version);
+ if (vfile_hdr.file_version != 1)
+ {
+ *err = WTAP_ERR_UNSUPPORTED;
+ *err_info = ws_strdup_printf("visual: file version %u unsupported", vfile_hdr.file_version);
+ return WTAP_OPEN_ERROR;
+ }
+
+ /* Translate the encapsulation type; these values are SNMP ifType
+ values, as found in https://www.iana.org/assignments/smi-numbers.
+
+ Note that a file with media type 22 ("propPointToPointSerial") may
+ contain Cisco HDLC or PPP over HDLC. This will get sorted out after
+ the first packet is read.
+
+ XXX - should we use WTAP_ENCAP_PER_PACKET for that? */
+ switch (pletoh16(&vfile_hdr.media_type))
+ {
+ case 6: /* ethernet-csmacd */
+ encap = WTAP_ENCAP_ETHERNET;
+ break;
+
+ case 9: /* IEEE802.5 */
+ encap = WTAP_ENCAP_TOKEN_RING;
+ break;
+
+ case 16: /* lapb */
+ encap = WTAP_ENCAP_LAPB;
+ break;
+
+ case 22: /* propPointToPointSerial */
+ case 118: /* HDLC */
+ encap = WTAP_ENCAP_CHDLC_WITH_PHDR;
+ break;
+
+ case 32: /* frame-relay */
+ encap = WTAP_ENCAP_FRELAY_WITH_PHDR;
+ break;
+
+ case 37: /* ATM */
+ encap = WTAP_ENCAP_ATM_PDUS;
+ break;
+
+ default:
+ *err = WTAP_ERR_UNSUPPORTED;
+ *err_info = ws_strdup_printf("visual: network type %u unknown or unsupported",
+ vfile_hdr.media_type);
+ return WTAP_OPEN_ERROR;
+ }
+
+ /* Fill in the wiretap struct with data from the file header */
+ wth->file_type_subtype = visual_file_type_subtype;
+ wth->file_encap = encap;
+ wth->snapshot_length = pletoh16(&vfile_hdr.max_length);
+
+ /* Set up the pointers to the handlers for this file type */
+ wth->subtype_read = visual_read;
+ wth->subtype_seek_read = visual_seek_read;
+ wth->file_tsprec = WTAP_TSPREC_MSEC;
+
+ /* Add Visual-specific information to the wiretap struct for later use. */
+ visual = g_new(struct visual_read_info, 1);
+ wth->priv = (void *)visual;
+ visual->num_pkts = pletoh32(&vfile_hdr.num_pkts);
+ visual->start_time = pletoh32(&vfile_hdr.start_time);
+ visual->current_pkt = 1;
+
+ /*
+ * Add an IDB; we don't know how many interfaces were
+ * involved, so we just say one interface, about which
+ * we only know the link-layer type, snapshot length,
+ * and time stamp resolution.
+ */
+ wtap_add_generated_idb(wth);
+
+ return WTAP_OPEN_MINE;
+}
+
+
+/* Read the next available packet from the file. This is called
+ in a loop to sequentially read the entire file one time. After
+ the file has been read once, any Future access to the packets is
+ done through seek_read. */
+static gboolean visual_read(wtap *wth, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info, gint64 *data_offset)
+{
+ struct visual_read_info *visual = (struct visual_read_info *)wth->priv;
+
+ /* Check for the end of the packet data. Note that a check for file EOF
+ will not work because there are index values stored after the last
+ packet's data. */
+ if (visual->current_pkt > visual->num_pkts)
+ {
+ *err = 0; /* it's just an EOF, not an error */
+ return FALSE;
+ }
+ visual->current_pkt++;
+
+ *data_offset = file_tell(wth->fh);
+
+ return visual_read_packet(wth, wth->fh, rec, buf, err, err_info);
+}
+
+/* Read packet header and data for random access. */
+static gboolean visual_seek_read(wtap *wth, gint64 seek_off,
+ wtap_rec *rec, Buffer *buf, int *err, gchar **err_info)
+{
+ /* Seek to the packet header */
+ if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
+ return FALSE;
+
+ /* Read the packet. */
+ if (!visual_read_packet(wth, wth->random_fh, rec, buf, err, err_info)) {
+ if (*err == 0)
+ *err = WTAP_ERR_SHORT_READ;
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static gboolean
+visual_read_packet(wtap *wth, FILE_T fh, wtap_rec *rec,
+ Buffer *buf, int *err, gchar **err_info)
+{
+ struct visual_read_info *visual = (struct visual_read_info *)wth->priv;
+ struct visual_pkt_hdr vpkt_hdr;
+ guint32 packet_size;
+ struct visual_atm_hdr vatm_hdr;
+ guint32 relmsecs;
+ guint32 packet_status;
+ guint8 *pd;
+
+ /* Read the packet header. */
+ if (!wtap_read_bytes_or_eof(fh, &vpkt_hdr, (unsigned int)sizeof vpkt_hdr, err, err_info))
+ {
+ return FALSE;
+ }
+
+ /* Get the included length of data. This includes extra headers + payload */
+ packet_size = pletoh16(&vpkt_hdr.incl_len);
+
+ rec->rec_type = REC_TYPE_PACKET;
+ rec->block = wtap_block_create(WTAP_BLOCK_PACKET);
+ rec->presence_flags = WTAP_HAS_TS|WTAP_HAS_CAP_LEN;
+
+ /* Set the packet time and length. */
+ relmsecs = pletoh32(&vpkt_hdr.ts_delta);
+ rec->ts.secs = visual->start_time + relmsecs/1000;
+ rec->ts.nsecs = (relmsecs % 1000)*1000000;
+
+ rec->rec_header.packet_header.len = pletoh16(&vpkt_hdr.orig_len);
+
+ packet_status = pletoh32(&vpkt_hdr.status);
+
+ /* Do encapsulation-specific processing.
+
+ Most Visual capture types include the FCS in the original length
+ value, but don't include the FCS as part of the payload or captured
+ length. This is different from the model used in most other capture
+ file formats, including pcap and pcapng in cases where the FCS isn't
+ captured (which are the typical cases), and causes the RTP audio
+ payload save to fail since then captured len != orig len.
+
+ We adjust the original length to remove the FCS bytes we counted based
+ on the file encapsualtion type. The only downside to this fix is
+ throughput calculations will be slightly lower as it won't include
+ the FCS bytes. However, as noted, that problem also exists with
+ other capture formats.
+
+ We also set status flags. The only status currently supported for
+ all encapsulations is direction. This either goes in the p2p or the
+ X.25 pseudo header. It would probably be better to move this up
+ into the phdr. */
+ switch (wth->file_encap)
+ {
+ case WTAP_ENCAP_ETHERNET:
+ /* Ethernet has a 4-byte FCS. */
+ if (rec->rec_header.packet_header.len < 4)
+ {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("visual: Ethernet packet has %u-byte original packet, less than the FCS length",
+ rec->rec_header.packet_header.len);
+ return FALSE;
+ }
+ rec->rec_header.packet_header.len -= 4;
+
+ /* XXX - the above implies that there's never an FCS; should this
+ set the FCS length to 0? */
+ rec->rec_header.packet_header.pseudo_header.eth.fcs_len = -1;
+ break;
+
+ case WTAP_ENCAP_CHDLC_WITH_PHDR:
+ /* This has a 2-byte FCS. */
+ if (rec->rec_header.packet_header.len < 2)
+ {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("visual: Cisco HDLC packet has %u-byte original packet, less than the FCS length",
+ rec->rec_header.packet_header.len);
+ return FALSE;
+ }
+ rec->rec_header.packet_header.len -= 2;
+
+ rec->rec_header.packet_header.pseudo_header.p2p.sent = (packet_status & PS_SENT) ? TRUE : FALSE;
+ break;
+
+ case WTAP_ENCAP_PPP_WITH_PHDR:
+ /* No FCS.
+ XXX - true? Note that PPP can negotiate no FCS, a 2-byte FCS,
+ or a 4-byte FCS. */
+ rec->rec_header.packet_header.pseudo_header.p2p.sent = (packet_status & PS_SENT) ? TRUE : FALSE;
+ break;
+
+ case WTAP_ENCAP_FRELAY_WITH_PHDR:
+ /* This has a 2-byte FCS. */
+ if (rec->rec_header.packet_header.len < 2)
+ {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("visual: Frame Relay packet has %u-byte original packet, less than the FCS length",
+ rec->rec_header.packet_header.len);
+ return FALSE;
+ }
+ rec->rec_header.packet_header.len -= 2;
+
+ rec->rec_header.packet_header.pseudo_header.dte_dce.flags =
+ (packet_status & PS_SENT) ? 0x00 : FROM_DCE;
+ break;
+
+ case WTAP_ENCAP_LAPB:
+ /* This has a 2-byte FCS. */
+ if (rec->rec_header.packet_header.len < 2)
+ {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("visual: Frame Relay packet has %u-byte original packet, less than the FCS length",
+ rec->rec_header.packet_header.len);
+ return FALSE;
+ }
+ rec->rec_header.packet_header.len -= 2;
+
+ rec->rec_header.packet_header.pseudo_header.dte_dce.flags =
+ (packet_status & PS_SENT) ? 0x00 : FROM_DCE;
+ break;
+
+ case WTAP_ENCAP_ATM_PDUS:
+ /* ATM original length doesn't include any FCS. Do nothing to
+ the packet length.
+
+ ATM packets have an additional packet header; read and
+ process it. */
+ if (!wtap_read_bytes(fh, &vatm_hdr, (unsigned int)sizeof vatm_hdr, err, err_info))
+ {
+ return FALSE;
+ }
+
+ /* Remove ATM header from length of included bytes in capture, as
+ this header was appended by the processor doing the packet
+ reassembly, and was not transmitted across the wire */
+ packet_size -= (guint32)sizeof vatm_hdr;
+
+ /* Set defaults */
+ rec->rec_header.packet_header.pseudo_header.atm.type = TRAF_UNKNOWN;
+ rec->rec_header.packet_header.pseudo_header.atm.subtype = TRAF_ST_UNKNOWN;
+ rec->rec_header.packet_header.pseudo_header.atm.aal5t_len = 0;
+
+ /* Next two items not supported. Defaulting to zero */
+ rec->rec_header.packet_header.pseudo_header.atm.aal5t_u2u = 0;
+ rec->rec_header.packet_header.pseudo_header.atm.aal5t_chksum = 0;
+
+ /* Flags appear only to convey that packet is a raw cell. Set to 0 */
+ rec->rec_header.packet_header.pseudo_header.atm.flags = 0;
+
+ /* Not supported. Defaulting to zero */
+ rec->rec_header.packet_header.pseudo_header.atm.aal2_cid = 0;
+
+ switch(vatm_hdr.category & VN_CAT_TYPE_MASK )
+ {
+ case VN_AAL1:
+ rec->rec_header.packet_header.pseudo_header.atm.aal = AAL_1;
+ break;
+
+ case VN_AAL2:
+ rec->rec_header.packet_header.pseudo_header.atm.aal = AAL_2;
+ break;
+
+ case VN_AAL34:
+ rec->rec_header.packet_header.pseudo_header.atm.aal = AAL_3_4;
+ break;
+
+ case VN_AAL5:
+ rec->rec_header.packet_header.pseudo_header.atm.aal = AAL_5;
+ rec->rec_header.packet_header.pseudo_header.atm.type = TRAF_LLCMX;
+ rec->rec_header.packet_header.pseudo_header.atm.aal5t_len = pntoh32(&vatm_hdr.data_length);
+ break;
+
+ case VN_OAM:
+ /* Marking next 3 as OAM versus unknown */
+ case VN_O191:
+ case VN_IDLE:
+ case VN_RM:
+ rec->rec_header.packet_header.pseudo_header.atm.aal = AAL_OAMCELL;
+ break;
+
+ case VN_UNKNOWN:
+ default:
+ rec->rec_header.packet_header.pseudo_header.atm.aal = AAL_UNKNOWN;
+ break;
+ }
+ rec->rec_header.packet_header.pseudo_header.atm.vpi = pntoh16(&vatm_hdr.vpi) & 0x0FFF;
+ rec->rec_header.packet_header.pseudo_header.atm.vci = pntoh16(&vatm_hdr.vci);
+ rec->rec_header.packet_header.pseudo_header.atm.cells = pntoh16(&vatm_hdr.cell_count);
+
+ /* Using bit value of 1 (DCE -> DTE) to indicate From Network */
+ rec->rec_header.packet_header.pseudo_header.atm.channel = vatm_hdr.info & FROM_NETWORK;
+ break;
+
+ /* Not sure about token ring. Just leaving alone for now. */
+ case WTAP_ENCAP_TOKEN_RING:
+ default:
+ break;
+ }
+
+ rec->rec_header.packet_header.caplen = packet_size;
+
+ /* Check for too-large packet. */
+ if (packet_size > WTAP_MAX_PACKET_SIZE_STANDARD)
+ {
+ /* Probably a corrupt capture file; don't blow up trying
+ to allocate space for an immensely-large packet. */
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("visual: File has %u-byte packet, bigger than maximum of %u",
+ packet_size, WTAP_MAX_PACKET_SIZE_STANDARD);
+ return FALSE;
+ }
+
+ /* Read the packet data */
+ if (!wtap_read_packet_bytes(fh, buf, packet_size, err, err_info))
+ return FALSE;
+
+ if (wth->file_encap == WTAP_ENCAP_CHDLC_WITH_PHDR)
+ {
+ /* Fill in the encapsulation. Visual files have a media type in the
+ file header and an encapsulation type in each packet header. Files
+ with a media type of HDLC can be either Cisco EtherType or PPP.
+
+ The encapsulation hint values we've seen are:
+
+ 2 - seen in an Ethernet capture
+ 13 - seen in a PPP capture; possibly also seen in Cisco HDLC
+ captures
+ 14 - seen in a PPP capture; probably seen only for PPP.
+
+ According to bug 2005, the collection probe can be configured
+ for PPP, in which case the encapsulation hint is 14, or can
+ be configured for auto-detect, in which case the encapsulation
+ hint is 13, and the encapsulation must be guessed from the
+ packet contents. Auto-detect is the default. */
+ pd = ws_buffer_start_ptr(buf);
+
+ /* If PPP is specified in the encap hint, then use that */
+ if (vpkt_hdr.encap_hint == 14)
+ {
+ /* But first we need to examine the first three octets to
+ try to determine the proper encapsulation, see RFC 2364. */
+ if (packet_size >= 3 &&
+ (0xfe == pd[0]) && (0xfe == pd[1]) && (0x03 == pd[2]))
+ {
+ /* It is actually LLC encapsulated PPP */
+ rec->rec_header.packet_header.pkt_encap = WTAP_ENCAP_ATM_RFC1483;
+ }
+ else
+ {
+ /* It is actually PPP */
+ rec->rec_header.packet_header.pkt_encap = WTAP_ENCAP_PPP_WITH_PHDR;
+ }
+ }
+ else
+ {
+ /* Otherwise, we need to examine the first two octets to
+ try to determine the encapsulation. */
+ if (packet_size >= 2 && (0xff == pd[0]) && (0x03 == pd[1]))
+ {
+ /* It is actually PPP */
+ rec->rec_header.packet_header.pkt_encap = WTAP_ENCAP_PPP_WITH_PHDR;
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+/* Check for media types that may be written in Visual file format.
+ Returns 0 if the specified encapsulation type is supported,
+ an error indication otherwise. */
+static int visual_dump_can_write_encap(int encap)
+{
+ /* Per-packet encapsulations aren't supported. */
+ if (encap == WTAP_ENCAP_PER_PACKET)
+ return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
+
+ /* Check for supported encapsulation types */
+ switch (encap)
+ {
+ case WTAP_ENCAP_ETHERNET:
+ case WTAP_ENCAP_TOKEN_RING:
+ case WTAP_ENCAP_LAPB:
+ case WTAP_ENCAP_CHDLC_WITH_PHDR:
+ case WTAP_ENCAP_FRELAY_WITH_PHDR:
+ case WTAP_ENCAP_PPP:
+ case WTAP_ENCAP_PPP_WITH_PHDR:
+ return 0;
+ }
+
+ return WTAP_ERR_UNWRITABLE_ENCAP;
+}
+
+
+/* Open a file for writing.
+ Returns TRUE on success, FALSE on failure; sets "*err" to an
+ error code on failure */
+static gboolean visual_dump_open(wtap_dumper *wdh, int *err, gchar **err_info _U_)
+{
+ struct visual_write_info *visual;
+
+ /* Set the write routines for a visual file. */
+ wdh->subtype_write = visual_dump;
+ wdh->subtype_finish = visual_dump_finish;
+
+ /* Create a struct to hold file information for the duration
+ of the write */
+ visual = g_new(struct visual_write_info, 1);
+ wdh->priv = (void *)visual;
+ visual->index_table_index = 0;
+ visual->index_table_size = 1024;
+ visual->index_table = 0;
+ visual->next_offset = CAPTUREFILE_HEADER_SIZE;
+
+ /* All of the fields in the file header aren't known yet so
+ just skip over it for now. It will be created after all
+ of the packets have been written. */
+ if (wtap_dump_file_seek(wdh, CAPTUREFILE_HEADER_SIZE, SEEK_SET, err) == -1)
+ return FALSE;
+
+ return TRUE;
+}
+
+
+/* Write a packet to a Visual dump file.
+ Returns TRUE on success, FALSE on failure. */
+static gboolean visual_dump(wtap_dumper *wdh, const wtap_rec *rec,
+ const guint8 *pd, int *err, gchar **err_info _U_)
+{
+ const union wtap_pseudo_header *pseudo_header = &rec->rec_header.packet_header.pseudo_header;
+ struct visual_write_info * visual = (struct visual_write_info *)wdh->priv;
+ struct visual_pkt_hdr vpkt_hdr = {0};
+ size_t hdr_size = sizeof vpkt_hdr;
+ guint delta_msec;
+ guint32 packet_status;
+
+ /* We can only write packet records. */
+ if (rec->rec_type != REC_TYPE_PACKET) {
+ *err = WTAP_ERR_UNWRITABLE_REC_TYPE;
+ return FALSE;
+ }
+
+ /*
+ * Make sure this packet doesn't have a link-layer type that
+ * differs from the one for the file.
+ */
+ if (wdh->file_encap != rec->rec_header.packet_header.pkt_encap) {
+ *err = WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
+ return FALSE;
+ }
+
+ /* Don't write anything we're not willing to read. */
+ if (rec->rec_header.packet_header.caplen > WTAP_MAX_PACKET_SIZE_STANDARD) {
+ *err = WTAP_ERR_PACKET_TOO_LARGE;
+ return FALSE;
+ }
+
+ /* If the visual structure was never allocated then nothing useful
+ can be done. */
+ if (visual == 0)
+ return FALSE;
+
+ /* Visual UpTime capture files have a capture start time in the
+ file header. Each packet has a capture time (in msec) relative
+ to the file start time. Use the time of the first packet as the
+ file start time. */
+ if (visual->index_table_index == 0)
+ {
+ /*
+ * This is the first packet. Save its start time as the file time.
+ *
+ * XXX - is the start time signed, or unsigned? If it's signed,
+ * in which case we should check against G_MININT32 and G_MAXINT32
+ * and make start_time a gint32.
+ */
+ if (rec->ts.secs < 0 || rec->ts.secs > WTAP_NSTIME_32BIT_SECS_MAX) {
+ *err = WTAP_ERR_TIME_STAMP_NOT_SUPPORTED;
+ return FALSE;
+ }
+ visual->start_time = (guint32)rec->ts.secs;
+
+ /* Initialize the index table */
+ visual->index_table = (guint32 *)g_malloc(1024 * sizeof *visual->index_table);
+ visual->index_table_size = 1024;
+ }
+
+ /* Calculate milliseconds since capture start. */
+ delta_msec = rec->ts.nsecs / 1000000;
+ delta_msec += (guint32)((rec->ts.secs - visual->start_time) * 1000);
+ vpkt_hdr.ts_delta = GUINT32_TO_LE(delta_msec);
+
+ /* Fill in the length fields. */
+ vpkt_hdr.orig_len = GUINT16_TO_LE(rec->rec_header.packet_header.len);
+ vpkt_hdr.incl_len = GUINT16_TO_LE(rec->rec_header.packet_header.caplen);
+
+ /* Fill in the encapsulation hint for the file's media type. */
+ switch (wdh->file_encap)
+ {
+ case WTAP_ENCAP_ETHERNET: /* Ethernet */
+ vpkt_hdr.encap_hint = 2;
+ break;
+ case WTAP_ENCAP_TOKEN_RING: /* Token Ring */
+ vpkt_hdr.encap_hint = 3;
+ break;
+ case WTAP_ENCAP_PPP: /* PPP */
+ case WTAP_ENCAP_PPP_WITH_PHDR:
+ vpkt_hdr.encap_hint = 14;
+ break;
+ case WTAP_ENCAP_CHDLC_WITH_PHDR: /* HDLC Router */
+ vpkt_hdr.encap_hint = 13;
+ break;
+ case WTAP_ENCAP_FRELAY_WITH_PHDR: /* Frame Relay Auto-detect */
+ vpkt_hdr.encap_hint = 12;
+ break;
+ case WTAP_ENCAP_LAPB: /* Unknown */
+ default:
+ vpkt_hdr.encap_hint = 1;
+ break;
+ }
+
+ /* Set status flags. The only status currently supported for all
+ encapsulations is direction. This either goes in the p2p or the
+ X.25 pseudo header. It would probably be better to move this up
+ into the phdr. */
+ packet_status = 0;
+ switch (wdh->file_encap)
+ {
+ case WTAP_ENCAP_CHDLC_WITH_PHDR:
+ packet_status |= (pseudo_header->p2p.sent ? PS_SENT : 0x00);
+ break;
+
+ case WTAP_ENCAP_FRELAY_WITH_PHDR:
+ case WTAP_ENCAP_LAPB:
+ packet_status |=
+ ((pseudo_header->dte_dce.flags & FROM_DCE) ? 0x00 : PS_SENT);
+ break;
+ }
+ vpkt_hdr.status = GUINT32_TO_LE(packet_status);
+
+ /* Write the packet header. */
+ if (!wtap_dump_file_write(wdh, &vpkt_hdr, hdr_size, err))
+ return FALSE;
+
+ /* Write the packet data */
+ if (!wtap_dump_file_write(wdh, pd, rec->rec_header.packet_header.caplen, err))
+ return FALSE;
+
+ /* Store the frame offset in the index table. */
+ if (visual->index_table_index >= visual->index_table_size)
+ {
+ /* End of table reached. Reallocate with a larger size */
+ visual->index_table_size *= 2;
+ visual->index_table = (guint32 *)g_realloc(visual->index_table,
+ visual->index_table_size * sizeof *visual->index_table);
+ }
+ visual->index_table[visual->index_table_index] = GUINT32_TO_LE(visual->next_offset);
+
+ /* Update the table index and offset for the next frame. */
+ visual->index_table_index++;
+ visual->next_offset += (guint32) hdr_size + rec->rec_header.packet_header.caplen;
+
+ return TRUE;
+}
+
+
+/* Finish writing to a dump file.
+ Returns TRUE on success, FALSE on failure. */
+static gboolean visual_dump_finish(wtap_dumper *wdh, int *err,
+ gchar **err_info _U_)
+{
+ struct visual_write_info * visual = (struct visual_write_info *)wdh->priv;
+ size_t n_to_write;
+ struct visual_file_hdr vfile_hdr = {0};
+ const char *magicp;
+ size_t magic_size;
+
+ /* If the visual structure was never allocated then nothing useful
+ can be done. */
+ if (visual == 0)
+ return FALSE;
+
+ /* Write out the frame table at the end of the file. */
+ if (visual->index_table)
+ {
+ /* Write the index table to the file. */
+ n_to_write = visual->index_table_index * sizeof *visual->index_table;
+ if (!wtap_dump_file_write(wdh, visual->index_table, n_to_write, err))
+ {
+ visual_dump_free(wdh);
+ return FALSE;
+ }
+ }
+
+ /* Write the magic number at the start of the file. */
+ if (wtap_dump_file_seek(wdh, 0, SEEK_SET, err) == -1)
+ return FALSE;
+ magicp = visual_magic;
+ magic_size = sizeof visual_magic;
+ if (!wtap_dump_file_write(wdh, magicp, magic_size, err))
+ {
+ visual_dump_free(wdh);
+ return FALSE;
+ }
+
+ vfile_hdr.num_pkts = GUINT32_TO_LE(visual->index_table_index);
+ vfile_hdr.start_time = GUINT32_TO_LE(visual->start_time);
+ vfile_hdr.max_length = GUINT16_TO_LE(65535);
+ vfile_hdr.file_flags = GUINT16_TO_LE(1); /* indexes are present */
+ vfile_hdr.file_version = GUINT16_TO_LE(1);
+ (void) g_strlcpy(vfile_hdr.description, "Wireshark file", 64);
+
+ /* Translate the encapsulation type */
+ switch (wdh->file_encap)
+ {
+ case WTAP_ENCAP_ETHERNET:
+ vfile_hdr.media_type = GUINT16_TO_LE(6);
+ break;
+
+ case WTAP_ENCAP_TOKEN_RING:
+ vfile_hdr.media_type = GUINT16_TO_LE(9);
+ break;
+
+ case WTAP_ENCAP_LAPB:
+ vfile_hdr.media_type = GUINT16_TO_LE(16);
+ break;
+
+ case WTAP_ENCAP_PPP: /* PPP is differentiated from CHDLC in PktHdr */
+ case WTAP_ENCAP_PPP_WITH_PHDR:
+ case WTAP_ENCAP_CHDLC_WITH_PHDR:
+ vfile_hdr.media_type = GUINT16_TO_LE(22);
+ break;
+
+ case WTAP_ENCAP_FRELAY_WITH_PHDR:
+ vfile_hdr.media_type = GUINT16_TO_LE(32);
+ break;
+ }
+
+ /* Write the file header following the magic bytes. */
+ if (!wtap_dump_file_write(wdh, &vfile_hdr, sizeof vfile_hdr, err))
+ {
+ visual_dump_free(wdh);
+ return FALSE;
+ }
+
+ /* Deallocate the file write data */
+ visual_dump_free(wdh);
+ return TRUE;
+}
+
+
+/* Free the memory allocated by a visual file writer. */
+static void visual_dump_free(wtap_dumper *wdh)
+{
+ struct visual_write_info * visual = (struct visual_write_info *)wdh->priv;
+
+ if (visual)
+ {
+ /* Free the index table memory. */
+ g_free(visual->index_table);
+ }
+}
+
+static const struct supported_block_type visual_blocks_supported[] = {
+ /*
+ * We support packet blocks, with no comments or other options.
+ */
+ { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
+};
+
+static const struct file_type_subtype_info visual_info = {
+ "Visual Networks traffic capture", "visual", NULL, NULL,
+ TRUE, BLOCKS_SUPPORTED(visual_blocks_supported),
+ visual_dump_can_write_encap, visual_dump_open, NULL
+};
+
+void register_visual(void)
+{
+ visual_file_type_subtype = wtap_register_file_type_subtype(&visual_info);
+
+ /*
+ * Register name for backwards compatibility with the
+ * wtap_filetypes table in Lua.
+ */
+ wtap_register_backwards_compatibility_lua_name("VISUAL_NETWORKS",
+ visual_file_type_subtype);
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * vi: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */
diff --git a/wiretap/visual.h b/wiretap/visual.h
new file mode 100644
index 00000000..c88e62f7
--- /dev/null
+++ b/wiretap/visual.h
@@ -0,0 +1,23 @@
+/** @file
+ *
+ * File read write routines for Visual Networks .cap files.
+ * Copyright 2001, Tom Nisbet tnisbet@visualnetworks.com
+ *
+ * Based on the code that handles netmon files.
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __VISUAL_H__
+#define __VISUAL_H__
+
+#include <glib.h>
+#include "wtap.h"
+#include "ws_symbol_export.h"
+
+wtap_open_return_val visual_open(wtap *wth, int *err, gchar **err_info);
+
+#endif
diff --git a/wiretap/vms.c b/wiretap/vms.c
new file mode 100644
index 00000000..f591695b
--- /dev/null
+++ b/wiretap/vms.c
@@ -0,0 +1,575 @@
+/* vms.c
+ *
+ * Wiretap Library
+ * Copyright (c) 2001 by Marc Milgram <ethereal@mmilgram.NOSPAMmail.net>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+/* Notes:
+ * TCPIPtrace TCP fragments don't have the header line. So, we are never
+ * to look for that line for the first line of a packet except the first
+ * packet. This allows us to read fragmented packets. Define
+ * TCPIPTRACE_FRAGMENTS_HAVE_HEADER_LINE to expect the first line to be
+ * at the start of every packet.
+ */
+#include "config.h"
+#include "wtap-int.h"
+#include "vms.h"
+#include "file_wrappers.h"
+
+#include <wsutil/strtoi.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+/* This module reads the output of the various VMS TCPIP trace utilities
+ * such as TCPIPTRACE, TCPTRACE and UCX$TRACE
+ *
+ * It was initially based on toshiba.c and refined with code from cosine.c
+
+--------------------------------------------------------------------------------
+ Example TCPIPTRACE TCPTRACE output data:
+
+ TCPIPtrace full display RCV packet 8 at 10-JUL-2001 14:54:19.56
+
+ IP Version = 4, IHL = 5, TOS = 00, Total Length = 84 = ^x0054
+ IP Identifier = ^x178F, Flags (0=0,DF=0,MF=0),
+ Fragment Offset = 0 = ^x0000, Calculated Offset = 0 = ^x0000
+ IP TTL = 64 = ^x40, Protocol = 17 = ^x11, Header Checksum = ^x4C71
+ IP Source Address = 10.12.1.80
+ IP Destination Address = 10.12.1.50
+
+ UDP Source Port = 731, UDP Destination Port = 111
+ UDP Header and Datagram Length = 64 = ^x0040, Checksum = ^xB6C0
+
+ 50010C0A 714C1140 00008F17 54000045 0000 E..T....@.Lq...P
+ 27E54C3C | C0B64000 6F00DB02 | 32010C0A 0010 ...2...o.@..<L.'
+ 02000000 A0860100 02000000 00000000 0020 ................
+ 00000000 00000000 00000000 03000000 0030 ................
+ 06000000 01000000 A5860100 00000000 0040 ................
+ 00000000 0050 ....
+--------------------------------------------------------------------------------
+
+ Example UCX$TRACE output data:
+
+ UCX INTERnet trace RCV packet seq # = 1 at 14-MAY-2003 11:32:10.93
+
+ IP Version = 4, IHL = 5, TOS = 00, Total Length = 583 = ^x0247
+ IP Identifier = ^x702E, Flags (0=0,DF=0,MF=0),
+ Fragment Offset = 0 = ^x0000, Calculated Offset = 0 = ^x0000
+ IP TTL = 128 = ^x80, Protocol = 17 = ^x11, Header Checksum = ^x70EC
+ IP Source Address = 10.20.4.159
+ IP Destination Address = 10.20.4.255
+
+ UDP Source Port = 138, UDP Destination Port = 138
+ UDP Header and Datagram Length = 563 = ^x0233, Checksum = ^xB913
+
+ 9F04140A 70EC1180 0000702E 47020045 0000 E..G.p.....p....
+ B1B80E11 | B9133302 8A008A00 | FF04140A 0010 .........3......
+ 46484648 45200000 1D028A00 9F04140A 0020 ...........EHFHF
+ 43414341 4341434D 454D4546 45454550 0030 PEEEFEMEMCACACAC
+
+--------------------------------------------------------------------------------
+
+ Alternate UCX$TRACE type output data:
+
+ TCPIP INTERnet trace RCV packet seq # = 1 at 23-OCT-1998 15:19:33.29
+
+ IP Version = 4, IHL = 5, TOS = 00, Total Length = 217 = ^x00D9
+ IP Identifier = ^x0065, Flags (0=0,DF=0,MF=0),
+ Fragment Offset = 0 = ^x0000, Calculated Offset = 0 = ^x0000
+ IP TTL = 32 = ^x20, Protocol = 17 = ^x11, Header Checksum = ^x8F6C
+ IP Source Address = 16.20.168.93
+ IP Destination Address = 16.20.255.255
+
+ UDP Source Port = 138, UDP Destination Port = 138
+ UDP Header and Datagram Length = 197 = ^x00C5, Checksum = ^x0E77
+
+ 5DA81410 8F6C1120 00000065 D9000045 0000 E...awe.....l....]
+ | 0E77C500 8A008A00 | FFFF1410 0010 ..........w.
+
+--------------------------------------------------------------------------------
+
+The only difference between the utilities is the Packet header line, primarily
+the utility identifier and the packet sequence formats.
+
+There appear to be 2 formats for packet sequencing
+
+Format 1:
+
+ ... packet nn at DD-MMM-YYYY hh:mm:ss.ss
+
+Format 2:
+
+ ... packet seq # = nn at DD-MMM-YYYY hh:mm:ss.ss
+
+If there are other formats then code will have to be written in parse_vms_packet()
+to handle them.
+
+--------------------------------------------------------------------------------
+
+ */
+
+/* Magic text to check for VMS-ness of file using possible utility names
+ *
+ */
+#define VMS_HDR_MAGIC_STR1 "TCPIPtrace"
+#define VMS_HDR_MAGIC_STR2 "TCPtrace"
+#define VMS_HDR_MAGIC_STR3 "INTERnet trace"
+
+/* Magic text for start of packet */
+#define VMS_REC_MAGIC_STR1 VMS_HDR_MAGIC_STR1
+#define VMS_REC_MAGIC_STR2 VMS_HDR_MAGIC_STR2
+#define VMS_REC_MAGIC_STR3 VMS_HDR_MAGIC_STR3
+
+#define VMS_HEADER_LINES_TO_CHECK 200
+#define VMS_LINE_LENGTH 240
+
+static gboolean vms_read(wtap *wth, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info, gint64 *data_offset);
+static gboolean vms_seek_read(wtap *wth, gint64 seek_off,
+ wtap_rec *rec, Buffer *buf, int *err, gchar **err_info);
+static gboolean parse_single_hex_dump_line(char* rec, guint8 *buf,
+ long byte_offset, int in_off, int remaining_bytes);
+static gboolean parse_vms_packet(FILE_T fh, wtap_rec *rec,
+ Buffer *buf, int *err, gchar **err_info);
+
+static int vms_file_type_subtype = -1;
+
+void register_vms(void);
+
+#ifdef TCPIPTRACE_FRAGMENTS_HAVE_HEADER_LINE
+/* Seeks to the beginning of the next packet, and returns the
+ byte offset. Returns -1 on failure, and sets "*err" to the error
+ and sets "*err_info" to null or an additional error string. */
+static long vms_seek_next_packet(wtap *wth, int *err, gchar **err_info)
+{
+ long cur_off;
+ char buf[VMS_LINE_LENGTH];
+
+ while (1) {
+ cur_off = file_tell(wth->fh);
+ if (cur_off == -1) {
+ /* Error */
+ *err = file_error(wth->fh, err_info);
+ return -1;
+ }
+ if (file_gets(buf, sizeof(buf), wth->fh) == NULL) {
+ /* EOF or error. */
+ *err = file_error(wth->fh, err_info);
+ break;
+ }
+ if (strstr(buf, VMS_REC_MAGIC_STR1) ||
+ strstr(buf, VMS_REC_MAGIC_STR2) ||
+ strstr(buf, VMS_REC_MAGIC_STR2)) {
+ (void) g_strlcpy(hdr, buf,VMS_LINE_LENGTH);
+ return cur_off;
+ }
+ }
+ return -1;
+}
+#endif /* TCPIPTRACE_FRAGMENTS_HAVE_HEADER_LINE */
+
+/* Look through the first part of a file to see if this is
+ * a VMS trace file.
+ *
+ * Returns TRUE if it is, FALSE if it isn't or if we get an I/O error;
+ * if we get an I/O error, "*err" will be set to a non-zero value and
+ * "*err_info will be set to null or an additional error string.
+ *
+ * Leaves file handle at beginning of line that contains the VMS Magic
+ * identifier.
+ */
+static gboolean vms_check_file_type(wtap *wth, int *err, gchar **err_info)
+{
+ char buf[VMS_LINE_LENGTH];
+ guint reclen, line;
+ gint64 mpos;
+
+ buf[VMS_LINE_LENGTH-1] = '\0';
+
+ for (line = 0; line < VMS_HEADER_LINES_TO_CHECK; line++) {
+ mpos = file_tell(wth->fh);
+ if (mpos == -1) {
+ /* Error. */
+ *err = file_error(wth->fh, err_info);
+ return FALSE;
+ }
+ if (file_gets(buf, VMS_LINE_LENGTH, wth->fh) == NULL) {
+ /* EOF or error. */
+ *err = file_error(wth->fh, err_info);
+ return FALSE;
+ }
+
+ reclen = (guint) strlen(buf);
+ if (reclen < strlen(VMS_HDR_MAGIC_STR1) ||
+ reclen < strlen(VMS_HDR_MAGIC_STR2) ||
+ reclen < strlen(VMS_HDR_MAGIC_STR3)) {
+ continue;
+ }
+
+ if (strstr(buf, VMS_HDR_MAGIC_STR1) ||
+ strstr(buf, VMS_HDR_MAGIC_STR2) ||
+ strstr(buf, VMS_HDR_MAGIC_STR3)) {
+ /* Go back to the beginning of this line, so we will
+ * re-read it. */
+ if (file_seek(wth->fh, mpos, SEEK_SET, err) == -1) {
+ /* Error. */
+ return FALSE;
+ }
+ return TRUE;
+ }
+ }
+ *err = 0;
+ return FALSE;
+}
+
+
+wtap_open_return_val vms_open(wtap *wth, int *err, gchar **err_info)
+{
+ /* Look for VMS header */
+ if (!vms_check_file_type(wth, err, err_info)) {
+ if (*err != 0 && *err != WTAP_ERR_SHORT_READ)
+ return WTAP_OPEN_ERROR;
+ return WTAP_OPEN_NOT_MINE;
+ }
+
+ wth->file_encap = WTAP_ENCAP_RAW_IP;
+ wth->file_type_subtype = vms_file_type_subtype;
+ wth->snapshot_length = 0; /* not known */
+ wth->subtype_read = vms_read;
+ wth->subtype_seek_read = vms_seek_read;
+ wth->file_tsprec = WTAP_TSPREC_10_MSEC;
+
+ /*
+ * Add an IDB; we don't know how many interfaces were
+ * involved, so we just say one interface, about which
+ * we only know the link-layer type, snapshot length,
+ * and time stamp resolution.
+ */
+ wtap_add_generated_idb(wth);
+
+ return WTAP_OPEN_MINE;
+}
+
+/* Find the next packet and parse it; called from wtap_read(). */
+static gboolean vms_read(wtap *wth, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info, gint64 *data_offset)
+{
+ gint64 offset = 0;
+
+ /* Find the next packet */
+#ifdef TCPIPTRACE_FRAGMENTS_HAVE_HEADER_LINE
+ offset = vms_seek_next_packet(wth, err, err_info);
+#else
+ offset = file_tell(wth->fh);
+#endif
+ if (offset < 1) {
+ *err = file_error(wth->fh, err_info);
+ return FALSE;
+ }
+ *data_offset = offset;
+
+ /* Parse the packet */
+ return parse_vms_packet(wth->fh, rec, buf, err, err_info);
+}
+
+/* Used to read packets in random-access fashion */
+static gboolean
+vms_seek_read(wtap *wth, gint64 seek_off, wtap_rec *rec,
+ Buffer *buf, int *err, gchar **err_info)
+{
+ if (file_seek(wth->random_fh, seek_off - 1, SEEK_SET, err) == -1)
+ return FALSE;
+
+ if (!parse_vms_packet(wth->random_fh, rec, buf, err, err_info)) {
+ if (*err == 0)
+ *err = WTAP_ERR_SHORT_READ;
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/* isdumpline assumes that dump lines start with some non-alphanumerics
+ * followed by 4 hex numbers - each 8 digits long, each hex number followed
+ * by 3 spaces.
+ */
+static int
+isdumpline( gchar *line )
+{
+ int i, j;
+
+ while (*line && !g_ascii_isalnum(*line))
+ line++;
+
+ for (j=0; j<4; j++) {
+ for (i=0; i<8; i++, line++)
+ if (! g_ascii_isxdigit(*line))
+ return FALSE;
+
+ for (i=0; i<3; i++, line++)
+ if (*line != ' ')
+ return FALSE;
+ }
+
+ return g_ascii_isspace(*line);
+}
+
+/* Parses a packet record. */
+static gboolean
+parse_vms_packet(FILE_T fh, wtap_rec *rec, Buffer *buf, int *err, gchar **err_info)
+{
+ char line[VMS_LINE_LENGTH + 1];
+ int num_items_scanned;
+ gboolean have_pkt_len = FALSE;
+ guint32 pkt_len = 0;
+ int pktnum;
+ int csec = 101;
+ struct tm tm;
+ char mon[4] = {'J', 'A', 'N', 0};
+ gchar *p;
+ const gchar *endp;
+ static const gchar months[] = "JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC";
+ guint32 i;
+ int offset = 0;
+ guint8 *pd;
+
+ tm.tm_year = 1970;
+ tm.tm_mon = 0;
+ tm.tm_mday = 1;
+ tm.tm_hour = 1;
+ tm.tm_min = 1;
+ tm.tm_sec = 1;
+
+ /* Skip lines until one starts with a hex number */
+ do {
+ if (file_gets(line, VMS_LINE_LENGTH, fh) == NULL) {
+ *err = file_error(fh, err_info);
+ if ((*err == 0) && (csec != 101)) {
+ *err = WTAP_ERR_SHORT_READ;
+ }
+ return FALSE;
+ }
+ line[VMS_LINE_LENGTH] = '\0';
+
+ if ((csec == 101) && (p = strstr(line, "packet ")) != NULL
+ && (! strstr(line, "could not save "))) {
+ /* Find text in line starting with "packet ". */
+
+ /* First look for the Format 1 type sequencing */
+ num_items_scanned = sscanf(p,
+ "packet %9d at %2d-%3s-%4d %2d:%2d:%2d.%9d",
+ &pktnum, &tm.tm_mday, mon,
+ &tm.tm_year, &tm.tm_hour,
+ &tm.tm_min, &tm.tm_sec, &csec);
+ /* Next look for the Format 2 type sequencing */
+ if (num_items_scanned != 8) {
+ num_items_scanned = sscanf(p,
+ "packet seq # = %9d at %2d-%3s-%4d %2d:%2d:%2d.%9d",
+ &pktnum, &tm.tm_mday, mon,
+ &tm.tm_year, &tm.tm_hour,
+ &tm.tm_min, &tm.tm_sec, &csec);
+ }
+ /* if unknown format then exit with error */
+ /* We will need to add code to handle new format */
+ if (num_items_scanned != 8) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("vms: header line not valid");
+ return FALSE;
+ }
+ }
+ if ( (! have_pkt_len) && (p = strstr(line, "Length "))) {
+ p += sizeof("Length ");
+ while (*p && ! g_ascii_isdigit(*p))
+ p++;
+
+ if ( !*p ) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("vms: Length field not valid");
+ return FALSE;
+ }
+
+ if (!ws_strtou32(p, &endp, &pkt_len) || (*endp != '\0' && !g_ascii_isspace(*endp))) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("vms: Length field '%s' not valid", p);
+ return FALSE;
+ }
+ have_pkt_len = TRUE;
+ break;
+ }
+ } while (! isdumpline(line));
+ if (! have_pkt_len) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("vms: Length field not found");
+ return FALSE;
+ }
+ if (pkt_len > WTAP_MAX_PACKET_SIZE_STANDARD) {
+ /*
+ * Probably a corrupt capture file; return an error,
+ * so that our caller doesn't blow up trying to allocate
+ * space for an immensely-large packet.
+ */
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("vms: File has %u-byte packet, bigger than maximum of %u",
+ pkt_len, WTAP_MAX_PACKET_SIZE_STANDARD);
+ return FALSE;
+ }
+
+ p = strstr(months, mon);
+ if (p)
+ tm.tm_mon = (int) (p - months) / 3;
+ tm.tm_year -= 1900;
+ tm.tm_isdst = -1;
+
+ rec->rec_type = REC_TYPE_PACKET;
+ rec->block = wtap_block_create(WTAP_BLOCK_PACKET);
+ rec->presence_flags = WTAP_HAS_TS;
+ rec->ts.secs = mktime(&tm);
+ rec->ts.nsecs = csec * 10000000;
+ rec->rec_header.packet_header.caplen = pkt_len;
+ rec->rec_header.packet_header.len = pkt_len;
+
+ /* Make sure we have enough room for the packet */
+ ws_buffer_assure_space(buf, pkt_len);
+ pd = ws_buffer_start_ptr(buf);
+
+ /* Convert the ASCII hex dump to binary data */
+ for (i = 0; i < pkt_len; i += 16) {
+ if (file_gets(line, VMS_LINE_LENGTH, fh) == NULL) {
+ *err = file_error(fh, err_info);
+ if (*err == 0) {
+ *err = WTAP_ERR_SHORT_READ;
+ }
+ return FALSE;
+ }
+ line[VMS_LINE_LENGTH] = '\0';
+ if (i == 0) {
+ while (! isdumpline(line)) { /* advance to start of hex data */
+ if (file_gets(line, VMS_LINE_LENGTH, fh) == NULL) {
+ *err = file_error(fh, err_info);
+ if (*err == 0) {
+ *err = WTAP_ERR_SHORT_READ;
+ }
+ return FALSE;
+ }
+ line[VMS_LINE_LENGTH] = '\0';
+ }
+ while (line[offset] && !g_ascii_isxdigit(line[offset]))
+ offset++;
+ }
+ if (!parse_single_hex_dump_line(line, pd, i,
+ offset, pkt_len - i)) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("vms: hex dump not valid");
+ return FALSE;
+ }
+ }
+ /* Avoid TCPIPTRACE-W-BUFFERSFUL, TCPIPtrace could not save n packets.
+ * errors.
+ *
+ * XXX - when we support packet drop report information in the
+ * Wiretap API, we should parse those lines and return "n" as
+ * a packet drop count. */
+ if (!file_gets(line, VMS_LINE_LENGTH, fh)) {
+ *err = file_error(fh, err_info);
+ if (*err == 0) {
+ /* There is no next line, so there's no "TCPIPtrace could not
+ * save n packets" line; not an error. */
+ return TRUE;
+ }
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/*
+ 1 2 3 4
+0123456789012345678901234567890123456789012345
+ 50010C0A A34C0640 00009017 2C000045 0000 E..,....@.L....P
+ 00000000 14945E52 0A00DC02 | 32010C0A 0010 ...2....R^......
+ 0000 | B4050402 00003496 00020260 0020 `....4........
+*/
+
+#define START_POS 7
+#define HEX_LENGTH ((8 * 4) + 7) /* eight clumps of 4 bytes with 7 inner spaces */
+/* Take a string representing one line from a hex dump and converts the
+ * text to binary data. We check the printed offset with the offset
+ * we are passed to validate the record. We place the bytes in the buffer
+ * at the specified offset.
+ *
+ * Returns TRUE if good hex dump, FALSE if bad.
+ */
+static gboolean
+parse_single_hex_dump_line(char* rec, guint8 *buf, long byte_offset,
+ int in_off, int remaining_bytes) {
+
+ int i;
+ char *s;
+ int value;
+ static const int offsets[16] = {39,37,35,33,28,26,24,22,17,15,13,11,6,4,2,0};
+ char lbuf[3] = {0,0,0};
+
+
+ /* Get the byte_offset directly from the record */
+ s = rec;
+ value = (int)strtoul(s + 45 + in_off, NULL, 16); /* XXX - error check? */
+
+ if (value != byte_offset) {
+ return FALSE;
+ }
+
+ if (remaining_bytes > 16)
+ remaining_bytes = 16;
+
+ /* Read the octets right to left, as that is how they are displayed
+ * in VMS.
+ */
+
+ for (i = 0; i < remaining_bytes; i++) {
+ lbuf[0] = rec[offsets[i] + in_off];
+ lbuf[1] = rec[offsets[i] + 1 + in_off];
+
+ buf[byte_offset + i] = (guint8) strtoul(lbuf, NULL, 16);
+ }
+
+ return TRUE;
+}
+
+static const struct supported_block_type vms_blocks_supported[] = {
+ /*
+ * We support packet blocks, with no comments or other options.
+ */
+ { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
+};
+
+static const struct file_type_subtype_info vms_info = {
+ "TCPIPtrace (VMS)", "tcpiptrace", "txt", NULL,
+ FALSE, BLOCKS_SUPPORTED(vms_blocks_supported),
+ NULL, NULL, NULL
+};
+
+void register_vms(void)
+{
+ vms_file_type_subtype = wtap_register_file_type_subtype(&vms_info);
+
+ /*
+ * Register name for backwards compatibility with the
+ * wtap_filetypes table in Lua.
+ */
+ wtap_register_backwards_compatibility_lua_name("VMS",
+ vms_file_type_subtype);
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * vi: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */
diff --git a/wiretap/vms.h b/wiretap/vms.h
new file mode 100644
index 00000000..cb8c98f8
--- /dev/null
+++ b/wiretap/vms.h
@@ -0,0 +1,19 @@
+/** @file
+ *
+ * Wiretap Library
+ * Copyright (c) 2001 by Marc Milgram <ethereal@mmilgram.NOSPAMmail.net>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ */
+
+#ifndef __W_VMS_H__
+#define __W_VMS_H__
+
+#include <glib.h>
+#include "wtap.h"
+#include "ws_symbol_export.h"
+
+wtap_open_return_val vms_open(wtap *wth, int *err, gchar **err_info);
+
+#endif
diff --git a/wiretap/vwr.c b/wiretap/vwr.c
new file mode 100644
index 00000000..251ddf52
--- /dev/null
+++ b/wiretap/vwr.c
@@ -0,0 +1,3443 @@
+/* vwr.c
+ * Copyright (c) 2011 by Tom Alexander <talexander@ixiacom.com>
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ */
+#include "config.h"
+
+#include <string.h>
+
+#include "wtap-int.h"
+#include "file_wrappers.h"
+
+#include "vwr.h"
+#include <wsutil/ws_assert.h>
+
+/* platform-specific definitions for portability */
+
+/* unsigned long long constants */
+# define NS_IN_US G_GUINT64_CONSTANT(1000) /* nanoseconds-to-microseconds */
+# define NS_IN_SEC G_GUINT64_CONSTANT(1000000000) /* nanoseconds-to-seconds */
+# define US_IN_SEC G_GUINT64_CONSTANT(1000000) /* microseconds-to-seconds */
+# define LL_ZERO G_GUINT64_CONSTANT(0) /* zero in unsigned long long */
+
+/*
+ * Fetch a 64-bit value in "Corey-endian" form.
+ */
+#define pcoreytohll(p) ((guint64)*((const guint8 *)(p)+4)<<56| \
+ (guint64)*((const guint8 *)(p)+5)<<48| \
+ (guint64)*((const guint8 *)(p)+6)<<40| \
+ (guint64)*((const guint8 *)(p)+7)<<32| \
+ (guint64)*((const guint8 *)(p)+0)<<24| \
+ (guint64)*((const guint8 *)(p)+1)<<16| \
+ (guint64)*((const guint8 *)(p)+2)<<8| \
+ (guint64)*((const guint8 *)(p)+3)<<0)
+
+/*
+ * Fetch a 48-bit value in "Corey-endian" form; it's stored as
+ * a 64-bit Corey-endian value, with the upper 16 bits ignored.
+ */
+#define pcorey48tohll(p) ((guint64)*((const guint8 *)(p)+6)<<40| \
+ (guint64)*((const guint8 *)(p)+7)<<32| \
+ (guint64)*((const guint8 *)(p)+0)<<24| \
+ (guint64)*((const guint8 *)(p)+1)<<16| \
+ (guint64)*((const guint8 *)(p)+2)<<8| \
+ (guint64)*((const guint8 *)(p)+3)<<0)
+
+/* .vwr log file defines */
+#define B_SIZE 32768 /* max var len message = 32 kB */
+#define VT_FRAME 0 /* varlen msg is a frame */
+#define VT_CPMSG 1 /* varlen msg is a CP<->PP msg */
+#define VT_UNKNOWN -1 /* varlen msg is unknown */
+#define MAX_TRACKED_CLIENTS 1024 /* track 1024 clients */
+#define MAX_TRACKED_FLOWS 65536 /* and 64K flows */
+
+/*
+ * The file consists of a sequence of records.
+ * A record begins with a 16-byte header, the first 8 bytes of which
+ * begin with a byte containing a command plus transmit-receive flags.
+ *
+ * Following that are two big-endian 32-bit quantities; for some records
+ * one or the other of them is the length of the rest of the record.
+ * Other records contain only the header.
+ */
+#define VW_RECORD_HEADER_LENGTH 16
+
+/*
+ * Maximum number of bytes to read looking for a valid frame starting
+ * with a command byte to determine if this is our file type. Arbitrary.
+ */
+#define VW_BYTES_TO_CHECK 0x3FFFFFFFU
+
+/* Command byte values */
+#define COMMAND_RX 0x21
+#define COMMAND_TX 0x31
+#define COMMAND_RFN 0x30
+#define COMMAND_RF 0x38
+#define COMMAND_RFRX 0x39
+
+/*
+ * The data in packet records begins with a sequence of metadata headers.
+ *
+ * For packet records from FPGA versions < 48:
+ *
+ * The first header is the IxVeriWave common header, and that's
+ * followed either by a WLAN metadata header or an Ethernet
+ * metadata header. The port type field indicates whether it's
+ * a WLAN packet or an Ethernet packet. Following that may, for
+ * WLAN, be 1 octet of information from the FPGA and 16 bytes of
+ * data including the PLCP header. After that comes the WLAN or
+ * Ethernet frame, beginning with the MAC header.
+ *
+ * For packet records from FPGA versions >= 48:
+ *
+ * The first header contains only a 1-octet port type value, which
+ * has a packet type value in the upper 4 bits and zero in the lower
+ * 4 bits. NOTE: this is indistinguishable from an old FPGA header
+ * if the packet type value is 0.
+ *
+ * If the packet type value isn't 3, the port type value is followed
+ * by a 1-octet FPGA version number, which is followed by a timestamp
+ * header.
+ *
+ * If the packet type value is 3 or 4, the next item is an RF metadata
+ * header. For type 3, that immediately follows the port number octet,
+ * otherwise it immediately follows the timestamp header.
+ *
+ * If the packet type isn't 3, the next item is a WLAN metadata header,
+ * in a format different from the WLAN metadata header for FPGA versions
+ * < 48. That is followed by a PLCP header, which is followed by a
+ * header giving additional layer 2 through 4 metadata.
+ *
+ * Following those headers is the WLAN or Ethernet frame, beginning with
+ * the MAC header.
+ */
+
+/*
+ * IxVeriWave common header:
+ *
+ * 1 octet - port type
+ * 1 octet - FPGA version, or 0
+ * 2 octets - length of the common header
+ * 2 octets - MSDU length
+ * 4 octets - flow ID
+ * 2 octets - VC ID
+ * 2 octets - flow sequence number
+ * 4 octets - latency or 0
+ * 4 octets - lower 32 bits of signature time stamp
+ * 8 octets - start time
+ * 8 octets - end time
+ * 4 octets - delta(?) time
+ */
+
+/* Size of the IxVeriWave common header */
+#define STATS_COMMON_FIELDS_LEN (1+1+2+2+4+2+2+4+4+8+8+4)
+
+/* Port type */
+#define WLAN_PORT 0
+#define ETHERNET_PORT 1
+
+/* For VeriWave WLAN and Ethernet metadata headers vw_flags field */
+#define VW_FLAGS_TXF 0x01 /* frame was transmitted */
+#define VW_FLAGS_FCSERR 0x02 /* FCS error detected */
+
+/*
+ * VeriWave WLAN metadata header:
+ *
+ * 2 octets - header length
+ * 2 octets - rflags
+ * 2 octets - channel flags
+ * 2 octets - PHY rate
+ * 1 octet - PLCP type
+ * 1 octet - MCS index
+ * 1 octet - number of spatial streams
+ * 1 octet - RSSI
+ * 1 octet - antenna b signal power, or 100 if missing
+ * 1 octet - antenna c signal power, or 100 if missing
+ * 1 octet - antenna d signal power, or 100 if missing
+ * 1 octet - padding
+ * 2 octets - VeriWave flags
+ * 2 octets - HT len
+ * 2 octets - info
+ * 2 octets - errors
+ */
+
+/* Size of the VeriWave WLAN metadata header */
+#define EXT_WLAN_FIELDS_LEN (2+2+2+2+1+1+1+1+1+1+1+1+2+2+2+4)
+
+/* Flags, for rflags field */
+#define FLAGS_SHORTPRE 0x0002 /* sent/received with short preamble */
+#define FLAGS_WEP 0x0004 /* sent/received with WEP encryption */
+#define FLAGS_CHAN_HT 0x0040 /* In HT mode */
+#define FLAGS_CHAN_VHT 0x0080 /* VHT Mode */
+#define FLAGS_CHAN_SHORTGI 0x0100 /* Short guard interval */
+#define FLAGS_CHAN_40MHZ 0x0200 /* 40 Mhz channel bandwidth */
+#define FLAGS_CHAN_80MHZ 0x0400 /* 80 Mhz channel bandwidth */
+#define FLAGS_CHAN_160MHZ 0x0800 /* 160 Mhz channel bandwidth */
+
+/* Channel flags, for channel flags field */
+#define CHAN_CCK 0x0020 /* CCK channel */
+#define CHAN_OFDM 0x0040 /* OFDM channel */
+
+/* For VeriWave WLAN metadata header vw_flags field */
+#define VW_FLAGS_RETRERR 0x04 /* excess retry error detected */
+#define VW_FLAGS_DCRERR 0x10 /* decrypt error detected (WLAN) */
+#define VW_FLAGS_ENCMSK 0x60 /* encryption type mask */
+ /* 0 = none, 1 = WEP, 2 = TKIP, 3 = CCKM */
+#define VW_FLAGS_IS_WEP 0x20 /* WEP */
+#define VW_FLAGS_IS_TKIP 0x40 /* TKIP */
+#define VW_FLAGS_IS_CCMP 0x60 /* CCMP */
+
+/*
+ * VeriWave Ethernet metadata header:
+ *
+ * 2 octets - header length
+ * 2 octets - VeriWave flags
+ * 2 octets - info
+ * 4 octets - errors
+ * 4 octets - layer 4 ID
+ * 4 octets - pad
+ *
+ * Ethernet frame follows, beginning with the MAC header
+ */
+
+/* Size of the VeriWave Ethernet metadata header */
+#define EXT_ETHERNET_FIELDS_LEN (2+2+2+4+4+4)
+
+/*
+ * OCTO timestamp header.
+ *
+ * 4 octets - latency or 0
+ * 4 octets - lower 32 bits of signature time stamp
+ * 8 octets - start time
+ * 8 octets - end time
+ * 4 octets - delta(?) time
+ */
+
+/* Size of Timestamp header */
+#define OCTO_TIMESTAMP_FIELDS_LEN (4+4+8+8+4+4)
+
+/*
+ * OCTO layer 1-4 header:
+ *
+ * 2 octets - header length
+ * 1 octet - l1p_1
+ * 1 octet - number of spatial streams
+ * 2 octets - PHY rate
+ * 1 octet - l1p_2
+ * 1 octet - RSSI
+ * 1 octet - antenna b signal power, or 100 if missing
+ * 1 octet - antenna c signal power, or 100 if missing
+ * 1 octet - antenna d signal power, or 100 if missing
+ * 1 octet - signal bandwidth mask
+ * 1 octet - antenna port energy detect and VU_MASK
+ * 1 octet - L1InfoC or 0
+ * 2 octets - MSDU length
+ * 16 octets - PLCP?
+ * 4 octets - BM, BV, CV, BSSID and ClientID
+ * 2 octets - FV, QT, HT, L4V, TID and WLAN type
+ * 1 octets - flow sequence number
+ * 3 octets - flow ID
+ * 2 octets - layer 4 ID
+ * 4 octets - payload decode
+ * 3 octets - info
+ * 4 octets - errors
+ */
+
+/* Size of Layer-1, PLCP, and Layer-2/4 header in case of OCTO version FPGA */
+#define OCTO_LAYER1TO4_LEN (2+14+16+23)
+
+/*
+ * OCTO modified RF layer:
+ *
+ * 1 octet - RF ID
+ * 3 octets - unused (zero)
+ * 8 octets - noise for 4 ports
+ * 8 octets - signal/noise ration for 4 ports
+ * 8 octets - PFE for 4 ports
+ * 8 octets - EVM SIG data for 4 ports
+ * 8 octets - EVM SIG pilot for 4 ports
+ * 8 octets - EVM Data data for 4 ports
+ * 8 octets - EVM Data pilot for 4 ports
+ * 8 octets - EVM worst symbol for 4 ports
+ * 8 octets - CONTEXT_P for 4 ports
+ *
+ * Not supplied:
+ * 24 octets of additional data
+ */
+
+/* Size of RF header, if all fields were supplied */
+#define OCTO_RF_MOD_ACTUAL_LEN 100 /* */
+
+/* Size of RF header with the fields we do supply */
+#define OCTO_MODIFIED_RF_LEN 76 /* 24 bytes of RF are not displayed*/
+
+/*Offset of different parameters of RF header for port-1*/
+#define RF_PORT_1_NOISE_OFF 4
+#define RF_PORT_1_SNR_OFF 6
+#define RF_PORT_1_PFE_OFF 8
+#define RF_PORT_1_CONTEXT_OFF 10
+#define RF_PORT_1_EVM_SD_SIG_OFF 12
+#define RF_PORT_1_EVM_SP_SIG_OFF 14
+#define RF_PORT_1_EVM_SD_DATA_OFF 16
+#define RF_PORT_1_EVM_SP_DATA_OFF 18
+#define RF_PORT_1_DSYMBOL_IDX_OFF 22
+#define RF_INTER_PORT_GAP_OFF 24 /*As size of RF information per port is 24 bytes*/
+#define RF_NUMBER_OF_PORTS 4
+
+/* FPGA-generated frame buffer STATS block offsets and definitions */
+
+/* definitions for v2.2 frames, Ethernet format */
+#define v22_E_STATS_LEN 44 /* length of stats block trailer */
+#define v22_E_VALID_OFF 0 /* bit 6 (0x40) is flow-is-valid flag */
+#define v22_E_MTYPE_OFF 1 /* offset of modulation type */
+#define v22_E_VCID_OFF 2 /* offset of VC ID */
+#define v22_E_FLOWSEQ_OFF 4 /* offset of signature sequence number */
+#define v22_E_FLOWID_OFF 5 /* offset of flow ID */
+#define v22_E_OCTET_OFF 8 /* offset of octets */
+#define v22_E_ERRORS_OFF 10 /* offset of error vector */
+#define v22_E_PATN_OFF 12 /* offset of pattern match vector */
+#define v22_E_L4ID_OFF 12
+#define v22_E_IPLEN_OFF 14
+#define v22_E_FRAME_TYPE_OFF 16 /* offset of frame type, 32 bits */
+#define v22_E_RSSI_OFF 21 /* RSSI (NOTE: invalid for Ethernet) */
+#define v22_E_STARTT_OFF 20 /* offset of start time, 64 bits */
+#define v22_E_ENDT_OFF 28 /* offset of end time, 64 bits */
+#define v22_E_LATVAL_OFF 36 /* offset of latency, 32 bits */
+#define v22_E_INFO_OFF 40 /* NO INFO FIELD IN ETHERNET STATS! */
+#define v22_E_DIFFERENTIATOR_OFF 0 /* offset to determine whether */
+ /* eth/802.11, 8 bits */
+/* Media types */
+#define v22_E_MT_10_HALF 0 /* 10 Mb/s half-duplex */
+#define v22_E_MT_10_FULL 1 /* 10 Mb/s full-duplex */
+#define v22_E_MT_100_HALF 2 /* 100 Mb/s half-duplex */
+#define v22_E_MT_100_FULL 3 /* 100 Mb/s full-duplex */
+#define v22_E_MT_1G_HALF 4 /* 1 Gb/s half-duplex */
+#define v22_E_MT_1G_FULL 5 /* 1 Gb/s full-duplex */
+
+/* Error flags */
+#define v22_E_FCS_ERROR 0x0002 /* FCS error flag in error vector */
+#define v22_E_CRYPTO_ERR 0x1f00 /* RX decrypt error flags (UNUSED) */
+#define v22_E_SIG_ERR 0x0004 /* signature magic byte mismatch */
+#define v22_E_PAYCHK_ERR 0x0008 /* payload checksum failure */
+#define v22_E_RETRY_ERR 0x0400 /* excessive retries on TX fail (UNUSED)*/
+
+/* Masks and defines */
+#define v22_E_IS_RX 0x08 /* TX/RX bit in STATS block */
+#define v22_E_MT_MASK 0x07 /* modulation type mask (UNUSED) */
+
+#define v22_E_VCID_MASK 0x03ff /* VC ID is only 10 bits */
+
+#define v22_E_FLOW_VALID 0x40 /* flow-is-valid flag (else force to 0) */
+
+#define v22_E_DIFFERENTIATOR_MASK 0x3F /* mask to differentiate ethernet from */
+
+/* Bits in FRAME_TYPE field */
+#define v22_E_IS_TCP 0x00000040 /* TCP bit in FRAME_TYPE field */
+#define v22_E_IS_UDP 0x00000010 /* UDP bit in FRAME_TYPE field */
+#define v22_E_IS_ICMP 0x00000020 /* ICMP bit in FRAME_TYPE field */
+#define v22_E_IS_IGMP 0x00000080 /* IGMP bit in FRAME_TYPE field */
+
+/* Bits in MTYPE field (WLAN only) */
+#define v22_E_IS_QOS 0x80 /* QoS bit in MTYPE field (WLAN only) */
+
+#define v22_E_IS_VLAN 0x00200000
+
+#define v22_E_RX_DECRYPTS 0x0007 /* RX-frame-was-decrypted (UNUSED) */
+#define v22_E_TX_DECRYPTS 0x0007 /* TX-frame-was-decrypted (UNUSED) */
+
+#define v22_E_FC_PROT_BIT 0x40 /* Protected Frame bit in FC1 of frame */
+
+#define v22_E_IS_ETHERNET 0x00700000 /* bits set in frame type if ethernet */
+#define v22_E_IS_80211 0x7F000000 /* bits set in frame type if 802.11 */
+
+/* definitions for v2.2 frames, WLAN format for VW510006 FPGA*/
+#define v22_W_STATS_LEN 64 /* length of stats block trailer */
+#define v22_W_VALID_OFF 0 /* bit 6 (0x40) is flow-is-valid flag */
+#define v22_W_MTYPE_OFF 1 /* offset of modulation type */
+#define v22_W_VCID_OFF 2 /* offset of VC ID */
+#define v22_W_FLOWSEQ_OFF 4 /* offset of signature sequence number */
+#define v22_W_FLOWID_OFF 5 /* offset of flow ID */
+#define v22_W_OCTET_OFF 8 /* offset of octets */
+#define v22_W_ERRORS_OFF 10 /* offset of error vector */
+#define v22_W_PATN_OFF 12
+#define v22_W_L4ID_OFF 12
+#define v22_W_IPLEN_OFF 14
+#define v22_W_FRAME_TYPE_OFF 16 /* offset of frame type, 32 bits */
+#define v22_W_RSSI_OFF 21 /* RSSI (NOTE: RSSI must be negated!) */
+#define v22_W_STARTT_OFF 24 /* offset of start time, 64 bits */
+#define v22_W_ENDT_OFF 32 /* offset of end time, 64 bits */
+#define v22_W_LATVAL_OFF 40 /* offset of latency, 32 bits */
+#define v22_W_INFO_OFF 54 /* offset of INFO field, 16 LSBs */
+#define v22_W_DIFFERENTIATOR_OFF 20 /* offset to determine whether */
+ /* eth/802.11, 32 bits */
+
+#define v22_W_PLCP_LENGTH_OFF 4 /* LENGTH field in the plcp header */
+
+/* Modulation types */
+#define v22_W_MT_CCKL 0 /* CCK modulation, long preamble */
+#define v22_W_MT_CCKS 1 /* CCK modulation, short preamble */
+#define v22_W_MT_OFDM 2 /* OFDM modulation */
+
+/* Bits in FRAME_TYPE field */
+#define v22_W_IS_TCP 0x00000040 /* TCP bit in FRAME_TYPE field */
+#define v22_W_IS_UDP 0x00000010 /* UDP bit in FRAME_TYPE field */
+#define v22_W_IS_ICMP 0x00000020 /* ICMP bit in FRAME_TYPE field */
+#define v22_W_IS_IGMP 0x00000080 /* IGMP bit in FRAME_TYPE field */
+
+/* Bits in MTYPE field (WLAN only) */
+#define v22_W_IS_QOS 0x80 /* QoS */
+
+/* Error flags */
+#define v22_W_FCS_ERROR 0x0002 /* FCS error flag in error vector */
+#define v22_W_CRYPTO_ERR 0x1f00 /* RX decrypt error flags */
+#define v22_W_SIG_ERR 0x0004 /* signature magic byte mismatch */
+#define v22_W_PAYCHK_ERR 0x0008 /* payload checksum failure */
+#define v22_W_RETRY_ERR 0x0400 /* excessive retries on TX failure */
+
+/* Masks and defines */
+#define v22_W_IS_RX 0x08 /* TX/RX bit in STATS block */
+#define v22_W_MT_MASK 0x07 /* modulation type mask */
+
+#define v22_W_VCID_MASK 0x01ff /* VC ID is only 9 bits */
+
+#define v22_W_FLOW_VALID 0x40 /* flow-is-valid flag (else force to 0) */
+
+#define v22_W_DIFFERENTIATOR_MASK 0xf0ff /* mask to differentiate ethernet from */
+ /* 802.11 capture */
+
+#define v22_W_RX_DECRYPTS 0x0007 /* RX-frame-was-decrypted bits */
+#define v22_W_TX_DECRYPTS 0x0007 /* TX-frame-was-decrypted bits */
+
+/* Info bits */
+#define v22_W_WEPTYPE 0x0001 /* WEP frame */
+#define v22_W_TKIPTYPE 0x0002 /* TKIP frame */
+#define v22_W_CCMPTYPE 0x0004 /* CCMP frame */
+#define v22_W_MPDU_OF_A_MPDU 0x0400 /* MPDU of A-MPDU */
+#define v22_W_FIRST_MPDU_OF_A_MPDU 0x0800 /* first MPDU of A-MPDU */
+#define v22_W_LAST_MPDU_OF_A_MPDU 0x1000 /* last MPDU of A-MPDU */
+#define v22_W_MSDU_OF_A_MSDU 0x2000 /* MSDU of A-MSDU */
+#define v22_W_FIRST_MSDU_OF_A_MSDU 0x4000 /* first MSDU of A-MSDU */
+#define v22_W_LAST_MSDU_OF_A_MSDU 0x8000 /* last MSDU of A-MSDU */
+
+/* All aggregation flags */
+#define v22_W_AGGREGATE_FLAGS \
+ (v22_W_MPDU_OF_A_MPDU | \
+ v22_W_FIRST_MPDU_OF_A_MPDU | \
+ v22_W_LAST_MPDU_OF_A_MPDU | \
+ v22_W_MSDU_OF_A_MSDU | \
+ v22_W_FIRST_MSDU_OF_A_MSDU | \
+ v22_W_LAST_MSDU_OF_A_MSDU)
+
+#define v22_W_FC_PROT_BIT 0x40 /* Protected Frame bit in FC1 of frame */
+
+#define v22_W_IS_ETHERNET 0x00100000 /* bits set in frame type if ethernet */
+#define v22_W_IS_80211 0x7F000000 /* bits set in frame type if 802.11 */
+
+/* definitions for VW510021 FPGA, WLAN format */
+/* FORMAT:
+ 16 BYTE header
+ 8 bytes of stat block
+ plcp stuff (11 bytes plcp + 1 byte pad)
+ data
+ remaining 48 bytes of stat block
+*/
+/* offsets in the stats block */
+#define vVW510021_W_STATS_HEADER_LEN 8 /* length of stats block header at beginning of record data */
+#define vVW510021_W_STATS_TRAILER_LEN 48 /* length of stats block trailer after the plcp portion*/
+#define vVW510021_W_STARTT_OFF 0 /* offset of start time, 64 bits */
+#define vVW510021_W_ENDT_OFF 8 /* offset of end time, 64 bits */
+#define vVW510021_W_ERRORS_OFF 16 /* offset of error vector */
+#define vVW510021_W_VALID_OFF 20 /* 2 Bytes with different validity bits */
+#define vVW510021_W_INFO_OFF 22 /* offset of INFO field, 16 LSBs */
+#define vVW510021_W_FRAME_TYPE_OFF 24
+#define vVW510021_W_L4ID_OFF 28
+#define vVW510021_W_IPLEN_OFF 30 /* offset of IP Total Length field */
+#define vVW510021_W_FLOWSEQ_OFF 32 /* offset of signature sequence number */
+#define vVW510021_W_FLOWID_OFF 33 /* offset of flow ID */
+#define vVW510021_W_LATVAL_OFF 36 /* offset of delay/flowtimestamp, 32b */
+#define vVW510021_W_DEBUG_OFF 40 /* offset of debug, 16 bits */
+#define S2_W_FPGA_VERSION_OFF 44 /* offset of fpga version, 16 bits */
+#define vVW510021_W_MATCH_OFF 47 /* offset of pattern match vector */
+
+/* offsets in the header block */
+#define vVW510021_W_HEADER_LEN 16 /* length of FRAME header */
+#define vVW510021_W_RXTX_OFF 0 /* rxtx offset, cmd byte of header */
+#define vVW510021_W_HEADER_VERSION_OFF 9 /* version, 2bytes */
+#define vVW510021_MSG_LENGTH_OFF 10 /* MSG LENGTH, 2bytes */
+#define vVW510021_W_DEVICE_TYPE_OFF 8 /* version, 2bytes */
+
+/* offsets that occur right after the header */
+#define vVW510021_W_AFTERHEADER_LEN 8 /* length of STATs info directly after header */
+#define vVW510021_W_L1P_1_OFF 0 /* offset of 1st byte of layer one info */
+#define vVW510021_W_L1P_2_OFF 1 /* offset of 2nd byte of layer one info */
+#define vVW510021_W_MTYPE_OFF vVW510021_W_L1P_2_OFF
+#define vVW510021_W_PREAMBLE_OFF vVW510021_W_L1P_1_OFF
+#define vVW510021_W_RSSI_TXPOWER_OFF 2 /* RSSI (NOTE: RSSI must be negated!) */
+#define vVW510021_W_MSDU_LENGTH_OFF 3 /* 7:0 of length, next byte 11:8 in top 4 bits */
+#define vVW510021_W_BVCV_VALID_OFF 4 /* BV,CV Determine validaity of bssid and txpower */
+#define vVW510021_W_VCID_OFF 6 /* offset of VC (client) ID */
+#define vVW510021_W_PLCP_LENGTH_OFF 12 /* LENGTH field in the plcp header */
+
+/* Masks and defines */
+#define vVW510021_W_IS_BV 0x04 /* BV bit in STATS block */
+#define vVW510021_W_IS_CV 0x02 /* BV bit in STATS block */
+#define vVW510021_W_FLOW_VALID 0x8000 /* valid_off flow-is-valid flag (else 0) */
+#define vVW510021_W_QOS_VALID 0x4000
+#define vVW510021_W_HT_VALID 0x2000
+#define vVW510021_W_L4ID_VALID 0x1000
+#define vVW510021_W_MCS_MASK 0x3f /* mcs index (a/b) type mask */
+#define vVW510021_W_MOD_SCHEME_MASK 0x3f /* modulation type mask */
+#define vVW510021_W_PLCPC_MASK 0x03 /* PLPCP type mask */
+#define vVW510021_W_SEL_MASK 0x80
+#define vVW510021_W_WEP_MASK 0x0001
+#define vVW510021_W_CBW_MASK 0xC0
+
+#define vVW510024_W_VCID_MASK 0x03ff /* VC ID is only 10 bits */
+
+#define vVW510021_W_MT_SEL_LEGACY 0x00
+
+#define vVW510021_W_IS_WEP 0x0001
+
+/* L1p byte 1 info */
+
+/* Common to Series II and Series III */
+
+#define vVW510021_W_IS_LONGPREAMBLE 0x40 /* short/long preamble bit */
+#define vVW510021_W_IS_LONGGI 0x40 /* short/long guard interval bit */
+
+/* Series II */
+
+/*
+ * Pre-HT - contains rate index.
+ */
+#define vVW510021_W_S2_RATE_INDEX(l1p_1) ((l1p_1) & 0x3f) /* rate index for pre-HT */
+
+/*
+ * HT - contains MCS index.
+ *
+ * XXX - MCS indices for HT go up to 76, which doesn't fit in 6 bits;
+ * either the mask is wrong, or the hardware can't receive packets
+ * with an MCS of 64 through 76, or the hardware can but misreports
+ * the MCS.
+ */
+#define vVW510021_W_S2_MCS_INDEX_HT(l1p_1) ((l1p_1) & 0x3f)
+
+/*
+ * VHT - contains MCS index and number of spatial streams.
+ * The number of spatial streams from the FPGA is zero-based, so we add
+ * 1 to it.
+ */
+#define vVW510021_W_S2_MCS_INDEX_VHT(l1p_1) ((l1p_1) & 0x0f) /* MCS index for VHT */
+#define vVW510021_W_S2_NSS_VHT(l1p_1) (((l1p_1) >> 4) + 1) /* NSS */
+
+/* Series III */
+
+/*
+ * Pre-HT - contains rate index.
+ */
+#define vVW510021_W_S3_RATE_INDEX(l1p_1) ((l1p_1) & 0x3f)
+
+/*
+ * HT - contains MCS index.
+ *
+ * XXX - MCS indices for HT go up to 76, which doesn't fit in 6 bits;
+ * either the mask is wrong, or the hardware can't receive packets
+ * with an MCS of 64 through 76, or the hardware can but misreports
+ * the MCS.
+ */
+#define vVW510021_W_S3_MCS_INDEX_HT(l1p_1) ((l1p_1) & 0x3f)
+
+/*
+ * VHT - contains MCS index and number of spatial streams.
+ * The number of spatial streams from the FPGA is zero-based, so we add
+ * 1 to it.
+ */
+#define vVW510021_W_S3_MCS_INDEX_VHT(l1p_1) ((l1p_1) & 0x0f) /* MCS index */
+#define vVW510021_W_S3_NSS_VHT(l1p_1) ((((l1p_1) >> 4) & 0x03) + 1) /* NSS */
+
+/* L1p byte 2 info */
+
+/* Common to Series II and Series III */
+#define vVW510021_W_BANDWIDTH_VHT(l1p_2) (((l1p_2) >> 4) & 0xf)
+/* 3 = 40 MHz, 4 = 80 MHz; what about 20 and 160 MHz? */
+
+/* Series II */
+#define vVW510021_W_S2_PLCP_TYPE(l1p_2) ((l1p_2) & 0x03) /* PLCP type */
+
+/* Series III */
+#define vVW510021_W_S3_PLCP_TYPE(l1p_2) ((l1p_2) & 0x0f) /* PLCP type */
+
+/* PLCP types */
+#define vVW510021_W_PLCP_LEGACY 0x00 /* pre-HT (11b/a/g) */
+#define vVW510021_W_PLCP_MIXED 0x01 /* HT, mixed (11n) */
+#define vVW510021_W_PLCP_GREENFIELD 0x02 /* HT, greenfield (11n) */
+#define vVW510021_W_PLCP_VHT_MIXED 0x03 /* VHT (11ac) */
+
+/* Bits in FRAME_TYPE field */
+#define vVW510021_W_IS_TCP 0x01000000 /* TCP */
+#define vVW510021_W_IS_UDP 0x00100000 /* UDP */
+#define vVW510021_W_IS_ICMP 0x00001000 /* ICMP */
+#define vVW510021_W_IS_IGMP 0x00010000 /* IGMP */
+
+#define vVW510021_W_HEADER_VERSION 0x00
+#define vVW510021_W_DEVICE_TYPE 0x15
+#define vVW510021_W_11n_DEVICE_TYPE 0x20
+#define S2_W_FPGA_VERSION 0x000C
+#define vVW510021_W_11n_FPGA_VERSION 0x000D
+
+/* Error flags */
+#define vVW510021_W_FCS_ERROR 0x01
+
+#define vVW510021_W_CRYPTO_ERROR 0x50000
+
+#define vVW510021_W_WEPTYPE 0x0001 /* WEP frame */
+#define vVW510021_W_TKIPTYPE 0x0002 /* TKIP frame */
+#define vVW510021_W_CCMPTYPE 0x0004 /* CCMP frame */
+
+/* definitions for VW510024 FPGA, wired ethernet format */
+/* FORMAT:
+ 16 BYTE header
+ 52 bytes of stats block trailer
+*/
+/* offsets in the stats block */
+#define vVW510024_E_STATS_LEN 48 /* length of stats block trailer */
+#define vVW510024_E_MSDU_LENGTH_OFF 0 /* MSDU 16 BITS */
+#define vVW510024_E_BMCV_VALID_OFF 2 /* BM,CV Determine validITY */
+#define vVW510024_E_VCID_OFF 2 /* offset of VC (client) ID 13:8, */
+ /* 7:0 IN offset 7*/
+#define vVW510024_E_STARTT_OFF 4 /* offset of start time, 64 bits */
+#define vVW510024_E_ENDT_OFF 12 /* offset of end time, 64 bits */
+#define vVW510024_E_ERRORS_OFF 22 /* offset of error vector */
+#define vVW510024_E_VALID_OFF 24 /* 2 Bytes with different validity bits */
+#define vVW510024_E_INFO_OFF 26 /* offset of INFO field, 16 LSBs */
+#define vVW510024_E_FRAME_TYPE_OFF 28
+#define vVW510024_E_L4ID_OFF 32
+#define vVW510024_E_IPLEN_OFF 34
+#define vVW510024_E_FLOWSEQ_OFF 36 /* offset of signature sequence number */
+#define vVW510024_E_FLOWID_OFF 37 /* offset of flow ID */
+#define vVW510024_E_LATVAL_OFF 40 /* offset of delay/flowtimestamp, 32 bits */
+#define vVW510024_E_FPGA_VERSION_OFF 20 /* offset of fpga version, 16 bits */
+#define vVW510024_E_MATCH_OFF 51 /* offset of pattern match vector */
+
+/* offsets in the header block */
+#define vVW510024_E_HEADER_LEN vVW510021_W_HEADER_LEN /* length of FRAME header */
+#define vVW510024_E_RXTX_OFF vVW510021_W_RXTX_OFF /* rxtx offset, cmd byte */
+#define vVW510024_E_HEADER_VERSION_OFF 16 /* version, 2bytes */
+#define vVW510024_E_MSG_LENGTH_OFF vVW510021_MSG_LENGTH_OFF /* MSG LENGTH, 2bytes */
+#define vVW510024_E_DEVICE_TYPE_OFF vVW510021_W_DEVICE_TYPE_OFF /* Device Type, 2bytes */
+
+/* Masks and defines */
+#define vVW510024_E_IS_BV 0x80 /* Bm bit in STATS block */
+#define vVW510024_E_IS_CV 0x40 /* cV bit in STATS block */
+#define vVW510024_E_FLOW_VALID 0x8000 /* valid_off flow-is-valid flag (else force to 0) */
+#define vVW510024_E_QOS_VALID 0x0000 /** not valid for ethernet **/
+#define vVW510024_E_L4ID_VALID 0x1000
+#define vVW510024_E_CBW_MASK 0xC0
+#define vVW510024_E_VCID_MASK 0x3FFF /* VCID is only 14 bits */
+
+#define vVW510024_E_IS_TCP 0x01000000 /* TCP bit in FRAME_TYPE field */
+#define vVW510024_E_IS_UDP 0x00100000 /* UDP bit in FRAME_TYPE field */
+#define vVW510024_E_IS_ICMP 0x00001000 /* ICMP bit in FRAME_TYPE field */
+#define vVW510024_E_IS_IGMP 0x00010000
+#define vVW510024_E_IS_VLAN 0x00004000
+
+#define vVW510024_E_HEADER_VERSION 0x00
+#define vVW510024_E_DEVICE_TYPE 0x18
+#define vVW510024_E_FPGA_VERSION 0x0001
+
+#define FPGA_VER_NOT_APPLICABLE 0
+
+#define UNKNOWN_FPGA 0
+#define S2_W_FPGA 1
+#define S1_W_FPGA 2
+#define vVW510012_E_FPGA 3
+#define vVW510024_E_FPGA 4
+#define S3_W_FPGA 5
+
+ /* the flow signature is:
+ Byte Description
+0 Magic Number (0xDD)
+1 Chassis Number[7:0]
+2 Slot Number[7:0]
+3 Port Number[7:0]
+4 Flow ID[7:0]
+5 Flow ID[15:8]
+6 Flow ID[23:16]
+7 Flow Sequence Number[7:0]
+8 Timestamp[7:0]
+9 Timestamp[15:8]
+10 Timestamp[23:16]
+11 Timestamp[31:24]
+12 Timestamp[39:32]
+13 Timestamp[47:40]
+14 CRC16
+15 CRC16
+
+*/
+#define SIG_SIZE 16 /* size of signature field, bytes */
+#define SIG_FID_OFF 4 /* offset of flow ID in signature */
+#define SIG_FSQ_OFF 7 /* offset of flow seqnum in signature */
+#define SIG_TS_OFF 8 /* offset of flow seqnum in signature */
+
+
+
+/*--------------------------------------------------------------------------------------*/
+/* Per-capture file private data structure */
+
+typedef struct {
+ /* offsets in stats block; these are dependent on the frame type (Ethernet/WLAN) and */
+ /* version number of .vwr file, and are set up by setup_defaults() */
+ guint32 STATS_LEN; /* length of stats block trailer */
+ guint32 STATS_START_OFF; /* STATS OFF AFTER HEADER */
+ guint32 VALID_OFF; /* bit 6 (0x40) is flow-is-valid flag */
+ guint32 MTYPE_OFF; /* offset of modulation type */
+ guint32 VCID_OFF; /* offset of VC ID */
+ guint32 FLOWSEQ_OFF; /* offset of signature sequence number */
+ guint32 FLOWID_OFF; /* offset of flow ID */
+ guint32 OCTET_OFF; /* offset of octets */
+ guint32 ERRORS_OFF; /* offset of error vector */
+ guint32 PATN_OFF; /* offset of pattern match vector */
+ guint32 RSSI_OFF; /* RSSI (NOTE: RSSI must be negated!) */
+ guint32 STARTT_OFF; /* offset of start time, 64 bits */
+ guint32 ENDT_OFF; /* offset of end time, 64 bits */
+ guint32 LATVAL_OFF; /* offset of latency, 32 bits */
+ guint32 INFO_OFF; /* offset of INFO field, 16 bits */
+ guint32 L1P_1_OFF; /* offset 1ST Byte of l1params */
+ guint32 L1P_2_OFF; /* offset 2nd Byte of l1params */
+ guint32 L4ID_OFF; /* LAYER 4 id offset*/
+ guint32 IPLEN_OFF; /* */
+ guint32 PLCP_LENGTH_OFF; /* offset of length field in the PLCP header */
+ guint32 FPGA_VERSION_OFF; /* offset of fpga version field, 16 bits */
+ guint32 HEADER_VERSION_OFF; /* offset of header version, 16 bits */
+ guint32 RXTX_OFF; /* offset of CMD bit, rx or tx */
+ guint32 FRAME_TYPE_OFF;
+
+ /* other information about the file in question */
+ guint32 MT_10_HALF; /* 10 Mb/s half-duplex */
+ guint32 MT_10_FULL; /* 10 Mb/s full-duplex */
+ guint32 MT_100_HALF; /* 100 Mb/s half-duplex */
+ guint32 MT_100_FULL; /* 100 Mb/s full-duplex */
+ guint32 MT_1G_HALF; /* 1 Gb/s half-duplex */
+ guint32 MT_1G_FULL; /* 1 Gb/s full-duplex */
+ guint32 FCS_ERROR; /* FCS error in frame */
+ guint32 CRYPTO_ERR; /* RX decrypt error flags */
+ guint32 PAYCHK_ERR; /* payload checksum failure */
+ guint32 RETRY_ERR; /* excessive retries on TX failure */
+ guint8 IS_RX; /* TX/RX bit in STATS block */
+ guint8 MT_MASK; /* modulation type mask */
+ guint16 VCID_MASK; /* VC ID might not be a full 16 bits */
+ guint32 FLOW_VALID; /* flow-is-valid flag (else force to 0) */
+ guint16 QOS_VALID;
+ guint32 RX_DECRYPTS; /* RX-frame-was-decrypted bits */
+ guint32 TX_DECRYPTS; /* TX-frame-was-decrypted bits */
+ guint32 FC_PROT_BIT; /* Protected Frame bit in FC1 of frame */
+ guint32 MT_CCKL; /* CCK modulation, long preamble */
+ guint32 MT_CCKS; /* CCK modulation, short preamble */
+ guint32 MT_OFDM; /* OFDM modulation */
+ guint32 MCS_INDEX_MASK; /* mcs index type mask */
+ guint32 FPGA_VERSION;
+ guint32 WEPTYPE; /* frame is WEP */
+ guint32 TKIPTYPE; /* frame is TKIP */
+ guint32 CCMPTYPE; /* frame is CCMP */
+ guint32 IS_TCP;
+ guint32 IS_UDP;
+ guint32 IS_ICMP;
+ guint32 IS_IGMP;
+ guint16 IS_QOS;
+ guint32 IS_VLAN;
+ guint32 MPDU_OFF;
+ guint32 OCTO_VERSION;
+} vwr_t;
+
+/*
+ * NSS for various MCS values.
+ */
+#define MAX_HT_MCS 76
+static guint nss_for_mcs[MAX_HT_MCS+1] = {
+ 1, 1, 1, 1, 1, 1, 1, 1, /* 0-7 */
+ 2, 2, 2, 2, 2, 2, 2, 2, /* 8-15 */
+ 3, 3, 3, 3, 3, 3, 3, 3, /* 16-23 */
+ 4, 4, 4, 4, 4, 4, 4, 4, /* 24-31 */
+ 1, /* 32 */
+ 2, 2, 2, 2, 2, 2, /* 33-38 */
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 39-52 */
+ 4, 4, 4, 4, 4, 4, /* 53-58 */
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 /* 59-76 */
+};
+
+/* internal utility functions */
+static int decode_msg(vwr_t *vwr, register guint8 *, int *, int *, int *);
+static guint8 get_ofdm_rate(const guint8 *);
+static guint8 get_cck_rate(const guint8 *plcp);
+static void setup_defaults(vwr_t *, guint16);
+
+static gboolean vwr_read(wtap *, wtap_rec *, Buffer *, int *,
+ gchar **, gint64 *);
+static gboolean vwr_seek_read(wtap *, gint64, wtap_rec *,
+ Buffer *, int *, gchar **);
+
+static gboolean vwr_read_rec_header(vwr_t *, FILE_T, int *, int *, int *, int *, gchar **);
+static gboolean vwr_process_rec_data(FILE_T fh, int rec_size,
+ wtap_rec *record, Buffer *buf,
+ vwr_t *vwr, int IS_TX, int log_mode, int *err,
+ gchar **err_info);
+
+static int vwr_get_fpga_version(wtap *, int *, gchar **);
+
+static gboolean vwr_read_s1_W_rec(vwr_t *, wtap_rec *, Buffer *,
+ const guint8 *, int, int *, gchar **);
+static gboolean vwr_read_s2_W_rec(vwr_t *, wtap_rec *, Buffer *,
+ const guint8 *, int, int, int *,
+ gchar **);
+/* For FPGA version >= 48 (OCTO Platform), following function will be used */
+static gboolean vwr_read_s3_W_rec(vwr_t *, wtap_rec *, Buffer *,
+ const guint8 *, int, int, int, int *,
+ gchar **);
+static gboolean vwr_read_rec_data_ethernet(vwr_t *, wtap_rec *,
+ Buffer *, const guint8 *, int,
+ int, int *, gchar **);
+
+static int find_signature(const guint8 *, int, int, register guint32, register guint8);
+static guint64 get_signature_ts(const guint8 *, int, int);
+static float get_legacy_rate(guint8);
+static float get_ht_rate(guint8, guint16);
+static float get_vht_rate(guint8, guint16, guint8);
+
+static int vwr_80211_file_type_subtype = -1;
+static int vwr_eth_file_type_subtype = -1;
+
+void register_vwr(void);
+
+/* Open a .vwr file for reading */
+/* This does very little, except setting the wiretap header for a VWR file type */
+/* and setting the timestamp precision to microseconds. */
+
+wtap_open_return_val vwr_open(wtap *wth, int *err, gchar **err_info)
+{
+ int fpgaVer;
+ vwr_t *vwr;
+
+ *err = 0;
+
+ fpgaVer = vwr_get_fpga_version(wth, err, err_info);
+ if (fpgaVer == -1) {
+ return WTAP_OPEN_ERROR; /* I/O error */
+ }
+ if (fpgaVer == UNKNOWN_FPGA) {
+ return WTAP_OPEN_NOT_MINE; /* not a VWR file */
+ }
+
+ /* This is a vwr file */
+ vwr = g_new0(vwr_t, 1);
+ wth->priv = (void *)vwr;
+
+ vwr->FPGA_VERSION = fpgaVer;
+ /* set the local module options first */
+ setup_defaults(vwr, fpgaVer);
+
+ wth->snapshot_length = 0;
+ wth->subtype_read = vwr_read;
+ wth->subtype_seek_read = vwr_seek_read;
+ wth->file_tsprec = WTAP_TSPREC_USEC;
+ wth->file_encap = WTAP_ENCAP_IXVERIWAVE;
+
+ if (fpgaVer == S2_W_FPGA || fpgaVer == S1_W_FPGA || fpgaVer == S3_W_FPGA)
+ wth->file_type_subtype = vwr_80211_file_type_subtype;
+ else if (fpgaVer == vVW510012_E_FPGA || fpgaVer == vVW510024_E_FPGA)
+ wth->file_type_subtype = vwr_eth_file_type_subtype;
+
+ /*
+ * Add an IDB; we don't know how many interfaces were
+ * involved, so we just say one interface, about which
+ * we only know the link-layer type, snapshot length,
+ * and time stamp resolution.
+ */
+ wtap_add_generated_idb(wth);
+
+ return WTAP_OPEN_MINE;
+}
+
+
+/* Read the next packet */
+/* Note that the VWR file format consists of a sequence of fixed 16-byte record headers of */
+/* different types; some types, including frame record headers, are followed by */
+/* variable-length data. */
+/* A frame record consists of: the above 16-byte record header, a 1-16384 byte raw PLCP */
+/* frame, and a 64-byte statistics block trailer. */
+/* The PLCP frame consists of a 4-byte or 6-byte PLCP header, followed by the MAC frame */
+
+static gboolean vwr_read(wtap *wth, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info, gint64 *data_offset)
+{
+ vwr_t *vwr = (vwr_t *)wth->priv;
+ int rec_size = 0, IS_TX = 0, log_mode = 0;
+
+ /* read the next frame record header in the capture file; if no more frames, return */
+ if (!vwr_read_rec_header(vwr, wth->fh, &rec_size, &IS_TX, &log_mode, err, err_info))
+ return FALSE; /* Read error or EOF */
+
+ /*
+ * We're past the header; return the offset of the header, not of
+ * the data past the header.
+ */
+ *data_offset = (file_tell(wth->fh) - VW_RECORD_HEADER_LENGTH);
+
+ /* got a frame record; read and process it */
+ if (!vwr_process_rec_data(wth->fh, rec_size, rec, buf, vwr, IS_TX,
+ log_mode, err, err_info))
+ return FALSE;
+
+ return TRUE;
+}
+
+/* read a random record in the middle of a file; the start of the record is @ seek_off */
+
+static gboolean vwr_seek_read(wtap *wth, gint64 seek_off,
+ wtap_rec *record, Buffer *buf, int *err, gchar **err_info)
+{
+ vwr_t *vwr = (vwr_t *)wth->priv;
+ int rec_size, IS_TX = 0, log_mode = 0;
+
+ /* first seek to the indicated record header */
+ if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
+ return FALSE;
+
+ /* read in the record header */
+ if (!vwr_read_rec_header(vwr, wth->random_fh, &rec_size, &IS_TX, &log_mode, err, err_info))
+ return FALSE; /* Read error or EOF */
+
+ return vwr_process_rec_data(wth->random_fh, rec_size, record, buf,
+ vwr, IS_TX, log_mode, err, err_info);
+}
+
+/* Scan down in the input capture file to find the next frame header. */
+/* Decode and skip over all non-frame messages that are in the way. */
+/* Return TRUE on success, FALSE on EOF or error. */
+/* Also return the frame size in bytes and the "is transmitted frame" flag. */
+
+static gboolean vwr_read_rec_header(vwr_t *vwr, FILE_T fh, int *rec_size, int *IS_TX, int *log_mode, int *err, gchar **err_info)
+{
+ int f_len, v_type;
+ guint8 header[VW_RECORD_HEADER_LENGTH];
+
+ *rec_size = 0;
+
+ /* Read out the file data in 16-byte messages, stopping either after we find a frame, */
+ /* or if we run out of data. */
+ /* Each 16-byte message is decoded; if we run across a non-frame message followed by a */
+ /* variable-length item, we read the variable length item out and discard it. */
+ /* If we find a frame, we return (with the header in the passed buffer). */
+ while (1) {
+ if (!wtap_read_bytes_or_eof(fh, header, VW_RECORD_HEADER_LENGTH, err, err_info))
+ return FALSE;
+
+ /* Got a header; invoke decode-message function to parse and process it. */
+ /* If the function returns a length, then a frame or variable-length message */
+ /* follows the 16-byte message. */
+ /* If the variable length message is not a frame, simply skip over it. */
+ if ((f_len = decode_msg(vwr, header, &v_type, IS_TX, log_mode)) != 0) {
+ if (f_len > B_SIZE) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("vwr: Invalid message record length %d", f_len);
+ return FALSE;
+ }
+ else if (v_type != VT_FRAME) {
+ if (!wtap_read_bytes(fh, NULL, f_len, err, err_info))
+ return FALSE;
+ }
+ else {
+ *rec_size = f_len;
+ return TRUE;
+ }
+ }
+ }
+}
+
+/* Figure out the FPGA version (and also see whether this is a VWR file type. */
+/* Return FPGA version if it's a known version, UNKNOWN_FPGA if it's not, */
+/* and -1 on an I/O error. */
+
+static int vwr_get_fpga_version(wtap *wth, int *err, gchar **err_info)
+{
+ guint8 *rec; /* local buffer (holds input record) */
+ guint8 header[VW_RECORD_HEADER_LENGTH];
+ int rec_size = 0;
+ guint8 i;
+ guint8 *s_510006_ptr = NULL;
+ guint8 *s_510024_ptr = NULL;
+ guint8 *s_510012_ptr = NULL; /* stats pointers */
+ gint64 filePos = -1;
+ guint64 bytes_read = 0;
+ guint32 frame_type = 0;
+ int f_len, v_type;
+ guint16 data_length = 0;
+ guint16 fpga_version;
+ gboolean valid_but_empty_file = FALSE;
+
+ filePos = file_tell(wth->fh);
+ if (filePos == -1) {
+ *err = file_error(wth->fh, err_info);
+ return -1;
+ }
+
+ fpga_version = 1000;
+ rec = (guint8*)g_malloc(B_SIZE);
+ /* Got a frame record; see if it is vwr */
+ /* If we don't get it all, then declare an error, we can't process the frame. */
+ /* Read out the file data in 16-byte messages, stopping either after we find a frame, */
+ /* or if we run out of data. */
+ /* Each 16-byte message is decoded; if we run across a non-frame message followed by a */
+ /* variable-length item, we read the variable length item out and discard it. */
+ /* If we find a frame, we return (with the header in the passed buffer). */
+ while (wtap_read_bytes(wth->fh, header, VW_RECORD_HEADER_LENGTH, err, err_info)) {
+ /* Got a header; invoke decode-message function to parse and process it. */
+ /* If the function returns a length, then a frame or variable-length message */
+ /* follows the 16-byte message. */
+ /* If the variable length message is not a frame, simply skip over it. */
+ if ((f_len = decode_msg(NULL, header, &v_type, NULL, NULL)) != 0) {
+ if (f_len > B_SIZE) {
+ g_free(rec);
+ /* Treat this here as an indication that the file probably */
+ /* isn't a vwr file. */
+ return UNKNOWN_FPGA;
+ }
+ else if (v_type != VT_FRAME) {
+ if (!wtap_read_bytes(wth->fh, NULL, f_len, err, err_info)) {
+ g_free(rec);
+ if (*err == WTAP_ERR_SHORT_READ)
+ return UNKNOWN_FPGA; /* short read - not a vwr file */
+ return -1;
+ }
+ else if (v_type == VT_CPMSG)
+ valid_but_empty_file = TRUE;
+ }
+ else {
+ rec_size = f_len;
+ /* Got a frame record; read over entire record (frame + trailer) into a local buffer */
+ /* If we don't get it all, assume this isn't a vwr file */
+ if (!wtap_read_bytes(wth->fh, rec, rec_size, err, err_info)) {
+ g_free(rec);
+ if (*err == WTAP_ERR_SHORT_READ)
+ return UNKNOWN_FPGA; /* short read - not a vwr file */
+ return -1;
+ }
+
+ /* I'll grab the bytes where the Ethernet "octets" field should be and the bytes where */
+ /* the 802.11 "octets" field should be. Then if I do rec_size - octets - */
+ /* size_of_stats_block and it's 0, I can select the correct type. */
+ /* octets + stats_len = rec_size only when octets have been incremented to nearest */
+ /* number divisible by 4. */
+
+ /* First check for series I WLAN since the check is more rigorous. */
+ if (rec_size > v22_W_STATS_LEN) {
+ s_510006_ptr = &(rec[rec_size - v22_W_STATS_LEN]); /* point to 510006 WLAN */
+ /* stats block */
+
+ data_length = pntoh16(&s_510006_ptr[v22_W_OCTET_OFF]);
+ i = 0;
+ while (((data_length + i) % 4) != 0)
+ i = i + 1;
+
+ frame_type = pntoh32(&s_510006_ptr[v22_W_FRAME_TYPE_OFF]);
+
+ if (rec_size == (data_length + v22_W_STATS_LEN + i) && (frame_type & v22_W_IS_80211) == 0x1000000) {
+ fpga_version = S1_W_FPGA;
+ }
+ }
+
+ /* Next for the series I Ethernet */
+ if ((rec_size > v22_E_STATS_LEN) && (fpga_version == 1000)) {
+ s_510012_ptr = &(rec[rec_size - v22_E_STATS_LEN]); /* point to 510012 enet */
+ /* stats block */
+ data_length = pntoh16(&s_510012_ptr[v22_E_OCTET_OFF]);
+ i = 0;
+ while (((data_length + i) % 4) != 0)
+ i = i + 1;
+
+ if (rec_size == (data_length + v22_E_STATS_LEN + i))
+ fpga_version = vVW510012_E_FPGA;
+ }
+
+
+ /* Next the series II WLAN */
+ if ((rec_size > vVW510021_W_STATS_TRAILER_LEN) && (fpga_version == 1000)) {
+ /* stats block */
+
+ if ((header[8] == 48) || (header[8] == 61) || (header[8] == 68))
+ fpga_version = S3_W_FPGA;
+ else {
+ data_length = (256 * (rec[vVW510021_W_MSDU_LENGTH_OFF + 1] & 0x1f)) + rec[vVW510021_W_MSDU_LENGTH_OFF];
+
+ i = 0;
+ while (((data_length + i) % 4) != 0)
+ i = i + 1;
+
+ /*the 12 is from the 12 bytes of plcp header */
+ if (rec_size == (data_length + vVW510021_W_STATS_TRAILER_LEN +vVW510021_W_AFTERHEADER_LEN+12+i))
+ fpga_version = S2_W_FPGA;
+ }
+ }
+
+ /* Finally the Series II Ethernet */
+ if ((rec_size > vVW510024_E_STATS_LEN) && (fpga_version == 1000)) {
+ s_510024_ptr = &(rec[rec_size - vVW510024_E_STATS_LEN]); /* point to 510024 ENET */
+ data_length = pntoh16(&s_510024_ptr[vVW510024_E_MSDU_LENGTH_OFF]);
+
+ i = 0;
+ while (((data_length + i) % 4) != 0)
+ i = i + 1;
+
+ if (rec_size == (data_length + vVW510024_E_STATS_LEN + i))
+ fpga_version = vVW510024_E_FPGA;
+ }
+
+ if (fpga_version != 1000)
+ {
+ /* reset the file position offset */
+ if (file_seek (wth->fh, filePos, SEEK_SET, err) == -1) {
+ g_free(rec);
+ return (-1);
+ }
+
+ /* We found an FPGA that works */
+ g_free(rec);
+ return fpga_version;
+ }
+ }
+ }
+ bytes_read += VW_RECORD_HEADER_LENGTH;
+ if (bytes_read > VW_BYTES_TO_CHECK) {
+ /* no frame found in VW_BYTES_TO_CHECK - not a vwr file */
+ g_free(rec);
+ return UNKNOWN_FPGA;
+ }
+ }
+
+ /* Is this a valid but empty file? If so, claim it's the S3_W_FPGA FPGA. */
+ if (valid_but_empty_file) {
+ g_free(rec);
+ return(S3_W_FPGA);
+ }
+
+ if (*err == WTAP_ERR_SHORT_READ) {
+ g_free(rec);
+ return UNKNOWN_FPGA; /* short read - not a vwr file */
+ }
+
+ /*
+ * Read error.
+ */
+ g_free(rec);
+ return -1;
+}
+
+/* Copy the actual packet data from the capture file into the target data block. */
+/* The packet is constructed as a 38-byte VeriWave metadata header plus the raw */
+/* MAC octets. */
+
+static gboolean vwr_read_s1_W_rec(vwr_t *vwr, wtap_rec *record,
+ Buffer *buf, const guint8 *rec, int rec_size,
+ int *err, gchar **err_info)
+{
+ guint8 *data_ptr;
+ int bytes_written = 0; /* bytes output to buf so far */
+ const guint8 *s_ptr, *m_ptr; /* stats pointer */
+ guint16 msdu_length, actual_octets; /* octets in frame */
+ guint16 plcp_hdr_len; /* PLCP header length */
+ guint16 rflags;
+ guint8 m_type; /* mod type (CCK-L/CCK-S/OFDM), seqnum */
+ guint flow_seq;
+ guint64 s_time = LL_ZERO, e_time = LL_ZERO; /* start/end */
+ /* times, nsec */
+ guint32 latency;
+ guint64 start_time, s_sec, s_usec = LL_ZERO; /* start time, sec + usec */
+ guint64 end_time; /* end time */
+ guint32 info; /* INFO/ERRORS fields in stats blk */
+ gint8 rssi; /* RSSI, signed 8-bit number */
+ int f_tx; /* flag: if set, is a TX frame */
+ guint8 rate_index; /* pre-HT only */
+ guint16 vc_id, ht_len=0; /* VC ID, total ip length */
+ guint flow_id; /* flow ID */
+ guint32 d_time, errors; /* packet duration & errors */
+ int sig_off, pay_off; /* MAC+SNAP header len, signature offset */
+ guint64 sig_ts; /* 32 LSBs of timestamp in signature */
+ guint16 phyRate;
+ guint16 vw_flags; /* VeriWave-specific packet flags */
+
+ /*
+ * The record data must be large enough to hold the statistics trailer.
+ */
+ if (rec_size < v22_W_STATS_LEN) {
+ *err_info = ws_strdup_printf("vwr: Invalid record length %d (must be at least %u)",
+ rec_size, v22_W_STATS_LEN);
+ *err = WTAP_ERR_BAD_FILE;
+ return FALSE;
+ }
+
+ /* Calculate the start of the statistics block in the buffer */
+ /* Also get a bunch of fields from the stats block */
+ s_ptr = &(rec[rec_size - v22_W_STATS_LEN]); /* point to it */
+ m_type = s_ptr[v22_W_MTYPE_OFF] & v22_E_MT_MASK;
+ f_tx = !(s_ptr[v22_W_MTYPE_OFF] & v22_E_IS_RX);
+ actual_octets = pntoh16(&s_ptr[v22_W_OCTET_OFF]);
+ vc_id = pntoh16(&s_ptr[v22_W_VCID_OFF]) & v22_E_VCID_MASK;
+ flow_seq = s_ptr[v22_W_FLOWSEQ_OFF];
+
+ latency = (guint32)pcorey48tohll(&s_ptr[v22_W_LATVAL_OFF]);
+
+ flow_id = pntoh16(&s_ptr[v22_W_FLOWID_OFF+1]); /* only 16 LSBs kept */
+ errors = pntoh16(&s_ptr[v22_W_ERRORS_OFF]);
+
+ info = pntoh16(&s_ptr[v22_W_INFO_OFF]);
+ rssi = (s_ptr[v22_W_RSSI_OFF] & 0x80) ? (-1 * (s_ptr[v22_W_RSSI_OFF] & 0x7f)) : s_ptr[v22_W_RSSI_OFF];
+
+ /*
+ * Sanity check the octets field to determine if it's greater than
+ * the packet data available in the record - i.e., the record size
+ * minus the length of the statistics block.
+ *
+ * Report an error if it is.
+ */
+ if (actual_octets > rec_size - v22_W_STATS_LEN) {
+ *err_info = ws_strdup_printf("vwr: Invalid data length %u (runs past the end of the record)",
+ actual_octets);
+ *err = WTAP_ERR_BAD_FILE;
+ return FALSE;
+ }
+
+ /* Decode OFDM or CCK PLCP header and determine rate and short preamble flag. */
+ /* The SIGNAL byte is always the first byte of the PLCP header in the frame. */
+ if (m_type == vwr->MT_OFDM)
+ rate_index = get_ofdm_rate(rec);
+ else if ((m_type == vwr->MT_CCKL) || (m_type == vwr->MT_CCKS))
+ rate_index = get_cck_rate(rec);
+ else
+ rate_index = 1;
+ rflags = (m_type == vwr->MT_CCKS) ? FLAGS_SHORTPRE : 0;
+ /* Calculate the MPDU size/ptr stuff; MPDU starts at 4 or 6 depending on OFDM/CCK. */
+ /* Note that the number of octets in the frame also varies depending on OFDM/CCK, */
+ /* because the PLCP header is prepended to the actual MPDU. */
+ plcp_hdr_len = (m_type == vwr->MT_OFDM) ? 4 : 6;
+ if (actual_octets >= plcp_hdr_len)
+ actual_octets -= plcp_hdr_len;
+ else {
+ *err_info = ws_strdup_printf("vwr: Invalid data length %u (too short to include %u-byte PLCP header)",
+ actual_octets, plcp_hdr_len);
+ *err = WTAP_ERR_BAD_FILE;
+ return FALSE;
+ }
+ m_ptr = &rec[plcp_hdr_len];
+ msdu_length = actual_octets;
+
+ /*
+ * The MSDU length includes the FCS.
+ *
+ * The packet data does *not* include the FCS - it's just 4 bytes
+ * of junk - so we have to remove it.
+ *
+ * We'll be stripping off that junk, so make sure we have at least
+ * 4 octets worth of packet data.
+ *
+ * There seems to be a special case of a length of 0.
+ */
+ if (actual_octets < 4) {
+ if (actual_octets != 0) {
+ *err_info = ws_strdup_printf("vwr: Invalid data length %u (too short to include %u-byte PLCP header and 4 bytes of FCS)",
+ actual_octets, plcp_hdr_len);
+ *err = WTAP_ERR_BAD_FILE;
+ return FALSE;
+ }
+ } else {
+ actual_octets -= 4;
+ }
+
+ /* Calculate start & end times (in sec/usec), converting 64-bit times to usec. */
+ /* 64-bit times are "Corey-endian" */
+ s_time = pcoreytohll(&s_ptr[v22_W_STARTT_OFF]);
+ e_time = pcoreytohll(&s_ptr[v22_W_ENDT_OFF]);
+
+ /* find the packet duration (difference between start and end times) */
+ d_time = (guint32)((e_time - s_time) / NS_IN_US); /* find diff, converting to usec */
+
+ /* also convert the packet start time to seconds and microseconds */
+ start_time = s_time / NS_IN_US; /* convert to microseconds first */
+ s_sec = (start_time / US_IN_SEC); /* get the number of seconds */
+ s_usec = start_time - (s_sec * US_IN_SEC); /* get the number of microseconds */
+
+ /* also convert the packet end time to seconds and microseconds */
+ end_time = e_time / NS_IN_US; /* convert to microseconds first */
+
+ /* extract the 32 LSBs of the signature timestamp field from the data block*/
+ pay_off = 42; /* 24 (MAC) + 8 (SNAP) + IP */
+ sig_off = find_signature(m_ptr, rec_size - 6, pay_off, flow_id, flow_seq);
+ if (m_ptr[sig_off] == 0xdd)
+ sig_ts = get_signature_ts(m_ptr, sig_off, rec_size - v22_W_STATS_LEN);
+ else
+ sig_ts = 0;
+
+ /*
+ * Fill up the per-packet header.
+ *
+ * We include the length of the metadata headers in the packet lengths.
+ *
+ * The maximum value of actual_octets is 8191, which, even after
+ * adding the lengths of the metadata headers, is less than
+ * WTAP_MAX_PACKET_SIZE_STANDARD will ever be, so we don't need to check it.
+ */
+ record->rec_header.packet_header.len = STATS_COMMON_FIELDS_LEN + EXT_WLAN_FIELDS_LEN + actual_octets;
+ record->rec_header.packet_header.caplen = STATS_COMMON_FIELDS_LEN + EXT_WLAN_FIELDS_LEN + actual_octets;
+
+ record->ts.secs = (time_t)s_sec;
+ record->ts.nsecs = (int)(s_usec * 1000);
+ record->rec_header.packet_header.pkt_encap = WTAP_ENCAP_IXVERIWAVE;
+
+ record->rec_type = REC_TYPE_PACKET;
+ record->block = wtap_block_create(WTAP_BLOCK_PACKET);
+ record->presence_flags = WTAP_HAS_TS;
+
+ ws_buffer_assure_space(buf, record->rec_header.packet_header.caplen);
+ data_ptr = ws_buffer_start_ptr(buf);
+
+ /*
+ * Generate and copy out the common metadata headers,
+ * set the port type to 0 (WLAN).
+ *
+ * All values are copied out in little-endian byte order.
+ */
+ /* 1st octet of record for port_type and command (command is 0, hence RX) */
+ phtole8(&data_ptr[bytes_written], WLAN_PORT);
+ bytes_written += 1;
+ /* 2nd octet of record for fpga version (0, hence pre-OCTO) */
+ phtole8(&data_ptr[bytes_written], 0);
+ bytes_written += 1;
+
+ phtoles(&data_ptr[bytes_written], STATS_COMMON_FIELDS_LEN); /* it_len */
+ bytes_written += 2;
+ phtoles(&data_ptr[bytes_written], msdu_length);
+ bytes_written += 2;
+ phtolel(&data_ptr[bytes_written], flow_id);
+ bytes_written += 4;
+ phtoles(&data_ptr[bytes_written], vc_id);
+ bytes_written += 2;
+ phtoles(&data_ptr[bytes_written], flow_seq);
+ bytes_written += 2;
+ if (!f_tx && sig_ts != 0) {
+ phtolel(&data_ptr[bytes_written], latency);
+ } else {
+ phtolel(&data_ptr[bytes_written], 0);
+ }
+ bytes_written += 4;
+ phtolel(&data_ptr[bytes_written], sig_ts); /* 32 LSBs of signature timestamp (nsec) */
+ bytes_written += 4;
+ phtolell(&data_ptr[bytes_written], start_time); /* record start & end times of frame */
+ bytes_written += 8;
+ phtolell(&data_ptr[bytes_written], end_time);
+ bytes_written += 8;
+ phtolel(&data_ptr[bytes_written], d_time);
+ bytes_written += 4;
+
+ /*
+ * Generate and copy out the WLAN metadata headers.
+ *
+ * All values are copied out in little-endian byte order.
+ */
+ phtoles(&data_ptr[bytes_written], EXT_WLAN_FIELDS_LEN);
+ bytes_written += 2;
+ phtoles(&data_ptr[bytes_written], rflags);
+ bytes_written += 2;
+ if (m_type == vwr->MT_OFDM) {
+ phtoles(&data_ptr[bytes_written], CHAN_OFDM);
+ } else {
+ phtoles(&data_ptr[bytes_written], CHAN_CCK);
+ }
+ bytes_written += 2;
+ phyRate = (guint16)(get_legacy_rate(rate_index) * 10);
+ phtoles(&data_ptr[bytes_written], phyRate);
+ bytes_written += 2;
+ data_ptr[bytes_written] = vVW510021_W_PLCP_LEGACY; /* pre-HT */
+ bytes_written += 1;
+ data_ptr[bytes_written] = rate_index;
+ bytes_written += 1;
+ data_ptr[bytes_written] = 1; /* pre-VHT, so NSS = 1 */
+ bytes_written += 1;
+ data_ptr[bytes_written] = rssi;
+ bytes_written += 1;
+ /* antennae b, c, d signal power */
+ data_ptr[bytes_written] = 100;
+ bytes_written += 1;
+ data_ptr[bytes_written] = 100;
+ bytes_written += 1;
+ data_ptr[bytes_written] = 100;
+ bytes_written += 1;
+ /* padding */
+ data_ptr[bytes_written] = 0;
+ bytes_written += 1;
+
+ /* fill in the VeriWave flags field */
+ vw_flags = 0;
+ if (f_tx)
+ vw_flags |= VW_FLAGS_TXF;
+ if (errors & vwr->FCS_ERROR)
+ vw_flags |= VW_FLAGS_FCSERR;
+ if (!f_tx && (errors & vwr->CRYPTO_ERR))
+ vw_flags |= VW_FLAGS_DCRERR;
+ if (!f_tx && (errors & vwr->RETRY_ERR))
+ vw_flags |= VW_FLAGS_RETRERR;
+ if (info & vwr->WEPTYPE)
+ vw_flags |= VW_FLAGS_IS_WEP;
+ else if (info & vwr->TKIPTYPE)
+ vw_flags |= VW_FLAGS_IS_TKIP;
+ else if (info & vwr->CCMPTYPE)
+ vw_flags |= VW_FLAGS_IS_CCMP;
+ phtoles(&data_ptr[bytes_written], vw_flags);
+ bytes_written += 2;
+
+ phtoles(&data_ptr[bytes_written], ht_len);
+ bytes_written += 2;
+ phtoles(&data_ptr[bytes_written], info);
+ bytes_written += 2;
+ phtolel(&data_ptr[bytes_written], errors);
+ bytes_written += 4;
+
+ /*
+ * Finally, copy the whole MAC frame to the packet buffer as-is.
+ * This does not include the PLCP; the MPDU starts at 4 or 6
+ * depending on OFDM/CCK.
+ * This also does not include the last 4 bytes, as those don't
+ * contain an FCS, they just contain junk.
+ */
+ memcpy(&data_ptr[bytes_written], &rec[plcp_hdr_len], actual_octets);
+
+ return TRUE;
+}
+
+
+static gboolean vwr_read_s2_W_rec(vwr_t *vwr, wtap_rec *record,
+ Buffer *buf, const guint8 *rec, int rec_size,
+ int IS_TX, int *err, gchar **err_info)
+{
+ guint8 *data_ptr;
+ int bytes_written = 0; /* bytes output to buf so far */
+ const guint8 *s_start_ptr,*s_trail_ptr, *plcp_ptr, *m_ptr; /* stats & MPDU ptr */
+ guint32 msdu_length, actual_octets; /* octets in frame */
+ guint8 l1p_1, l1p_2, plcp_type, rate_mcs_index, nss; /* mod (CCK-L/CCK-S/OFDM) */
+ guint flow_seq;
+ guint64 s_time = LL_ZERO, e_time = LL_ZERO; /* start/end */
+ /* times, nsec */
+ guint64 latency = LL_ZERO;
+ guint64 start_time, s_sec, s_usec = LL_ZERO; /* start time, sec + usec */
+ guint64 end_time; /* end time */
+ guint16 info; /* INFO/ERRORS fields in stats blk */
+ guint32 errors;
+ gint8 rssi[] = {0,0,0,0}; /* RSSI, signed 8-bit number */
+ int f_tx; /* flag: if set, is a TX frame */
+ guint16 vc_id, ht_len=0; /* VC ID , total ip length*/
+ guint32 flow_id, d_time; /* flow ID, packet duration*/
+ int sig_off, pay_off; /* MAC+SNAP header len, signature offset */
+ guint64 sig_ts, tsid; /* 32 LSBs of timestamp in signature */
+ guint16 chanflags = 0; /* channel flags for WLAN metadata header */
+ guint16 radioflags = 0; /* flags for WLAN metadata header */
+ guint64 delta_b; /* Used for calculating latency */
+ float rate;
+ guint16 phyRate;
+ guint16 vw_flags; /* VeriWave-specific packet flags */
+
+ /*
+ * The record data must be large enough to hold the statistics header,
+ * the PLCP, and the statistics trailer.
+ */
+ if ((guint)rec_size < vwr->MPDU_OFF + vVW510021_W_STATS_TRAILER_LEN) {
+ *err_info = ws_strdup_printf("vwr: Invalid record length %d (must be at least %u)",
+ rec_size,
+ vwr->MPDU_OFF + vVW510021_W_STATS_TRAILER_LEN);
+ *err = WTAP_ERR_BAD_FILE;
+ return FALSE;
+ }
+
+ /* Calculate the start of the statistics blocks in the buffer */
+ /* Also get a bunch of fields from the stats blocks */
+ s_start_ptr = &(rec[0]); /* point to stats header */
+ s_trail_ptr = &(rec[rec_size - vVW510021_W_STATS_TRAILER_LEN]); /* point to stats trailer */
+
+ l1p_1 = s_start_ptr[vVW510021_W_L1P_1_OFF];
+ l1p_2 = s_start_ptr[vVW510021_W_L1P_2_OFF];
+ plcp_type = vVW510021_W_S2_PLCP_TYPE(l1p_2);
+ /* we do the range checks at the end before copying the values
+ into the wtap header */
+ msdu_length = ((s_start_ptr[vVW510021_W_MSDU_LENGTH_OFF+1] & 0x1f) << 8)
+ + s_start_ptr[vVW510021_W_MSDU_LENGTH_OFF];
+
+ vc_id = pntoh16(&s_start_ptr[vVW510021_W_VCID_OFF]);
+ if (IS_TX)
+ {
+ rssi[0] = (s_start_ptr[vVW510021_W_RSSI_TXPOWER_OFF] & 0x80) ?
+ -1 * (s_start_ptr[vVW510021_W_RSSI_TXPOWER_OFF] & 0x7f) :
+ s_start_ptr[vVW510021_W_RSSI_TXPOWER_OFF] & 0x7f;
+ }
+ else
+ {
+ rssi[0] = (s_start_ptr[vVW510021_W_RSSI_TXPOWER_OFF] & 0x80) ?
+ (s_start_ptr[vVW510021_W_RSSI_TXPOWER_OFF]- 256) :
+ s_start_ptr[vVW510021_W_RSSI_TXPOWER_OFF];
+ }
+ rssi[1] = 100;
+ rssi[2] = 100;
+ rssi[3] = 100;
+
+ plcp_ptr = &(rec[8]);
+
+ actual_octets = msdu_length;
+
+ /*
+ * Sanity check the octets field to determine if it's greater than
+ * the packet data available in the record - i.e., the record size
+ * minus the sum of (length of statistics header + PLCP) and
+ * (length of statistics trailer).
+ *
+ * Report an error if it is.
+ */
+ if (actual_octets > rec_size - (vwr->MPDU_OFF + vVW510021_W_STATS_TRAILER_LEN)) {
+ *err_info = ws_strdup_printf("vwr: Invalid data length %u (runs past the end of the record)",
+ actual_octets);
+ *err = WTAP_ERR_BAD_FILE;
+ return FALSE;
+ }
+
+ f_tx = IS_TX;
+ flow_seq = s_trail_ptr[vVW510021_W_FLOWSEQ_OFF];
+
+ latency = 0x00000000; /* clear latency */
+ flow_id = pntoh24(&s_trail_ptr[vVW510021_W_FLOWID_OFF]); /* all 24 bits valid */
+ /* For tx latency is duration, for rx latency is timestamp */
+ /* Get 48-bit latency value */
+ tsid = pcorey48tohll(&s_trail_ptr[vVW510021_W_LATVAL_OFF]);
+
+ errors = pntoh32(&s_trail_ptr[vVW510021_W_ERRORS_OFF]);
+ info = pntoh16(&s_trail_ptr[vVW510021_W_INFO_OFF]);
+ if ((info & v22_W_AGGREGATE_FLAGS) != 0)
+ /* this length includes the Start_Spacing + Delimiter + MPDU + Padding for each piece of the aggregate*/
+ ht_len = pletoh16(&s_start_ptr[vwr->PLCP_LENGTH_OFF]);
+
+
+ /* decode OFDM or CCK PLCP header and determine rate and short preamble flag */
+ /* the SIGNAL byte is always the first byte of the PLCP header in the frame */
+ switch (plcp_type)
+ {
+ case vVW510021_W_PLCP_LEGACY:
+ /*
+ * From IEEE Std 802.11-2012:
+ *
+ * According to section 17.2.2 "PPDU format", the PLCP header
+ * for the High Rate DSSS PHY (11b) has a SIGNAL field that's
+ * 8 bits, followed by a SERVICE field that's 8 bits, followed
+ * by a LENGTH field that's 16 bits, followed by a CRC field
+ * that's 16 bits. The PSDU follows it. Section 17.2.3 "PPDU
+ * field definitions" describes those fields.
+ *
+ * According to sections 18.3.2 "PLCP frame format" and 18.3.4
+ * "SIGNAL field", the PLCP for the OFDM PHY (11a) has a SIGNAL
+ * field that's 24 bits, followed by a service field that's
+ * 16 bits, followed by the PSDU. Section 18.3.5.2 "SERVICE
+ * field" describes the SERVICE field.
+ *
+ * According to section 19.3.2 "PPDU format", the frames for the
+ * Extended Rate PHY (11g) either extend the 11b format, using
+ * additional bits in the SERVICE field, or extend the 11a
+ * format.
+ */
+ rate_mcs_index = vVW510021_W_S2_RATE_INDEX(l1p_1);
+ if (rate_mcs_index < 4) {
+ chanflags |= CHAN_CCK;
+ }
+ else {
+ chanflags |= CHAN_OFDM;
+ }
+ rate = get_legacy_rate(rate_mcs_index);
+ nss = 0;
+ break;
+
+ case vVW510021_W_PLCP_MIXED:
+ /*
+ * According to section 20.3.2 "PPDU format", the HT-mixed
+ * PLCP header has a "Non-HT SIGNAL field" (L-SIG), which
+ * looks like an 11a SIGNAL field, followed by an HT SIGNAL
+ * field (HT-SIG) described in section 20.3.9.4.3 "HT-SIG
+ * definition".
+ *
+ * This means that the first octet of HT-SIG is at
+ * plcp_ptr[3], skipping the 3 octets of the L-SIG field.
+ *
+ * 0x80 is the CBW 20/40 bit of HT-SIG.
+ */
+ /* set the appropriate flags to indicate HT mode and CB */
+ rate_mcs_index = vVW510021_W_S2_MCS_INDEX_HT(l1p_1);
+ radioflags |= FLAGS_CHAN_HT | ((plcp_ptr[3] & 0x80) ? FLAGS_CHAN_40MHZ : 0) |
+ ((l1p_1 & vVW510021_W_IS_LONGGI) ? 0 : FLAGS_CHAN_SHORTGI);
+ chanflags |= CHAN_OFDM;
+ nss = (rate_mcs_index < MAX_HT_MCS) ? nss_for_mcs[rate_mcs_index] : 0;
+ rate = get_ht_rate(rate_mcs_index, radioflags);
+ break;
+
+ case vVW510021_W_PLCP_GREENFIELD:
+ /*
+ * According to section 20.3.2 "PPDU format", the HT-greenfield
+ * PLCP header just has the HT SIGNAL field (HT-SIG) above, with
+ * no L-SIG field.
+ *
+ * This means that the first octet of HT-SIG is at
+ * plcp_ptr[0], as there's no L-SIG field to skip.
+ *
+ * 0x80 is the CBW 20/40 bit of HT-SIG.
+ */
+ /* set the appropriate flags to indicate HT mode and CB */
+ rate_mcs_index = vVW510021_W_S2_MCS_INDEX_HT(l1p_1);
+ radioflags |= FLAGS_CHAN_HT | ((plcp_ptr[0] & 0x80) ? FLAGS_CHAN_40MHZ : 0) |
+ ((l1p_1 & vVW510021_W_IS_LONGGI) ? 0 : FLAGS_CHAN_SHORTGI);
+ chanflags |= CHAN_OFDM;
+ nss = (rate_mcs_index < MAX_HT_MCS) ? nss_for_mcs[rate_mcs_index] : 0;
+ rate = get_ht_rate(rate_mcs_index, radioflags);
+ break;
+
+ case vVW510021_W_PLCP_VHT_MIXED:
+ /*
+ * According to section 22.3.2 "VHT PPDU format" of IEEE Std
+ * 802.11ac-2013, the VHT PLCP header has a "non-HT SIGNAL field"
+ * (L-SIG), which looks like an 11a SIGNAL field, followed by
+ * a VHT Signal A field (VHT-SIG-A) described in section
+ * 22.3.8.3.3 "VHT-SIG-A definition", with training fields
+ * between it and a VHT Signal B field (VHT-SIG-B) described
+ * in section 22.3.8.3.6 "VHT-SIG-B definition", followed by
+ * the PSDU.
+ */
+ {
+ guint8 SBW = vVW510021_W_BANDWIDTH_VHT(l1p_2);
+ rate_mcs_index = vVW510021_W_S2_MCS_INDEX_VHT(l1p_1);
+ radioflags |= FLAGS_CHAN_VHT | ((l1p_1 & vVW510021_W_IS_LONGGI) ? 0 : FLAGS_CHAN_SHORTGI);
+ chanflags |= CHAN_OFDM;
+ if (SBW == 3)
+ radioflags |= FLAGS_CHAN_40MHZ;
+ else if (SBW == 4)
+ radioflags |= FLAGS_CHAN_80MHZ;
+ nss = vVW510021_W_S2_NSS_VHT(l1p_1);
+ rate = get_vht_rate(rate_mcs_index, radioflags, nss);
+ }
+ break;
+
+ default:
+ rate_mcs_index = 0;
+ nss = 0;
+ rate = 0.0f;
+ break;
+ }
+
+ /*
+ * The MSDU length includes the FCS.
+ *
+ * The packet data does *not* include the FCS - it's just 4 bytes
+ * of junk - so we have to remove it.
+ *
+ * We'll be stripping off that junk, so make sure we have at least
+ * 4 octets worth of packet data.
+ *
+ * There seems to be a special case of a length of 0.
+ */
+ if (actual_octets < 4) {
+ if (actual_octets != 0) {
+ *err_info = ws_strdup_printf("vwr: Invalid data length %u (too short to include 4 bytes of FCS)",
+ actual_octets);
+ *err = WTAP_ERR_BAD_FILE;
+ return FALSE;
+ }
+ } else {
+ actual_octets -= 4;
+ }
+
+ /* Calculate start & end times (in sec/usec), converting 64-bit times to usec. */
+ /* 64-bit times are "Corey-endian" */
+ s_time = pcoreytohll(&s_trail_ptr[vVW510021_W_STARTT_OFF]);
+ e_time = pcoreytohll(&s_trail_ptr[vVW510021_W_ENDT_OFF]);
+
+ /* find the packet duration (difference between start and end times) */
+ d_time = (guint32)((e_time - s_time) / NS_IN_US); /* find diff, converting to usec */
+
+ /* also convert the packet start time to seconds and microseconds */
+ start_time = s_time / NS_IN_US; /* convert to microseconds first */
+ s_sec = (start_time / US_IN_SEC); /* get the number of seconds */
+ s_usec = start_time - (s_sec * US_IN_SEC); /* get the number of microseconds */
+
+ /* also convert the packet end time to seconds and microseconds */
+ end_time = e_time / NS_IN_US; /* convert to microseconds first */
+
+ /* extract the 32 LSBs of the signature timestamp field */
+ m_ptr = &(rec[8+12]);
+ pay_off = 42; /* 24 (MAC) + 8 (SNAP) + IP */
+ sig_off = find_signature(m_ptr, rec_size - 20, pay_off, flow_id, flow_seq);
+ if (m_ptr[sig_off] == 0xdd)
+ sig_ts = get_signature_ts(m_ptr, sig_off, rec_size - vVW510021_W_STATS_TRAILER_LEN);
+ else
+ sig_ts = 0;
+
+ /* Set latency based on rx/tx and signature timestamp */
+ if (!IS_TX) {
+ if (tsid < s_time) {
+ latency = s_time - tsid;
+ } else {
+ /* Account for the rollover case. Since we cannot use 0x100000000 - l_time + s_time */
+ /* we look for a large difference between l_time and s_time. */
+ delta_b = tsid - s_time;
+ if (delta_b > 0x10000000)
+ latency = 0;
+ else
+ latency = delta_b;
+ }
+ }
+
+ /*
+ * Fill up the per-packet header.
+ *
+ * We include the length of the metadata headers in the packet lengths.
+ *
+ * The maximum value of actual_octets is 8191, which, even after
+ * adding the lengths of the metadata headers, is less than
+ * WTAP_MAX_PACKET_SIZE_STANDARD will ever be, so we don't need to check it.
+ */
+ record->rec_header.packet_header.len = STATS_COMMON_FIELDS_LEN + EXT_WLAN_FIELDS_LEN + actual_octets;
+ record->rec_header.packet_header.caplen = STATS_COMMON_FIELDS_LEN + EXT_WLAN_FIELDS_LEN + actual_octets;
+
+ record->ts.secs = (time_t)s_sec;
+ record->ts.nsecs = (int)(s_usec * 1000);
+
+ record->rec_type = REC_TYPE_PACKET;
+ record->block = wtap_block_create(WTAP_BLOCK_PACKET);
+ record->presence_flags = WTAP_HAS_TS;
+
+ ws_buffer_assure_space(buf, record->rec_header.packet_header.caplen);
+ data_ptr = ws_buffer_start_ptr(buf);
+
+ /*
+ * Generate and copy out the common metadata headers,
+ * set the port type to 0 (WLAN).
+ *
+ * All values are copied out in little-endian byte order.
+ */
+ /*** msdu_length = msdu_length + 16; ***/
+ /* 1st octet of record for port_type and command (command is 0, hence RX) */
+ phtole8(&data_ptr[bytes_written], WLAN_PORT);
+ bytes_written += 1;
+ /* 2nd octet of record for fpga version (0, hence pre-OCTO) */
+ phtole8(&data_ptr[bytes_written], 0);
+ bytes_written += 1;
+
+ phtoles(&data_ptr[bytes_written], STATS_COMMON_FIELDS_LEN); /* it_len */
+ bytes_written += 2;
+ phtoles(&data_ptr[bytes_written], msdu_length);
+ bytes_written += 2;
+ phtolel(&data_ptr[bytes_written], flow_id);
+ bytes_written += 4;
+ phtoles(&data_ptr[bytes_written], vc_id);
+ bytes_written += 2;
+ phtoles(&data_ptr[bytes_written], flow_seq);
+ bytes_written += 2;
+ if (!f_tx && sig_ts != 0) {
+ phtolel(&data_ptr[bytes_written], latency);
+ } else {
+ phtolel(&data_ptr[bytes_written], 0);
+ }
+ bytes_written += 4;
+ phtolel(&data_ptr[bytes_written], sig_ts); /* 32 LSBs of signature timestamp (nsec) */
+ bytes_written += 4;
+ phtolell(&data_ptr[bytes_written], start_time); /* record start & end times of frame */
+ bytes_written += 8;
+ phtolell(&data_ptr[bytes_written], end_time);
+ bytes_written += 8;
+ phtolel(&data_ptr[bytes_written], d_time);
+ bytes_written += 4;
+
+ /*
+ * Generate and copy out the WLAN metadata headers.
+ *
+ * All values are copied out in little-endian byte order.
+ */
+ phtoles(&data_ptr[bytes_written], EXT_WLAN_FIELDS_LEN);
+ bytes_written += 2;
+ if (info & vVW510021_W_IS_WEP)
+ radioflags |= FLAGS_WEP;
+ if (!(l1p_1 & vVW510021_W_IS_LONGPREAMBLE) && (plcp_type == vVW510021_W_PLCP_LEGACY))
+ radioflags |= FLAGS_SHORTPRE;
+ phtoles(&data_ptr[bytes_written], radioflags);
+ bytes_written += 2;
+ phtoles(&data_ptr[bytes_written], chanflags);
+ bytes_written += 2;
+ phyRate = (guint16)(rate * 10);
+ phtoles(&data_ptr[bytes_written], phyRate);
+ bytes_written += 2;
+
+ data_ptr[bytes_written] = plcp_type;
+ bytes_written += 1;
+
+ data_ptr[bytes_written] = rate_mcs_index;
+ bytes_written += 1;
+
+ data_ptr[bytes_written] = nss;
+ bytes_written += 1;
+ data_ptr[bytes_written] = rssi[0];
+ bytes_written += 1;
+ data_ptr[bytes_written] = rssi[1];
+ bytes_written += 1;
+ data_ptr[bytes_written] = rssi[2];
+ bytes_written += 1;
+ data_ptr[bytes_written] = rssi[3];
+ bytes_written += 1;
+ /* padding */
+ data_ptr[bytes_written] = 0;
+ bytes_written += 1;
+
+ /* fill in the VeriWave flags field */
+ vw_flags = 0;
+ if (f_tx)
+ vw_flags |= VW_FLAGS_TXF;
+ if (errors & 0x1f) /* If any error is flagged, then set the FCS error bit */
+ vw_flags |= VW_FLAGS_FCSERR;
+ if (!f_tx && (errors & vwr->CRYPTO_ERR))
+ vw_flags |= VW_FLAGS_DCRERR;
+ if (!f_tx && (errors & vwr->RETRY_ERR))
+ vw_flags |= VW_FLAGS_RETRERR;
+ if (info & vwr->WEPTYPE)
+ vw_flags |= VW_FLAGS_IS_WEP;
+ else if (info & vwr->TKIPTYPE)
+ vw_flags |= VW_FLAGS_IS_TKIP;
+ else if (info & vwr->CCMPTYPE)
+ vw_flags |= VW_FLAGS_IS_CCMP;
+ phtoles(&data_ptr[bytes_written], vw_flags);
+ bytes_written += 2;
+
+ phtoles(&data_ptr[bytes_written], ht_len);
+ bytes_written += 2;
+ phtoles(&data_ptr[bytes_written], info);
+ bytes_written += 2;
+ phtolel(&data_ptr[bytes_written], errors);
+ bytes_written += 4;
+
+ /* Finally, copy the whole MAC frame to the packet buffer as-is.
+ * This does not include the stats header or the PLCP.
+ * This also does not include the last 4 bytes, as those don't
+ * contain an FCS, they just contain junk.
+ */
+ memcpy(&data_ptr[bytes_written], &rec[vwr->MPDU_OFF], actual_octets);
+
+ return TRUE;
+}
+
+static gboolean vwr_read_s3_W_rec(vwr_t *vwr, wtap_rec *record,
+ Buffer *buf, const guint8 *rec, int rec_size,
+ int IS_TX, int log_mode, int *err,
+ gchar **err_info)
+{
+ guint8 *data_ptr;
+ int bytes_written = 0; /* bytes output to buf so far */
+ int i;
+ int stats_offset = 0;
+ const guint8 *s_start_ptr = NULL,*s_trail_ptr = NULL, *plcp_ptr, *m_ptr; /* stats & MPDU ptr */
+ guint32 msdu_length = 0, actual_octets = 0; /* octets in frame */
+ guint8 l1p_1 = 0,l1p_2 = 0, plcp_type, rate_mcs_index, nss; /* mod (CCK-L/CCK-S/OFDM) */
+ guint64 s_time = LL_ZERO, e_time = LL_ZERO; /* start/end */
+ /* times, nsec */
+ guint64 latency = LL_ZERO;
+ guint64 start_time = 0, s_sec = 0, s_usec = LL_ZERO; /* start time, sec + usec */
+ guint64 end_time = 0; /* end time */
+ guint16 info = 0; /* INFO/ERRORS fields in stats blk */
+ guint32 errors = 0;
+ gint8 info_2nd = 0,rssi[] = {0,0,0,0}; /* RSSI, signed 8-bit number */
+ int frame_size;
+ guint32 d_time = 0, flow_id = 0; /* packet duration, Flow Signature ID*/
+ int sig_off, pay_off; /* MAC+SNAP header len, signature offset */
+ guint64 sig_ts = 0, tsid; /* 32 LSBs of timestamp in signature */
+ guint64 delta_b; /* Used for calculating latency */
+ guint8 L1InfoC = 0, port_type, ver_fpga = 0;
+ guint8 flow_seq =0,plcp_hdr_flag = 0,rf_id = 0; /* indicates plcp hdr info */
+ const guint8 *rf_ptr = NULL;
+ float rate;
+ guint16 phyRate;
+
+ /*
+ * The record data must be large enough to hold the statistics header,
+ * the PLCP, and the statistics trailer.
+ */
+ if (IS_TX == 3) { /*IS_TX =3, i.e., command type is RF Modified*/
+ if ((guint)rec_size < OCTO_MODIFIED_RF_LEN) {
+ *err_info = ws_strdup_printf("vwr: Invalid record length %d (must be at least %u)",
+ rec_size,
+ OCTO_MODIFIED_RF_LEN);
+ *err = WTAP_ERR_BAD_FILE;
+ return FALSE;
+ }
+ rf_ptr = &(rec[0]);
+ rf_id = rf_ptr[0];
+
+ /*
+ * Fill up the per-packet header.
+ *
+ * We include the length of the metadata headers in the packet lengths.
+ *
+ * OCTO_MODIFIED_RF_LEN + 1 is less than WTAP_MAX_PACKET_SIZE_STANDARD will
+ * ever be, so we don't need to check it.
+ */
+ record->rec_header.packet_header.len = OCTO_MODIFIED_RF_LEN + 1; /* 1st octet is reserved for detecting type of frame while displaying in wireshark */
+ record->rec_header.packet_header.caplen = OCTO_MODIFIED_RF_LEN + 1;
+
+ record->ts.secs = (time_t)s_sec;
+ record->ts.nsecs = (int)(s_usec * 1000);
+
+ record->rec_type = REC_TYPE_PACKET;
+ record->block = wtap_block_create(WTAP_BLOCK_PACKET);
+ record->presence_flags = WTAP_HAS_TS;
+
+ ws_buffer_assure_space(buf, record->rec_header.packet_header.caplen);
+ data_ptr = ws_buffer_start_ptr(buf);
+
+ port_type = IS_TX << 4;
+
+ nss = 0;
+ phyRate = 0;
+ }
+ else {
+ /* Calculate the start of the statistics blocks in the buffer */
+ /* Also get a bunch of fields from the stats blocks */
+ /* 'stats_offset' variable is use to locate the exact offset.
+ * When a RX frame contrains RF,
+ * the position of Stats, Layer 1-4, PLCP parameters are shifted to
+ * + OCTO_RF_MOD_ACTUAL_LEN bytes
+ */
+ if (IS_TX == 4) /*IS_TX =4, i.e., command type is RF-RX Modified*/
+ {
+ stats_offset = OCTO_RF_MOD_ACTUAL_LEN;
+ if ((guint)rec_size < stats_offset + vwr->MPDU_OFF + vVW510021_W_STATS_TRAILER_LEN) {
+ *err_info = ws_strdup_printf("vwr: Invalid record length %d (must be at least %u)",
+ rec_size,
+ stats_offset + vwr->MPDU_OFF + vVW510021_W_STATS_TRAILER_LEN);
+ *err = WTAP_ERR_BAD_FILE;
+ return FALSE;
+ }
+ rf_ptr = &(rec[0]);
+ rf_id = rf_ptr[0];
+ }
+ else
+ {
+ stats_offset = 0;
+ if ((guint)rec_size < vwr->MPDU_OFF + vVW510021_W_STATS_TRAILER_LEN) {
+ *err_info = ws_strdup_printf("vwr: Invalid record length %d (must be at least %u)",
+ rec_size,
+ vwr->MPDU_OFF + vVW510021_W_STATS_TRAILER_LEN);
+ *err = WTAP_ERR_BAD_FILE;
+ return FALSE;
+ }
+ }
+
+ s_start_ptr = &(rec[stats_offset]); /* point to stats header */
+ s_trail_ptr = &(rec[rec_size - vVW510021_W_STATS_TRAILER_LEN] ); /* point to stats trailer */
+
+ l1p_1 = s_start_ptr[vVW510021_W_L1P_1_OFF];
+ l1p_2 = s_start_ptr[vVW510021_W_L1P_2_OFF];
+
+ plcp_type = vVW510021_W_S3_PLCP_TYPE(l1p_2);
+ switch (plcp_type)
+ {
+ case vVW510021_W_PLCP_LEGACY:
+ /* pre-HT */
+ rate_mcs_index = vVW510021_W_S3_RATE_INDEX(l1p_1);
+ nss = 0;
+ break;
+
+ case vVW510021_W_PLCP_MIXED:
+ case vVW510021_W_PLCP_GREENFIELD:
+ rate_mcs_index = vVW510021_W_S3_MCS_INDEX_HT(l1p_1);
+ nss = (rate_mcs_index < MAX_HT_MCS) ? nss_for_mcs[rate_mcs_index] : 0;
+ break;
+
+ case vVW510021_W_PLCP_VHT_MIXED:
+ rate_mcs_index = vVW510021_W_S3_MCS_INDEX_VHT(l1p_1);
+ nss = vVW510021_W_S3_NSS_VHT(l1p_1);
+ plcp_hdr_flag = 1;
+ break;
+
+ default:
+ rate_mcs_index = 0;
+ nss = 0;
+ plcp_hdr_flag = 0;
+ break;
+ }
+
+ for (i = 0; i < 4; i++)
+ {
+ if (IS_TX == 1)
+ {
+ rssi[i] = (s_start_ptr[4+i] & 0x80) ? -1 * (s_start_ptr[4+i] & 0x7f) : s_start_ptr[4+i] & 0x7f;
+ }
+ else
+ {
+ rssi[i] = (s_start_ptr[4+i] >= 128) ? (s_start_ptr[4+i] - 256) : s_start_ptr[4+i];
+ }
+ }
+
+ if (IS_TX == 0 || IS_TX == 4){
+ L1InfoC = s_start_ptr[8];
+ }
+
+ msdu_length = pntoh24(&s_start_ptr[9]);
+
+ /*** 16 bytes of PLCP header + 1 byte of L1P for user position ***/
+ plcp_ptr = &(rec[stats_offset+16]);
+
+ /*** Add the PLCP length for S3_W_FPGA version VHT frames for Beamforming decode ***/
+ if (log_mode == 3) {
+ frame_size = rec_size - (stats_offset + vwr->MPDU_OFF + vVW510021_W_STATS_TRAILER_LEN);
+ if (frame_size > ((int) msdu_length))
+ actual_octets = msdu_length;
+ else {
+ /*
+ * XXX - does this mean "the packet was cut short during
+ * capture" or "this is a malformed record"?
+ */
+ actual_octets = frame_size;
+ }
+ }
+ else
+ {
+ actual_octets = msdu_length;
+ }
+ /*
+ * Sanity check the octets field to determine if it's greater than
+ * the packet data available in the record - i.e., the record size
+ * minus the sum of (length of statistics header + PLCP) and
+ * (length of statistics trailer).
+ *
+ * Report an error if it is.
+ */
+ if (actual_octets > rec_size - (stats_offset + vwr->MPDU_OFF + vVW510021_W_STATS_TRAILER_LEN)) {
+ *err_info = ws_strdup_printf("vwr: Invalid data length %u (runs past the end of the record)",
+ actual_octets);
+ *err = WTAP_ERR_BAD_FILE;
+ return FALSE;
+ }
+
+ flow_seq = s_trail_ptr[vVW510021_W_FLOWSEQ_OFF];
+
+ latency = 0x00000000; /* clear latency */
+ flow_id = pntoh24(&s_trail_ptr[vVW510021_W_FLOWID_OFF]); /* all 24 bits valid */
+ /* For tx latency is duration, for rx latency is timestamp */
+ /* Get 48-bit latency value */
+ tsid = pcorey48tohll(&s_trail_ptr[vVW510021_W_LATVAL_OFF]);
+
+ errors = pntoh32(&s_trail_ptr[vVW510021_W_ERRORS_OFF]);
+ info = pntoh16(&s_trail_ptr[vVW510021_W_INFO_OFF]);
+
+ if (IS_TX == 0 || IS_TX == 4)
+ info_2nd = s_trail_ptr[41];
+
+ /*** Calculate Data rate based on
+ * PLCP type, MCS index and number of spatial stream
+ * radioflags is temporarily calculated, which is used in
+ * get_ht_rate() and get_vht_rate().
+ **/
+ switch (plcp_type)
+ {
+ case vVW510021_W_PLCP_LEGACY:
+ rate = get_legacy_rate(rate_mcs_index);
+ break;
+
+ case vVW510021_W_PLCP_MIXED:
+ /*
+ * According to section 20.3.2 "PPDU format", the HT-mixed
+ * PLCP header has a "Non-HT SIGNAL field" (L-SIG), which
+ * looks like an 11a SIGNAL field, followed by an HT SIGNAL
+ * field (HT-SIG) described in section 20.3.9.4.3 "HT-SIG
+ * definition".
+ *
+ * This means that the first octet of HT-SIG is at
+ * plcp_ptr[3], skipping the 3 octets of the L-SIG field.
+ *
+ * 0x80 is the CBW 20/40 bit of HT-SIG.
+ */
+ {
+ /* set the appropriate flags to indicate HT mode and CB */
+ guint16 radioflags = FLAGS_CHAN_HT | ((plcp_ptr[3] & 0x80) ? FLAGS_CHAN_40MHZ : 0) |
+ ((l1p_1 & vVW510021_W_IS_LONGGI) ? 0 : FLAGS_CHAN_SHORTGI);
+ rate = get_ht_rate(rate_mcs_index, radioflags);
+ }
+ break;
+
+ case vVW510021_W_PLCP_GREENFIELD:
+ /*
+ * According to section 20.3.2 "PPDU format", the HT-greenfield
+ * PLCP header just has the HT SIGNAL field (HT-SIG) above, with
+ * no L-SIG field.
+ *
+ * This means that the first octet of HT-SIG is at
+ * plcp_ptr[0], as there's no L-SIG field to skip.
+ *
+ * 0x80 is the CBW 20/40 bit of HT-SIG.
+ */
+ {
+ /* set the appropriate flags to indicate HT mode and CB */
+ guint16 radioflags = FLAGS_CHAN_HT | ((plcp_ptr[0] & 0x80) ? FLAGS_CHAN_40MHZ : 0) |
+ ((l1p_1 & vVW510021_W_IS_LONGGI) ? 0 : FLAGS_CHAN_SHORTGI);
+ rate = get_ht_rate(rate_mcs_index, radioflags);
+ }
+ break;
+
+ case vVW510021_W_PLCP_VHT_MIXED:
+ /*
+ * According to section 22.3.2 "VHT PPDU format" of IEEE Std
+ * 802.11ac-2013, the VHT PLCP header has a "non-HT SIGNAL field"
+ * (L-SIG), which looks like an 11a SIGNAL field, followed by
+ * a VHT Signal A field (VHT-SIG-A) described in section
+ * 22.3.8.3.3 "VHT-SIG-A definition", with training fields
+ * between it and a VHT Signal B field (VHT-SIG-B) described
+ * in section 22.3.8.3.6 "VHT-SIG-B definition", followed by
+ * the PSDU.
+ */
+ {
+ guint8 SBW = vVW510021_W_BANDWIDTH_VHT(l1p_2);
+ guint16 radioflags = FLAGS_CHAN_VHT | ((l1p_1 & vVW510021_W_IS_LONGGI) ? 0 : FLAGS_CHAN_SHORTGI);
+ if (SBW == 3)
+ radioflags |= FLAGS_CHAN_40MHZ;
+ else if (SBW == 4)
+ radioflags |= FLAGS_CHAN_80MHZ;
+ rate = get_vht_rate(rate_mcs_index, radioflags, nss);
+ }
+ break;
+
+ default:
+ rate = 0.0f;
+ break;
+ }
+ phyRate = (guint16)(rate * 10);
+ /* Calculation of Data rate ends*/
+
+ /* 'ver_fpga' is the 2nd Octet of each frame.
+ * msb/lsb nibble indicates log mode/fpga version respectively.
+ * where log mode = 0 is normal capture and 1 is reduced capture,
+ * lsb nibble is set to 1 always as this function is applicable for only FPGA version >= 48
+ */
+ if (log_mode == 3) {
+ if (frame_size >= (int) msdu_length) {
+ /*
+ * The MSDU length includes the FCS.
+ *
+ * The packet data does *not* include the FCS - it's just 4
+ * bytes of junk - so we have to remove it.
+ *
+ * We'll be stripping off that junk, so make sure we have at
+ * least 4 octets worth of packet data.
+ *
+ * XXX - is the FCS actually present here, as it appears to be
+ * if log_mode isn't 3?
+ *
+ * There seems to be a special case of a length of 0.
+ */
+ if (actual_octets < 4) {
+ if (actual_octets != 0) {
+ *err_info = ws_strdup_printf("vwr: Invalid data length %u (too short to include 4 bytes of FCS)",
+ actual_octets);
+ *err = WTAP_ERR_BAD_FILE;
+ return FALSE;
+ }
+ } else {
+ actual_octets -= 4;
+ }
+ }
+ ver_fpga = 0x11;
+ } else {
+ ver_fpga = 0x01;
+ }
+
+ /* Calculate start & end times (in sec/usec), converting 64-bit times to usec. */
+ /* 64-bit times are "Corey-endian" */
+ s_time = pcoreytohll(&s_trail_ptr[vVW510021_W_STARTT_OFF]);
+ e_time = pcoreytohll(&s_trail_ptr[vVW510021_W_ENDT_OFF]);
+
+ /* find the packet duration (difference between start and end times) */
+ d_time = (guint32)((e_time - s_time) / NS_IN_US); /* find diff, converting to usec */
+
+ /* also convert the packet start time to seconds and microseconds */
+ start_time = s_time / NS_IN_US; /* convert to microseconds first */
+ s_sec = (start_time / US_IN_SEC); /* get the number of seconds */
+ s_usec = start_time - (s_sec * US_IN_SEC); /* get the number of microseconds */
+
+ /* also convert the packet end time to seconds and microseconds */
+ end_time = e_time / NS_IN_US; /* convert to microseconds first */
+
+ /* extract the 32 LSBs of the signature timestamp field */
+ int m_ptr_offset = stats_offset + 8 + 12;
+ m_ptr = rec + m_ptr_offset;
+ pay_off = 42; /* 24 (MAC) + 8 (SNAP) + IP */
+ sig_off = find_signature(m_ptr, rec_size - m_ptr_offset, pay_off, flow_id, flow_seq);
+ if (m_ptr[sig_off] == 0xdd)
+ sig_ts = get_signature_ts(m_ptr, sig_off, rec_size - vVW510021_W_STATS_TRAILER_LEN);
+ else
+ sig_ts = 0;
+
+ /* Set latency based on rx/tx and signature timestamp */
+ if (IS_TX == 0 || IS_TX == 4) {
+ if (tsid < s_time) {
+ latency = s_time - tsid;
+ } else {
+ /* Account for the rollover case. Since we cannot use 0x100000000 - l_time + s_time */
+ /* we look for a large difference between l_time and s_time. */
+ delta_b = tsid - s_time;
+ if (delta_b > 0x10000000)
+ latency = 0;
+ else
+ latency = delta_b;
+ }
+ }
+
+ port_type = IS_TX << 4;
+
+ /*
+ * Fill up the per-packet header.
+ *
+ * We include the length of the metadata headers in the packet lengths.
+ */
+ if (IS_TX == 4) {
+ record->rec_header.packet_header.len = OCTO_MODIFIED_RF_LEN + OCTO_TIMESTAMP_FIELDS_LEN + OCTO_LAYER1TO4_LEN + actual_octets;
+ record->rec_header.packet_header.caplen = OCTO_MODIFIED_RF_LEN + OCTO_TIMESTAMP_FIELDS_LEN + OCTO_LAYER1TO4_LEN + actual_octets;
+ } else {
+ record->rec_header.packet_header.len = OCTO_TIMESTAMP_FIELDS_LEN + OCTO_LAYER1TO4_LEN + actual_octets;
+ record->rec_header.packet_header.caplen = OCTO_TIMESTAMP_FIELDS_LEN + OCTO_LAYER1TO4_LEN + actual_octets;
+ }
+ if (record->rec_header.packet_header.caplen > WTAP_MAX_PACKET_SIZE_STANDARD) {
+ /*
+ * Probably a corrupt capture file; return an error,
+ * so that our caller doesn't blow up trying to allocate
+ * space for an immensely-large packet.
+ */
+ *err_info = ws_strdup_printf("vwr: File has %u-byte packet, bigger than maximum of %u",
+ record->rec_header.packet_header.caplen, WTAP_MAX_PACKET_SIZE_STANDARD);
+ *err = WTAP_ERR_BAD_FILE;
+ return FALSE;
+ }
+
+ record->ts.secs = (time_t)s_sec;
+ record->ts.nsecs = (int)(s_usec * 1000);
+
+ record->rec_type = REC_TYPE_PACKET;
+ record->block = wtap_block_create(WTAP_BLOCK_PACKET);
+ record->presence_flags = WTAP_HAS_TS;
+
+ ws_buffer_assure_space(buf, record->rec_header.packet_header.caplen);
+ data_ptr = ws_buffer_start_ptr(buf);
+ }
+
+ /*
+ * Generate and copy out the common metadata headers,
+ * set the port type to port_type (XXX).
+ *
+ * All values are copied out in little-endian byte order.
+ */
+ /*** msdu_length = msdu_length + 16; ***/
+
+ /* 1st octet of record for port_type and other crud */
+ phtole8(&data_ptr[bytes_written], port_type);
+ bytes_written += 1;
+
+ if (IS_TX != 3) {
+ phtole8(&data_ptr[bytes_written], ver_fpga); /* 2nd octet of record for FPGA version*/
+ bytes_written += 1;
+
+ phtoles(&data_ptr[bytes_written], OCTO_TIMESTAMP_FIELDS_LEN); /* it_len */
+ bytes_written += 2;
+
+ /*** Time Collapsible header started***/
+ if (IS_TX == 1 && sig_ts != 0) {
+ phtolel(&data_ptr[bytes_written], latency);
+ } else {
+ phtolel(&data_ptr[bytes_written], 0);
+ }
+ bytes_written += 4;
+ phtolel(&data_ptr[bytes_written], sig_ts); /* 32 LSBs of signature timestamp (nsec) */
+ bytes_written += 4;
+ phtolell(&data_ptr[bytes_written], start_time); /* record start & end times of frame */
+ bytes_written += 8;
+ phtolell(&data_ptr[bytes_written], end_time);
+ bytes_written += 8;
+ phtolel(&data_ptr[bytes_written], d_time);
+ bytes_written += 4;
+ /*** Time Collapsible header ends ***/
+ }
+
+ /*** RF Collapsible header starts***/
+ if (IS_TX == 3 || IS_TX == 4) {
+ phtole8(&data_ptr[bytes_written], rf_id);
+ bytes_written += 1;
+ data_ptr[bytes_written] = 0;
+ bytes_written += 1;
+ data_ptr[bytes_written] = 0;
+ bytes_written += 1;
+ data_ptr[bytes_written] = 0;
+ bytes_written += 1;
+
+ /*** NOISE for all 4 Ports ***/
+ for (i = 0; i < RF_NUMBER_OF_PORTS; i++)
+ {
+ if (pntoh16(&rf_ptr[RF_PORT_1_NOISE_OFF+i*RF_INTER_PORT_GAP_OFF]) == 0) {
+ phtoles(&data_ptr[bytes_written], 0);
+ bytes_written += 2;
+ } else {
+ data_ptr[bytes_written] = rf_ptr[RF_PORT_1_NOISE_OFF+i*RF_INTER_PORT_GAP_OFF];
+ bytes_written += 1;
+ data_ptr[bytes_written] = rf_ptr[RF_PORT_1_NOISE_OFF+1+i*RF_INTER_PORT_GAP_OFF];
+ bytes_written += 1;
+ }
+ }
+
+ /*** SNR for all 4 Ports ***/
+ for (i = 0; i < RF_NUMBER_OF_PORTS; i++)
+ {
+ if (pntoh16(&rf_ptr[RF_PORT_1_SNR_OFF+i*RF_INTER_PORT_GAP_OFF]) == 0) {
+ phtoles(&data_ptr[bytes_written], 0);
+ bytes_written += 2;
+ } else {
+ data_ptr[bytes_written] = rf_ptr[RF_PORT_1_SNR_OFF+i*RF_INTER_PORT_GAP_OFF];
+ bytes_written += 1;
+ data_ptr[bytes_written] = rf_ptr[RF_PORT_1_SNR_OFF+1+i*RF_INTER_PORT_GAP_OFF];
+ bytes_written += 1;
+ }
+ }
+
+ /*** PFE for all 4 Ports ***/
+ for (i = 0; i < RF_NUMBER_OF_PORTS; i++)
+ {
+ if (pntoh16(&rf_ptr[RF_PORT_1_PFE_OFF+i*RF_INTER_PORT_GAP_OFF]) == 0) {
+ phtoles(&data_ptr[bytes_written], 0);
+ bytes_written += 2;
+ } else {
+ data_ptr[bytes_written] = rf_ptr[RF_PORT_1_PFE_OFF+i*RF_INTER_PORT_GAP_OFF];
+ bytes_written += 1;
+ data_ptr[bytes_written] = rf_ptr[RF_PORT_1_PFE_OFF+1+i*RF_INTER_PORT_GAP_OFF];
+ bytes_written += 1;
+ }
+ }
+
+ /*** EVM SIG Data for all 4 Ports ***/
+ for (i = 0; i < RF_NUMBER_OF_PORTS; i++)
+ {
+ if (pntoh16(&rf_ptr[RF_PORT_1_EVM_SD_SIG_OFF+i*RF_INTER_PORT_GAP_OFF]) == 0) {
+ phtoles(&data_ptr[bytes_written], 0);
+ bytes_written += 2;
+ } else {
+ data_ptr[bytes_written] = rf_ptr[RF_PORT_1_EVM_SD_SIG_OFF+i*RF_INTER_PORT_GAP_OFF];
+ bytes_written += 1;
+ data_ptr[bytes_written] = rf_ptr[RF_PORT_1_EVM_SD_SIG_OFF+1+i*RF_INTER_PORT_GAP_OFF];
+ bytes_written += 1;
+ }
+ }
+
+ /*** EVM SIG PILOT for all 4 Ports ***/
+ for (i = 0; i < RF_NUMBER_OF_PORTS; i++)
+ {
+ if (pntoh16(&rf_ptr[RF_PORT_1_EVM_SP_SIG_OFF+i*RF_INTER_PORT_GAP_OFF]) == 0) {
+ phtoles(&data_ptr[bytes_written], 0);
+ bytes_written += 2;
+ } else {
+ data_ptr[bytes_written] = rf_ptr[RF_PORT_1_EVM_SP_SIG_OFF+i*RF_INTER_PORT_GAP_OFF];
+ bytes_written += 1;
+ data_ptr[bytes_written] = rf_ptr[RF_PORT_1_EVM_SP_SIG_OFF+1+i*RF_INTER_PORT_GAP_OFF];
+ bytes_written += 1;
+ }
+ }
+
+ /*** EVM Data Data for all 4 Ports ***/
+ for (i = 0; i < RF_NUMBER_OF_PORTS; i++)
+ {
+ if (pntoh16(&rf_ptr[RF_PORT_1_EVM_SD_DATA_OFF+i*RF_INTER_PORT_GAP_OFF]) == 0) {
+ phtoles(&data_ptr[bytes_written], 0);
+ bytes_written += 2;
+ } else {
+ data_ptr[bytes_written] = rf_ptr[RF_PORT_1_EVM_SD_DATA_OFF+i*RF_INTER_PORT_GAP_OFF];
+ bytes_written += 1;
+ data_ptr[bytes_written] = rf_ptr[RF_PORT_1_EVM_SD_DATA_OFF+1+i*RF_INTER_PORT_GAP_OFF];
+ bytes_written += 1;
+ }
+ }
+
+ /*** EVM Data PILOT for all 4 Ports ***/
+ for (i = 0; i < RF_NUMBER_OF_PORTS; i++)
+ {
+ if (pntoh16(&rf_ptr[RF_PORT_1_EVM_SP_DATA_OFF+i*RF_INTER_PORT_GAP_OFF]) == 0) {
+ phtoles(&data_ptr[bytes_written], 0);
+ bytes_written += 2;
+ } else {
+ data_ptr[bytes_written] = rf_ptr[RF_PORT_1_EVM_SP_DATA_OFF+i*RF_INTER_PORT_GAP_OFF];
+ bytes_written += 1;
+ data_ptr[bytes_written] = rf_ptr[RF_PORT_1_EVM_SP_DATA_OFF+1+i*RF_INTER_PORT_GAP_OFF];
+ bytes_written += 1;
+ }
+ }
+
+ /*** EVM WORST SYMBOL for all 4 Ports ***/
+ for (i = 0; i < RF_NUMBER_OF_PORTS; i++)
+ {
+ if (pntoh16(&rf_ptr[RF_PORT_1_DSYMBOL_IDX_OFF+i*RF_INTER_PORT_GAP_OFF]) == 0) {
+ phtoles(&data_ptr[bytes_written], 0);
+ bytes_written += 2;
+ } else {
+ data_ptr[bytes_written] = rf_ptr[RF_PORT_1_DSYMBOL_IDX_OFF+i*RF_INTER_PORT_GAP_OFF];
+ bytes_written += 1;
+ data_ptr[bytes_written] = rf_ptr[RF_PORT_1_DSYMBOL_IDX_OFF+1+i*RF_INTER_PORT_GAP_OFF];
+ bytes_written += 1;
+ }
+ }
+
+ /*** CONTEXT_P for all 4 Ports ***/
+ for (i = 0; i < RF_NUMBER_OF_PORTS; i++)
+ {
+ if (pntoh16(&rf_ptr[RF_PORT_1_CONTEXT_OFF+i*RF_INTER_PORT_GAP_OFF]) == 0) {
+ phtoles(&data_ptr[bytes_written], 0);
+ bytes_written += 2;
+ } else {
+ data_ptr[bytes_written] = rf_ptr[RF_PORT_1_CONTEXT_OFF+i*RF_INTER_PORT_GAP_OFF];
+ bytes_written += 1;
+ data_ptr[bytes_written] = rf_ptr[RF_PORT_1_CONTEXT_OFF+1+i*RF_INTER_PORT_GAP_OFF];
+ bytes_written += 1;
+ }
+ }
+
+ /*** FOR rest 24 RF data bytes are commented for future use ***/
+/***
+ for (i = 0; i < RF_NUMBER_OF_PORTS; i++)
+ {
+ if (pntoh16(&rf_ptr[20+i*RF_INTER_PORT_GAP_OFF]) == 0) {
+ phtoles(&data_ptr[bytes_written], 0);
+ bytes_written += 2;
+ } else {
+ data_ptr[bytes_written] = rf_ptr[20+i*RF_INTER_PORT_GAP_OFF];
+ bytes_written += 1;
+ data_ptr[bytes_written] = rf_ptr[21+i*RF_INTER_PORT_GAP_OFF];
+ bytes_written += 1;
+ }
+ }
+ for (i = 0; i < RF_NUMBER_OF_PORTS; i++)
+ {
+ if (pntoh16(&rf_ptr[24+i*RF_INTER_PORT_GAP_OFF]) == 0) {
+ phtoles(&data_ptr[bytes_written], 0);
+ bytes_written += 2;
+ } else {
+ data_ptr[bytes_written] = rf_ptr[24+i*RF_INTER_PORT_GAP_OFF];
+ bytes_written += 1;
+ data_ptr[bytes_written] = rf_ptr[25+i*RF_INTER_PORT_GAP_OFF];
+ bytes_written += 1;
+ }
+ }
+ for (i = 0; i < RF_NUMBER_OF_PORTS; i++)
+ {
+ if (pntoh16(&rf_ptr[26+i*RF_INTER_PORT_GAP_OFF]) == 0) {
+ phtoles(&data_ptr[bytes_written], 0);
+ bytes_written += 2;
+ } else {
+ data_ptr[bytes_written] = rf_ptr[26+i*RF_INTER_PORT_GAP_OFF];
+ bytes_written += 1;
+ data_ptr[bytes_written] = rf_ptr[27+i*RF_INTER_PORT_GAP_OFF];
+ bytes_written += 1;
+ }
+ }
+***/
+ }
+ /*** RF Collapsible header ends***/
+
+ if (IS_TX != 3) {
+ /*
+ * Generate and copy out the WLAN metadata headers.
+ *
+ * All values are copied out in little-endian byte order.
+ */
+ phtoles(&data_ptr[bytes_written], OCTO_LAYER1TO4_LEN);
+ bytes_written += 2;
+
+ /*** Layer-1 Collapsible header started***/
+ data_ptr[bytes_written] = l1p_1;
+ bytes_written += 1;
+
+ data_ptr[bytes_written] = (nss << 4) | IS_TX;
+ bytes_written += 1;
+
+ phtoles(&data_ptr[bytes_written], phyRate); /* To dosplay Data rate based on the PLCP type & MCS*/
+ bytes_written += 2;
+
+ data_ptr[bytes_written] = l1p_2;
+ bytes_written += 1;
+
+ data_ptr[bytes_written] = rssi[0];
+ bytes_written += 1;
+ data_ptr[bytes_written] = rssi[1];
+ bytes_written += 1;
+ data_ptr[bytes_written] = rssi[2];
+ bytes_written += 1;
+ data_ptr[bytes_written] = rssi[3];
+ bytes_written += 1;
+
+ /* padding may not be required for S3_W*/
+
+ data_ptr[bytes_written] = s_start_ptr[2]; /*** For Signal Bandwidth Mask ***/
+ bytes_written += 1;
+ data_ptr[bytes_written] = s_start_ptr[3]; /*** For Antenna Port Energy Detect and MU_MASK ***/
+ bytes_written += 1;
+
+ if (plcp_hdr_flag == 1 && (IS_TX == 0 || IS_TX == 4)) {
+ data_ptr[bytes_written] = L1InfoC; /*** For Other plcp type = VHT ***/
+ } else {
+ data_ptr[bytes_written] = 0; /*** For Other plcp type, this offset is set to 0***/
+ }
+ bytes_written += 1;
+
+ phtoles(&data_ptr[bytes_written], msdu_length);
+ bytes_written += 2;
+ /*** Layer-1 Collapsible header Ends ***/
+
+ /*** PLCP Collapsible header Starts ***/
+ memcpy(&data_ptr[bytes_written], &rec[stats_offset+16], 16);
+ bytes_written += 16;
+ /*** PLCP Collapsible header Ends ***/
+
+ /*** Layer 2-4 Collapsible header Starts ***/
+
+ phtolel(&data_ptr[bytes_written], pntoh32(&s_start_ptr[12])); /*** This 4 bytes includes BM,BV,CV,BSSID and ClientID ***/
+ bytes_written += 4;
+ phtoles(&data_ptr[bytes_written], pntoh16(&s_trail_ptr[20])); /*** 2 bytes includes FV,QT,HT,L4V,TID and WLAN type ***/
+ bytes_written += 2;
+ data_ptr[bytes_written] = flow_seq;
+ bytes_written += 1;
+ phtole24(&data_ptr[bytes_written], flow_id);
+ bytes_written += 3;
+ phtoles(&data_ptr[bytes_written], pntoh16(&s_trail_ptr[28])); /*** 2 bytes for Layer 4 ID ***/
+ bytes_written += 2;
+ phtolel(&data_ptr[bytes_written], pntoh32(&s_trail_ptr[24])); /*** 4 bytes for Payload Decode ***/
+ bytes_written += 4;
+
+ /*** In case of RX, Info has 3 bytes of data, whereas for TX, 2 bytes ***/
+ if (IS_TX == 0 || IS_TX == 4) {
+ phtoles(&data_ptr[bytes_written], info);
+ bytes_written += 2;
+ data_ptr[bytes_written] = info_2nd;
+ bytes_written += 1;
+ }
+ else {
+ phtoles(&data_ptr[bytes_written], info);
+ bytes_written += 2;
+ data_ptr[bytes_written] = 0;
+ bytes_written += 1;
+ }
+
+ phtolel(&data_ptr[bytes_written], errors);
+ bytes_written += 4;
+ /*** Layer 2-4 Collapsible header Ends ***/
+
+ /* Finally, copy the whole MAC frame to the packet buffer as-is.
+ * This does not include the stats header or the PLCP.
+ * This also does not include the last 4 bytes, as those don't
+ * contain an FCS, they just contain junk.
+ */
+ memcpy(&data_ptr[bytes_written], &rec[stats_offset+(vwr->MPDU_OFF)], actual_octets);
+ }
+
+ return TRUE;
+}
+
+/* read an Ethernet packet */
+/* Copy the actual packet data from the capture file into the target data block. */
+/* The packet is constructed as a 38-byte VeriWave-extended Radiotap header plus the raw */
+/* MAC octets. */
+static gboolean vwr_read_rec_data_ethernet(vwr_t *vwr, wtap_rec *record,
+ Buffer *buf, const guint8 *rec,
+ int rec_size, int IS_TX, int *err,
+ gchar **err_info)
+{
+ guint8 *data_ptr;
+ int bytes_written = 0; /* bytes output to buf so far */
+ const guint8 *s_ptr, *m_ptr; /* stats and MPDU pointers */
+ guint16 msdu_length, actual_octets; /* octets in frame */
+ guint flow_seq; /* seqnum */
+ guint64 s_time = LL_ZERO, e_time = LL_ZERO; /* start/end */
+ /* times, nsec */
+ guint32 latency = 0;
+ guint64 start_time, s_sec = LL_ZERO, s_usec = LL_ZERO; /* start time, sec + usec */
+ guint64 end_time; /* end time */
+ guint l4id;
+ guint16 info, validityBits; /* INFO/ERRORS fields in stats */
+ guint32 errors;
+ guint16 vc_id; /* VC ID, total (incl of aggregates) */
+ guint32 flow_id, d_time; /* packet duration */
+ int f_flow; /* flags: flow valid */
+ guint32 frame_type; /* frame type field */
+ int mac_len, sig_off, pay_off; /* MAC header len, signature offset */
+ /* XXX - the code here fetched tsid, but never used it! */
+ guint64 sig_ts/*, tsid*/; /* 32 LSBs of timestamp in signature */
+ guint64 delta_b; /* Used for calculating latency */
+ guint16 vw_flags; /* VeriWave-specific packet flags */
+
+ if ((guint)rec_size < vwr->STATS_LEN) {
+ *err_info = ws_strdup_printf("vwr: Invalid record length %d (must be at least %u)", rec_size, vwr->STATS_LEN);
+ *err = WTAP_ERR_BAD_FILE;
+ return FALSE;
+ }
+
+ /* Calculate the start of the statistics block in the buffer. */
+ /* Also get a bunch of fields from the stats block. */
+ m_ptr = &(rec[0]); /* point to the data block */
+ s_ptr = &(rec[rec_size - vwr->STATS_LEN]); /* point to the stats block */
+
+ msdu_length = pntoh16(&s_ptr[vwr->OCTET_OFF]);
+ actual_octets = msdu_length;
+
+ /*
+ * Sanity check the octets field to determine if it's greater than
+ * the packet data available in the record - i.e., the record size
+ * minus the length of the statistics block.
+ *
+ * Report an error if it is.
+ */
+ if (actual_octets > rec_size - vwr->STATS_LEN) {
+ *err_info = ws_strdup_printf("vwr: Invalid data length %u (runs past the end of the record)",
+ actual_octets);
+ *err = WTAP_ERR_BAD_FILE;
+ return FALSE;
+ }
+
+ vc_id = pntoh16(&s_ptr[vwr->VCID_OFF]) & vwr->VCID_MASK;
+ flow_seq = s_ptr[vwr->FLOWSEQ_OFF];
+ frame_type = pntoh32(&s_ptr[vwr->FRAME_TYPE_OFF]);
+
+ if (vwr->FPGA_VERSION == vVW510024_E_FPGA) {
+ validityBits = pntoh16(&s_ptr[vwr->VALID_OFF]);
+ f_flow = validityBits & vwr->FLOW_VALID;
+
+ mac_len = (validityBits & vwr->IS_VLAN) ? 16 : 14; /* MAC hdr length based on VLAN tag */
+
+
+ errors = pntoh16(&s_ptr[vwr->ERRORS_OFF]);
+ }
+ else {
+ f_flow = s_ptr[vwr->VALID_OFF] & vwr->FLOW_VALID;
+ mac_len = (frame_type & vwr->IS_VLAN) ? 16 : 14; /* MAC hdr length based on VLAN tag */
+
+ /* for older fpga errors is only represented by 16 bits) */
+ errors = pntoh16(&s_ptr[vwr->ERRORS_OFF]);
+ }
+
+ info = pntoh16(&s_ptr[vwr->INFO_OFF]);
+ /* 24 LSBs */
+ flow_id = pntoh24(&s_ptr[vwr->FLOWID_OFF]);
+
+#if 0
+ /* For tx latency is duration, for rx latency is timestamp. */
+ /* Get 64-bit latency value. */
+ tsid = pcorey48tohll(&s_ptr[vwr->LATVAL_OFF]);
+#endif
+
+ l4id = pntoh16(&s_ptr[vwr->L4ID_OFF]);
+
+ /*
+ * The MSDU length includes the FCS.
+ *
+ * The packet data does *not* include the FCS - it's just 4 bytes
+ * of junk - so we have to remove it.
+ *
+ * We'll be stripping off that junk, so make sure we have at least
+ * 4 octets worth of packet data.
+ *
+ * There seems to be a special case of a length of 0.
+ */
+ if (actual_octets < 4) {
+ if (actual_octets != 0) {
+ *err_info = ws_strdup_printf("vwr: Invalid data length %u (too short to include 4 bytes of FCS)",
+ actual_octets);
+ *err = WTAP_ERR_BAD_FILE;
+ return FALSE;
+ }
+ } else {
+ actual_octets -= 4;
+ }
+
+ /* Calculate start & end times (in sec/usec), converting 64-bit times to usec. */
+ /* 64-bit times are "Corey-endian" */
+ s_time = pcoreytohll(&s_ptr[vwr->STARTT_OFF]);
+ e_time = pcoreytohll(&s_ptr[vwr->ENDT_OFF]);
+
+ /* find the packet duration (difference between start and end times) */
+ d_time = (guint32)((e_time - s_time)); /* find diff, leaving in nsec for Ethernet */
+
+ /* also convert the packet start time to seconds and microseconds */
+ start_time = s_time / NS_IN_US; /* convert to microseconds first */
+ s_sec = (start_time / US_IN_SEC); /* get the number of seconds */
+ s_usec = start_time - (s_sec * US_IN_SEC); /* get the number of microseconds */
+
+ /* also convert the packet end time to seconds and microseconds */
+ end_time = e_time / NS_IN_US; /* convert to microseconds first */
+
+ if (frame_type & vwr->IS_TCP) /* signature offset for TCP frame */
+ {
+ pay_off = mac_len + 40;
+ }
+ else if (frame_type & vwr->IS_UDP) /* signature offset for UDP frame */
+ {
+ pay_off = mac_len + 28;
+ }
+ else if (frame_type & vwr->IS_ICMP) /* signature offset for ICMP frame */
+ {
+ pay_off = mac_len + 24;
+ }
+ else if (frame_type & vwr->IS_IGMP) /* signature offset for IGMPv2 frame */
+ {
+ pay_off = mac_len + 28;
+ }
+ else /* signature offset for raw IP frame */
+ {
+ pay_off = mac_len + 20;
+ }
+
+ sig_off = find_signature(m_ptr, rec_size, pay_off, flow_id, flow_seq);
+ if ((m_ptr[sig_off] == 0xdd) && (f_flow != 0))
+ sig_ts = get_signature_ts(m_ptr, sig_off, msdu_length);
+ else
+ sig_ts = 0;
+
+ /* Set latency based on rx/tx and signature timestamp */
+ if (!IS_TX) {
+ if (sig_ts < s_time) {
+ latency = (guint32)(s_time - sig_ts);
+ } else {
+ /* Account for the rollover case. Since we cannot use 0x100000000 - l_time + s_time */
+ /* we look for a large difference between l_time and s_time. */
+ delta_b = sig_ts - s_time;
+ if (delta_b > 0x10000000) {
+ latency = 0;
+ } else
+ latency = (guint32)delta_b;
+ }
+ }
+
+ /*
+ * Fill up the per-packet header.
+ *
+ * We include the length of the metadata headers in the packet lengths.
+ *
+ * The maximum value of actual_octets is 65535, which, even after
+ * adding the lengths of the metadata headers, is less than
+ * WTAP_MAX_PACKET_SIZE_STANDARD will ever be, so we don't need to check it.
+ */
+ record->rec_header.packet_header.len = STATS_COMMON_FIELDS_LEN + EXT_ETHERNET_FIELDS_LEN + actual_octets;
+ record->rec_header.packet_header.caplen = STATS_COMMON_FIELDS_LEN + EXT_ETHERNET_FIELDS_LEN + actual_octets;
+
+ record->ts.secs = (time_t)s_sec;
+ record->ts.nsecs = (int)(s_usec * 1000);
+
+ record->rec_type = REC_TYPE_PACKET;
+ record->block = wtap_block_create(WTAP_BLOCK_PACKET);
+ record->presence_flags = WTAP_HAS_TS;
+
+ /*etap_hdr.vw_ip_length = (guint16)ip_len;*/
+
+ ws_buffer_assure_space(buf, record->rec_header.packet_header.caplen);
+ data_ptr = ws_buffer_start_ptr(buf);
+
+ /*
+ * Generate and copy out the common metadata headers,
+ * set the port type to 1 (Ethernet).
+ *
+ * All values are copied out in little-endian byte order.
+ */
+ /* 1st octet of record for port_type and command (command is 0, hence RX) */
+ phtole8(&data_ptr[bytes_written], ETHERNET_PORT);
+ bytes_written += 1;
+ /* 2nd octet of record for fpga version (Ethernet, hence non-OCTO) */
+ phtole8(&data_ptr[bytes_written], 0);
+ bytes_written += 1;
+
+ phtoles(&data_ptr[bytes_written], STATS_COMMON_FIELDS_LEN);
+ bytes_written += 2;
+ phtoles(&data_ptr[bytes_written], msdu_length);
+ bytes_written += 2;
+ phtolel(&data_ptr[bytes_written], flow_id);
+ bytes_written += 4;
+ phtoles(&data_ptr[bytes_written], vc_id);
+ bytes_written += 2;
+ phtoles(&data_ptr[bytes_written], flow_seq);
+ bytes_written += 2;
+ if (!IS_TX && (sig_ts != 0)) {
+ phtolel(&data_ptr[bytes_written], latency);
+ } else {
+ phtolel(&data_ptr[bytes_written], 0);
+ }
+ bytes_written += 4;
+ phtolel(&data_ptr[bytes_written], sig_ts);
+ bytes_written += 4;
+ phtolell(&data_ptr[bytes_written], start_time) /* record start & end times of frame */
+ bytes_written += 8;
+ phtolell(&data_ptr[bytes_written], end_time);
+ bytes_written += 8;
+ phtolel(&data_ptr[bytes_written], d_time);
+ bytes_written += 4;
+
+ /*
+ * Generate and copy out the Ethernet metadata headers.
+ *
+ * All values are copied out in little-endian byte order.
+ */
+ phtoles(&data_ptr[bytes_written], EXT_ETHERNET_FIELDS_LEN);
+ bytes_written += 2;
+ vw_flags = 0;
+ if (IS_TX)
+ vw_flags |= VW_FLAGS_TXF;
+ if (errors & vwr->FCS_ERROR)
+ vw_flags |= VW_FLAGS_FCSERR;
+ phtoles(&data_ptr[bytes_written], vw_flags);
+ bytes_written += 2;
+ phtoles(&data_ptr[bytes_written], info);
+ bytes_written += 2;
+ phtolel(&data_ptr[bytes_written], errors);
+ bytes_written += 4;
+ phtolel(&data_ptr[bytes_written], l4id);
+ bytes_written += 4;
+
+ /* Add in pad */
+ phtolel(&data_ptr[bytes_written], 0);
+ bytes_written += 4;
+
+ /*
+ * Finally, copy the whole MAC frame to the packet buffer as-is.
+ * This also does not include the last 4 bytes, as those don't
+ * contain an FCS, they just contain junk.
+ */
+ memcpy(&data_ptr[bytes_written], m_ptr, actual_octets);
+
+ return TRUE;
+}
+
+/*--------------------------------------------------------------------------------------*/
+/* utility to split up and decode a 16-byte message record */
+
+static int decode_msg(vwr_t *vwr, guint8 *rec, int *v_type, int *IS_TX, int *log_mode)
+{
+ guint8 cmd,fpga_log_mode; /* components of message */
+ guint32 wd2, wd3;
+ int v_size; /* size of var-len message */
+
+ /* break up the message record into its pieces */
+ cmd = rec[0];
+ fpga_log_mode = rec[1];
+ fpga_log_mode = ((fpga_log_mode & 0x30) >> 4);
+
+ wd2 = pntoh32(&rec[8]);
+ wd3 = pntoh32(&rec[12]);
+
+ if (vwr != NULL)
+ *log_mode = fpga_log_mode; /* Log mode = 3, when MPDU data is reduced */
+
+ /* now decode based on the command byte */
+ switch (cmd) {
+ case COMMAND_RX:
+ if (vwr != NULL) {
+ *IS_TX = 0;
+ }
+ v_size = (int)(wd2 & 0xffff);
+ *v_type = VT_FRAME;
+ break;
+
+ case COMMAND_TX:
+ if (vwr != NULL) {
+ *IS_TX = 1;
+ }
+ v_size = (int)(wd2 & 0xffff);
+ *v_type = VT_FRAME;
+ break;
+
+/*
+ case COMMAND_RFN:
+ if (vwr != NULL) {
+ *IS_TX = 3;
+ }
+ v_size = (int)(wd2 & 0xffff);
+ *v_type = VT_FRAME;
+ break;
+*/
+
+ case COMMAND_RF: /* For RF Modified only */
+ if (vwr != NULL) {
+ *IS_TX = 3;
+ }
+ v_size = (int)(wd2 & 0xffff);
+ *v_type = VT_FRAME;
+ break;
+
+ case COMMAND_RFRX: /* For RF_RX Modified only */
+ if (vwr != NULL) {
+ *IS_TX = 4;
+ }
+ v_size = (int)(wd2 & 0xffff);
+ *v_type = VT_FRAME;
+ break;
+
+ case 0xc1:
+ case 0x8b:
+ case 0xbb:
+ if (vwr != NULL) {
+ *IS_TX = 2;
+ }
+ v_size = (int)(wd2 & 0xffff);
+ *v_type = VT_CPMSG;
+ break;
+
+ case 0xfe:
+ if (vwr != NULL) {
+ *IS_TX = 2;
+ }
+ v_size = (int)(wd3 & 0xffff);
+ *v_type = VT_CPMSG;
+ break;
+
+ default:
+ if (vwr != NULL) {
+ *IS_TX = 2;
+ }
+ v_size = 0;
+ *v_type = VT_UNKNOWN;
+ break;
+ }
+
+ return v_size;
+}
+
+
+/*---------------------------------------------------------------------------------------*/
+/* Utilities to extract and decode the PHY bit rate from 802.11 PLCP headers (OFDM/CCK). */
+/* They are passed a pointer to 4 or 6 consecutive bytes of PLCP header. */
+/* The integer returned by the get_xxx_rate() functions is in units of 0.5 Mb/s. */
+/* The string returned by the decode_xxx_rate() functions is 3 characters wide. */
+
+static guint8 get_ofdm_rate(const guint8 *plcp)
+{
+ /* extract the RATE field (LS nibble of first byte) then convert it to the MCS index used by the L1p fields */
+ switch (plcp[0] & 0x0f) {
+ case 0x0b: return 4;
+ case 0x0f: return 5;
+ case 0x0a: return 6;
+ case 0x0e: return 7;
+ case 0x09: return 8;
+ case 0x0d: return 9;
+ case 0x08: return 10;
+ case 0x0c: return 11;
+ default: return 0;
+ }
+}
+
+static guint8 get_cck_rate(const guint8 *plcp)
+{
+ /* extract rate from the SIGNAL field then convert it to the MCS index used by the L1p fields */
+ switch (plcp[0]) {
+ case 0x0a: return 0;
+ case 0x14: return 1;
+ case 0x37: return 2;
+ case 0x6e: return 3;
+ default: return 0;
+ }
+}
+
+/*--------------------------------------------------------------------------------------*/
+/* utility to set up offsets and bitmasks for decoding the stats blocks */
+
+static void setup_defaults(vwr_t *vwr, guint16 fpga)
+{
+ switch (fpga) {
+ /* WLAN frames */
+ case S2_W_FPGA:
+ vwr->STATS_LEN = vVW510021_W_STATS_TRAILER_LEN;
+
+ vwr->VALID_OFF = vVW510021_W_VALID_OFF;
+ vwr->MTYPE_OFF = vVW510021_W_MTYPE_OFF;
+ vwr->VCID_OFF = vVW510021_W_VCID_OFF;
+ vwr->FLOWSEQ_OFF = vVW510021_W_FLOWSEQ_OFF;
+ vwr->FLOWID_OFF = vVW510021_W_FLOWID_OFF;
+
+ /*vwr->OCTET_OFF = v22_W_OCTET_OFF;*/
+
+ vwr->ERRORS_OFF = vVW510021_W_ERRORS_OFF;
+ vwr->PATN_OFF = vVW510021_W_MATCH_OFF;
+ vwr->RSSI_OFF = vVW510021_W_RSSI_TXPOWER_OFF;
+ vwr->STARTT_OFF = vVW510021_W_STARTT_OFF;
+ vwr->ENDT_OFF = vVW510021_W_ENDT_OFF;
+ vwr->LATVAL_OFF = vVW510021_W_LATVAL_OFF;
+ vwr->INFO_OFF = vVW510021_W_INFO_OFF;
+ vwr->FPGA_VERSION_OFF = S2_W_FPGA_VERSION_OFF;
+ vwr->HEADER_VERSION_OFF = vVW510021_W_HEADER_VERSION_OFF;
+ vwr->OCTET_OFF = vVW510021_W_MSDU_LENGTH_OFF;
+ vwr->L1P_1_OFF = vVW510021_W_L1P_1_OFF;
+ vwr->L1P_2_OFF = vVW510021_W_L1P_2_OFF;
+ vwr->L4ID_OFF = vVW510021_W_L4ID_OFF;
+ vwr->IPLEN_OFF = vVW510021_W_IPLEN_OFF;
+ vwr->PLCP_LENGTH_OFF = vVW510021_W_PLCP_LENGTH_OFF;
+
+ vwr->MT_MASK = vVW510021_W_SEL_MASK;
+ vwr->MCS_INDEX_MASK = vVW510021_W_MCS_MASK;
+ vwr->VCID_MASK = 0xffff;
+ vwr->FLOW_VALID = vVW510021_W_FLOW_VALID;
+ vwr->STATS_START_OFF = vVW510021_W_HEADER_LEN;
+ vwr->FCS_ERROR = vVW510021_W_FCS_ERROR;
+ vwr->CRYPTO_ERR = v22_W_CRYPTO_ERR;
+ vwr->RETRY_ERR = v22_W_RETRY_ERR;
+
+ /*vwr->STATS_START_OFF = 0;*/
+
+ vwr->RXTX_OFF = vVW510021_W_RXTX_OFF;
+
+ vwr->MT_10_HALF = 0;
+ vwr->MT_10_FULL = 0;
+ vwr->MT_100_HALF = 0;
+ vwr->MT_100_FULL = 0;
+ vwr->MT_1G_HALF = 0;
+ vwr->MT_1G_FULL = 0;
+ vwr->MT_CCKL = v22_W_MT_CCKL;
+ vwr->MT_CCKS = v22_W_MT_CCKS;
+ /*vwr->MT_OFDM = vVW510021_W_MT_OFDM;*/
+
+ vwr->WEPTYPE = vVW510021_W_WEPTYPE;
+ vwr->TKIPTYPE = vVW510021_W_TKIPTYPE;
+ vwr->CCMPTYPE = vVW510021_W_CCMPTYPE;
+
+ vwr->FRAME_TYPE_OFF = vVW510021_W_FRAME_TYPE_OFF;
+ vwr->IS_TCP = vVW510021_W_IS_TCP;
+ vwr->IS_UDP = vVW510021_W_IS_UDP;
+ vwr->IS_ICMP = vVW510021_W_IS_ICMP;
+ vwr->IS_IGMP = vVW510021_W_IS_IGMP;
+ vwr->IS_QOS = vVW510021_W_QOS_VALID;
+
+ /*
+ * vVW510021_W_STATS_HEADER_LEN = 8 is:
+ *
+ * 2 bytes of l1p_1/l1p_2;
+ * 1 byte of RSSI;
+ * 2 bytes of MSDU length + other bits
+ * 1 byte of XXX;
+ * 2 bytes of VCID.
+ *
+ * The 12 is for 11 bytes of PLCP and 1 byte of pad
+ * before the data.
+ */
+ vwr->MPDU_OFF = vVW510021_W_STATS_HEADER_LEN + 12;
+
+ break;
+
+ case S3_W_FPGA:
+ vwr->STATS_LEN = vVW510021_W_STATS_TRAILER_LEN;
+ vwr->PLCP_LENGTH_OFF = 16;
+
+ /*
+ * The 16 + 16 is:
+ *
+ * 2 bytes of l1p_1/l1p_2;
+ * 1 byte of signal bandwidth mask;
+ * 1 byte of antenna port energy;
+ * 4 bytes of per-antenna RSSI;
+ * 1 byte of L1InfoC;
+ * 3 bytes of MSDU length;
+ * 4 bytes of something;
+ * 16 bytes of PLCP.
+ */
+ vwr->MPDU_OFF = 16 + 16;
+
+ break;
+
+ case vVW510012_E_FPGA:
+ vwr->STATS_LEN = v22_E_STATS_LEN;
+
+ vwr->VALID_OFF = v22_E_VALID_OFF;
+ vwr->MTYPE_OFF = v22_E_MTYPE_OFF;
+ vwr->VCID_OFF = v22_E_VCID_OFF;
+ vwr->FLOWSEQ_OFF = v22_E_FLOWSEQ_OFF;
+ vwr->FLOWID_OFF = v22_E_FLOWID_OFF;
+ vwr->OCTET_OFF = v22_E_OCTET_OFF;
+ vwr->ERRORS_OFF = v22_E_ERRORS_OFF;
+ vwr->PATN_OFF = v22_E_PATN_OFF;
+ vwr->RSSI_OFF = v22_E_RSSI_OFF;
+ vwr->STARTT_OFF = v22_E_STARTT_OFF;
+ vwr->ENDT_OFF = v22_E_ENDT_OFF;
+ vwr->LATVAL_OFF = v22_E_LATVAL_OFF;
+ vwr->INFO_OFF = v22_E_INFO_OFF;
+ vwr->L4ID_OFF = v22_E_L4ID_OFF;
+
+ vwr->IS_RX = v22_E_IS_RX;
+ vwr->MT_MASK = v22_E_MT_MASK;
+ vwr->VCID_MASK = v22_E_VCID_MASK;
+ vwr->FLOW_VALID = v22_E_FLOW_VALID;
+ vwr->FCS_ERROR = v22_E_FCS_ERROR;
+
+ vwr->RX_DECRYPTS = v22_E_RX_DECRYPTS;
+ vwr->TX_DECRYPTS = v22_E_TX_DECRYPTS;
+ vwr->FC_PROT_BIT = v22_E_FC_PROT_BIT;
+
+ vwr->MT_10_HALF = v22_E_MT_10_HALF;
+ vwr->MT_10_FULL = v22_E_MT_10_FULL;
+ vwr->MT_100_HALF = v22_E_MT_100_HALF;
+ vwr->MT_100_FULL = v22_E_MT_100_FULL;
+ vwr->MT_1G_HALF = v22_E_MT_1G_HALF;
+ vwr->MT_1G_FULL = v22_E_MT_1G_FULL;
+ vwr->MT_CCKL = 0;
+ vwr->MT_CCKS = 0;
+ vwr->MT_OFDM = 0;
+
+ vwr->FRAME_TYPE_OFF = v22_E_FRAME_TYPE_OFF;
+ vwr->IS_TCP = v22_E_IS_TCP;
+ vwr->IS_UDP = v22_E_IS_UDP;
+ vwr->IS_ICMP = v22_E_IS_ICMP;
+ vwr->IS_IGMP = v22_E_IS_IGMP;
+ vwr->IS_QOS = v22_E_IS_QOS;
+ vwr->IS_VLAN = v22_E_IS_VLAN;
+
+ break;
+
+ /* WLAN frames */
+ case S1_W_FPGA:
+ vwr->STATS_LEN = v22_W_STATS_LEN;
+
+ vwr->MTYPE_OFF = v22_W_MTYPE_OFF;
+ vwr->VALID_OFF = v22_W_VALID_OFF;
+ vwr->VCID_OFF = v22_W_VCID_OFF;
+ vwr->FLOWSEQ_OFF = v22_W_FLOWSEQ_OFF;
+ vwr->FLOWID_OFF = v22_W_FLOWID_OFF;
+ vwr->OCTET_OFF = v22_W_OCTET_OFF;
+ vwr->ERRORS_OFF = v22_W_ERRORS_OFF;
+ vwr->PATN_OFF = v22_W_PATN_OFF;
+ vwr->RSSI_OFF = v22_W_RSSI_OFF;
+ vwr->STARTT_OFF = v22_W_STARTT_OFF;
+ vwr->ENDT_OFF = v22_W_ENDT_OFF;
+ vwr->LATVAL_OFF = v22_W_LATVAL_OFF;
+ vwr->INFO_OFF = v22_W_INFO_OFF;
+ vwr->L4ID_OFF = v22_W_L4ID_OFF;
+ vwr->IPLEN_OFF = v22_W_IPLEN_OFF;
+ vwr->PLCP_LENGTH_OFF = v22_W_PLCP_LENGTH_OFF;
+
+ vwr->FCS_ERROR = v22_W_FCS_ERROR;
+ vwr->CRYPTO_ERR = v22_W_CRYPTO_ERR;
+ vwr->PAYCHK_ERR = v22_W_PAYCHK_ERR;
+ vwr->RETRY_ERR = v22_W_RETRY_ERR;
+ vwr->IS_RX = v22_W_IS_RX;
+ vwr->MT_MASK = v22_W_MT_MASK;
+ vwr->VCID_MASK = v22_W_VCID_MASK;
+ vwr->FLOW_VALID = v22_W_FLOW_VALID;
+
+ vwr->RX_DECRYPTS = v22_W_RX_DECRYPTS;
+ vwr->TX_DECRYPTS = v22_W_TX_DECRYPTS;
+ vwr->FC_PROT_BIT = v22_W_FC_PROT_BIT;
+
+ vwr->MT_10_HALF = 0;
+ vwr->MT_10_FULL = 0;
+ vwr->MT_100_HALF = 0;
+ vwr->MT_100_FULL = 0;
+ vwr->MT_1G_HALF = 0;
+ vwr->MT_1G_FULL = 0;
+ vwr->MT_CCKL = v22_W_MT_CCKL;
+ vwr->MT_CCKS = v22_W_MT_CCKS;
+ vwr->MT_OFDM = v22_W_MT_OFDM;
+
+ vwr->WEPTYPE = v22_W_WEPTYPE;
+ vwr->TKIPTYPE = v22_W_TKIPTYPE;
+ vwr->CCMPTYPE = v22_W_CCMPTYPE;
+
+ vwr->FRAME_TYPE_OFF = v22_W_FRAME_TYPE_OFF;
+ vwr->IS_TCP = v22_W_IS_TCP;
+ vwr->IS_UDP = v22_W_IS_UDP;
+ vwr->IS_ICMP = v22_W_IS_ICMP;
+ vwr->IS_IGMP = v22_W_IS_IGMP;
+ vwr->IS_QOS = v22_W_IS_QOS;
+
+ break;
+
+ /* Ethernet frames */
+ case vVW510024_E_FPGA:
+ vwr->STATS_LEN = vVW510024_E_STATS_LEN;
+
+ vwr->VALID_OFF = vVW510024_E_VALID_OFF;
+ vwr->VCID_OFF = vVW510024_E_VCID_OFF;
+ vwr->FLOWSEQ_OFF = vVW510024_E_FLOWSEQ_OFF;
+ vwr->FLOWID_OFF = vVW510024_E_FLOWID_OFF;
+ vwr->OCTET_OFF = vVW510024_E_MSDU_LENGTH_OFF;
+ vwr->ERRORS_OFF = vVW510024_E_ERRORS_OFF;
+ vwr->PATN_OFF = vVW510024_E_MATCH_OFF;
+ vwr->STARTT_OFF = vVW510024_E_STARTT_OFF;
+ vwr->ENDT_OFF = vVW510024_E_ENDT_OFF;
+ vwr->LATVAL_OFF = vVW510024_E_LATVAL_OFF;
+ vwr->INFO_OFF = vVW510024_E_INFO_OFF;
+ vwr->L4ID_OFF = vVW510024_E_L4ID_OFF;
+ vwr->IPLEN_OFF = vVW510024_E_IPLEN_OFF;
+
+ vwr->FPGA_VERSION_OFF = vVW510024_E_FPGA_VERSION_OFF;
+ vwr->HEADER_VERSION_OFF = vVW510024_E_HEADER_VERSION_OFF;
+
+ vwr->VCID_MASK = vVW510024_E_VCID_MASK;
+ vwr->FLOW_VALID = vVW510024_E_FLOW_VALID;
+ vwr->FCS_ERROR = v22_E_FCS_ERROR;
+
+ vwr->FRAME_TYPE_OFF = vVW510024_E_FRAME_TYPE_OFF;
+ vwr->IS_TCP = vVW510024_E_IS_TCP;
+ vwr->IS_UDP = vVW510024_E_IS_UDP;
+ vwr->IS_ICMP = vVW510024_E_IS_ICMP;
+ vwr->IS_IGMP = vVW510024_E_IS_IGMP;
+ vwr->IS_QOS = vVW510024_E_QOS_VALID;
+ vwr->IS_VLAN = vVW510024_E_IS_VLAN;
+
+ break;
+ }
+}
+#define SIG_SCAN_RANGE 64 /* range of signature scanning region */
+
+/* Utility routine: check that signature is at specified location; scan for it if not. */
+/* If we can't find a signature at all, then simply return the originally supplied offset. */
+int find_signature(const guint8 *m_ptr, int rec_size, int pay_off, guint32 flow_id, guint8 flow_seq)
+{
+ int tgt; /* temps */
+ guint32 fid;
+
+ /* initial check is very simple: look for a '0xdd' at the target location */
+ if (m_ptr[pay_off] == 0xdd) /* if magic byte is present */
+ return pay_off; /* got right offset, return it */
+
+ /* Hmmm, signature magic byte is not where it is supposed to be; scan from start of */
+ /* payload until maximum scan range exhausted to see if we can find it. */
+ /* The scanning process consists of looking for a '0xdd', then checking for the correct */
+ /* flow ID and sequence number at the appropriate offsets. */
+ for (tgt = pay_off; tgt < (rec_size); tgt++) {
+ if (m_ptr[tgt] == 0xdd) { /* found magic byte? check fields */
+ if ((tgt + 15 < rec_size) && (m_ptr[tgt + 15] == 0xe2)) {
+ if (m_ptr[tgt + 4] != flow_seq)
+ continue;
+
+ fid = pletoh24(&m_ptr[tgt + 1]);
+
+ if (fid != flow_id)
+ continue;
+
+ return (tgt);
+ }
+ else if (tgt + SIG_FSQ_OFF < rec_size)
+ { /* out which one... */
+ if (m_ptr[tgt + SIG_FSQ_OFF] != flow_seq) /* check sequence number */
+ continue; /* if failed, keep scanning */
+
+ fid = pletoh24(&m_ptr[tgt + SIG_FID_OFF]); /* assemble flow ID from signature */
+ if (fid != flow_id) /* check flow ID against expected */
+ continue; /* if failed, keep scanning */
+
+ /* matched magic byte, sequence number, flow ID; found the signature */
+ return (tgt); /* return offset of signature */
+ }
+ }
+ }
+
+ /* failed to find the signature, return the original offset as default */
+ return pay_off;
+}
+
+/* utility routine: harvest the signature time stamp from the data frame */
+guint64 get_signature_ts(const guint8 *m_ptr,int sig_off, int sig_max)
+{
+ int ts_offset;
+ guint64 sig_ts;
+
+ if (sig_off + 15 >= sig_max)
+ return 0;
+
+ if (m_ptr[sig_off + 15] == 0xe2)
+ ts_offset = 5;
+ else
+ ts_offset = 8;
+
+ sig_ts = pletoh32(&m_ptr[sig_off + ts_offset]);
+
+ return (sig_ts & 0xffffffff);
+}
+
+static float
+get_legacy_rate(guint8 rate_index)
+{
+ /* Rate conversion data */
+ static const float canonical_rate_legacy[] = {1.0f, 2.0f, 5.5f, 11.0f, 6.0f, 9.0f, 12.0f, 18.0f, 24.0f, 36.0f, 48.0f, 54.0f};
+
+ float bitrate = 0.0f;
+
+ if (rate_index < G_N_ELEMENTS(canonical_rate_legacy))
+ bitrate = canonical_rate_legacy[rate_index];
+
+ return bitrate;
+}
+
+static float
+get_ht_rate(guint8 mcs_index, guint16 rflags)
+{
+ /* Rate conversion data */
+ static const int canonical_ndbps_20_ht[8] = {26, 52, 78, 104, 156, 208, 234, 260};
+ static const int canonical_ndbps_40_ht[8] = {54, 108, 162, 216, 324, 432, 486, 540};
+
+ float symbol_tx_time, bitrate;
+ int ndbps;
+
+ if (rflags & FLAGS_CHAN_SHORTGI)
+ symbol_tx_time = 3.6f;
+ else
+ symbol_tx_time = 4.0f;
+
+ if (rflags & FLAGS_CHAN_40MHZ)
+ ndbps = canonical_ndbps_40_ht[mcs_index - 8*(int)(mcs_index/8)];
+ else
+ ndbps = canonical_ndbps_20_ht[mcs_index - 8*(int)(mcs_index/8)];
+
+ bitrate = (ndbps * (((int)(mcs_index >> 3) + 1))) / symbol_tx_time;
+
+ return bitrate;
+}
+
+static float
+get_vht_rate(guint8 mcs_index, guint16 rflags, guint8 nss)
+{
+ /* Rate conversion data */
+ static const int canonical_ndbps_20_vht[9] = {26, 52, 78, 104, 156, 208, 234, 260, 312};
+ static const int canonical_ndbps_40_vht[10] = {54, 108, 162, 216, 324, 432, 486, 540, 648, 720};
+ static const int canonical_ndbps_80_vht[10] = {117, 234, 351, 468, 702, 936, 1053, 1170, 1404, 1560};
+
+ float symbol_tx_time, bitrate;
+
+ if (rflags & FLAGS_CHAN_SHORTGI)
+ symbol_tx_time = 3.6f;
+ else
+ symbol_tx_time = 4.0f;
+
+ /*
+ * Check for the out of range mcs_index.
+ * Should never happen, but if mcs index is greater than 9 just
+ * return 0.
+ */
+ if (mcs_index > 9)
+ return 0.0f;
+ if (rflags & FLAGS_CHAN_40MHZ)
+ bitrate = (canonical_ndbps_40_vht[mcs_index] * nss) / symbol_tx_time;
+ else if (rflags & FLAGS_CHAN_80MHZ)
+ bitrate = (canonical_ndbps_80_vht[mcs_index] * nss) / symbol_tx_time;
+ else
+ {
+ if (mcs_index == 9)
+ {
+ /* This is a special case for 20 MHz. */
+ if (nss == 3)
+ bitrate = 1040 / symbol_tx_time;
+ else if (nss == 6)
+ bitrate = 2080 / symbol_tx_time;
+ else
+ bitrate = 0.0f;
+ }
+ else
+ bitrate = (canonical_ndbps_20_vht[mcs_index] * nss) / symbol_tx_time;
+ }
+
+ return bitrate;
+}
+
+static gboolean
+vwr_process_rec_data(FILE_T fh, int rec_size,
+ wtap_rec *record, Buffer *buf, vwr_t *vwr,
+ int IS_TX, int log_mode, int *err, gchar **err_info)
+{
+ guint8* rec; /* local buffer (holds input record) */
+ gboolean ret = FALSE;
+
+ rec = (guint8*)g_malloc(B_SIZE);
+
+ /* Read over the entire record (frame + trailer) into a local buffer. */
+ /* If we don't get it all, then declare an error, we can't process the frame. */
+ if (!wtap_read_bytes(fh, rec, rec_size, err, err_info))
+ {
+ g_free(rec);
+ return FALSE;
+ }
+
+ /* now format up the frame data */
+ switch (vwr->FPGA_VERSION)
+ {
+ case S1_W_FPGA:
+ ret = vwr_read_s1_W_rec(vwr, record, buf, rec, rec_size, err, err_info);
+ break;
+ case S2_W_FPGA:
+ ret = vwr_read_s2_W_rec(vwr, record, buf, rec, rec_size, IS_TX, err, err_info);
+ break;
+ case S3_W_FPGA:
+ ret = vwr_read_s3_W_rec(vwr, record, buf, rec, rec_size, IS_TX, log_mode, err, err_info);
+ break;
+ case vVW510012_E_FPGA:
+ case vVW510024_E_FPGA:
+ ret = vwr_read_rec_data_ethernet(vwr, record, buf, rec, rec_size, IS_TX, err, err_info);
+ break;
+ default:
+ g_free(rec);
+ ws_assert_not_reached();
+ return ret;
+ }
+
+ g_free(rec);
+ return ret;
+}
+
+static const struct supported_block_type vwr_80211_blocks_supported[] = {
+ /*
+ * We support packet blocks, with no comments or other options.
+ */
+ { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
+};
+
+static const struct file_type_subtype_info vwr_80211_info = {
+ "Ixia IxVeriWave .vwr Raw 802.11 Capture", "vwr80211", "vwr", NULL,
+ FALSE, BLOCKS_SUPPORTED(vwr_80211_blocks_supported),
+ NULL, NULL, NULL
+};
+
+static const struct supported_block_type vwr_eth_blocks_supported[] = {
+ /*
+ * We support packet blocks, with no comments or other options.
+ */
+ { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
+};
+
+static const struct file_type_subtype_info vwr_eth_info = {
+ "Ixia IxVeriWave .vwr Raw Ethernet Capture", "vwreth", "vwr", NULL,
+ FALSE, BLOCKS_SUPPORTED(vwr_eth_blocks_supported),
+ NULL, NULL, NULL
+};
+
+void register_vwr(void)
+{
+ vwr_80211_file_type_subtype = wtap_register_file_type_subtype(&vwr_80211_info);
+ vwr_eth_file_type_subtype = wtap_register_file_type_subtype(&vwr_eth_info);
+
+ /*
+ * Register names for backwards compatibility with the
+ * wtap_filetypes table in Lua.
+ */
+ wtap_register_backwards_compatibility_lua_name("VWR_80211",
+ vwr_80211_file_type_subtype);
+ wtap_register_backwards_compatibility_lua_name("VWR_ETH",
+ vwr_eth_file_type_subtype);
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * vi: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */
diff --git a/wiretap/vwr.h b/wiretap/vwr.h
new file mode 100644
index 00000000..d2074168
--- /dev/null
+++ b/wiretap/vwr.h
@@ -0,0 +1,17 @@
+/** @file
+ *
+ * Wiretap Library
+ * Copyright (c) 1998-2010 by Tom Alexander <talexander@ixiacom.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ */
+
+#ifndef __VWR_H__
+#define __VWR_H__
+
+#include "ws_symbol_export.h"
+
+wtap_open_return_val vwr_open(wtap *wth, int *err, gchar **err_info);
+
+#endif
diff --git a/wiretap/wtap-int.h b/wiretap/wtap-int.h
new file mode 100644
index 00000000..e09819f2
--- /dev/null
+++ b/wiretap/wtap-int.h
@@ -0,0 +1,467 @@
+/** @file
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __WTAP_INT_H__
+#define __WTAP_INT_H__
+
+#include "wtap.h"
+#include <time.h>
+
+#ifdef _WIN32
+#include <winsock2.h>
+#endif
+
+#include <wsutil/file_util.h>
+
+#include "wtap_opttypes.h"
+
+void wtap_init_file_type_subtypes(void);
+
+WS_DLL_PUBLIC
+int wtap_fstat(wtap *wth, ws_statb64 *statb, int *err);
+
+typedef gboolean (*subtype_read_func)(struct wtap*, wtap_rec *,
+ Buffer *, int *, char **, gint64 *);
+typedef gboolean (*subtype_seek_read_func)(struct wtap*, gint64, wtap_rec *,
+ Buffer *, int *, char **);
+
+/**
+ * Struct holding data of the currently read file.
+ */
+struct wtap {
+ FILE_T fh;
+ FILE_T random_fh; /**< Secondary FILE_T for random access */
+ gboolean ispipe; /**< TRUE if the file is a pipe */
+ int file_type_subtype;
+ guint snapshot_length;
+ GArray *shb_hdrs;
+ GArray *interface_data; /**< An array holding the interface data from pcapng IDB:s or equivalent(?)*/
+ guint next_interface_data; /**< Next interface data that wtap_get_next_interface_description() will show */
+ GArray *nrbs; /**< holds the Name Res Blocks, or NULL */
+ GArray *dsbs; /**< An array of DSBs (of type wtap_block_t), or NULL if not supported. */
+ GArray *sysdig_meta_events; /**< An array of Sysdig meta eventss (of type wtap_block_t), or NULL if not supported. */
+
+ char *pathname; /**< File pathname; might just be "-" */
+
+ void *priv; /* this one holds per-file state and is free'd automatically by wtap_close() */
+ void *wslua_data; /* this one holds wslua state info and is not free'd */
+
+ subtype_read_func subtype_read;
+ subtype_seek_read_func subtype_seek_read;
+ void (*subtype_sequential_close)(struct wtap*);
+ void (*subtype_close)(struct wtap*);
+ int file_encap; /* per-file, for those
+ * file formats that have
+ * per-file encapsulation
+ * types rather than per-packet
+ * encapsulation types
+ */
+ int file_tsprec; /* per-file timestamp precision
+ * of the fractional part of
+ * the time stamp, for those
+ * file formats that have
+ * per-file timestamp
+ * precision rather than
+ * per-packet timestamp
+ * precision
+ * e.g. WTAP_TSPREC_USEC
+ */
+ wtap_new_ipv4_callback_t add_new_ipv4;
+ wtap_new_ipv6_callback_t add_new_ipv6;
+ wtap_new_secrets_callback_t add_new_secrets;
+ wtap_new_sysdig_meta_event_callback_t add_new_sysdig_meta_event;
+ GPtrArray *fast_seek;
+};
+
+struct wtap_dumper;
+
+/*
+ * This could either be a FILE * or a gzFile.
+ */
+typedef void *WFILE_T;
+
+typedef gboolean (*subtype_add_idb_func)(struct wtap_dumper*, wtap_block_t,
+ int *, gchar **);
+
+typedef gboolean (*subtype_write_func)(struct wtap_dumper*,
+ const wtap_rec *rec,
+ const guint8*, int*, gchar**);
+typedef gboolean (*subtype_finish_func)(struct wtap_dumper*, int*, gchar**);
+
+struct wtap_dumper {
+ WFILE_T fh;
+ int file_type_subtype;
+ int snaplen;
+ int file_encap; /* per-file, for those
+ * file formats that have
+ * per-file encapsulation
+ * types rather than per-packet
+ * encapsulation types
+ */
+ wtap_compression_type compression_type;
+ gboolean needs_reload; /* TRUE if the file requires re-loading after saving with wtap */
+ gint64 bytes_dumped;
+
+ void *priv; /* this one holds per-file state and is free'd automatically by wtap_dump_close() */
+ void *wslua_data; /* this one holds wslua state info and is not free'd */
+
+ subtype_add_idb_func subtype_add_idb; /* add an IDB, writing it as necessary */
+ subtype_write_func subtype_write; /* write out a record */
+ subtype_finish_func subtype_finish; /* write out information to finish writing file */
+
+ addrinfo_lists_t *addrinfo_lists; /**< Struct containing lists of resolved addresses */
+ GArray *shb_hdrs;
+ GArray *interface_data; /**< An array holding the interface data from pcapng IDB:s or equivalent(?) NULL if not present.*/
+ GArray *dsbs_initial; /**< An array of initial DSBs (of type wtap_block_t) */
+
+ /*
+ * Additional blocks that might grow as data is being collected.
+ * Subtypes should write these blocks before writing new packet blocks.
+ */
+ const GArray *nrbs_growing; /**< A reference to an array of NRBs (of type wtap_block_t) */
+ const GArray *dsbs_growing; /**< A reference to an array of DSBs (of type wtap_block_t) */
+ const GArray *sysdig_mev_growing; /**< A reference to an array of Sysdig meta events (of type wtap_block_t) */
+ guint nrbs_growing_written; /**< Number of already processed NRBs in nrbs_growing. */
+ guint dsbs_growing_written; /**< Number of already processed DSBs in dsbs_growing. */
+ guint sysdig_mev_growing_written; /**< Number of already processed meta events in sysdig_mev_growing. */
+};
+
+WS_DLL_PUBLIC gboolean wtap_dump_file_write(wtap_dumper *wdh, const void *buf,
+ size_t bufsize, int *err);
+WS_DLL_PUBLIC gint64 wtap_dump_file_seek(wtap_dumper *wdh, gint64 offset, int whence, int *err);
+WS_DLL_PUBLIC gint64 wtap_dump_file_tell(wtap_dumper *wdh, int *err);
+
+extern gint wtap_num_file_types;
+
+#include <wsutil/pint.h>
+
+/* Macros to byte-swap possibly-unaligned 64-bit, 32-bit and 16-bit quantities;
+ * they take a pointer to the quantity, and byte-swap it in place.
+ */
+#define PBSWAP64(p) \
+ { \
+ guint8 tmp; \
+ tmp = (p)[7]; \
+ (p)[7] = (p)[0]; \
+ (p)[0] = tmp; \
+ tmp = (p)[6]; \
+ (p)[6] = (p)[1]; \
+ (p)[1] = tmp; \
+ tmp = (p)[5]; \
+ (p)[5] = (p)[2]; \
+ (p)[2] = tmp; \
+ tmp = (p)[4]; \
+ (p)[4] = (p)[3]; \
+ (p)[3] = tmp; \
+ }
+#define PBSWAP32(p) \
+ { \
+ guint8 tmp; \
+ tmp = (p)[3]; \
+ (p)[3] = (p)[0]; \
+ (p)[0] = tmp; \
+ tmp = (p)[2]; \
+ (p)[2] = (p)[1]; \
+ (p)[1] = tmp; \
+ }
+#define PBSWAP16(p) \
+ { \
+ guint8 tmp; \
+ tmp = (p)[1]; \
+ (p)[1] = (p)[0]; \
+ (p)[0] = tmp; \
+ }
+
+
+/* Pointer routines to put items out in a particular byte order.
+ * These will work regardless of the byte alignment of the pointer.
+ */
+
+#ifndef phtons
+#define phtons(p, v) \
+ { \
+ (p)[0] = (guint8)((v) >> 8); \
+ (p)[1] = (guint8)((v) >> 0); \
+ }
+#endif
+
+#ifndef phton24
+#define phton24(p, v) \
+ { \
+ (p)[0] = (guint8)((v) >> 16); \
+ (p)[1] = (guint8)((v) >> 8); \
+ (p)[2] = (guint8)((v) >> 0); \
+ }
+#endif
+
+#ifndef phtonl
+#define phtonl(p, v) \
+ { \
+ (p)[0] = (guint8)((v) >> 24); \
+ (p)[1] = (guint8)((v) >> 16); \
+ (p)[2] = (guint8)((v) >> 8); \
+ (p)[3] = (guint8)((v) >> 0); \
+ }
+#endif
+
+#ifndef phtonll
+#define phtonll(p, v) \
+ { \
+ (p)[0] = (guint8)((v) >> 56); \
+ (p)[1] = (guint8)((v) >> 48); \
+ (p)[2] = (guint8)((v) >> 40); \
+ (p)[3] = (guint8)((v) >> 32); \
+ (p)[4] = (guint8)((v) >> 24); \
+ (p)[5] = (guint8)((v) >> 16); \
+ (p)[6] = (guint8)((v) >> 8); \
+ (p)[7] = (guint8)((v) >> 0); \
+ }
+#endif
+
+#ifndef phtole8
+#define phtole8(p, v) \
+ { \
+ (p)[0] = (guint8)((v) >> 0); \
+ }
+#endif
+
+#ifndef phtoles
+#define phtoles(p, v) \
+ { \
+ (p)[0] = (guint8)((v) >> 0); \
+ (p)[1] = (guint8)((v) >> 8); \
+ }
+#endif
+
+#ifndef phtole24
+#define phtole24(p, v) \
+ { \
+ (p)[0] = (guint8)((v) >> 0); \
+ (p)[1] = (guint8)((v) >> 8); \
+ (p)[2] = (guint8)((v) >> 16); \
+ }
+#endif
+
+#ifndef phtolel
+#define phtolel(p, v) \
+ { \
+ (p)[0] = (guint8)((v) >> 0); \
+ (p)[1] = (guint8)((v) >> 8); \
+ (p)[2] = (guint8)((v) >> 16); \
+ (p)[3] = (guint8)((v) >> 24); \
+ }
+#endif
+
+#ifndef phtolell
+#define phtolell(p, v) \
+ { \
+ (p)[0] = (guint8)((v) >> 0); \
+ (p)[1] = (guint8)((v) >> 8); \
+ (p)[2] = (guint8)((v) >> 16); \
+ (p)[3] = (guint8)((v) >> 24); \
+ (p)[4] = (guint8)((v) >> 32); \
+ (p)[5] = (guint8)((v) >> 40); \
+ (p)[6] = (guint8)((v) >> 48); \
+ (p)[7] = (guint8)((v) >> 56); \
+ }
+#endif
+
+/* glib doesn't have g_ptr_array_len of all things!*/
+#ifndef g_ptr_array_len
+#define g_ptr_array_len(a) ((a)->len)
+#endif
+
+/*
+ * Read a given number of bytes from a file into a buffer or, if
+ * buf is NULL, just discard them.
+ *
+ * If we succeed, return TRUE.
+ *
+ * If we get an EOF, return FALSE with *err set to 0, reporting this
+ * as an EOF.
+ *
+ * If we get fewer bytes than the specified number, return FALSE with
+ * *err set to WTAP_ERR_SHORT_READ, reporting this as a short read
+ * error.
+ *
+ * If we get a read error, return FALSE with *err and *err_info set
+ * appropriately.
+ */
+WS_DLL_PUBLIC
+gboolean
+wtap_read_bytes_or_eof(FILE_T fh, void *buf, unsigned int count, int *err,
+ gchar **err_info);
+
+/*
+ * Read a given number of bytes from a file into a buffer or, if
+ * buf is NULL, just discard them.
+ *
+ * If we succeed, return TRUE.
+ *
+ * If we get fewer bytes than the specified number, including getting
+ * an EOF, return FALSE with *err set to WTAP_ERR_SHORT_READ, reporting
+ * this as a short read error.
+ *
+ * If we get a read error, return FALSE with *err and *err_info set
+ * appropriately.
+ */
+WS_DLL_PUBLIC
+gboolean
+wtap_read_bytes(FILE_T fh, void *buf, unsigned int count, int *err,
+ gchar **err_info);
+
+/*
+ * Read packet data into a Buffer, growing the buffer as necessary.
+ *
+ * This returns an error on a short read, even if the short read hit
+ * the EOF immediately. (The assumption is that each packet has a
+ * header followed by raw packet data, and that we've already read the
+ * header, so if we get an EOF trying to read the packet data, the file
+ * has been cut short, even if the read didn't read any data at all.)
+ */
+WS_DLL_PUBLIC
+gboolean
+wtap_read_packet_bytes(FILE_T fh, Buffer *buf, guint length, int *err,
+ gchar **err_info);
+
+/*
+ * Implementation of wth->subtype_read that reads the full file contents
+ * as a single packet.
+ */
+gboolean
+wtap_full_file_read(wtap *wth, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info, gint64 *data_offset);
+
+/*
+ * Implementation of wth->subtype_seek_read that reads the full file contents
+ * as a single packet.
+ */
+gboolean
+wtap_full_file_seek_read(wtap *wth, gint64 seek_off, wtap_rec *rec, Buffer *buf, int *err, gchar **err_info);
+
+/**
+ * Add an IDB to the interface data for a file.
+ */
+void
+wtap_add_idb(wtap *wth, wtap_block_t idb);
+
+/**
+ * Invokes the callback with the given name resolution block.
+ */
+void
+wtapng_process_nrb(wtap *wth, wtap_block_t nrb);
+
+/**
+ * Invokes the callback with the given decryption secrets block.
+ */
+void
+wtapng_process_dsb(wtap *wth, wtap_block_t dsb);
+
+/**
+ * Invokes the callback with the given Sysdig meta event block.
+ */
+void
+wtapng_process_sysdig_mev(wtap *wth, wtap_block_t mev);
+
+void
+wtap_register_compatibility_file_subtype_name(const char *old_name,
+ const char *new_name);
+
+void
+wtap_register_backwards_compatibility_lua_name(const char *name, int ft);
+
+struct backwards_compatibiliity_lua_name {
+ const char *name;
+ int ft;
+};
+
+WS_DLL_PUBLIC
+const GArray *get_backwards_compatibility_lua_table(void);
+
+/**
+ * @brief Gets new section header block for new file, based on existing info.
+ * @details Creates a new wtap_block_t section header block and only
+ * copies appropriate members of the SHB for a new file. In
+ * particular, the comment string is copied, and any custom options
+ * which should be copied are copied. The os, hardware, and
+ * application strings are *not* copied.
+ *
+ * @note Use wtap_free_shb() to free the returned section header.
+ *
+ * @param wth The wiretap session.
+ * @return The new section header, which must be wtap_free_shb'd.
+ */
+GArray* wtap_file_get_shb_for_new_file(wtap *wth);
+
+/**
+ * @brief Generate an IDB, given a wiretap handle for the file,
+ * using the file's encapsulation type, snapshot length,
+ * and time stamp resolution, and add it to the interface
+ * data for a file.
+ * @note This requires that the encapsulation type and time stamp
+ * resolution not be per-packet; it will terminate the process
+ * if either of them are.
+ *
+ * @param wth The wiretap handle for the file.
+ */
+WS_DLL_PUBLIC
+void wtap_add_generated_idb(wtap *wth);
+
+/**
+ * @brief Generate an IDB, given a set of dump parameters, using the
+ * parameters' encapsulation type, snapshot length, and time stamp
+ * resolution. For use when a dump file has a given encapsulation type,
+ * and the source is not passing IDBs.
+ * @note This requires that the encapsulation type and time stamp
+ * resolution not be per-packet; it will terminate the process
+ * if either of them are.
+ *
+ * @param params The wtap dump parameters.
+ */
+
+wtap_block_t wtap_dump_params_generate_idb(const wtap_dump_params *params);
+
+/**
+ * @brief Generate an IDB, given a packet record, using the records's
+ * encapsulation type and time stamp resolution, and the default
+ * snap length for the encapsulation type. For use when a file has
+ * per-packet encapsulation, and the source is not passing along IDBs.
+ * @note This requires that the record type be REC_TYPE_PACKET, and the
+ * encapsulation type and time stamp resolution not be per-packet;
+ * it will terminate the process if any of them are.
+ *
+ * @param rec The packet record.
+ */
+wtap_block_t wtap_rec_generate_idb(const wtap_rec *rec);
+
+/**
+ * @brief Gets new name resolution info for new file, based on existing info.
+ * @details Creates a new wtap_block_t of name resolution info and only
+ * copies appropriate members for a new file.
+ *
+ * @note Use wtap_free_nrb() to free the returned pointer.
+ *
+ * @param wth The wiretap session.
+ * @return The new name resolution info, which must be freed.
+ */
+GArray* wtap_file_get_nrb_for_new_file(wtap *wth);
+
+#endif /* __WTAP_INT_H__ */
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * vi: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */
diff --git a/wiretap/wtap.c b/wiretap/wtap.c
new file mode 100644
index 00000000..18432ef2
--- /dev/null
+++ b/wiretap/wtap.c
@@ -0,0 +1,2142 @@
+/* wtap.c
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include <config.h>
+
+#define WS_LOG_DOMAIN LOG_DOMAIN_WIRETAP
+
+#include <string.h>
+
+#include <sys/types.h>
+
+#include "wtap-int.h"
+#include "wtap_opttypes.h"
+
+#include "file_wrappers.h"
+#include <wsutil/file_util.h>
+#include <wsutil/buffer.h>
+#include <wsutil/ws_assert.h>
+#include <wsutil/wslog.h>
+#include <wsutil/exported_pdu_tlvs.h>
+#ifdef HAVE_PLUGINS
+#include <wsutil/plugins.h>
+#endif
+
+#ifdef HAVE_PLUGINS
+static plugins_t *libwiretap_plugins = NULL;
+#endif
+
+#define PADDING4(x) ((((x + 3) >> 2) << 2) - x)
+
+static GSList *wtap_plugins = NULL;
+
+#ifdef HAVE_PLUGINS
+void
+wtap_register_plugin(const wtap_plugin *plug)
+{
+ wtap_plugins = g_slist_prepend(wtap_plugins, (wtap_plugin *)plug);
+}
+#else /* HAVE_PLUGINS */
+void
+wtap_register_plugin(const wtap_plugin *plug _U_)
+{
+ ws_warning("wtap_register_plugin: built without support for binary plugins");
+}
+#endif /* HAVE_PLUGINS */
+
+int
+wtap_plugins_supported(void)
+{
+#ifdef HAVE_PLUGINS
+ return plugins_supported() ? 0 : 1;
+#else
+ return -1;
+#endif
+}
+
+static void
+call_plugin_register_wtap_module(gpointer data, gpointer user_data _U_)
+{
+ wtap_plugin *plug = (wtap_plugin *)data;
+
+ if (plug->register_wtap_module) {
+ plug->register_wtap_module();
+ }
+}
+
+/*
+ * Return the size of the file, as reported by the OS.
+ * (gint64, in case that's 64 bits.)
+ */
+gint64
+wtap_file_size(wtap *wth, int *err)
+{
+ ws_statb64 statb;
+
+ if (file_fstat((wth->fh == NULL) ? wth->random_fh : wth->fh,
+ &statb, err) == -1)
+ return -1;
+ return statb.st_size;
+}
+
+/*
+ * Do an fstat on the file.
+ */
+int
+wtap_fstat(wtap *wth, ws_statb64 *statb, int *err)
+{
+ if (file_fstat((wth->fh == NULL) ? wth->random_fh : wth->fh,
+ statb, err) == -1)
+ return -1;
+ return 0;
+}
+
+int
+wtap_file_type_subtype(wtap *wth)
+{
+ return wth->file_type_subtype;
+}
+
+guint
+wtap_snapshot_length(wtap *wth)
+{
+ return wth->snapshot_length;
+}
+
+int
+wtap_file_encap(wtap *wth)
+{
+ return wth->file_encap;
+}
+
+int
+wtap_file_tsprec(wtap *wth)
+{
+ return wth->file_tsprec;
+}
+
+guint
+wtap_file_get_num_shbs(wtap *wth)
+{
+ return wth->shb_hdrs->len;
+}
+
+wtap_block_t
+wtap_file_get_shb(wtap *wth, guint shb_num)
+{
+ if ((wth == NULL) || (wth->shb_hdrs == NULL) || (shb_num >= wth->shb_hdrs->len))
+ return NULL;
+
+ return g_array_index(wth->shb_hdrs, wtap_block_t, shb_num);
+}
+
+GArray*
+wtap_file_get_shb_for_new_file(wtap *wth)
+{
+ guint shb_count;
+ wtap_block_t shb_hdr_src, shb_hdr_dest;
+ GArray* shb_hdrs;
+
+ if ((wth == NULL) || (wth->shb_hdrs == NULL) || (wth->shb_hdrs->len == 0))
+ return NULL;
+
+ shb_hdrs = g_array_new(FALSE, FALSE, sizeof(wtap_block_t));
+
+ for (shb_count = 0; shb_count < wth->shb_hdrs->len; shb_count++) {
+ shb_hdr_src = g_array_index(wth->shb_hdrs, wtap_block_t, shb_count);
+ shb_hdr_dest = wtap_block_make_copy(shb_hdr_src);
+ g_array_append_val(shb_hdrs, shb_hdr_dest);
+ }
+
+ return shb_hdrs;
+}
+
+/*
+ * XXX - replace with APIs that let us handle multiple comments.
+ */
+void
+wtap_write_shb_comment(wtap *wth, gchar *comment)
+{
+ if ((wth != NULL) && (wth->shb_hdrs != NULL) && (wth->shb_hdrs->len > 0)) {
+ wtap_block_set_nth_string_option_value(g_array_index(wth->shb_hdrs, wtap_block_t, 0), OPT_COMMENT, 0, comment, (gsize)(comment ? strlen(comment) : 0));
+ }
+}
+
+wtapng_iface_descriptions_t *
+wtap_file_get_idb_info(wtap *wth)
+{
+ wtapng_iface_descriptions_t *idb_info;
+
+ idb_info = g_new(wtapng_iface_descriptions_t,1);
+
+ idb_info->interface_data = wth->interface_data;
+
+ return idb_info;
+}
+
+wtap_block_t
+wtap_get_next_interface_description(wtap *wth)
+{
+ if (wth->next_interface_data < wth->interface_data->len) {
+ /*
+ * We have an IDB to return. Advance to the next
+ * IDB, and return this one.
+ */
+ wtap_block_t idb;
+
+ idb = g_array_index(wth->interface_data, wtap_block_t,
+ wth->next_interface_data);
+ wth->next_interface_data++;
+ return idb;
+ }
+
+ /*
+ * We've returned all the interface descriptions we currently
+ * have. (There may be more in the future, if we read more.)
+ */
+ return NULL;
+}
+
+void
+wtap_file_add_decryption_secrets(wtap *wth, const wtap_block_t dsb)
+{
+ if (!wth->dsbs) {
+ wth->dsbs = g_array_new(FALSE, FALSE, sizeof(wtap_block_t));
+ }
+ g_array_append_val(wth->dsbs, dsb);
+}
+
+gboolean
+wtap_file_discard_decryption_secrets(wtap *wth)
+{
+ if (!wth->dsbs || wth->dsbs->len == 0)
+ return FALSE;
+
+ wtap_block_array_free(wth->dsbs);
+ wth->dsbs = NULL;
+ return TRUE;
+}
+
+void
+wtap_file_add_sysdig_meta_event(wtap *wth, const wtap_block_t mev)
+{
+ if (!wth->sysdig_meta_events) {
+ wth->sysdig_meta_events = g_array_new(FALSE, FALSE, sizeof(wtap_block_t));
+ }
+ g_array_append_val(wth->sysdig_meta_events, mev);
+}
+
+gboolean
+wtap_file_discard_sysdig_meta_events(wtap *wth)
+{
+ if (!wth->sysdig_meta_events || wth->sysdig_meta_events->len == 0)
+ return false;
+
+ wtap_block_array_free(wth->sysdig_meta_events);
+ wth->sysdig_meta_events = NULL;
+ return true;
+}
+
+void
+wtap_add_idb(wtap *wth, wtap_block_t idb)
+{
+ g_array_append_val(wth->interface_data, idb);
+}
+
+static wtap_block_t
+wtap_generate_idb(int encap, int tsprec, int snaplen)
+{
+ wtap_block_t idb;
+ wtapng_if_descr_mandatory_t *if_descr_mand;
+
+ ws_assert(encap != WTAP_ENCAP_UNKNOWN &&
+ encap != WTAP_ENCAP_PER_PACKET &&
+ encap != WTAP_ENCAP_NONE);
+
+ idb = wtap_block_create(WTAP_BLOCK_IF_ID_AND_INFO);
+
+ if_descr_mand = (wtapng_if_descr_mandatory_t*)wtap_block_get_mandatory_data(idb);
+ if_descr_mand->wtap_encap = encap;
+ if_descr_mand->tsprecision = tsprec;
+ if (tsprec < 0 || tsprec > WS_TSPREC_MAX) {
+ /*
+ * Either WTAP_TSPREC_PER_PACKET, WTAP_TSPREC_UNKNOWN,
+ * or not a valid WTAP_TSPREC_ value.
+ *
+ * Unknown timestamp precision; use the default of
+ * microsecond resolution.
+ */
+ tsprec = 6; /* microsecond resolution */
+ }
+
+ /*
+ * Compute 10^{params->tsprec}.
+ */
+ if_descr_mand->time_units_per_second = 1;
+ for (int i = 0; i < tsprec; i++)
+ if_descr_mand->time_units_per_second *= 10;
+
+ if (tsprec != WTAP_TSPREC_USEC) {
+ /*
+ * Microsecond precision is the default, so we only
+ * add an option if the precision isn't microsecond
+ * precision.
+ */
+ wtap_block_add_uint8_option(idb, OPT_IDB_TSRESOL, tsprec);
+ }
+ if (snaplen == 0) {
+ /*
+ * No snapshot length was specified. Pick an
+ * appropriate snapshot length for this
+ * link-layer type.
+ *
+ * We use WTAP_MAX_PACKET_SIZE_STANDARD for everything except
+ * D-Bus, which has a maximum packet size of 128MB,
+ * and EBHSCR, which has a maximum packet size of 8MB,
+ * which is more than we want to put into files
+ * with other link-layer header types, as that
+ * might cause some software reading those files
+ * to allocate an unnecessarily huge chunk of
+ * memory for a packet buffer.
+ */
+ if (encap == WTAP_ENCAP_DBUS)
+ snaplen = 128*1024*1024;
+ else if (encap == WTAP_ENCAP_EBHSCR)
+ snaplen = 8*1024*1024;
+ else
+ snaplen = WTAP_MAX_PACKET_SIZE_STANDARD;
+ }
+ if_descr_mand->snap_len = snaplen;
+ if_descr_mand->num_stat_entries = 0; /* Number of ISBs */
+ if_descr_mand->interface_statistics = NULL;
+
+ return idb;
+}
+
+void
+wtap_add_generated_idb(wtap *wth)
+{
+ wtap_block_t idb;
+
+ idb = wtap_generate_idb(wth->file_encap, wth->file_tsprec, wth->snapshot_length);
+ /*
+ * Add this IDB.
+ */
+ wtap_add_idb(wth, idb);
+}
+
+void
+wtap_free_idb_info(wtapng_iface_descriptions_t *idb_info)
+{
+ if (idb_info == NULL)
+ return;
+
+ wtap_block_array_free(idb_info->interface_data);
+ g_free(idb_info);
+}
+
+gchar *
+wtap_get_debug_if_descr(const wtap_block_t if_descr,
+ const int indent,
+ const char* line_end)
+{
+ char* tmp_content;
+ wtapng_if_descr_mandatory_t* if_descr_mand;
+ GString *info = g_string_new("");
+ gint64 itmp64;
+ guint64 tmp64;
+ guint8 tmp8;
+ if_filter_opt_t if_filter;
+
+ ws_assert(if_descr);
+
+ if_descr_mand = (wtapng_if_descr_mandatory_t*)wtap_block_get_mandatory_data(if_descr);
+ if (wtap_block_get_string_option_value(if_descr, OPT_IDB_NAME, &tmp_content) == WTAP_OPTTYPE_SUCCESS) {
+ g_string_printf(info,
+ "%*cName = %s%s", indent, ' ',
+ tmp_content ? tmp_content : "UNKNOWN",
+ line_end);
+ }
+
+ if (wtap_block_get_string_option_value(if_descr, OPT_IDB_DESCRIPTION, &tmp_content) == WTAP_OPTTYPE_SUCCESS) {
+ g_string_append_printf(info,
+ "%*cDescription = %s%s", indent, ' ',
+ tmp_content ? tmp_content : "NONE",
+ line_end);
+ }
+
+ g_string_append_printf(info,
+ "%*cEncapsulation = %s (%d - %s)%s", indent, ' ',
+ wtap_encap_description(if_descr_mand->wtap_encap),
+ if_descr_mand->wtap_encap,
+ wtap_encap_name(if_descr_mand->wtap_encap),
+ line_end);
+
+ if (wtap_block_get_string_option_value(if_descr, OPT_IDB_HARDWARE, &tmp_content) == WTAP_OPTTYPE_SUCCESS) {
+ g_string_append_printf(info,
+ "%*cHardware = %s%s", indent, ' ',
+ tmp_content ? tmp_content : "NONE",
+ line_end);
+ }
+
+ if (wtap_block_get_uint64_option_value(if_descr, OPT_IDB_SPEED, &tmp64) == WTAP_OPTTYPE_SUCCESS) {
+ g_string_append_printf(info,
+ "%*cSpeed = %" PRIu64 "%s", indent, ' ',
+ tmp64,
+ line_end);
+ }
+
+ g_string_append_printf(info,
+ "%*cCapture length = %u%s", indent, ' ',
+ if_descr_mand->snap_len,
+ line_end);
+
+ if (wtap_block_get_uint8_option_value(if_descr, OPT_IDB_FCSLEN, &tmp8) == WTAP_OPTTYPE_SUCCESS) {
+ g_string_append_printf(info,
+ "%*cFCS length = %u%s", indent, ' ',
+ tmp8,
+ line_end);
+ }
+
+ g_string_append_printf(info,
+ "%*cTime precision = %s (%d)%s", indent, ' ',
+ wtap_tsprec_string(if_descr_mand->tsprecision),
+ if_descr_mand->tsprecision,
+ line_end);
+
+ g_string_append_printf(info,
+ "%*cTime ticks per second = %" PRIu64 "%s", indent, ' ',
+ if_descr_mand->time_units_per_second,
+ line_end);
+
+ if (wtap_block_get_uint8_option_value(if_descr, OPT_IDB_TSRESOL, &tmp8) == WTAP_OPTTYPE_SUCCESS) {
+ g_string_append_printf(info,
+ "%*cTime resolution = 0x%.2x%s", indent, ' ',
+ tmp8,
+ line_end);
+ }
+
+ if (wtap_block_get_int64_option_value(if_descr, OPT_IDB_TSOFFSET, &itmp64) == WTAP_OPTTYPE_SUCCESS) {
+ g_string_append_printf(info,
+ "%*cTimestamp offset = %" G_GINT64_FORMAT "%s", indent, ' ',
+ itmp64,
+ line_end);
+ }
+
+ if (wtap_block_get_if_filter_option_value(if_descr, OPT_IDB_FILTER, &if_filter) == WTAP_OPTTYPE_SUCCESS) {
+ switch (if_filter.type) {
+
+ case if_filter_pcap:
+ g_string_append_printf(info,
+ "%*cFilter string = %s%s", indent, ' ',
+ if_filter.data.filter_str,
+ line_end);
+ break;
+
+ case if_filter_bpf:
+ g_string_append_printf(info,
+ "%*cBPF filter length = %u%s", indent, ' ',
+ if_filter.data.bpf_prog.bpf_prog_len,
+ line_end);
+ break;
+
+ default:
+ g_string_append_printf(info,
+ "%*cUnknown filter type %u%s", indent, ' ',
+ if_filter.type,
+ line_end);
+ break;
+ }
+ }
+
+ if (wtap_block_get_string_option_value(if_descr, OPT_IDB_OS, &tmp_content) == WTAP_OPTTYPE_SUCCESS) {
+ g_string_append_printf(info,
+ "%*cOperating system = %s%s", indent, ' ',
+ tmp_content ? tmp_content : "UNKNOWN",
+ line_end);
+ }
+
+ /*
+ * XXX - support multiple comments.
+ */
+ if (wtap_block_get_nth_string_option_value(if_descr, OPT_COMMENT, 0, &tmp_content) == WTAP_OPTTYPE_SUCCESS) {
+ g_string_append_printf(info,
+ "%*cComment = %s%s", indent, ' ',
+ tmp_content ? tmp_content : "NONE",
+ line_end);
+ }
+
+ g_string_append_printf(info,
+ "%*cNumber of stat entries = %u%s", indent, ' ',
+ if_descr_mand->num_stat_entries,
+ line_end);
+
+ return g_string_free(info, FALSE);
+}
+
+wtap_block_t
+wtap_file_get_nrb(wtap *wth)
+{
+ if ((wth == NULL) || (wth->nrbs == NULL) || (wth->nrbs->len == 0))
+ return NULL;
+
+ return g_array_index(wth->nrbs, wtap_block_t, 0);
+}
+
+GArray*
+wtap_file_get_nrb_for_new_file(wtap *wth)
+{
+ guint nrb_count;
+ wtap_block_t nrb_src, nrb_dest;
+ GArray* nrbs;
+
+ if ((wth == NULL || wth->nrbs == NULL) || (wth->nrbs->len == 0))
+ return NULL;
+
+ nrbs = g_array_new(FALSE, FALSE, sizeof(wtap_block_t));
+
+ for (nrb_count = 0; nrb_count < wth->nrbs->len; nrb_count++) {
+ nrb_src = g_array_index(wth->nrbs, wtap_block_t, nrb_count);
+ nrb_dest = wtap_block_make_copy(nrb_src);
+ g_array_append_val(nrbs, nrb_dest);
+ }
+
+ return nrbs;
+}
+
+void
+wtap_dump_params_init(wtap_dump_params *params, wtap *wth)
+{
+ memset(params, 0, sizeof(*params));
+ if (wth == NULL)
+ return;
+
+ params->encap = wtap_file_encap(wth);
+ params->snaplen = wtap_snapshot_length(wth);
+ params->tsprec = wtap_file_tsprec(wth);
+ params->shb_hdrs = wtap_file_get_shb_for_new_file(wth);
+ params->idb_inf = wtap_file_get_idb_info(wth);
+ /* Assume that the input handle remains open until the dumper is closed.
+ * Refer to the DSBs from the input file, wtap_dump will then copy DSBs
+ * as they become available. */
+ params->nrbs_growing = wth->nrbs;
+ params->dsbs_growing = wth->dsbs;
+ params->sysdig_mev_growing = wth->sysdig_meta_events;
+ params->dont_copy_idbs = FALSE;
+}
+
+/*
+ * XXX - eventually, we should make this wtap_dump_params_init(),
+ * and have everything copy IDBs as they're read.
+ */
+void
+wtap_dump_params_init_no_idbs(wtap_dump_params *params, wtap *wth)
+{
+ memset(params, 0, sizeof(*params));
+ if (wth == NULL)
+ return;
+
+ params->encap = wtap_file_encap(wth);
+ params->snaplen = wtap_snapshot_length(wth);
+ params->tsprec = wtap_file_tsprec(wth);
+ params->shb_hdrs = wtap_file_get_shb_for_new_file(wth);
+ params->idb_inf = wtap_file_get_idb_info(wth);
+ /* Assume that the input handle remains open until the dumper is closed.
+ * Refer to the DSBs from the input file, wtap_dump will then copy DSBs
+ * as they become available. */
+ params->nrbs_growing = wth->nrbs;
+ params->dsbs_growing = wth->dsbs;
+ params->dont_copy_idbs = TRUE;
+}
+
+void
+wtap_dump_params_discard_name_resolution(wtap_dump_params *params)
+{
+ params->nrbs_growing = NULL;
+}
+
+void
+wtap_dump_params_discard_decryption_secrets(wtap_dump_params *params)
+{
+ params->dsbs_initial = NULL;
+ params->dsbs_growing = NULL;
+}
+
+void
+wtap_dump_params_discard_sysdig_meta_events(wtap_dump_params *params)
+{
+ params->sysdig_mev_growing = NULL;
+}
+
+void
+wtap_dump_params_cleanup(wtap_dump_params *params)
+{
+ wtap_block_array_free(params->shb_hdrs);
+ /* params->idb_inf is currently expected to be freed by the caller. */
+
+ memset(params, 0, sizeof(*params));
+}
+
+wtap_block_t
+wtap_dump_params_generate_idb(const wtap_dump_params *params)
+{
+ return wtap_generate_idb(params->encap, params->tsprec, params->snaplen);
+}
+
+/* Table of the encapsulation types we know about. */
+struct encap_type_info {
+ const char *name;
+ const char *description;
+};
+
+static struct encap_type_info encap_table_base[] = {
+ /* WTAP_ENCAP_UNKNOWN */
+ { "unknown", "Unknown" },
+
+ /* WTAP_ENCAP_ETHERNET */
+ { "ether", "Ethernet" },
+
+ /* WTAP_ENCAP_TOKEN_RING */
+ { "tr", "Token Ring" },
+
+ /* WTAP_ENCAP_SLIP */
+ { "slip", "SLIP" },
+
+ /* WTAP_ENCAP_PPP */
+ { "ppp", "PPP" },
+
+ /* WTAP_ENCAP_FDDI */
+ { "fddi", "FDDI" },
+
+ /* WTAP_ENCAP_FDDI_BITSWAPPED */
+ { "fddi-swapped", "FDDI with bit-swapped MAC addresses" },
+
+ /* WTAP_ENCAP_RAW_IP */
+ { "rawip", "Raw IP" },
+
+ /* WTAP_ENCAP_ARCNET */
+ { "arcnet", "ARCNET" },
+
+ /* WTAP_ENCAP_ARCNET_LINUX */
+ { "arcnet_linux", "Linux ARCNET" },
+
+ /* WTAP_ENCAP_ATM_RFC1483 */
+ { "atm-rfc1483", "RFC 1483 ATM" },
+
+ /* WTAP_ENCAP_LINUX_ATM_CLIP */
+ { "linux-atm-clip", "Linux ATM CLIP" },
+
+ /* WTAP_ENCAP_LAPB */
+ { "lapb", "LAPB" },
+
+ /* WTAP_ENCAP_ATM_PDUS */
+ { "atm-pdus", "ATM PDUs" },
+
+ /* WTAP_ENCAP_ATM_PDUS_UNTRUNCATED */
+ { "atm-pdus-untruncated", "ATM PDUs - untruncated" },
+
+ /* WTAP_ENCAP_NULL */
+ { "null", "NULL/Loopback" },
+
+ /* WTAP_ENCAP_ASCEND */
+ { "ascend", "Lucent/Ascend access equipment" },
+
+ /* WTAP_ENCAP_ISDN */
+ { "isdn", "ISDN" },
+
+ /* WTAP_ENCAP_IP_OVER_FC */
+ { "ip-over-fc", "RFC 2625 IP-over-Fibre Channel" },
+
+ /* WTAP_ENCAP_PPP_WITH_PHDR */
+ { "ppp-with-direction", "PPP with Directional Info" },
+
+ /* WTAP_ENCAP_IEEE_802_11 */
+ { "ieee-802-11", "IEEE 802.11 Wireless LAN" },
+
+ /* WTAP_ENCAP_IEEE_802_11_PRISM */
+ { "ieee-802-11-prism", "IEEE 802.11 plus Prism II monitor mode radio header" },
+
+ /* WTAP_ENCAP_IEEE_802_11_WITH_RADIO */
+ { "ieee-802-11-radio", "IEEE 802.11 Wireless LAN with radio information" },
+
+ /* WTAP_ENCAP_IEEE_802_11_RADIOTAP */
+ { "ieee-802-11-radiotap", "IEEE 802.11 plus radiotap radio header" },
+
+ /* WTAP_ENCAP_IEEE_802_11_AVS */
+ { "ieee-802-11-avs", "IEEE 802.11 plus AVS radio header" },
+
+ /* WTAP_ENCAP_SLL */
+ { "linux-sll", "Linux cooked-mode capture v1" },
+
+ /* WTAP_ENCAP_FRELAY */
+ { "frelay", "Frame Relay" },
+
+ /* WTAP_ENCAP_FRELAY_WITH_PHDR */
+ { "frelay-with-direction", "Frame Relay with Directional Info" },
+
+ /* WTAP_ENCAP_CHDLC */
+ { "chdlc", "Cisco HDLC" },
+
+ /* WTAP_ENCAP_CISCO_IOS */
+ { "ios", "Cisco IOS internal" },
+
+ /* WTAP_ENCAP_LOCALTALK */
+ { "ltalk", "Localtalk" },
+
+ /* WTAP_ENCAP_OLD_PFLOG */
+ { "pflog-old", "OpenBSD PF Firewall logs, pre-3.4" },
+
+ /* WTAP_ENCAP_HHDLC */
+ { "hhdlc", "HiPath HDLC" },
+
+ /* WTAP_ENCAP_DOCSIS */
+ { "docsis", "Data Over Cable Service Interface Specification" },
+
+ /* WTAP_ENCAP_COSINE */
+ { "cosine", "CoSine L2 debug log" },
+
+ /* WTAP_ENCAP_WFLEET_HDLC */
+ { "whdlc", "Wellfleet HDLC" },
+
+ /* WTAP_ENCAP_SDLC */
+ { "sdlc", "SDLC" },
+
+ /* WTAP_ENCAP_TZSP */
+ { "tzsp", "Tazmen sniffer protocol" },
+
+ /* WTAP_ENCAP_ENC */
+ { "enc", "OpenBSD enc(4) encapsulating interface" },
+
+ /* WTAP_ENCAP_PFLOG */
+ { "pflog", "OpenBSD PF Firewall logs" },
+
+ /* WTAP_ENCAP_CHDLC_WITH_PHDR */
+ { "chdlc-with-direction", "Cisco HDLC with Directional Info" },
+
+ /* WTAP_ENCAP_BLUETOOTH_H4 */
+ { "bluetooth-h4", "Bluetooth H4" },
+
+ /* WTAP_ENCAP_MTP2 */
+ { "mtp2", "SS7 MTP2" },
+
+ /* WTAP_ENCAP_MTP3 */
+ { "mtp3", "SS7 MTP3" },
+
+ /* WTAP_ENCAP_IRDA */
+ { "irda", "IrDA" },
+
+ /* WTAP_ENCAP_USER0 */
+ { "user0", "USER 0" },
+
+ /* WTAP_ENCAP_USER1 */
+ { "user1", "USER 1" },
+
+ /* WTAP_ENCAP_USER2 */
+ { "user2", "USER 2" },
+
+ /* WTAP_ENCAP_USER3 */
+ { "user3", "USER 3" },
+
+ /* WTAP_ENCAP_USER4 */
+ { "user4", "USER 4" },
+
+ /* WTAP_ENCAP_USER5 */
+ { "user5", "USER 5" },
+
+ /* WTAP_ENCAP_USER6 */
+ { "user6", "USER 6" },
+
+ /* WTAP_ENCAP_USER7 */
+ { "user7", "USER 7" },
+
+ /* WTAP_ENCAP_USER8 */
+ { "user8", "USER 8" },
+
+ /* WTAP_ENCAP_USER9 */
+ { "user9", "USER 9" },
+
+ /* WTAP_ENCAP_USER10 */
+ { "user10", "USER 10" },
+
+ /* WTAP_ENCAP_USER11 */
+ { "user11", "USER 11" },
+
+ /* WTAP_ENCAP_USER12 */
+ { "user12", "USER 12" },
+
+ /* WTAP_ENCAP_USER13 */
+ { "user13", "USER 13" },
+
+ /* WTAP_ENCAP_USER14 */
+ { "user14", "USER 14" },
+
+ /* WTAP_ENCAP_USER15 */
+ { "user15", "USER 15" },
+
+ /* WTAP_ENCAP_SYMANTEC */
+ { "symantec", "Symantec Enterprise Firewall" },
+
+ /* WTAP_ENCAP_APPLE_IP_OVER_IEEE1394 */
+ { "ap1394", "Apple IP-over-IEEE 1394" },
+
+ /* WTAP_ENCAP_BACNET_MS_TP */
+ { "bacnet-ms-tp", "BACnet MS/TP" },
+
+ /* WTAP_ENCAP_NETTL_RAW_ICMP */
+ { "raw-icmp-nettl", "Raw ICMP with nettl headers" },
+
+ /* WTAP_ENCAP_NETTL_RAW_ICMPV6 */
+ { "raw-icmpv6-nettl", "Raw ICMPv6 with nettl headers" },
+
+ /* WTAP_ENCAP_GPRS_LLC */
+ { "gprs-llc", "GPRS LLC" },
+
+ /* WTAP_ENCAP_JUNIPER_ATM1 */
+ { "juniper-atm1", "Juniper ATM1" },
+
+ /* WTAP_ENCAP_JUNIPER_ATM2 */
+ { "juniper-atm2", "Juniper ATM2" },
+
+ /* WTAP_ENCAP_REDBACK */
+ { "redback", "Redback SmartEdge" },
+
+ /* WTAP_ENCAP_NETTL_RAW_IP */
+ { "rawip-nettl", "Raw IP with nettl headers" },
+
+ /* WTAP_ENCAP_NETTL_ETHERNET */
+ { "ether-nettl", "Ethernet with nettl headers" },
+
+ /* WTAP_ENCAP_NETTL_TOKEN_RING */
+ { "tr-nettl", "Token Ring with nettl headers" },
+
+ /* WTAP_ENCAP_NETTL_FDDI */
+ { "fddi-nettl", "FDDI with nettl headers" },
+
+ /* WTAP_ENCAP_NETTL_UNKNOWN */
+ { "unknown-nettl", "Unknown link-layer type with nettl headers" },
+
+ /* WTAP_ENCAP_MTP2_WITH_PHDR */
+ { "mtp2-with-phdr", "MTP2 with pseudoheader" },
+
+ /* WTAP_ENCAP_JUNIPER_PPPOE */
+ { "juniper-pppoe", "Juniper PPPoE" },
+
+ /* WTAP_ENCAP_GCOM_TIE1 */
+ { "gcom-tie1", "GCOM TIE1" },
+
+ /* WTAP_ENCAP_GCOM_SERIAL */
+ { "gcom-serial", "GCOM Serial" },
+
+ /* WTAP_ENCAP_NETTL_X25 */
+ { "x25-nettl", "X.25 with nettl headers" },
+
+ /* WTAP_ENCAP_K12 */
+ { "k12", "K12 protocol analyzer" },
+
+ /* WTAP_ENCAP_JUNIPER_MLPPP */
+ { "juniper-mlppp", "Juniper MLPPP" },
+
+ /* WTAP_ENCAP_JUNIPER_MLFR */
+ { "juniper-mlfr", "Juniper MLFR" },
+
+ /* WTAP_ENCAP_JUNIPER_ETHER */
+ { "juniper-ether", "Juniper Ethernet" },
+
+ /* WTAP_ENCAP_JUNIPER_PPP */
+ { "juniper-ppp", "Juniper PPP" },
+
+ /* WTAP_ENCAP_JUNIPER_FRELAY */
+ { "juniper-frelay", "Juniper Frame-Relay" },
+
+ /* WTAP_ENCAP_JUNIPER_CHDLC */
+ { "juniper-chdlc", "Juniper C-HDLC" },
+
+ /* WTAP_ENCAP_JUNIPER_GGSN */
+ { "juniper-ggsn", "Juniper GGSN" },
+
+ /* WTAP_ENCAP_LINUX_LAPD */
+ { "linux-lapd", "LAPD with Linux pseudo-header" },
+
+ /* WTAP_ENCAP_CATAPULT_DCT2000 */
+ { "dct2000", "Catapult DCT2000" },
+
+ /* WTAP_ENCAP_BER */
+ { "ber", "ASN.1 Basic Encoding Rules" },
+
+ /* WTAP_ENCAP_JUNIPER_VP */
+ { "juniper-vp", "Juniper Voice PIC" },
+
+ /* WTAP_ENCAP_USB_FREEBSD */
+ { "usb-freebsd", "USB packets with FreeBSD header" },
+
+ /* WTAP_ENCAP_IEEE802_16_MAC_CPS */
+ { "ieee-802-16-mac-cps", "IEEE 802.16 MAC Common Part Sublayer" },
+
+ /* WTAP_ENCAP_NETTL_RAW_TELNET */
+ { "raw-telnet-nettl", "Raw telnet with nettl headers" },
+
+ /* WTAP_ENCAP_USB_LINUX */
+ { "usb-linux", "USB packets with Linux header" },
+
+ /* WTAP_ENCAP_MPEG */
+ { "mpeg", "MPEG" },
+
+ /* WTAP_ENCAP_PPI */
+ { "ppi", "Per-Packet Information header" },
+
+ /* WTAP_ENCAP_ERF */
+ { "erf", "Extensible Record Format" },
+
+ /* WTAP_ENCAP_BLUETOOTH_H4_WITH_PHDR */
+ { "bluetooth-h4-linux", "Bluetooth H4 with linux header" },
+
+ /* WTAP_ENCAP_SITA */
+ { "sita-wan", "SITA WAN packets" },
+
+ /* WTAP_ENCAP_SCCP */
+ { "sccp", "SS7 SCCP" },
+
+ /* WTAP_ENCAP_BLUETOOTH_HCI */
+ { "bluetooth-hci", "Bluetooth without transport layer" },
+
+ /* WTAP_ENCAP_IPMB_KONTRON */
+ { "ipmb-kontron", "Intelligent Platform Management Bus with Kontron pseudo-header" },
+
+ /* WTAP_ENCAP_IEEE802_15_4 */
+ { "wpan", "IEEE 802.15.4 Wireless PAN" },
+
+ /* WTAP_ENCAP_X2E_XORAYA */
+ { "x2e-xoraya", "X2E Xoraya" },
+
+ /* WTAP_ENCAP_FLEXRAY */
+ { "flexray", "FlexRay" },
+
+ /* WTAP_ENCAP_LIN */
+ { "lin", "Local Interconnect Network" },
+
+ /* WTAP_ENCAP_MOST */
+ { "most", "Media Oriented Systems Transport" },
+
+ /* WTAP_ENCAP_CAN20B */
+ { "can20b", "Controller Area Network 2.0B" },
+
+ /* WTAP_ENCAP_LAYER1_EVENT */
+ { "layer1-event", "EyeSDN Layer 1 event" },
+
+ /* WTAP_ENCAP_X2E_SERIAL */
+ { "x2e-serial", "X2E serial line capture" },
+
+ /* WTAP_ENCAP_I2C_LINUX */
+ { "i2c-linux", "I2C with Linux-specific pseudo-header" },
+
+ /* WTAP_ENCAP_IEEE802_15_4_NONASK_PHY */
+ { "wpan-nonask-phy", "IEEE 802.15.4 Wireless PAN non-ASK PHY" },
+
+ /* WTAP_ENCAP_TNEF */
+ { "tnef", "Transport-Neutral Encapsulation Format" },
+
+ /* WTAP_ENCAP_USB_LINUX_MMAPPED */
+ { "usb-linux-mmap", "USB packets with Linux header and padding" },
+
+ /* WTAP_ENCAP_GSM_UM */
+ { "gsm_um", "GSM Um Interface" },
+
+ /* WTAP_ENCAP_DPNSS */
+ { "dpnss_link", "Digital Private Signalling System No 1 Link Layer" },
+
+ /* WTAP_ENCAP_PACKETLOGGER */
+ { "packetlogger", "Apple Bluetooth PacketLogger" },
+
+ /* WTAP_ENCAP_NSTRACE_1_0 */
+ { "nstrace10", "NetScaler Encapsulation 1.0 of Ethernet" },
+
+ /* WTAP_ENCAP_NSTRACE_2_0 */
+ { "nstrace20", "NetScaler Encapsulation 2.0 of Ethernet" },
+
+ /* WTAP_ENCAP_FIBRE_CHANNEL_FC2 */
+ { "fc2", "Fibre Channel FC-2" },
+
+ /* WTAP_ENCAP_FIBRE_CHANNEL_FC2_WITH_FRAME_DELIMS */
+ { "fc2sof", "Fibre Channel FC-2 With Frame Delimiter" },
+
+ /* WTAP_ENCAP_JPEG_JFIF */
+ { "jfif", "JPEG/JFIF" },
+
+ /* WTAP_ENCAP_IPNET */
+ { "ipnet", "Solaris IPNET" },
+
+ /* WTAP_ENCAP_SOCKETCAN */
+ { "socketcan", "SocketCAN" },
+
+ /* WTAP_ENCAP_IEEE_802_11_NETMON */
+ { "ieee-802-11-netmon", "IEEE 802.11 plus Network Monitor radio header" },
+
+ /* WTAP_ENCAP_IEEE802_15_4_NOFCS */
+ { "wpan-nofcs", "IEEE 802.15.4 Wireless PAN with FCS not present" },
+
+ /* WTAP_ENCAP_RAW_IPFIX */
+ { "ipfix", "RFC 5655/RFC 5101 IPFIX" },
+
+ /* WTAP_ENCAP_RAW_IP4 */
+ { "rawip4", "Raw IPv4" },
+
+ /* WTAP_ENCAP_RAW_IP6 */
+ { "rawip6", "Raw IPv6" },
+
+ /* WTAP_ENCAP_LAPD */
+ { "lapd", "LAPD" },
+
+ /* WTAP_ENCAP_DVBCI */
+ { "dvbci", "DVB-CI (Common Interface)" },
+
+ /* WTAP_ENCAP_MUX27010 */
+ { "mux27010", "MUX27010" },
+
+ /* WTAP_ENCAP_MIME */
+ { "mime", "MIME" },
+
+ /* WTAP_ENCAP_NETANALYZER */
+ { "netanalyzer", "Hilscher netANALYZER" },
+
+ /* WTAP_ENCAP_NETANALYZER_TRANSPARENT */
+ { "netanalyzer-transparent", "Hilscher netANALYZER-Transparent" },
+
+ /* WTAP_ENCAP_IP_OVER_IB */
+ { "ip-over-ib", "IP over InfiniBand" },
+
+ /* WTAP_ENCAP_MPEG_2_TS */
+ { "mp2ts", "ISO/IEC 13818-1 MPEG2-TS" },
+
+ /* WTAP_ENCAP_PPP_ETHER */
+ { "pppoes", "PPP-over-Ethernet session" },
+
+ /* WTAP_ENCAP_NFC_LLCP */
+ { "nfc-llcp", "NFC LLCP" },
+
+ /* WTAP_ENCAP_NFLOG */
+ { "nflog", "NFLOG" },
+
+ /* WTAP_ENCAP_V5_EF */
+ { "v5-ef", "V5 Envelope Function" },
+
+ /* WTAP_ENCAP_BACNET_MS_TP_WITH_PHDR */
+ { "bacnet-ms-tp-with-direction", "BACnet MS/TP with Directional Info" },
+
+ /* WTAP_ENCAP_IXVERIWAVE */
+ { "ixveriwave", "IxVeriWave header and stats block" },
+
+ /* WTAP_ENCAP_SDH */
+ { "sdh", "SDH" },
+
+ /* WTAP_ENCAP_DBUS */
+ { "dbus", "D-Bus" },
+
+ /* WTAP_ENCAP_AX25_KISS */
+ { "ax25-kiss", "AX.25 with KISS header" },
+
+ /* WTAP_ENCAP_AX25 */
+ { "ax25", "Amateur Radio AX.25" },
+
+ /* WTAP_ENCAP_SCTP */
+ { "sctp", "SCTP" },
+
+ /* WTAP_ENCAP_INFINIBAND */
+ { "infiniband", "InfiniBand" },
+
+ /* WTAP_ENCAP_JUNIPER_SVCS */
+ { "juniper-svcs", "Juniper Services" },
+
+ /* WTAP_ENCAP_USBPCAP */
+ { "usb-usbpcap", "USB packets with USBPcap header" },
+
+ /* WTAP_ENCAP_RTAC_SERIAL */
+ { "rtac-serial", "RTAC serial-line" },
+
+ /* WTAP_ENCAP_BLUETOOTH_LE_LL */
+ { "bluetooth-le-ll", "Bluetooth Low Energy Link Layer" },
+
+ /* WTAP_ENCAP_WIRESHARK_UPPER_PDU */
+ { "wireshark-upper-pdu", "Wireshark Upper PDU export" },
+
+ /* WTAP_ENCAP_STANAG_4607 */
+ { "s4607", "STANAG 4607" },
+
+ /* WTAP_ENCAP_STANAG_5066_D_PDU */
+ { "s5066-dpdu", "STANAG 5066 Data Transfer Sublayer PDUs(D_PDU)" },
+
+ /* WTAP_ENCAP_NETLINK */
+ { "netlink", "Linux Netlink" },
+
+ /* WTAP_ENCAP_BLUETOOTH_LINUX_MONITOR */
+ { "bluetooth-linux-monitor", "Bluetooth Linux Monitor" },
+
+ /* WTAP_ENCAP_BLUETOOTH_BREDR_BB */
+ { "bluetooth-bredr-bb-rf", "Bluetooth BR/EDR Baseband RF" },
+
+ /* WTAP_ENCAP_BLUETOOTH_LE_LL_WITH_PHDR */
+ { "bluetooth-le-ll-rf", "Bluetooth Low Energy Link Layer RF" },
+
+ /* WTAP_ENCAP_NSTRACE_3_0 */
+ { "nstrace30", "NetScaler Encapsulation 3.0 of Ethernet" },
+
+ /* WTAP_ENCAP_LOGCAT */
+ { "logcat", "Android Logcat Binary format" },
+
+ /* WTAP_ENCAP_LOGCAT_BRIEF */
+ { "logcat_brief", "Android Logcat Brief text format" },
+
+ /* WTAP_ENCAP_LOGCAT_PROCESS */
+ { "logcat_process", "Android Logcat Process text format" },
+
+ /* WTAP_ENCAP_LOGCAT_TAG */
+ { "logcat_tag", "Android Logcat Tag text format" },
+
+ /* WTAP_ENCAP_LOGCAT_THREAD */
+ { "logcat_thread", "Android Logcat Thread text format" },
+
+ /* WTAP_ENCAP_LOGCAT_TIME */
+ { "logcat_time", "Android Logcat Time text format" },
+
+ /* WTAP_ENCAP_LOGCAT_THREADTIME */
+ { "logcat_threadtime", "Android Logcat Threadtime text format" },
+
+ /* WTAP_ENCAP_LOGCAT_LONG */
+ { "logcat_long", "Android Logcat Long text format" },
+
+ /* WTAP_ENCAP_PKTAP */
+ { "pktap", "Apple PKTAP" },
+
+ /* WTAP_ENCAP_EPON */
+ { "epon", "Ethernet Passive Optical Network" },
+
+ /* WTAP_ENCAP_IPMI_TRACE */
+ { "ipmi-trace", "IPMI Trace Data Collection" },
+
+ /* WTAP_ENCAP_LOOP */
+ { "loop", "OpenBSD loopback" },
+
+ /* WTAP_ENCAP_JSON */
+ { "json", "JavaScript Object Notation" },
+
+ /* WTAP_ENCAP_NSTRACE_3_5 */
+ { "nstrace35", "NetScaler Encapsulation 3.5 of Ethernet" },
+
+ /* WTAP_ENCAP_ISO14443 */
+ { "iso14443", "ISO 14443 contactless smartcard standards" },
+
+ /* WTAP_ENCAP_GFP_T */
+ { "gfp-t", "ITU-T G.7041/Y.1303 Generic Framing Procedure Transparent mode" },
+
+ /* WTAP_ENCAP_GFP_F */
+ { "gfp-f", "ITU-T G.7041/Y.1303 Generic Framing Procedure Frame-mapped mode" },
+
+ /* WTAP_ENCAP_IP_OVER_IB_PCAP */
+ { "ip-ib", "IP over IB" },
+
+ /* WTAP_ENCAP_JUNIPER_VN */
+ { "juniper-vn", "Juniper VN" },
+
+ /* WTAP_ENCAP_USB_DARWIN */
+ { "usb-darwin", "USB packets with Darwin (macOS, etc.) headers" },
+
+ /* WTAP_ENCAP_LORATAP */
+ { "loratap", "LoRaTap" },
+
+ /* WTAP_ENCAP_3MB_ETHERNET */
+ { "xeth", "Xerox 3MB Ethernet" },
+
+ /* WTAP_ENCAP_VSOCK */
+ { "vsock", "Linux vsock" },
+
+ /* WTAP_ENCAP_NORDIC_BLE */
+ { "nordic_ble", "nRF Sniffer for Bluetooth LE" },
+
+ /* WTAP_ENCAP_NETMON_NET_NETEVENT */
+ { "netmon_event", "Network Monitor Network Event" },
+
+ /* WTAP_ENCAP_NETMON_HEADER */
+ { "netmon_header", "Network Monitor Header" },
+
+ /* WTAP_ENCAP_NETMON_NET_FILTER */
+ { "netmon_filter", "Network Monitor Filter" },
+
+ /* WTAP_ENCAP_NETMON_NETWORK_INFO_EX */
+ { "netmon_network_info", "Network Monitor Network Info" },
+
+ /* WTAP_ENCAP_MA_WFP_CAPTURE_V4 */
+ { "message_analyzer_wfp_capture_v4", "Message Analyzer WFP Capture v4" },
+
+ /* WTAP_ENCAP_MA_WFP_CAPTURE_V6 */
+ { "message_analyzer_wfp_capture_v6", "Message Analyzer WFP Capture v6" },
+
+ /* WTAP_ENCAP_MA_WFP_CAPTURE_2V4 */
+ { "message_analyzer_wfp_capture2_v4", "Message Analyzer WFP Capture2 v4" },
+
+ /* WTAP_ENCAP_MA_WFP_CAPTURE_2V6 */
+ { "message_analyzer_wfp_capture2_v6", "Message Analyzer WFP Capture2 v6" },
+
+ /* WTAP_ENCAP_MA_WFP_CAPTURE_AUTH_V4 */
+ { "message_analyzer_wfp_capture_auth_v4", "Message Analyzer WFP Capture Auth v4" },
+
+ /* WTAP_ENCAP_MA_WFP_CAPTURE_AUTH_V6 */
+ { "message_analyzer_wfp_capture_auth_v6", "Message Analyzer WFP Capture Auth v6" },
+
+ /* WTAP_ENCAP_JUNIPER_ST */
+ { "juniper-st", "Juniper Secure Tunnel Information" },
+
+ /* WTAP_ENCAP_ETHERNET_MPACKET */
+ { "ether-mpacket", "IEEE 802.3br mPackets" },
+
+ /* WTAP_ENCAP_DOCSIS31_XRA31 */
+ { "docsis31_xra31", "DOCSIS with Excentis XRA pseudo-header" },
+
+ /* WTAP_ENCAP_DPAUXMON */
+ { "dpauxmon", "DisplayPort AUX channel with Unigraf pseudo-header" },
+
+ /* WTAP_ENCAP_RUBY_MARSHAL */
+ { "ruby_marshal", "Ruby marshal object" },
+
+ /* WTAP_ENCAP_RFC7468 */
+ { "rfc7468", "RFC 7468 file" },
+
+ /* WTAP_ENCAP_SYSTEMD_JOURNAL */
+ { "sdjournal", "systemd journal" },
+
+ /* WTAP_ENCAP_EBHSCR */
+ { "ebhscr", "Elektrobit High Speed Capture and Replay" },
+
+ /* WTAP_ENCAP_VPP */
+ { "vpp", "Vector Packet Processing graph dispatch trace" },
+
+ /* WTAP_ENCAP_IEEE802_15_4_TAP */
+ { "wpan-tap", "IEEE 802.15.4 Wireless with TAP pseudo-header" },
+
+ /* WTAP_ENCAP_LOG_3GPP */
+ { "log_3GPP", "3GPP Phone Log" },
+
+ /* WTAP_ENCAP_USB_2_0 */
+ { "usb-20", "USB 2.0/1.1/1.0 packets" },
+
+ /* WTAP_ENCAP_MP4 */
+ { "mp4", "MP4 files" },
+
+ /* WTAP_ENCAP_SLL2 */
+ { "linux-sll2", "Linux cooked-mode capture v2" },
+
+ /* WTAP_ENCAP_ZWAVE_SERIAL */
+ { "zwave-serial", "Z-Wave Serial API packets" },
+
+ /* WTAP_ENCAP_ETW */
+ { "etw", "Event Tracing for Windows messages" },
+
+ /* WTAP_ENCAP_ERI_ENB_LOG */
+ { "eri_enb_log", "Ericsson eNode-B raw log" },
+
+ /* WTAP_ENCAP_ZBNCP */
+ { "zbncp", "ZBOSS NCP" },
+
+ /* WTAP_ENCAP_USB_2_0_LOW_SPEED */
+ { "usb-20-low", "Low-Speed USB 2.0/1.1/1.0 packets" },
+
+ /* WTAP_ENCAP_USB_2_0_FULL_SPEED */
+ { "usb-20-full", "Full-Speed USB 2.0/1.1/1.0 packets" },
+
+ /* WTAP_ENCAP_USB_2_0_HIGH_SPEED */
+ { "usb-20-high", "High-Speed USB 2.0 packets" },
+
+ /* WTAP_ENCAP_AUTOSAR_DLT */
+ { "autosardlt", "AUTOSAR DLT" },
+
+ /* WTAP_ENCAP_AUERSWALD_LOG */
+ { "auerlog", "Auerswald Log" },
+
+ /* WTAP_ENCAP_ATSC_ALP */
+ { "alp", "ATSC Link-Layer Protocol (A/330) packets" },
+
+ /* WTAP_ENCAP_FIRA_UCI */
+ { "fira-uci", "FiRa UWB Controller Interface (UCI) protocol." },
+
+ /* WTAP_ENCAP_SILABS_DEBUG_CHANNEL */
+ { "silabs-dch", "Silabs Debug Channel"},
+
+ /* WTAP_ENCAP_MDB */
+ { "mdb", "MDB (Multi-Drop Bus)"},
+};
+
+WS_DLL_LOCAL
+gint wtap_num_encap_types = sizeof(encap_table_base) / sizeof(struct encap_type_info);
+static GArray* encap_table_arr = NULL;
+
+#define encap_table_entry(encap) \
+ g_array_index(encap_table_arr, struct encap_type_info, encap)
+
+static void wtap_init_encap_types(void) {
+
+ if (encap_table_arr) return;
+
+ encap_table_arr = g_array_new(FALSE,TRUE,sizeof(struct encap_type_info));
+
+ g_array_append_vals(encap_table_arr,encap_table_base,wtap_num_encap_types);
+}
+
+static void wtap_cleanup_encap_types(void) {
+ if (encap_table_arr) {
+ g_array_free(encap_table_arr, TRUE);
+ encap_table_arr = NULL;
+ }
+}
+
+int wtap_get_num_encap_types(void) {
+ return wtap_num_encap_types;
+}
+
+
+int
+wtap_register_encap_type(const char *description, const char *name)
+{
+ struct encap_type_info e;
+
+ e.name = g_strdup(name);
+ e.description = g_strdup(description);
+
+ g_array_append_val(encap_table_arr,e);
+
+ return wtap_num_encap_types++;
+}
+
+/* Name to use in, say, a command-line flag specifying the type. */
+const char *
+wtap_encap_name(int encap)
+{
+ if (encap < WTAP_ENCAP_NONE || encap >= WTAP_NUM_ENCAP_TYPES)
+ return "illegal";
+ else if (encap == WTAP_ENCAP_NONE)
+ return "none";
+ else if (encap == WTAP_ENCAP_PER_PACKET)
+ return "per-packet";
+ else
+ return encap_table_entry(encap).name;
+}
+
+/* Description to show to users. */
+const char *
+wtap_encap_description(int encap)
+{
+ if (encap < WTAP_ENCAP_NONE || encap >= WTAP_NUM_ENCAP_TYPES)
+ return "Illegal";
+ else if (encap == WTAP_ENCAP_NONE)
+ return "None";
+ else if (encap == WTAP_ENCAP_PER_PACKET)
+ return "Per packet";
+ else
+ return encap_table_entry(encap).description;
+}
+
+/* Translate a name to a capture file type. */
+int
+wtap_name_to_encap(const char *name)
+{
+ int encap;
+
+ for (encap = 0; encap < WTAP_NUM_ENCAP_TYPES; encap++) {
+ if (encap_table_entry(encap).name != NULL &&
+ strcmp(name, encap_table_entry(encap).name) == 0)
+ return encap;
+ }
+ return -1; /* no such encapsulation type */
+}
+
+/*
+ * For precision values that correspond to a specific precision.
+ */
+static const char *precnames[NUM_WS_TSPREC_VALS] = {
+ "seconds",
+ "100 milliseconds (deciseconds)",
+ "10 milliseconds (centiseconds)",
+ "milliseconds",
+ "100 microseconds",
+ "10 microseconds",
+ "microseconds",
+ "100 nanoseconds",
+ "10 nanoseconds",
+ "nanoseconds",
+};
+
+const char*
+wtap_tsprec_string(int tsprec)
+{
+ const char* s;
+ if (tsprec == WTAP_TSPREC_PER_PACKET)
+ s = "per-packet";
+ else if (tsprec >= 0 && tsprec < NUM_WS_TSPREC_VALS)
+ s = precnames[tsprec];
+ else if (tsprec == WTAP_TSPREC_UNKNOWN)
+ s = "UNKNOWN";
+ else
+ s = "INVALID";
+ return s;
+}
+
+static const char *wtap_errlist[] = {
+ /* WTAP_ERR_NOT_REGULAR_FILE */
+ "The file isn't a plain file or pipe",
+
+ /* WTAP_ERR_RANDOM_OPEN_PIPE */
+ "The file is being opened for random access but is a pipe",
+
+ /* WTAP_ERR_FILE_UNKNOWN_FORMAT */
+ "The file isn't a capture file in a known format",
+
+ /* WTAP_ERR_UNSUPPORTED */
+ "File contains record data we don't support",
+
+ /* WTAP_ERR_CANT_WRITE_TO_PIPE */
+ "That file format cannot be written to a pipe",
+
+ /* WTAP_ERR_CANT_OPEN */
+ NULL,
+
+ /* WTAP_ERR_UNWRITABLE_FILE_TYPE */
+ "Files can't be saved in that format",
+
+ /* WTAP_ERR_UNWRITABLE_ENCAP */
+ "Packets with that network type can't be saved in that format",
+
+ /* WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED */
+ "That file format doesn't support per-packet encapsulations",
+
+ /* WTAP_ERR_CANT_WRITE */
+ "A write failed for some unknown reason",
+
+ /* WTAP_ERR_CANT_CLOSE */
+ NULL,
+
+ /* WTAP_ERR_SHORT_READ */
+ "Less data was read than was expected",
+
+ /* WTAP_ERR_BAD_FILE */
+ "The file appears to be damaged or corrupt",
+
+ /* WTAP_ERR_SHORT_WRITE */
+ "Less data was written than was requested",
+
+ /* WTAP_ERR_UNC_OVERFLOW */
+ "Uncompression error: data would overflow buffer",
+
+ /* WTAP_ERR_RANDOM_OPEN_STDIN */
+ "The standard input cannot be opened for random access",
+
+ /* WTAP_ERR_COMPRESSION_NOT_SUPPORTED */
+ "That file format doesn't support compression",
+
+ /* WTAP_ERR_CANT_SEEK */
+ NULL,
+
+ /* WTAP_ERR_CANT_SEEK_COMPRESSED */
+ NULL,
+
+ /* WTAP_ERR_DECOMPRESS */
+ "Uncompression error",
+
+ /* WTAP_ERR_INTERNAL */
+ "Internal error",
+
+ /* WTAP_ERR_PACKET_TOO_LARGE */
+ "The packet being written is too large for that format",
+
+ /* WTAP_ERR_CHECK_WSLUA */
+ NULL,
+
+ /* WTAP_ERR_UNWRITABLE_REC_TYPE */
+ "That record type cannot be written in that format",
+
+ /* WTAP_ERR_UNWRITABLE_REC_DATA */
+ "That record can't be written in that format",
+
+ /* WTAP_ERR_DECOMPRESSION_NOT_SUPPORTED */
+ "We don't support decompressing that type of compressed file",
+
+ /* WTAP_ERR_TIME_STAMP_NOT_SUPPORTED */
+ "We don't support writing that record's time stamp to that file type",
+};
+#define WTAP_ERRLIST_SIZE (sizeof wtap_errlist / sizeof wtap_errlist[0])
+
+const char *
+wtap_strerror(int err)
+{
+ static char errbuf[128];
+ unsigned int wtap_errlist_index;
+
+ if (err < 0) {
+ wtap_errlist_index = -1 - err;
+ if (wtap_errlist_index >= WTAP_ERRLIST_SIZE) {
+ snprintf(errbuf, 128, "Error %d", err);
+ return errbuf;
+ }
+ if (wtap_errlist[wtap_errlist_index] == NULL)
+ return "Unknown reason";
+ return wtap_errlist[wtap_errlist_index];
+ } else
+ return g_strerror(err);
+}
+
+/* Close only the sequential side, freeing up memory it uses.
+
+ Note that we do *not* want to call the subtype's close function,
+ as it would free any per-subtype data, and that data may be
+ needed by the random-access side.
+
+ Instead, if the subtype has a "sequential close" function, we call it,
+ to free up stuff used only by the sequential side. */
+void
+wtap_sequential_close(wtap *wth)
+{
+ if (wth->subtype_sequential_close != NULL)
+ (*wth->subtype_sequential_close)(wth);
+
+ if (wth->fh != NULL) {
+ file_close(wth->fh);
+ wth->fh = NULL;
+ }
+}
+
+static void
+g_fast_seek_item_free(gpointer data, gpointer user_data _U_)
+{
+ g_free(data);
+}
+
+/*
+ * Close the file descriptors for the sequential and random streams, but
+ * don't discard any information about those streams. Used on Windows if
+ * we need to rename a file that we have open or if we need to rename on
+ * top of a file we have open.
+ */
+void
+wtap_fdclose(wtap *wth)
+{
+ if (wth->fh != NULL)
+ file_fdclose(wth->fh);
+ if (wth->random_fh != NULL)
+ file_fdclose(wth->random_fh);
+}
+
+void
+wtap_close(wtap *wth)
+{
+ wtap_sequential_close(wth);
+
+ if (wth->subtype_close != NULL)
+ (*wth->subtype_close)(wth);
+
+ if (wth->random_fh != NULL)
+ file_close(wth->random_fh);
+
+ g_free(wth->priv);
+
+ g_free(wth->pathname);
+
+ if (wth->fast_seek != NULL) {
+ g_ptr_array_foreach(wth->fast_seek, g_fast_seek_item_free, NULL);
+ g_ptr_array_free(wth->fast_seek, TRUE);
+ }
+
+ wtap_block_array_free(wth->shb_hdrs);
+ wtap_block_array_free(wth->nrbs);
+ wtap_block_array_free(wth->interface_data);
+ wtap_block_array_free(wth->dsbs);
+ wtap_block_array_free(wth->sysdig_meta_events);
+
+ g_free(wth);
+}
+
+void
+wtap_cleareof(wtap *wth) {
+ /* Reset EOF */
+ file_clearerr(wth->fh);
+}
+
+static inline void
+wtapng_process_nrb_ipv4(wtap *wth, wtap_block_t nrb)
+{
+ const wtapng_nrb_mandatory_t *nrb_mand = (wtapng_nrb_mandatory_t*)wtap_block_get_mandatory_data(nrb);
+
+ if (wth->add_new_ipv4) {
+ for (GList *elem = nrb_mand->ipv4_addr_list; elem != NULL; elem = elem->next) {
+ hashipv4_t *tp = elem->data;
+ wth->add_new_ipv4(tp->addr, tp->name, FALSE);
+ }
+ }
+}
+
+static inline void
+wtapng_process_nrb_ipv6(wtap *wth, wtap_block_t nrb)
+{
+ const wtapng_nrb_mandatory_t *nrb_mand = (wtapng_nrb_mandatory_t*)wtap_block_get_mandatory_data(nrb);
+
+ if (wth->add_new_ipv6) {
+ for (GList *elem = nrb_mand->ipv6_addr_list; elem != NULL; elem = elem->next) {
+ hashipv6_t *tp = elem->data;
+ wth->add_new_ipv6(tp->addr, tp->name, FALSE);
+ }
+ }
+}
+
+void wtap_set_cb_new_ipv4(wtap *wth, wtap_new_ipv4_callback_t add_new_ipv4) {
+ if (!wth)
+ return;
+
+ wth->add_new_ipv4 = add_new_ipv4;
+
+ /* Are there any existing NRBs? */
+ if (!wth->nrbs)
+ return;
+ /*
+ * Send all NRBs that were read so far to the new callback. file.c
+ * relies on this to support redissection (during redissection, the
+ * previous name resolutions are lost and has to be resupplied).
+ */
+ for (guint i = 0; i < wth->nrbs->len; i++) {
+ wtap_block_t nrb = g_array_index(wth->nrbs, wtap_block_t, i);
+ wtapng_process_nrb_ipv4(wth, nrb);
+ }
+}
+
+void wtap_set_cb_new_ipv6(wtap *wth, wtap_new_ipv6_callback_t add_new_ipv6) {
+ if (!wth)
+ return;
+
+ wth->add_new_ipv6 = add_new_ipv6;
+
+ /* Are there any existing NRBs? */
+ if (!wth->nrbs)
+ return;
+ /*
+ * Send all NRBs that were read so far to the new callback. file.c
+ * relies on this to support redissection (during redissection, the
+ * previous name resolutions are lost and has to be resupplied).
+ */
+ for (guint i = 0; i < wth->nrbs->len; i++) {
+ wtap_block_t nrb = g_array_index(wth->nrbs, wtap_block_t, i);
+ wtapng_process_nrb_ipv6(wth, nrb);
+ }
+}
+
+void
+wtapng_process_nrb(wtap *wth, wtap_block_t nrb)
+{
+ wtapng_process_nrb_ipv4(wth, nrb);
+ wtapng_process_nrb_ipv6(wth, nrb);
+}
+
+void wtap_set_cb_new_secrets(wtap *wth, wtap_new_secrets_callback_t add_new_secrets) {
+ /* Is a valid wth given that supports DSBs? */
+ if (!wth || !wth->dsbs)
+ return;
+
+ wth->add_new_secrets = add_new_secrets;
+ /*
+ * Send all DSBs that were read so far to the new callback. file.c
+ * relies on this to support redissection (during redissection, the
+ * previous secrets are lost and has to be resupplied).
+ */
+ for (guint i = 0; i < wth->dsbs->len; i++) {
+ wtap_block_t dsb = g_array_index(wth->dsbs, wtap_block_t, i);
+ wtapng_process_dsb(wth, dsb);
+ }
+}
+
+void
+wtapng_process_dsb(wtap *wth, wtap_block_t dsb)
+{
+ const wtapng_dsb_mandatory_t *dsb_mand = (wtapng_dsb_mandatory_t*)wtap_block_get_mandatory_data(dsb);
+
+ if (wth->add_new_secrets)
+ wth->add_new_secrets(dsb_mand->secrets_type, dsb_mand->secrets_data, dsb_mand->secrets_len);
+}
+
+void
+wtapng_process_sysdig_meta_event(wtap *wth, wtap_block_t mev)
+{
+ const wtapng_sysdig_mev_mandatory_t *mev_mand = (wtapng_sysdig_mev_mandatory_t*)wtap_block_get_mandatory_data(mev);
+
+ if (wth->add_new_sysdig_meta_event)
+ wth->add_new_sysdig_meta_event(mev_mand->mev_type, mev_mand->mev_data, mev_mand->mev_data_len);
+}
+
+/* Perform per-packet initialization */
+static void
+wtap_init_rec(wtap *wth, wtap_rec *rec)
+{
+ /*
+ * Set the packet encapsulation to the file's encapsulation
+ * value; if that's not WTAP_ENCAP_PER_PACKET, it's the
+ * right answer (and means that the read routine for this
+ * capture file type doesn't have to set it), and if it
+ * *is* WTAP_ENCAP_PER_PACKET, the caller needs to set it
+ * anyway.
+ *
+ * Do the same for the packet time stamp resolution.
+ */
+ rec->rec_header.packet_header.pkt_encap = wth->file_encap;
+ rec->tsprec = wth->file_tsprec;
+ rec->block = NULL;
+ rec->block_was_modified = FALSE;
+
+ /*
+ * Assume the file has only one section; the module for the
+ * file type needs to indicate the section number if there's
+ * more than one section.
+ */
+ rec->section_number = 0;
+}
+
+gboolean
+wtap_read(wtap *wth, wtap_rec *rec, Buffer *buf, int *err,
+ gchar **err_info, gint64 *offset)
+{
+ /*
+ * Initialize the record to default values.
+ */
+ wtap_init_rec(wth, rec);
+ ws_buffer_clean(buf);
+
+ *err = 0;
+ *err_info = NULL;
+ if (!wth->subtype_read(wth, rec, buf, err, err_info, offset)) {
+ /*
+ * If we didn't get an error indication, we read
+ * the last packet. See if there's any deferred
+ * error, as might, for example, occur if we're
+ * reading a compressed file, and we got an error
+ * reading compressed data from the file, but
+ * got enough compressed data to decompress the
+ * last packet of the file.
+ */
+ if (*err == 0)
+ *err = file_error(wth->fh, err_info);
+ if (rec->block != NULL) {
+ /*
+ * Unreference any block created for this record.
+ */
+ wtap_block_unref(rec->block);
+ rec->block = NULL;
+ }
+ return FALSE; /* failure */
+ }
+
+ /*
+ * Is this a packet record?
+ */
+ if (rec->rec_type == REC_TYPE_PACKET) {
+ /*
+ * Make sure that it's not WTAP_ENCAP_PER_PACKET, as that
+ * probably means the file has that encapsulation type
+ * but the read routine didn't set this packet's
+ * encapsulation type.
+ */
+ ws_assert(rec->rec_header.packet_header.pkt_encap != WTAP_ENCAP_PER_PACKET);
+ ws_assert(rec->rec_header.packet_header.pkt_encap != WTAP_ENCAP_NONE);
+ }
+
+ return TRUE; /* success */
+}
+
+/*
+ * Read a given number of bytes from a file into a buffer or, if
+ * buf is NULL, just discard them.
+ *
+ * If we succeed, return TRUE.
+ *
+ * If we get an EOF, return FALSE with *err set to 0, reporting this
+ * as an EOF.
+ *
+ * If we get fewer bytes than the specified number, return FALSE with
+ * *err set to WTAP_ERR_SHORT_READ, reporting this as a short read
+ * error.
+ *
+ * If we get a read error, return FALSE with *err and *err_info set
+ * appropriately.
+ */
+gboolean
+wtap_read_bytes_or_eof(FILE_T fh, void *buf, unsigned int count, int *err,
+ gchar **err_info)
+{
+ int bytes_read;
+
+ bytes_read = file_read(buf, count, fh);
+ if (bytes_read < 0 || (guint)bytes_read != count) {
+ *err = file_error(fh, err_info);
+ if (*err == 0 && bytes_read > 0)
+ *err = WTAP_ERR_SHORT_READ;
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/*
+ * Read a given number of bytes from a file into a buffer or, if
+ * buf is NULL, just discard them.
+ *
+ * If we succeed, return TRUE.
+ *
+ * If we get fewer bytes than the specified number, including getting
+ * an EOF, return FALSE with *err set to WTAP_ERR_SHORT_READ, reporting
+ * this as a short read error.
+ *
+ * If we get a read error, return FALSE with *err and *err_info set
+ * appropriately.
+ */
+gboolean
+wtap_read_bytes(FILE_T fh, void *buf, unsigned int count, int *err,
+ gchar **err_info)
+{
+ int bytes_read;
+
+ bytes_read = file_read(buf, count, fh);
+ if (bytes_read < 0 || (guint)bytes_read != count) {
+ *err = file_error(fh, err_info);
+ if (*err == 0)
+ *err = WTAP_ERR_SHORT_READ;
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/*
+ * Read packet data into a Buffer, growing the buffer as necessary.
+ *
+ * This returns an error on a short read, even if the short read hit
+ * the EOF immediately. (The assumption is that each packet has a
+ * header followed by raw packet data, and that we've already read the
+ * header, so if we get an EOF trying to read the packet data, the file
+ * has been cut short, even if the read didn't read any data at all.)
+ */
+gboolean
+wtap_read_packet_bytes(FILE_T fh, Buffer *buf, guint length, int *err,
+ gchar **err_info)
+{
+ gboolean rv;
+ ws_buffer_assure_space(buf, length);
+ rv = wtap_read_bytes(fh, ws_buffer_end_ptr(buf), length, err,
+ err_info);
+ if (rv) {
+ ws_buffer_increase_length(buf, length);
+ }
+ return rv;
+}
+
+/*
+ * Return an approximation of the amount of data we've read sequentially
+ * from the file so far. (gint64, in case that's 64 bits.)
+ */
+gint64
+wtap_read_so_far(wtap *wth)
+{
+ return file_tell_raw(wth->fh);
+}
+
+/* Perform global/initial initialization */
+void
+wtap_rec_init(wtap_rec *rec)
+{
+ memset(rec, 0, sizeof *rec);
+ ws_buffer_init(&rec->options_buf, 0);
+ /* In the future, see if we can create rec->block here once
+ * and have it be reused like the rest of rec.
+ * Currently it's recreated for each packet.
+ */
+}
+
+/* re-initialize record */
+void
+wtap_rec_reset(wtap_rec *rec)
+{
+ wtap_block_unref(rec->block);
+ rec->block = NULL;
+ rec->block_was_modified = FALSE;
+}
+
+/* clean up record metadata */
+void
+wtap_rec_cleanup(wtap_rec *rec)
+{
+ wtap_rec_reset(rec);
+ ws_buffer_free(&rec->options_buf);
+}
+
+wtap_block_t
+wtap_rec_generate_idb(const wtap_rec *rec)
+{
+ int tsprec;
+ ws_assert(rec->rec_type == REC_TYPE_PACKET);
+ if (rec->presence_flags & WTAP_HAS_TS) {
+ tsprec = rec->tsprec;
+ } else {
+ tsprec = WTAP_TSPREC_USEC;
+ /* The default */
+ }
+ return wtap_generate_idb(rec->rec_header.packet_header.pkt_encap, tsprec, 0);
+}
+
+gboolean
+wtap_seek_read(wtap *wth, gint64 seek_off, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info)
+{
+ /*
+ * Initialize the record to default values.
+ */
+ wtap_init_rec(wth, rec);
+ ws_buffer_clean(buf);
+
+ *err = 0;
+ *err_info = NULL;
+ if (!wth->subtype_seek_read(wth, seek_off, rec, buf, err, err_info)) {
+ if (rec->block != NULL) {
+ /*
+ * Unreference any block created for this record.
+ */
+ wtap_block_unref(rec->block);
+ rec->block = NULL;
+ }
+ return FALSE;
+ }
+
+ /*
+ * Is this a packet record?
+ */
+ if (rec->rec_type == REC_TYPE_PACKET) {
+ /*
+ * Make sure that it's not WTAP_ENCAP_PER_PACKET, as that
+ * probably means the file has that encapsulation type
+ * but the read routine didn't set this packet's
+ * encapsulation type.
+ */
+ ws_assert(rec->rec_header.packet_header.pkt_encap != WTAP_ENCAP_PER_PACKET);
+ ws_assert(rec->rec_header.packet_header.pkt_encap != WTAP_ENCAP_NONE);
+ }
+
+ return TRUE;
+}
+
+static gboolean
+wtap_full_file_read_file(wtap *wth, FILE_T fh, wtap_rec *rec, Buffer *buf, int *err, gchar **err_info)
+{
+ gint64 file_size;
+ int packet_size = 0;
+ const int block_size = 1024 * 1024;
+
+ if ((file_size = wtap_file_size(wth, err)) == -1)
+ return FALSE;
+
+ if (file_size > G_MAXINT) {
+ /*
+ * Avoid allocating space for an immensely-large file.
+ */
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("%s: File has %" PRId64 "-byte packet, bigger than maximum of %u",
+ wtap_encap_name(wth->file_encap), file_size, G_MAXINT);
+ return FALSE;
+ }
+
+ /*
+ * Compressed files might expand to a larger size than the actual file
+ * size. Try to read the full size and then read in smaller increments
+ * to avoid frequent memory reallocations.
+ */
+ int buffer_size = block_size * (1 + (int)file_size / block_size);
+ for (;;) {
+ if (buffer_size <= 0) {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = ws_strdup_printf("%s: Uncompressed file is bigger than maximum of %u",
+ wtap_encap_name(wth->file_encap), G_MAXINT);
+ return FALSE;
+ }
+ ws_buffer_assure_space(buf, buffer_size);
+ int nread = file_read(ws_buffer_start_ptr(buf) + packet_size, buffer_size - packet_size, fh);
+ if (nread < 0) {
+ *err = file_error(fh, err_info);
+ if (*err == 0)
+ *err = WTAP_ERR_BAD_FILE;
+ return FALSE;
+ }
+ packet_size += nread;
+ if (packet_size != buffer_size) {
+ /* EOF */
+ break;
+ }
+ buffer_size += block_size;
+ }
+
+ rec->rec_type = REC_TYPE_PACKET;
+ rec->presence_flags = 0; /* yes, we have no bananas^Wtime stamp */
+ rec->ts.secs = 0;
+ rec->ts.nsecs = 0;
+ rec->rec_header.packet_header.caplen = packet_size;
+ rec->rec_header.packet_header.len = packet_size;
+
+ return TRUE;
+}
+
+gboolean
+wtap_full_file_read(wtap *wth, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info, gint64 *data_offset)
+{
+ gint64 offset = file_tell(wth->fh);
+
+ /* There is only one packet with the full file contents. */
+ if (offset != 0) {
+ *err = 0;
+ return FALSE;
+ }
+
+ *data_offset = offset;
+ return wtap_full_file_read_file(wth, wth->fh, rec, buf, err, err_info);
+}
+
+gboolean
+wtap_full_file_seek_read(wtap *wth, gint64 seek_off, wtap_rec *rec, Buffer *buf, int *err, gchar **err_info)
+{
+ /* There is only one packet with the full file contents. */
+ if (seek_off > 0) {
+ *err = 0;
+ return FALSE;
+ }
+
+ if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
+ return FALSE;
+
+ return wtap_full_file_read_file(wth, wth->random_fh, rec, buf, err, err_info);
+}
+
+void
+wtap_buffer_append_epdu_tag(Buffer *buf, guint16 epdu_tag, const guint8 *data, guint16 data_len)
+{
+ guint8 pad_len = 0;
+ guint space_needed = 4; /* 2 for tag field, 2 for length field */
+ guint8 *buf_data;
+
+ if (epdu_tag != 0 && data != NULL && data_len != 0) {
+ pad_len += PADDING4(data_len);
+ space_needed += data_len + pad_len;
+ }
+ else {
+ data_len = 0;
+ }
+
+ ws_buffer_assure_space(buf, space_needed);
+ buf_data = ws_buffer_end_ptr(buf);
+ memset(buf_data, 0, space_needed);
+ phton16(buf_data + 0, epdu_tag);
+ /* It seems as though the convention for exported_pdu is to specify
+ * the fully-padded length of the tag value, not just its useful length.
+ * e.g. the string value 'a' would be given a length of 4.
+ */
+ phton16(buf_data + 2, data_len + pad_len);
+ if (data_len > 0) {
+ /* Still only copy as many bytes as we actually have */
+ memcpy(buf_data + 4, data, data_len);
+ }
+ ws_buffer_increase_length(buf, space_needed);
+}
+
+void
+wtap_buffer_append_epdu_uint(Buffer *buf, guint16 epdu_tag, guint32 val)
+{
+ const guint space_needed = 8; /* 2 for tag field, 2 for length field, 4 for value */
+ guint8 *buf_data;
+
+ ws_assert(epdu_tag != 0);
+ ws_buffer_assure_space(buf, space_needed);
+ buf_data = ws_buffer_end_ptr(buf);
+ memset(buf_data, 0, space_needed);
+ phton16(buf_data + 0, epdu_tag);
+ phton16(buf_data + 2, 4);
+ phton32(buf_data + 4, val);
+ ws_buffer_increase_length(buf, space_needed);
+}
+
+void
+wtap_buffer_append_epdu_string(Buffer *buf, guint16 epdu_tag, const char *val)
+{
+ size_t string_len;
+
+ string_len = strlen(val);
+ /*
+ * Cut off string length at G_MAXUINT16.
+ *
+ * XXX - make sure we don't leave an incomplete UTF-8
+ * sequence at the end.
+ */
+ if (string_len > G_MAXUINT16)
+ string_len = G_MAXUINT16;
+ wtap_buffer_append_epdu_tag(buf, epdu_tag, val, (guint16) string_len);
+}
+
+gint
+wtap_buffer_append_epdu_end(Buffer *buf)
+{
+ const guint space_needed = 4; /* 2 for tag (=0000), 2 for length field (=0) */
+ guint8 *buf_data;
+
+ ws_buffer_assure_space(buf, space_needed);
+ buf_data = ws_buffer_end_ptr(buf);
+ memset(buf_data, 0, space_needed);
+ ws_buffer_increase_length(buf, space_needed);
+
+ return (gint)ws_buffer_length(buf);
+}
+
+/*
+ * Initialize the library.
+ */
+void
+wtap_init(gboolean load_wiretap_plugins)
+{
+ init_open_routines();
+ wtap_opttypes_initialize();
+ wtap_init_encap_types();
+ wtap_init_file_type_subtypes();
+ if (load_wiretap_plugins) {
+#ifdef HAVE_PLUGINS
+ libwiretap_plugins = plugins_init(WS_PLUGIN_WIRETAP);
+#endif
+ g_slist_foreach(wtap_plugins, call_plugin_register_wtap_module, NULL);
+ }
+}
+
+/*
+ * Cleanup the library
+ */
+void
+wtap_cleanup(void)
+{
+ wtap_cleanup_encap_types();
+ wtap_opttypes_cleanup();
+ ws_buffer_cleanup();
+ cleanup_open_routines();
+ g_slist_free(wtap_plugins);
+ wtap_plugins = NULL;
+#ifdef HAVE_PLUGINS
+ plugins_cleanup(libwiretap_plugins);
+ libwiretap_plugins = NULL;
+#endif
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/wiretap/wtap.h b/wiretap/wtap.h
new file mode 100644
index 00000000..93bcca42
--- /dev/null
+++ b/wiretap/wtap.h
@@ -0,0 +1,2646 @@
+/** @file
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __WTAP_H__
+#define __WTAP_H__
+
+#include <wireshark.h>
+#include <time.h>
+#include <wsutil/buffer.h>
+#include <wsutil/nstime.h>
+#include <wsutil/inet_addr.h>
+#include "wtap_opttypes.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/* Encapsulation types. Choose names that truly reflect
+ * what is contained in the packet trace file.
+ *
+ * WTAP_ENCAP_PER_PACKET is a value passed to "wtap_dump_open()" or
+ * "wtap_dump_fdopen()" to indicate that there is no single encapsulation
+ * type for all packets in the file; this may cause those routines to
+ * fail if the capture file format being written can't support that.
+ * It's also returned by "wtap_file_encap()" for capture files that
+ * don't have a single encapsulation type for all packets in the file.
+ *
+ * WTAP_ENCAP_UNKNOWN is returned by "wtap_pcap_encap_to_wtap_encap()"
+ * if it's handed an unknown encapsulation. It is also used by file
+ * types for encapsulations which are unsupported by libwiretap.
+ *
+ * WTAP_ENCAP_NONE is an initial value used by file types like pcapng
+ * that do not have a single file level encapsulation type. If and when
+ * something that indicate encapsulation is read, the encapsulation will
+ * change (possibly to WTAP_ENCAP_PER_PACKET) and appropriate IDBs will
+ * be generated. If a file type uses this value, it MUST provide IDBs
+ * (possibly fake) when the encapsulation changes; otherwise, it should
+ * return WTAP_ENCAP_UNKNOWN so that attempts to write an output file
+ * without reading the entire input file first fail gracefully.
+ *
+ * WTAP_ENCAP_FDDI_BITSWAPPED is for FDDI captures on systems where the
+ * MAC addresses you get from the hardware are bit-swapped. Ideally,
+ * the driver would tell us that, but I know of none that do, so, for
+ * now, we base it on the machine on which we're *reading* the
+ * capture, rather than on the machine on which the capture was taken
+ * (they're probably likely to be the same). We assume that they're
+ * bit-swapped on everything except for systems running Ultrix, Alpha
+ * systems, and BSD/OS systems (that's what "tcpdump" does; I guess
+ * Digital decided to bit-swap addresses in the hardware or in the
+ * driver, and I guess BSDI bit-swapped them in the driver, given that
+ * BSD/OS generally runs on Boring Old PC's). If we create a wiretap
+ * save file format, we'd use the WTAP_ENCAP values to flag the
+ * encapsulation of a packet, so there we'd at least be able to base
+ * it on the machine on which the capture was taken.
+ *
+ * WTAP_ENCAP_LINUX_ATM_CLIP is the encapsulation you get with the
+ * ATM on Linux code from <http://linux-atm.sourceforge.net/>;
+ * that code adds a DLT_ATM_CLIP DLT_ code of 19, and that
+ * encapsulation isn't the same as the DLT_ATM_RFC1483 encapsulation
+ * presumably used on some BSD systems, which we turn into
+ * WTAP_ENCAP_ATM_RFC1483.
+ *
+ * WTAP_ENCAP_NULL corresponds to DLT_NULL from "libpcap". This
+ * corresponds to
+ *
+ * 1) PPP-over-HDLC encapsulation, at least with some versions
+ * of ISDN4BSD (but not the current ones, it appears, unless
+ * I've missed something);
+ *
+ * 2) a 4-byte header containing the AF_ address family, in
+ * the byte order of the machine that saved the capture,
+ * for the packet, as used on many BSD systems for the
+ * loopback device and some other devices, or a 4-byte header
+ * containing the AF_ address family in network byte order,
+ * as used on recent OpenBSD systems for the loopback device;
+ *
+ * 3) a 4-byte header containing 2 octets of 0 and an Ethernet
+ * type in the byte order from an Ethernet header, that being
+ * what older versions of "libpcap" on Linux turn the Ethernet
+ * header for loopback interfaces into (0.6.0 and later versions
+ * leave the Ethernet header alone and make it DLT_EN10MB). */
+#define WTAP_ENCAP_NONE -2
+#define WTAP_ENCAP_PER_PACKET -1
+#define WTAP_ENCAP_UNKNOWN 0
+#define WTAP_ENCAP_ETHERNET 1
+#define WTAP_ENCAP_TOKEN_RING 2
+#define WTAP_ENCAP_SLIP 3
+#define WTAP_ENCAP_PPP 4
+#define WTAP_ENCAP_FDDI 5
+#define WTAP_ENCAP_FDDI_BITSWAPPED 6
+#define WTAP_ENCAP_RAW_IP 7
+#define WTAP_ENCAP_ARCNET 8
+#define WTAP_ENCAP_ARCNET_LINUX 9
+#define WTAP_ENCAP_ATM_RFC1483 10
+#define WTAP_ENCAP_LINUX_ATM_CLIP 11
+#define WTAP_ENCAP_LAPB 12
+#define WTAP_ENCAP_ATM_PDUS 13
+#define WTAP_ENCAP_ATM_PDUS_UNTRUNCATED 14
+#define WTAP_ENCAP_NULL 15
+#define WTAP_ENCAP_ASCEND 16
+#define WTAP_ENCAP_ISDN 17
+#define WTAP_ENCAP_IP_OVER_FC 18
+#define WTAP_ENCAP_PPP_WITH_PHDR 19
+#define WTAP_ENCAP_IEEE_802_11 20
+#define WTAP_ENCAP_IEEE_802_11_PRISM 21
+#define WTAP_ENCAP_IEEE_802_11_WITH_RADIO 22
+#define WTAP_ENCAP_IEEE_802_11_RADIOTAP 23
+#define WTAP_ENCAP_IEEE_802_11_AVS 24
+#define WTAP_ENCAP_SLL 25
+#define WTAP_ENCAP_FRELAY 26
+#define WTAP_ENCAP_FRELAY_WITH_PHDR 27
+#define WTAP_ENCAP_CHDLC 28
+#define WTAP_ENCAP_CISCO_IOS 29
+#define WTAP_ENCAP_LOCALTALK 30
+#define WTAP_ENCAP_OLD_PFLOG 31
+#define WTAP_ENCAP_HHDLC 32
+#define WTAP_ENCAP_DOCSIS 33
+#define WTAP_ENCAP_COSINE 34
+#define WTAP_ENCAP_WFLEET_HDLC 35
+#define WTAP_ENCAP_SDLC 36
+#define WTAP_ENCAP_TZSP 37
+#define WTAP_ENCAP_ENC 38
+#define WTAP_ENCAP_PFLOG 39
+#define WTAP_ENCAP_CHDLC_WITH_PHDR 40
+#define WTAP_ENCAP_BLUETOOTH_H4 41
+#define WTAP_ENCAP_MTP2 42
+#define WTAP_ENCAP_MTP3 43
+#define WTAP_ENCAP_IRDA 44
+#define WTAP_ENCAP_USER0 45
+#define WTAP_ENCAP_USER1 46
+#define WTAP_ENCAP_USER2 47
+#define WTAP_ENCAP_USER3 48
+#define WTAP_ENCAP_USER4 49
+#define WTAP_ENCAP_USER5 50
+#define WTAP_ENCAP_USER6 51
+#define WTAP_ENCAP_USER7 52
+#define WTAP_ENCAP_USER8 53
+#define WTAP_ENCAP_USER9 54
+#define WTAP_ENCAP_USER10 55
+#define WTAP_ENCAP_USER11 56
+#define WTAP_ENCAP_USER12 57
+#define WTAP_ENCAP_USER13 58
+#define WTAP_ENCAP_USER14 59
+#define WTAP_ENCAP_USER15 60
+#define WTAP_ENCAP_SYMANTEC 61
+#define WTAP_ENCAP_APPLE_IP_OVER_IEEE1394 62
+#define WTAP_ENCAP_BACNET_MS_TP 63
+#define WTAP_ENCAP_NETTL_RAW_ICMP 64
+#define WTAP_ENCAP_NETTL_RAW_ICMPV6 65
+#define WTAP_ENCAP_GPRS_LLC 66
+#define WTAP_ENCAP_JUNIPER_ATM1 67
+#define WTAP_ENCAP_JUNIPER_ATM2 68
+#define WTAP_ENCAP_REDBACK 69
+#define WTAP_ENCAP_NETTL_RAW_IP 70
+#define WTAP_ENCAP_NETTL_ETHERNET 71
+#define WTAP_ENCAP_NETTL_TOKEN_RING 72
+#define WTAP_ENCAP_NETTL_FDDI 73
+#define WTAP_ENCAP_NETTL_UNKNOWN 74
+#define WTAP_ENCAP_MTP2_WITH_PHDR 75
+#define WTAP_ENCAP_JUNIPER_PPPOE 76
+#define WTAP_ENCAP_GCOM_TIE1 77
+#define WTAP_ENCAP_GCOM_SERIAL 78
+#define WTAP_ENCAP_NETTL_X25 79
+#define WTAP_ENCAP_K12 80
+#define WTAP_ENCAP_JUNIPER_MLPPP 81
+#define WTAP_ENCAP_JUNIPER_MLFR 82
+#define WTAP_ENCAP_JUNIPER_ETHER 83
+#define WTAP_ENCAP_JUNIPER_PPP 84
+#define WTAP_ENCAP_JUNIPER_FRELAY 85
+#define WTAP_ENCAP_JUNIPER_CHDLC 86
+#define WTAP_ENCAP_JUNIPER_GGSN 87
+#define WTAP_ENCAP_LINUX_LAPD 88
+#define WTAP_ENCAP_CATAPULT_DCT2000 89
+#define WTAP_ENCAP_BER 90
+#define WTAP_ENCAP_JUNIPER_VP 91
+#define WTAP_ENCAP_USB_FREEBSD 92
+#define WTAP_ENCAP_IEEE802_16_MAC_CPS 93
+#define WTAP_ENCAP_NETTL_RAW_TELNET 94
+#define WTAP_ENCAP_USB_LINUX 95
+#define WTAP_ENCAP_MPEG 96
+#define WTAP_ENCAP_PPI 97
+#define WTAP_ENCAP_ERF 98
+#define WTAP_ENCAP_BLUETOOTH_H4_WITH_PHDR 99
+#define WTAP_ENCAP_SITA 100
+#define WTAP_ENCAP_SCCP 101
+#define WTAP_ENCAP_BLUETOOTH_HCI 102 /*raw packets without a transport layer header e.g. H4*/
+#define WTAP_ENCAP_IPMB_KONTRON 103
+#define WTAP_ENCAP_IEEE802_15_4 104
+#define WTAP_ENCAP_X2E_XORAYA 105
+#define WTAP_ENCAP_FLEXRAY 106
+#define WTAP_ENCAP_LIN 107
+#define WTAP_ENCAP_MOST 108
+#define WTAP_ENCAP_CAN20B 109
+#define WTAP_ENCAP_LAYER1_EVENT 110
+#define WTAP_ENCAP_X2E_SERIAL 111
+#define WTAP_ENCAP_I2C_LINUX 112
+#define WTAP_ENCAP_IEEE802_15_4_NONASK_PHY 113
+#define WTAP_ENCAP_TNEF 114
+#define WTAP_ENCAP_USB_LINUX_MMAPPED 115
+#define WTAP_ENCAP_GSM_UM 116
+#define WTAP_ENCAP_DPNSS 117
+#define WTAP_ENCAP_PACKETLOGGER 118
+#define WTAP_ENCAP_NSTRACE_1_0 119
+#define WTAP_ENCAP_NSTRACE_2_0 120
+#define WTAP_ENCAP_FIBRE_CHANNEL_FC2 121
+#define WTAP_ENCAP_FIBRE_CHANNEL_FC2_WITH_FRAME_DELIMS 122
+#define WTAP_ENCAP_JPEG_JFIF 123 /* obsoleted by WTAP_ENCAP_MIME*/
+#define WTAP_ENCAP_IPNET 124
+#define WTAP_ENCAP_SOCKETCAN 125
+#define WTAP_ENCAP_IEEE_802_11_NETMON 126
+#define WTAP_ENCAP_IEEE802_15_4_NOFCS 127
+#define WTAP_ENCAP_RAW_IPFIX 128
+#define WTAP_ENCAP_RAW_IP4 129
+#define WTAP_ENCAP_RAW_IP6 130
+#define WTAP_ENCAP_LAPD 131
+#define WTAP_ENCAP_DVBCI 132
+#define WTAP_ENCAP_MUX27010 133
+#define WTAP_ENCAP_MIME 134
+#define WTAP_ENCAP_NETANALYZER 135
+#define WTAP_ENCAP_NETANALYZER_TRANSPARENT 136
+#define WTAP_ENCAP_IP_OVER_IB_SNOOP 137
+#define WTAP_ENCAP_MPEG_2_TS 138
+#define WTAP_ENCAP_PPP_ETHER 139
+#define WTAP_ENCAP_NFC_LLCP 140
+#define WTAP_ENCAP_NFLOG 141
+#define WTAP_ENCAP_V5_EF 142
+#define WTAP_ENCAP_BACNET_MS_TP_WITH_PHDR 143
+#define WTAP_ENCAP_IXVERIWAVE 144
+#define WTAP_ENCAP_SDH 145
+#define WTAP_ENCAP_DBUS 146
+#define WTAP_ENCAP_AX25_KISS 147
+#define WTAP_ENCAP_AX25 148
+#define WTAP_ENCAP_SCTP 149
+#define WTAP_ENCAP_INFINIBAND 150
+#define WTAP_ENCAP_JUNIPER_SVCS 151
+#define WTAP_ENCAP_USBPCAP 152
+#define WTAP_ENCAP_RTAC_SERIAL 153
+#define WTAP_ENCAP_BLUETOOTH_LE_LL 154
+#define WTAP_ENCAP_WIRESHARK_UPPER_PDU 155
+#define WTAP_ENCAP_STANAG_4607 156
+#define WTAP_ENCAP_STANAG_5066_D_PDU 157
+#define WTAP_ENCAP_NETLINK 158
+#define WTAP_ENCAP_BLUETOOTH_LINUX_MONITOR 159
+#define WTAP_ENCAP_BLUETOOTH_BREDR_BB 160
+#define WTAP_ENCAP_BLUETOOTH_LE_LL_WITH_PHDR 161
+#define WTAP_ENCAP_NSTRACE_3_0 162
+#define WTAP_ENCAP_LOGCAT 163
+#define WTAP_ENCAP_LOGCAT_BRIEF 164
+#define WTAP_ENCAP_LOGCAT_PROCESS 165
+#define WTAP_ENCAP_LOGCAT_TAG 166
+#define WTAP_ENCAP_LOGCAT_THREAD 167
+#define WTAP_ENCAP_LOGCAT_TIME 168
+#define WTAP_ENCAP_LOGCAT_THREADTIME 169
+#define WTAP_ENCAP_LOGCAT_LONG 170
+#define WTAP_ENCAP_PKTAP 171
+#define WTAP_ENCAP_EPON 172
+#define WTAP_ENCAP_IPMI_TRACE 173
+#define WTAP_ENCAP_LOOP 174
+#define WTAP_ENCAP_JSON 175
+#define WTAP_ENCAP_NSTRACE_3_5 176
+#define WTAP_ENCAP_ISO14443 177
+#define WTAP_ENCAP_GFP_T 178
+#define WTAP_ENCAP_GFP_F 179
+#define WTAP_ENCAP_IP_OVER_IB_PCAP 180
+#define WTAP_ENCAP_JUNIPER_VN 181
+#define WTAP_ENCAP_USB_DARWIN 182
+#define WTAP_ENCAP_LORATAP 183
+#define WTAP_ENCAP_3MB_ETHERNET 184
+#define WTAP_ENCAP_VSOCK 185
+#define WTAP_ENCAP_NORDIC_BLE 186
+#define WTAP_ENCAP_NETMON_NET_NETEVENT 187
+#define WTAP_ENCAP_NETMON_HEADER 188
+#define WTAP_ENCAP_NETMON_NET_FILTER 189
+#define WTAP_ENCAP_NETMON_NETWORK_INFO_EX 190
+#define WTAP_ENCAP_MA_WFP_CAPTURE_V4 191
+#define WTAP_ENCAP_MA_WFP_CAPTURE_V6 192
+#define WTAP_ENCAP_MA_WFP_CAPTURE_2V4 193
+#define WTAP_ENCAP_MA_WFP_CAPTURE_2V6 194
+#define WTAP_ENCAP_MA_WFP_CAPTURE_AUTH_V4 195
+#define WTAP_ENCAP_MA_WFP_CAPTURE_AUTH_V6 196
+#define WTAP_ENCAP_JUNIPER_ST 197
+#define WTAP_ENCAP_ETHERNET_MPACKET 198
+#define WTAP_ENCAP_DOCSIS31_XRA31 199
+#define WTAP_ENCAP_DPAUXMON 200
+#define WTAP_ENCAP_RUBY_MARSHAL 201
+#define WTAP_ENCAP_RFC7468 202
+#define WTAP_ENCAP_SYSTEMD_JOURNAL 203 /* Event, not a packet */
+#define WTAP_ENCAP_EBHSCR 204
+#define WTAP_ENCAP_VPP 205
+#define WTAP_ENCAP_IEEE802_15_4_TAP 206
+#define WTAP_ENCAP_LOG_3GPP 207
+#define WTAP_ENCAP_USB_2_0 208
+#define WTAP_ENCAP_MP4 209
+#define WTAP_ENCAP_SLL2 210
+#define WTAP_ENCAP_ZWAVE_SERIAL 211
+#define WTAP_ENCAP_ETW 212
+#define WTAP_ENCAP_ERI_ENB_LOG 213
+#define WTAP_ENCAP_ZBNCP 214
+#define WTAP_ENCAP_USB_2_0_LOW_SPEED 215
+#define WTAP_ENCAP_USB_2_0_FULL_SPEED 216
+#define WTAP_ENCAP_USB_2_0_HIGH_SPEED 217
+#define WTAP_ENCAP_AUTOSAR_DLT 218
+#define WTAP_ENCAP_AUERSWALD_LOG 219
+#define WTAP_ENCAP_ATSC_ALP 220
+#define WTAP_ENCAP_FIRA_UCI 221
+#define WTAP_ENCAP_SILABS_DEBUG_CHANNEL 222
+#define WTAP_ENCAP_MDB 223
+
+/* After adding new item here, please also add new item to encap_table_base array */
+
+#define WTAP_NUM_ENCAP_TYPES wtap_get_num_encap_types()
+
+/* Value to be used as a file type/subtype value if the type is unknown */
+#define WTAP_FILE_TYPE_SUBTYPE_UNKNOWN -1
+
+/* timestamp precision (currently only these values are supported) */
+#define WTAP_TSPREC_UNKNOWN -2
+#define WTAP_TSPREC_PER_PACKET -1 /* as a per-file value, means per-packet */
+/*
+ * These values are the number of digits of precision after the integral part.
+ * Thry're the same as WS_TSPREC values; we define them here so that
+ * tools/make-enums.py sees them.
+ */
+#define WTAP_TSPREC_SEC 0
+#define WTAP_TSPREC_100_MSEC 1
+#define WTAP_TSPREC_DSEC 1 /* Backwards compatibility */
+#define WTAP_TSPREC_10_MSEC 2
+#define WTAP_TSPREC_CSEC 2 /* Backwards compatibility */
+#define WTAP_TSPREC_MSEC 3
+#define WTAP_TSPREC_100_USEC 4
+#define WTAP_TSPREC_10_USEC 5
+#define WTAP_TSPREC_USEC 6
+#define WTAP_TSPREC_100_NSEC 7
+#define WTAP_TSPREC_10_NSEC 8
+#define WTAP_TSPREC_NSEC 9
+/* if you add to the above, update wtap_tsprec_string() */
+
+/*
+ * Maximum packet sizes.
+ *
+ * For most link-layer types, we use 262144, which is currently
+ * libpcap's MAXIMUM_SNAPLEN.
+ *
+ * For WTAP_ENCAP_DBUS, the maximum is 128MiB, as per
+ *
+ * https://dbus.freedesktop.org/doc/dbus-specification.html#message-protocol-messages
+ *
+ * For WTAP_ENCAP_EBHSCR, the maximum is 8MiB, as per
+ *
+ * https://www.elektrobit.com/ebhscr
+ *
+ * For WTAP_ENCAP_USBPCAP, the maximum is 128MiB, as per
+ *
+ * https://gitlab.com/wireshark/wireshark/-/issues/15985
+ *
+ * We don't want to write out files that specify a maximum packet size
+ * greater than 262144 if we don't have to, as software reading those
+ * files might allocate a buffer much larger than necessary, wasting memory.
+ */
+#define WTAP_MAX_PACKET_SIZE_STANDARD 262144U
+#define WTAP_MAX_PACKET_SIZE_USBPCAP (128U*1024U*1024U)
+#define WTAP_MAX_PACKET_SIZE_EBHSCR (32U*1024U*1024U)
+#define WTAP_MAX_PACKET_SIZE_DBUS (128U*1024U*1024U)
+
+/*
+ * "Pseudo-headers" are used to supply to the clients of wiretap
+ * per-packet information that's not part of the packet payload
+ * proper.
+ *
+ * NOTE: do not use pseudo-header structures to hold information
+ * used by the code to read a particular capture file type; to
+ * keep that sort of state information, add a new structure for
+ * that private information to "wtap-int.h", add a pointer to that
+ * type of structure to the "capture" member of the "struct wtap"
+ * structure, and allocate one of those structures and set that member
+ * in the "open" routine for that capture file type if the open
+ * succeeds. See various other capture file type handlers for examples
+ * of that.
+ */
+
+
+/* Packet "pseudo-header" information for Ethernet capture files. */
+struct eth_phdr {
+ gint fcs_len; /* Number of bytes of FCS - -1 means "unknown" */
+};
+
+/* Packet "pseudo-header" information for capture files for traffic
+ between DTE and DCE. */
+#define FROM_DCE 0x80
+struct dte_dce_phdr {
+ guint8 flags; /* ENCAP_LAPB, ENCAP_V120, ENCAP_FRELAY: 1st bit means From DCE */
+};
+
+/* Packet "pseudo-header" information for ISDN capture files. */
+
+/* Direction */
+struct isdn_phdr {
+ gboolean uton;
+ guint8 channel; /* 0 = D-channel; n = B-channel n */
+};
+
+/* Packet "pseudo-header" for ATM capture files.
+ Not all of this information is supplied by all capture types.
+ These originally came from the Network General (DOS-based)
+ ATM Sniffer file format, but we've added some additional
+ items. */
+
+/*
+ * Status bits.
+ */
+#define ATM_RAW_CELL 0x01 /* TRUE if the packet is a single cell */
+#define ATM_NO_HEC 0x02 /* TRUE if the cell has HEC stripped out */
+#define ATM_AAL2_NOPHDR 0x04 /* TRUE if the AAL2 PDU has no pseudo-header */
+#define ATM_REASSEMBLY_ERROR 0x08 /* TRUE if this is an incompletely-reassembled PDU */
+
+/*
+ * AAL types.
+ */
+#define AAL_UNKNOWN 0 /* AAL unknown */
+#define AAL_1 1 /* AAL1 */
+#define AAL_2 2 /* AAL2 */
+#define AAL_3_4 3 /* AAL3/4 */
+#define AAL_5 4 /* AAL5 */
+#define AAL_USER 5 /* User AAL */
+#define AAL_SIGNALLING 6 /* Signaling AAL */
+#define AAL_OAMCELL 7 /* OAM cell */
+
+/*
+ * Traffic types.
+ */
+#define TRAF_UNKNOWN 0 /* Unknown */
+#define TRAF_LLCMX 1 /* LLC multiplexed (RFC 1483) */
+#define TRAF_VCMX 2 /* VC multiplexed (RFC 1483) */
+#define TRAF_LANE 3 /* LAN Emulation */
+#define TRAF_ILMI 4 /* ILMI */
+#define TRAF_FR 5 /* Frame Relay */
+#define TRAF_SPANS 6 /* FORE SPANS */
+#define TRAF_IPSILON 7 /* Ipsilon */
+#define TRAF_UMTS_FP 8 /* UMTS Frame Protocol */
+#define TRAF_GPRS_NS 9 /* GPRS Network Services */
+#define TRAF_SSCOP 10 /* SSCOP */
+
+/*
+ * Traffic subtypes.
+ */
+#define TRAF_ST_UNKNOWN 0 /* Unknown */
+
+/*
+ * For TRAF_VCMX:
+ */
+#define TRAF_ST_VCMX_802_3_FCS 1 /* 802.3 with an FCS */
+#define TRAF_ST_VCMX_802_4_FCS 2 /* 802.4 with an FCS */
+#define TRAF_ST_VCMX_802_5_FCS 3 /* 802.5 with an FCS */
+#define TRAF_ST_VCMX_FDDI_FCS 4 /* FDDI with an FCS */
+#define TRAF_ST_VCMX_802_6_FCS 5 /* 802.6 with an FCS */
+#define TRAF_ST_VCMX_802_3 7 /* 802.3 without an FCS */
+#define TRAF_ST_VCMX_802_4 8 /* 802.4 without an FCS */
+#define TRAF_ST_VCMX_802_5 9 /* 802.5 without an FCS */
+#define TRAF_ST_VCMX_FDDI 10 /* FDDI without an FCS */
+#define TRAF_ST_VCMX_802_6 11 /* 802.6 without an FCS */
+#define TRAF_ST_VCMX_FRAGMENTS 12 /* Fragments */
+#define TRAF_ST_VCMX_BPDU 13 /* BPDU */
+
+/*
+ * For TRAF_LANE:
+ */
+#define TRAF_ST_LANE_LE_CTRL 1 /* LANE: LE Ctrl */
+#define TRAF_ST_LANE_802_3 2 /* LANE: 802.3 */
+#define TRAF_ST_LANE_802_5 3 /* LANE: 802.5 */
+#define TRAF_ST_LANE_802_3_MC 4 /* LANE: 802.3 multicast */
+#define TRAF_ST_LANE_802_5_MC 5 /* LANE: 802.5 multicast */
+
+/*
+ * For TRAF_IPSILON:
+ */
+#define TRAF_ST_IPSILON_FT0 1 /* Ipsilon: Flow Type 0 */
+#define TRAF_ST_IPSILON_FT1 2 /* Ipsilon: Flow Type 1 */
+#define TRAF_ST_IPSILON_FT2 3 /* Ipsilon: Flow Type 2 */
+
+struct atm_phdr {
+ guint32 flags; /* status flags */
+ guint8 aal; /* AAL of the traffic */
+ guint8 type; /* traffic type */
+ guint8 subtype; /* traffic subtype */
+ guint16 vpi; /* virtual path identifier */
+ guint16 vci; /* virtual circuit identifier */
+ guint8 aal2_cid; /* channel id */
+ guint16 channel; /* link: 0 for DTE->DCE, 1 for DCE->DTE */
+ guint16 cells; /* number of cells */
+ guint16 aal5t_u2u; /* user-to-user indicator */
+ guint16 aal5t_len; /* length of the packet */
+ guint32 aal5t_chksum; /* checksum for AAL5 packet */
+};
+
+/* Packet "pseudo-header" for the output from "wandsession", "wannext",
+ "wandisplay", and similar commands on Lucent/Ascend access equipment. */
+
+#define ASCEND_MAX_STR_LEN 64
+
+#define ASCEND_PFX_WDS_X 1
+#define ASCEND_PFX_WDS_R 2
+#define ASCEND_PFX_WDD 3
+#define ASCEND_PFX_ISDN_X 4
+#define ASCEND_PFX_ISDN_R 5
+#define ASCEND_PFX_ETHER 6
+
+struct ascend_phdr {
+ guint16 type; /* ASCEND_PFX_*, as defined above */
+ char user[ASCEND_MAX_STR_LEN]; /* Username, from wandsession header */
+ guint32 sess; /* Session number, from wandsession header */
+ char call_num[ASCEND_MAX_STR_LEN]; /* Called number, from WDD header */
+ guint32 chunk; /* Chunk number, from WDD header */
+ guint32 task; /* Task number */
+};
+
+/* Packet "pseudo-header" for point-to-point links with direction flags. */
+struct p2p_phdr {
+ gboolean sent;
+};
+
+/*
+ * Packet "pseudo-header" information for 802.11.
+ * Radio information is only present in this form for
+ * WTAP_ENCAP_IEEE_802_11_WITH_RADIO. This is used for file formats in
+ * which the radio information isn't provided as a pseudo-header in the
+ * packet data. It is also used by the dissectors for the pseudo-headers
+ * in the packet data to supply radio information, in a form independent
+ * of the file format and pseudo-header format, to the "802.11 radio"
+ * dissector.
+ *
+ * Signal strength, etc. information:
+ *
+ * Raw signal strength can be measured in milliwatts.
+ * It can also be represented as dBm, which is 10 times the log base 10
+ * of the signal strength in mW.
+ *
+ * The Receive Signal Strength Indicator is an integer in the range 0 to 255.
+ * The actual RSSI value for a given signal strength is dependent on the
+ * vendor (and perhaps on the adapter). The maximum possible RSSI value
+ * is also dependent on the vendor and perhaps the adapter.
+ *
+ * The signal strength can be represented as a percentage, which is 100
+ * times the ratio of the RSSI and the maximum RSSI.
+ */
+
+/*
+ * PHY types.
+ */
+#define PHDR_802_11_PHY_UNKNOWN 0 /* PHY not known */
+#define PHDR_802_11_PHY_11_FHSS 1 /* 802.11 FHSS */
+#define PHDR_802_11_PHY_11_IR 2 /* 802.11 IR */
+#define PHDR_802_11_PHY_11_DSSS 3 /* 802.11 DSSS */
+#define PHDR_802_11_PHY_11B 4 /* 802.11b */
+#define PHDR_802_11_PHY_11A 5 /* 802.11a */
+#define PHDR_802_11_PHY_11G 6 /* 802.11g */
+#define PHDR_802_11_PHY_11N 7 /* 802.11n */
+#define PHDR_802_11_PHY_11AC 8 /* 802.11ac */
+#define PHDR_802_11_PHY_11AD 9 /* 802.11ad */
+#define PHDR_802_11_PHY_11AH 10 /* 802.11ah */
+#define PHDR_802_11_PHY_11AX 11 /* 802.11ax */
+#define PHDR_802_11_PHY_11BE 12 /* 802.11be - EHT */
+
+/*
+ * PHY-specific information.
+ */
+
+/*
+ * 802.11 legacy FHSS.
+ */
+struct ieee_802_11_fhss {
+ guint has_hop_set:1;
+ guint has_hop_pattern:1;
+ guint has_hop_index:1;
+
+ guint8 hop_set; /* Hop set */
+ guint8 hop_pattern; /* Hop pattern */
+ guint8 hop_index; /* Hop index */
+};
+
+/*
+ * 802.11b.
+ */
+struct ieee_802_11b {
+ /* Which of this information is present? */
+ guint has_short_preamble:1;
+
+ gboolean short_preamble; /* Short preamble */
+};
+
+/*
+ * 802.11a.
+ */
+struct ieee_802_11a {
+ /* Which of this information is present? */
+ guint has_channel_type:1;
+ guint has_turbo_type:1;
+
+ guint channel_type:2;
+ guint turbo_type:2;
+};
+
+/*
+ * Channel type values.
+ */
+#define PHDR_802_11A_CHANNEL_TYPE_NORMAL 0
+#define PHDR_802_11A_CHANNEL_TYPE_HALF_CLOCKED 1
+#define PHDR_802_11A_CHANNEL_TYPE_QUARTER_CLOCKED 2
+
+/*
+ * "Turbo" is an Atheros proprietary extension with 40 MHz-wide channels.
+ * It can be dynamic or static.
+ *
+ * See
+ *
+ * http://wifi-insider.com/atheros/turbo.htm
+ */
+#define PHDR_802_11A_TURBO_TYPE_NORMAL 0
+#define PHDR_802_11A_TURBO_TYPE_TURBO 1 /* If we don't know whether it's static or dynamic */
+#define PHDR_802_11A_TURBO_TYPE_DYNAMIC_TURBO 2
+#define PHDR_802_11A_TURBO_TYPE_STATIC_TURBO 3
+
+/*
+ * 802.11g.
+ *
+ * This should only be used for packets sent using OFDM; packets
+ * sent on an 11g network using DSSS should have the PHY set to
+ * 11b.
+ */
+struct ieee_802_11g {
+ /* Which of this information is present? */
+ guint has_mode:1;
+
+ guint32 mode; /* Various proprietary extensions */
+};
+
+/*
+ * Mode values.
+ */
+#define PHDR_802_11G_MODE_NORMAL 0
+#define PHDR_802_11G_MODE_SUPER_G 1 /* Atheros Super G */
+
+/*
+ * 802.11n.
+ */
+struct ieee_802_11n {
+ /* Which of this information is present? */
+ guint has_mcs_index:1;
+ guint has_bandwidth:1;
+ guint has_short_gi:1;
+ guint has_greenfield:1;
+ guint has_fec:1;
+ guint has_stbc_streams:1;
+ guint has_ness:1;
+
+ guint16 mcs_index; /* MCS index */
+ guint bandwidth; /* Bandwidth = 20 MHz, 40 MHz, etc. */
+ guint short_gi:1; /* True for short guard interval */
+ guint greenfield:1; /* True for greenfield, short for mixed */
+ guint fec:1; /* FEC: 0 = BCC, 1 = LDPC */
+ guint stbc_streams:2; /* Number of STBC streams */
+ guint ness; /* Number of extension spatial streams */
+};
+
+/*
+ * Bandwidth values; used for both 11n and 11ac.
+ */
+#define PHDR_802_11_BANDWIDTH_20_MHZ 0 /* 20 MHz */
+#define PHDR_802_11_BANDWIDTH_40_MHZ 1 /* 40 MHz */
+#define PHDR_802_11_BANDWIDTH_20_20L 2 /* 20 + 20L, 40 MHz */
+#define PHDR_802_11_BANDWIDTH_20_20U 3 /* 20 + 20U, 40 MHz */
+#define PHDR_802_11_BANDWIDTH_80_MHZ 4 /* 80 MHz */
+#define PHDR_802_11_BANDWIDTH_40_40L 5 /* 40 + 40L MHz, 80 MHz */
+#define PHDR_802_11_BANDWIDTH_40_40U 6 /* 40 + 40U MHz, 80 MHz */
+#define PHDR_802_11_BANDWIDTH_20LL 7 /* ???, 80 MHz */
+#define PHDR_802_11_BANDWIDTH_20LU 8 /* ???, 80 MHz */
+#define PHDR_802_11_BANDWIDTH_20UL 9 /* ???, 80 MHz */
+#define PHDR_802_11_BANDWIDTH_20UU 10 /* ???, 80 MHz */
+#define PHDR_802_11_BANDWIDTH_160_MHZ 11 /* 160 MHz */
+#define PHDR_802_11_BANDWIDTH_80_80L 12 /* 80 + 80L, 160 MHz */
+#define PHDR_802_11_BANDWIDTH_80_80U 13 /* 80 + 80U, 160 MHz */
+#define PHDR_802_11_BANDWIDTH_40LL 14 /* ???, 160 MHz */
+#define PHDR_802_11_BANDWIDTH_40LU 15 /* ???, 160 MHz */
+#define PHDR_802_11_BANDWIDTH_40UL 16 /* ???, 160 MHz */
+#define PHDR_802_11_BANDWIDTH_40UU 17 /* ???, 160 MHz */
+#define PHDR_802_11_BANDWIDTH_20LLL 18 /* ???, 160 MHz */
+#define PHDR_802_11_BANDWIDTH_20LLU 19 /* ???, 160 MHz */
+#define PHDR_802_11_BANDWIDTH_20LUL 20 /* ???, 160 MHz */
+#define PHDR_802_11_BANDWIDTH_20LUU 21 /* ???, 160 MHz */
+#define PHDR_802_11_BANDWIDTH_20ULL 22 /* ???, 160 MHz */
+#define PHDR_802_11_BANDWIDTH_20ULU 23 /* ???, 160 MHz */
+#define PHDR_802_11_BANDWIDTH_20UUL 24 /* ???, 160 MHz */
+#define PHDR_802_11_BANDWIDTH_20UUU 25 /* ???, 160 MHz */
+
+/*
+ * 802.11ac.
+ */
+struct ieee_802_11ac {
+ /* Which of this information is present? */
+ guint has_stbc:1;
+ guint has_txop_ps_not_allowed:1;
+ guint has_short_gi:1;
+ guint has_short_gi_nsym_disambig:1;
+ guint has_ldpc_extra_ofdm_symbol:1;
+ guint has_beamformed:1;
+ guint has_bandwidth:1;
+ guint has_fec:1;
+ guint has_group_id:1;
+ guint has_partial_aid:1;
+
+ guint stbc:1; /* 1 if all spatial streams have STBC */
+ guint txop_ps_not_allowed:1;
+ guint short_gi:1; /* True for short guard interval */
+ guint short_gi_nsym_disambig:1;
+ guint ldpc_extra_ofdm_symbol:1;
+ guint beamformed:1;
+ guint8 bandwidth; /* Bandwidth = 20 MHz, 40 MHz, etc. */
+ guint8 mcs[4]; /* MCS index per user */
+ guint8 nss[4]; /* NSS per user */
+ guint8 fec; /* Bit array of FEC per user: 0 = BCC, 1 = LDPC */
+ guint8 group_id;
+ guint16 partial_aid;
+};
+
+/*
+ * 802.11ad.
+ */
+
+/*
+ * Min and Max frequencies for 802.11ad and a macro for checking for 802.11ad.
+ */
+
+#define PHDR_802_11AD_MIN_FREQUENCY 57000
+#define PHDR_802_11AD_MAX_FREQUENCY 71000
+
+#define IS_80211AD(frequency) (((frequency) >= PHDR_802_11AD_MIN_FREQUENCY) &&\
+ ((frequency) <= PHDR_802_11AD_MAX_FREQUENCY))
+
+struct ieee_802_11ad {
+ /* Which of this information is present? */
+ guint has_mcs_index:1;
+
+ guint8 mcs; /* MCS index */
+};
+
+/*
+ * 802.11ax (HE).
+ */
+struct ieee_802_11ax {
+ /* Which of this information is present? */
+ guint has_mcs_index:1;
+ guint has_bwru:1;
+ guint has_gi:1;
+
+ guint8 nsts:4; /* Number of Space-time Streams */
+ guint8 mcs:4; /* MCS index */
+ guint8 bwru:4; /* Bandwidth/RU allocation */
+ guint8 gi:2; /* Guard Interval */
+};
+
+union ieee_802_11_phy_info {
+ struct ieee_802_11_fhss info_11_fhss;
+ struct ieee_802_11b info_11b;
+ struct ieee_802_11a info_11a;
+ struct ieee_802_11g info_11g;
+ struct ieee_802_11n info_11n;
+ struct ieee_802_11ac info_11ac;
+ struct ieee_802_11ad info_11ad;
+ struct ieee_802_11ax info_11ax;
+};
+
+struct ieee_802_11_phdr {
+ gint fcs_len; /* Number of bytes of FCS - -1 means "unknown" */
+ guint decrypted:1; /* TRUE if frame is decrypted even if "protected" bit is set */
+ guint datapad:1; /* TRUE if frame has padding between 802.11 header and payload */
+ guint no_a_msdus:1; /* TRUE if we should ignore the A-MSDU bit */
+ guint phy; /* PHY type */
+ union ieee_802_11_phy_info phy_info;
+
+ /* Which of this information is present? */
+ guint has_channel:1;
+ guint has_frequency:1;
+ guint has_data_rate:1;
+ guint has_signal_percent:1;
+ guint has_noise_percent:1;
+ guint has_signal_dbm:1;
+ guint has_noise_dbm:1;
+ guint has_signal_db:1;
+ guint has_noise_db:1;
+ guint has_tsf_timestamp:1;
+ guint has_aggregate_info:1; /* aggregate flags and ID */
+ guint has_zero_length_psdu_type:1; /* zero-length PSDU type */
+
+ guint16 channel; /* Channel number */
+ guint32 frequency; /* Channel center frequency */
+ guint16 data_rate; /* Data rate, in .5 Mb/s units */
+ guint8 signal_percent; /* Signal level, as a percentage */
+ guint8 noise_percent; /* Noise level, as a percentage */
+ gint8 signal_dbm; /* Signal level, in dBm */
+ gint8 noise_dbm; /* Noise level, in dBm */
+ guint8 signal_db; /* Signal level, in dB from an arbitrary point */
+ guint8 noise_db; /* Noise level, in dB from an arbitrary point */
+ guint64 tsf_timestamp;
+ guint32 aggregate_flags; /* A-MPDU flags */
+ guint32 aggregate_id; /* ID for A-MPDU reassembly */
+ guint8 zero_length_psdu_type; /* type of zero-length PSDU */
+};
+
+/*
+ * A-MPDU flags.
+ */
+#define PHDR_802_11_LAST_PART_OF_A_MPDU 0x00000001 /* this is the last part of an A-MPDU */
+#define PHDR_802_11_A_MPDU_DELIM_CRC_ERROR 0x00000002 /* delimiter CRC error after this part */
+
+/*
+ * Zero-length PSDU types.
+ */
+#define PHDR_802_11_SOUNDING_PSDU 0 /* sounding PPDU */
+#define PHDR_802_11_DATA_NOT_CAPTURED 1 /* data not captured, (e.g. multi-user PPDU) */
+#define PHDR_802_11_0_LENGTH_PSDU_S1G_NDP 2 /* S1G NDP CMAC */
+#define PHDR_802_11_0_LENGTH_PSDU_VENDOR_SPECIFIC 0xff
+
+/* Packet "pseudo-header" for the output from CoSine L2 debug output. */
+
+#define COSINE_MAX_IF_NAME_LEN 128
+
+#define COSINE_ENCAP_TEST 1
+#define COSINE_ENCAP_PPoATM 2
+#define COSINE_ENCAP_PPoFR 3
+#define COSINE_ENCAP_ATM 4
+#define COSINE_ENCAP_FR 5
+#define COSINE_ENCAP_HDLC 6
+#define COSINE_ENCAP_PPP 7
+#define COSINE_ENCAP_ETH 8
+#define COSINE_ENCAP_UNKNOWN 99
+
+#define COSINE_DIR_TX 1
+#define COSINE_DIR_RX 2
+
+struct cosine_phdr {
+ guint8 encap; /* COSINE_ENCAP_* as defined above */
+ guint8 direction; /* COSINE_DIR_*, as defined above */
+ char if_name[COSINE_MAX_IF_NAME_LEN]; /* Encap & Logical I/F name */
+ guint16 pro; /* Protocol */
+ guint16 off; /* Offset */
+ guint16 pri; /* Priority */
+ guint16 rm; /* Rate Marking */
+ guint16 err; /* Error Code */
+};
+
+/* Packet "pseudo-header" for IrDA capture files. */
+
+/*
+ * Direction of the packet
+ */
+#define IRDA_INCOMING 0x0000
+#define IRDA_OUTGOING 0x0004
+
+/*
+ * "Inline" log messages produced by IrCOMM2k on Windows
+ */
+#define IRDA_LOG_MESSAGE 0x0100 /* log message */
+#define IRDA_MISSED_MSG 0x0101 /* missed log entry or frame */
+
+/*
+ * Differentiate between frames and log messages
+ */
+#define IRDA_CLASS_FRAME 0x0000
+#define IRDA_CLASS_LOG 0x0100
+#define IRDA_CLASS_MASK 0xFF00
+
+struct irda_phdr {
+ guint16 pkttype; /* packet type */
+};
+
+/* Packet "pseudo-header" for nettl (HP-UX) capture files. */
+
+struct nettl_phdr {
+ guint16 subsys;
+ guint32 devid;
+ guint32 kind;
+ gint32 pid;
+ guint32 uid;
+};
+
+/* Packet "pseudo-header" for MTP2 files. */
+
+#define MTP2_ANNEX_A_NOT_USED 0
+#define MTP2_ANNEX_A_USED 1
+#define MTP2_ANNEX_A_USED_UNKNOWN 2
+
+struct mtp2_phdr {
+ guint8 sent;
+ guint8 annex_a_used;
+ guint16 link_number;
+};
+
+/* Packet "pseudo-header" for K12 files. */
+
+typedef union {
+ struct {
+ guint16 vp;
+ guint16 vc;
+ guint16 cid;
+ } atm;
+
+ guint32 ds0mask;
+} k12_input_info_t;
+
+struct k12_phdr {
+ guint32 input;
+ const gchar *input_name;
+ const gchar *stack_file;
+ guint32 input_type;
+ k12_input_info_t input_info;
+ guint8 *extra_info;
+ guint32 extra_length;
+ void* stuff;
+};
+
+#define K12_PORT_DS0S 0x00010008
+#define K12_PORT_DS1 0x00100008
+#define K12_PORT_ATMPVC 0x01020000
+
+struct lapd_phdr {
+ guint16 pkttype; /* packet type */
+ guint8 we_network;
+};
+
+struct wtap;
+struct catapult_dct2000_phdr
+{
+ union
+ {
+ struct isdn_phdr isdn;
+ struct atm_phdr atm;
+ struct p2p_phdr p2p;
+ } inner_pseudo_header;
+ gint64 seek_off;
+ struct wtap *wth;
+};
+
+/*
+ * Endace Record Format pseudo header
+ */
+struct erf_phdr {
+ guint64 ts; /* Time stamp */
+ guint8 type;
+ guint8 flags;
+ guint16 rlen;
+ guint16 lctr;
+ guint16 wlen;
+};
+
+struct erf_ehdr {
+ guint64 ehdr;
+};
+
+/*
+ * ERF pseudo header with optional subheader
+ * (Multichannel or Ethernet)
+ */
+
+#define MAX_ERF_EHDR 16
+
+struct wtap_erf_eth_hdr {
+ guint8 offset;
+ guint8 pad;
+};
+
+struct erf_mc_phdr {
+ struct erf_phdr phdr;
+ struct erf_ehdr ehdr_list[MAX_ERF_EHDR];
+ union
+ {
+ struct wtap_erf_eth_hdr eth_hdr;
+ guint32 mc_hdr;
+ guint32 aal2_hdr;
+ } subhdr;
+};
+
+#define SITA_FRAME_DIR_TXED (0x00) /* values of sita_phdr.flags */
+#define SITA_FRAME_DIR_RXED (0x01)
+#define SITA_FRAME_DIR (0x01) /* mask */
+#define SITA_ERROR_NO_BUFFER (0x80)
+
+#define SITA_SIG_DSR (0x01) /* values of sita_phdr.signals */
+#define SITA_SIG_DTR (0x02)
+#define SITA_SIG_CTS (0x04)
+#define SITA_SIG_RTS (0x08)
+#define SITA_SIG_DCD (0x10)
+#define SITA_SIG_UNDEF1 (0x20)
+#define SITA_SIG_UNDEF2 (0x40)
+#define SITA_SIG_UNDEF3 (0x80)
+
+#define SITA_ERROR_TX_UNDERRUN (0x01) /* values of sita_phdr.errors2 (if SITA_FRAME_DIR_TXED) */
+#define SITA_ERROR_TX_CTS_LOST (0x02)
+#define SITA_ERROR_TX_UART_ERROR (0x04)
+#define SITA_ERROR_TX_RETX_LIMIT (0x08)
+#define SITA_ERROR_TX_UNDEF1 (0x10)
+#define SITA_ERROR_TX_UNDEF2 (0x20)
+#define SITA_ERROR_TX_UNDEF3 (0x40)
+#define SITA_ERROR_TX_UNDEF4 (0x80)
+
+#define SITA_ERROR_RX_FRAMING (0x01) /* values of sita_phdr.errors1 (if SITA_FRAME_DIR_RXED) */
+#define SITA_ERROR_RX_PARITY (0x02)
+#define SITA_ERROR_RX_COLLISION (0x04)
+#define SITA_ERROR_RX_FRAME_LONG (0x08)
+#define SITA_ERROR_RX_FRAME_SHORT (0x10)
+#define SITA_ERROR_RX_UNDEF1 (0x20)
+#define SITA_ERROR_RX_UNDEF2 (0x40)
+#define SITA_ERROR_RX_UNDEF3 (0x80)
+
+#define SITA_ERROR_RX_NONOCTET_ALIGNED (0x01) /* values of sita_phdr.errors2 (if SITA_FRAME_DIR_RXED) */
+#define SITA_ERROR_RX_ABORT (0x02)
+#define SITA_ERROR_RX_CD_LOST (0x04)
+#define SITA_ERROR_RX_DPLL (0x08)
+#define SITA_ERROR_RX_OVERRUN (0x10)
+#define SITA_ERROR_RX_FRAME_LEN_VIOL (0x20)
+#define SITA_ERROR_RX_CRC (0x40)
+#define SITA_ERROR_RX_BREAK (0x80)
+
+#define SITA_PROTO_UNUSED (0x00) /* values of sita_phdr.proto */
+#define SITA_PROTO_BOP_LAPB (0x01)
+#define SITA_PROTO_ETHERNET (0x02)
+#define SITA_PROTO_ASYNC_INTIO (0x03)
+#define SITA_PROTO_ASYNC_BLKIO (0x04)
+#define SITA_PROTO_ALC (0x05)
+#define SITA_PROTO_UTS (0x06)
+#define SITA_PROTO_PPP_HDLC (0x07)
+#define SITA_PROTO_SDLC (0x08)
+#define SITA_PROTO_TOKENRING (0x09)
+#define SITA_PROTO_I2C (0x10)
+#define SITA_PROTO_DPM_LINK (0x11)
+#define SITA_PROTO_BOP_FRL (0x12)
+
+struct sita_phdr {
+ guint8 sita_flags;
+ guint8 sita_signals;
+ guint8 sita_errors1;
+ guint8 sita_errors2;
+ guint8 sita_proto;
+};
+
+/*pseudo header for Bluetooth HCI*/
+struct bthci_phdr {
+ gboolean sent;
+ guint32 channel;
+};
+
+#define BTHCI_CHANNEL_COMMAND 1
+#define BTHCI_CHANNEL_ACL 2
+#define BTHCI_CHANNEL_SCO 3
+#define BTHCI_CHANNEL_EVENT 4
+#define BTHCI_CHANNEL_ISO 5
+
+/* pseudo header for WTAP_ENCAP_BLUETOOTH_LINUX_MONITOR */
+struct btmon_phdr {
+ guint16 adapter_id;
+ guint16 opcode;
+};
+
+/* pseudo header for WTAP_ENCAP_LAYER1_EVENT */
+struct l1event_phdr {
+ gboolean uton;
+};
+
+/* * I2C pseudo header */
+struct i2c_phdr {
+ guint8 is_event;
+ guint8 bus;
+ guint32 flags;
+};
+
+/* pseudo header for WTAP_ENCAP_GSM_UM */
+struct gsm_um_phdr {
+ gboolean uplink;
+ guint8 channel;
+ /* The following are only populated for downlink */
+ guint8 bsic;
+ guint16 arfcn;
+ guint32 tdma_frame;
+ guint8 error;
+ guint16 timeshift;
+};
+
+#define GSM_UM_CHANNEL_UNKNOWN 0
+#define GSM_UM_CHANNEL_BCCH 1
+#define GSM_UM_CHANNEL_SDCCH 2
+#define GSM_UM_CHANNEL_SACCH 3
+#define GSM_UM_CHANNEL_FACCH 4
+#define GSM_UM_CHANNEL_CCCH 5
+#define GSM_UM_CHANNEL_RACH 6
+#define GSM_UM_CHANNEL_AGCH 7
+#define GSM_UM_CHANNEL_PCH 8
+
+/* Pseudo-header for nstrace packets */
+struct nstr_phdr {
+ gint64 rec_offset;
+ gint32 rec_len;
+ guint8 nicno_offset;
+ guint8 nicno_len;
+ guint8 dir_offset;
+ guint8 dir_len;
+ guint16 eth_offset;
+ guint8 pcb_offset;
+ guint8 l_pcb_offset;
+ guint8 rec_type;
+ guint8 vlantag_offset;
+ guint8 coreid_offset;
+ guint8 srcnodeid_offset;
+ guint8 destnodeid_offset;
+ guint8 clflags_offset;
+ guint8 src_vmname_len_offset;
+ guint8 dst_vmname_len_offset;
+ guint8 ns_activity_offset;
+ guint8 data_offset;
+};
+
+/* Packet "pseudo-header" for Nokia output */
+struct nokia_phdr {
+ struct eth_phdr eth;
+ guint8 stuff[4]; /* mysterious stuff */
+};
+
+#define LLCP_PHDR_FLAG_SENT 0
+struct llcp_phdr {
+ guint8 adapter;
+ guint8 flags;
+};
+
+/* pseudo header for WTAP_ENCAP_LOGCAT */
+struct logcat_phdr {
+ gint version;
+};
+
+/* Packet "pseudo-header" information for header data from NetMon files. */
+
+struct netmon_phdr {
+ guint8* title; /* Comment title, as a null-terminated UTF-8 string */
+ guint32 descLength; /* Number of bytes in the comment description */
+ guint8* description; /* Comment description, in ASCII RTF */
+ guint sub_encap; /* "Real" encap value for the record that will be used once pseudo header data is display */
+ union sub_wtap_pseudo_header {
+ struct eth_phdr eth;
+ struct atm_phdr atm;
+ struct ieee_802_11_phdr ieee_802_11;
+ } subheader;
+};
+
+/* File "pseudo-header" for BER data files. */
+struct ber_phdr {
+ const char *pathname; /* Path name of file. */
+};
+
+union wtap_pseudo_header {
+ struct eth_phdr eth;
+ struct dte_dce_phdr dte_dce;
+ struct isdn_phdr isdn;
+ struct atm_phdr atm;
+ struct ascend_phdr ascend;
+ struct p2p_phdr p2p;
+ struct ieee_802_11_phdr ieee_802_11;
+ struct cosine_phdr cosine;
+ struct irda_phdr irda;
+ struct nettl_phdr nettl;
+ struct mtp2_phdr mtp2;
+ struct k12_phdr k12;
+ struct lapd_phdr lapd;
+ struct catapult_dct2000_phdr dct2000;
+ struct erf_mc_phdr erf;
+ struct sita_phdr sita;
+ struct bthci_phdr bthci;
+ struct btmon_phdr btmon;
+ struct l1event_phdr l1event;
+ struct i2c_phdr i2c;
+ struct gsm_um_phdr gsm_um;
+ struct nstr_phdr nstr;
+ struct nokia_phdr nokia;
+ struct llcp_phdr llcp;
+ struct logcat_phdr logcat;
+ struct netmon_phdr netmon;
+ struct ber_phdr ber;
+};
+
+/*
+ * Record type values.
+ *
+ * This list will expand over time, so don't assume everything will
+ * forever be one of the types listed below.
+ *
+ * For file-type-specific records, the "ftsrec" field of the pseudo-header
+ * contains a file-type-specific subtype value, such as a block type for
+ * a pcapng file.
+ *
+ * An "event" is an indication that something happened during the capture
+ * process, such as a status transition of some sort on the network.
+ * These should, ideally, have a time stamp and, if they're relevant to
+ * a particular interface on a multi-interface capture, should also have
+ * an interface ID. The data for the event is file-type-specific and
+ * subtype-specific. These should be dissected and displayed just as
+ * packets are.
+ *
+ * A "report" supplies information not corresponding to an event;
+ * for example, a pcapng Interface Statistics Block would be a report,
+ * as it doesn't correspond to something happening on the network.
+ * They may have a time stamp, and should be dissected and displayed
+ * just as packets are.
+ *
+ * We distinguish between "events" and "reports" so that, for example,
+ * the packet display can show the delta between a packet and an event
+ * but not show the delta between a packet and a report, as the time
+ * stamp of a report may not correspond to anything interesting on
+ * the network but the time stamp of an event would.
+ *
+ * XXX - are there any file-type-specific records that *shouldn't* be
+ * dissected and displayed? If so, they should be parsed and the
+ * information in them stored somewhere, and used somewhere, whether
+ * it's just used when saving the file in its native format or also
+ * used to parse *other* file-type-specific records.
+ *
+ * These would be similar to, for example, pcapng Interface Description
+ * Blocks, for which the position within the file is significant only
+ * in that an IDB for an interface must appear before any packets from
+ * the interface; the fact that an IDB appears at some point doesn't
+ * necessarily mean something happened in the capture at that point.
+ * Name Resolution Blocks are another example of such a record.
+ *
+ * (XXX - if you want to have a record that says "this interface first
+ * showed up at this time", that needs to be a separate record type
+ * from the IDB. We *could* add a "New Interface Description Block",
+ * with a time stamp, for that purpose, but we'd *still* have to
+ * provide IDBs for those interfaces, for compatibility with programs
+ * that don't know about the NIDB. An ISB with only an isb_starttime
+ * option would suffice for this purpose, so nothing needs to be
+ * added to pcapng for this.)
+ */
+#define REC_TYPE_PACKET 0 /**< packet */
+#define REC_TYPE_FT_SPECIFIC_EVENT 1 /**< file-type-specific event */
+#define REC_TYPE_FT_SPECIFIC_REPORT 2 /**< file-type-specific report */
+#define REC_TYPE_SYSCALL 3 /**< system call */
+#define REC_TYPE_SYSTEMD_JOURNAL_EXPORT 4 /**< systemd journal entry */
+#define REC_TYPE_CUSTOM_BLOCK 5 /**< pcapng custom block */
+
+typedef struct {
+ guint32 caplen; /* data length in the file */
+ guint32 len; /* data length on the wire */
+ int pkt_encap; /* WTAP_ENCAP_ value for this packet */
+ /* pcapng variables */
+ guint32 interface_id; /* identifier of the interface. */
+ /* options */
+
+ union wtap_pseudo_header pseudo_header;
+} wtap_packet_header;
+
+/*
+ * The pcapng specification says "The word is encoded as an unsigned
+ * 32-bit integer, using the endianness of the Section Header Block
+ * scope it is in. In the following table, the bits are numbered with
+ * 0 being the most-significant bit and 31 being the least-significant
+ * bit of the 32-bit unsigned integer."
+ *
+ * From that, the direction, in bits 0 and 1, is at the *top* of the word.
+ *
+ * However, several implementations, such as:
+ *
+ * the Wireshark pcapng file reading code;
+ *
+ * macOS libpcap and tcpdump;
+ *
+ * text2pcap;
+ *
+ * and probably the software that generated the capture in bug 11665;
+ *
+ * treat 0 as the *least*-significant bit and bit 31 being the *most*-
+ * significant bit of the flags word, and put the direction at the
+ * *bottom* of the word.
+ *
+ * For now, we go with the known implementations.
+ */
+
+/* Direction field of the packet flags */
+#define PACK_FLAGS_DIRECTION_MASK 0x00000003 /* unshifted */
+#define PACK_FLAGS_DIRECTION_SHIFT 0
+#define PACK_FLAGS_DIRECTION(pack_flags) (((pack_flags) & PACK_FLAGS_DIRECTION_MASK) >> PACK_FLAGS_DIRECTION_SHIFT)
+#define PACK_FLAGS_DIRECTION_UNKNOWN 0
+#define PACK_FLAGS_DIRECTION_INBOUND 1
+#define PACK_FLAGS_DIRECTION_OUTBOUND 2
+
+/* Reception type field of the packet flags */
+#define PACK_FLAGS_RECEPTION_TYPE_MASK 0x0000001C /* unshifted */
+#define PACK_FLAGS_RECEPTION_TYPE_SHIFT 2
+#define PACK_FLAGS_RECEPTION_TYPE(pack_flags) (((pack_flags) & PACK_FLAGS_RECEPTION_TYPE_MASK) >> PACK_FLAGS_RECEPTION_TYPE_SHIFT)
+#define PACK_FLAGS_RECEPTION_TYPE_UNSPECIFIED 0
+#define PACK_FLAGS_RECEPTION_TYPE_UNICAST 1
+#define PACK_FLAGS_RECEPTION_TYPE_MULTICAST 2
+#define PACK_FLAGS_RECEPTION_TYPE_BROADCAST 3
+#define PACK_FLAGS_RECEPTION_TYPE_PROMISCUOUS 4
+
+/* FCS length field of the packet flags */
+#define PACK_FLAGS_FCS_LENGTH_MASK 0x000001E0 /* unshifted */
+#define PACK_FLAGS_FCS_LENGTH_SHIFT 5
+#define PACK_FLAGS_FCS_LENGTH(pack_flags) (((pack_flags) & PACK_FLAGS_FCS_LENGTH_MASK) >> PACK_FLAGS_FCS_LENGTH_SHIFT)
+
+/* Reserved bits of the packet flags */
+#define PACK_FLAGS_RESERVED_MASK 0x0000FE00
+
+/* Link-layer-dependent errors of the packet flags */
+
+/* For Ethernet and possibly some other network types */
+#define PACK_FLAGS_CRC_ERROR 0x01000000
+#define PACK_FLAGS_PACKET_TOO_LONG 0x02000000
+#define PACK_FLAGS_PACKET_TOO_SHORT 0x04000000
+#define PACK_FLAGS_WRONG_INTER_FRAME_GAP 0x08000000
+#define PACK_FLAGS_UNALIGNED_FRAME 0x10000000
+#define PACK_FLAGS_START_FRAME_DELIMITER_ERROR 0x20000000
+#define PACK_FLAGS_PREAMBLE_ERROR 0x40000000
+#define PACK_FLAGS_SYMBOL_ERROR 0x80000000
+
+/* Construct a pack_flags value from its subfield values */
+#define PACK_FLAGS_VALUE(direction, reception_type, fcs_length, ll_dependent_errors) \
+ (((direction) << 30) | \
+ ((reception_type) << 27) | \
+ ((fcs_length) << 23) | \
+ (ll_dependent_errors))
+
+typedef struct {
+ guint record_type; /* the type of record this is - file type-specific value */
+ guint32 record_len; /* length of the record */
+} wtap_ft_specific_header;
+
+typedef struct {
+ guint record_type; /* XXX match ft_specific_record_phdr so that we chain off of packet-pcapng_block for now. */
+ int byte_order;
+ /* guint32 sentinel; */
+ guint64 timestamp; /* ns since epoch - XXX dup of ts */
+ guint64 thread_id;
+ guint32 event_len; /* length of the event */
+ guint32 event_filelen; /* event data length in the file */
+ guint16 event_type;
+ guint32 nparams; /* number of parameters of the event */
+ guint16 cpu_id;
+ /* ... Event ... */
+} wtap_syscall_header;
+
+typedef struct {
+ guint32 record_len; /* length of the record */
+} wtap_systemd_journal_export_header;
+
+typedef struct {
+ guint32 length; /* length of the record */
+ guint32 pen; /* private enterprise number */
+ gboolean copy_allowed; /* CB can be written */
+ union {
+ struct nflx {
+ guint32 type; /* block type */
+ guint32 skipped; /* Used if type == BBLOG_TYPE_SKIPPED_BLOCK */
+ } nflx_custom_data_header;
+ } custom_data_header;
+} wtap_custom_block_header;
+
+#define BBLOG_TYPE_EVENT_BLOCK 1
+#define BBLOG_TYPE_SKIPPED_BLOCK 2
+
+/*
+ * The largest nstime.secs value that can be put into an unsigned
+ * 32-bit quantity.
+ *
+ * We assume that time_t is signed; it is signed on Windows/MSVC and
+ * on many UN*Xes.
+ *
+ * So, if time_t is 32-bit, we define this as G_MAXINT32, as that's
+ * the largest value a time_t can have, and it fits in an unsigned
+ * 32-bit quantity. If it's 64-bit or larger, we define this as
+ * G_MAXUINT32, as, even if it's signed, it can be as large as
+ * G_MAXUINT32, and that's the largest value that can fit in
+ * a 32-bit unsigned quantity.
+ *
+ * Comparing against this, rather than against G_MAXINT2, when checking
+ * whether a time stamp will fit in a 32-bit unsigned integer seconds
+ * field in a capture file being written avoids signed vs. unsigned
+ * warnings if time_t is a signed 32-bit type.
+ *
+ * XXX - what if time_t is unsigned? Are there any platforms where
+ * it is?
+ */
+#define WTAP_NSTIME_32BIT_SECS_MAX ((time_t)(sizeof(time_t) > sizeof(gint32) ? G_MAXUINT32 : G_MAXINT32))
+
+typedef struct wtap_rec {
+ guint rec_type; /* what type of record is this? */
+ guint32 presence_flags; /* what stuff do we have? */
+ guint section_number; /* section, within file, containing this record */
+ nstime_t ts; /* time stamp */
+ int tsprec; /* WTAP_TSPREC_ value for this record */
+ nstime_t ts_rel_cap; /* time stamp relative from capture start */
+ gboolean ts_rel_cap_valid; /* is ts_rel_cap valid and can be used? */
+ union {
+ wtap_packet_header packet_header;
+ wtap_ft_specific_header ft_specific_header;
+ wtap_syscall_header syscall_header;
+ wtap_systemd_journal_export_header systemd_journal_export_header;
+ wtap_custom_block_header custom_block_header;
+ } rec_header;
+
+ wtap_block_t block ; /* packet block; holds comments and verdicts in its options */
+ gboolean block_was_modified; /* TRUE if ANY aspect of the block has been modified */
+
+ /*
+ * We use a Buffer so that we don't have to allocate and free
+ * a buffer for the options for each record.
+ */
+ Buffer options_buf; /* file-type specific data */
+} wtap_rec;
+
+/*
+ * Bits in presence_flags, indicating which of the fields we have.
+ *
+ * For the time stamp, we may need some more flags to indicate
+ * whether the time stamp is an absolute date-and-time stamp, an
+ * absolute time-only stamp (which can make relative time
+ * calculations tricky, as you could in theory have two time
+ * stamps separated by an unknown number of days), or a time stamp
+ * relative to some unspecified time in the past (see mpeg.c).
+ *
+ * There is no presence flag for len - there has to be *some* length
+ * value for the packet. (The "captured length" can be missing if
+ * the file format doesn't report a captured length distinct from
+ * the on-the-network length because the application(s) producing those
+ * files don't support slicing packets.)
+ *
+ * There could be a presence flag for the packet encapsulation - if it's
+ * absent, use the file encapsulation - but it's not clear that's useful;
+ * we currently do that in the module for the file format.
+ *
+ * Only WTAP_HAS_TS and WTAP_HAS_SECTION_NUMBER apply to all record types.
+ */
+#define WTAP_HAS_TS 0x00000001 /**< time stamp */
+#define WTAP_HAS_CAP_LEN 0x00000002 /**< captured length separate from on-the-network length */
+#define WTAP_HAS_INTERFACE_ID 0x00000004 /**< interface ID */
+#define WTAP_HAS_SECTION_NUMBER 0x00000008 /**< section number */
+
+#ifndef MAXNAMELEN
+#define MAXNAMELEN 64 /* max name length (hostname and port name) */
+#endif
+
+typedef struct hashipv4 {
+ guint addr;
+ guint8 flags; /* B0 dummy_entry, B1 resolve, B2 If the address is used in the trace */
+ gchar ip[WS_INET_ADDRSTRLEN];
+ gchar name[MAXNAMELEN];
+} hashipv4_t;
+
+typedef struct hashipv6 {
+ guint8 addr[16];
+ guint8 flags; /* B0 dummy_entry, B1 resolve, B2 If the address is used in the trace */
+ gchar ip6[WS_INET6_ADDRSTRLEN];
+ gchar name[MAXNAMELEN];
+} hashipv6_t;
+
+/** A struct with lists of resolved addresses.
+ * Used when writing name resolutions blocks (NRB)
+ */
+typedef struct addrinfo_lists {
+ GList *ipv4_addr_list; /**< A list of resolved hashipv4_t*/
+ GList *ipv6_addr_list; /**< A list of resolved hashipv6_t*/
+} addrinfo_lists_t;
+
+/**
+ * Parameters for various wtap_dump_* functions, specifying per-file
+ * information. The structure itself is no longer used after returning
+ * from wtap_dump_*, but its pointer fields must remain valid until
+ * wtap_dump_close is called.
+ *
+ * @note The shb_hdr and idb_inf arguments will be used until
+ * wtap_dump_close() is called, but will not be free'd by the dumper. If
+ * you created them, you must free them yourself after wtap_dump_close().
+ * dsbs_initial will be freed by wtap_dump_close(),
+ * dsbs_growing typically refers to another wth->dsbs.
+ * nrbs_growing typically refers to another wth->nrbs.
+ *
+ * @see wtap_dump_params_init, wtap_dump_params_cleanup.
+ */
+typedef struct wtap_dump_params {
+ int encap; /**< Per-file packet encapsulation, or WTAP_ENCAP_PER_PACKET */
+ int snaplen; /**< Per-file snapshot length (what if it's per-interface?) */
+ int tsprec; /**< Per-file time stamp precision */
+ GArray *shb_hdrs; /**< The section header block(s) information, or NULL. */
+ wtapng_iface_descriptions_t *idb_inf; /**< The interface description information, or NULL. */
+ const GArray *nrbs_growing; /**< NRBs that will be written while writing packets, or NULL.
+ This array may grow since the dumper was opened and will subsequently
+ be written before newer packets are written in wtap_dump. */
+ GArray *dsbs_initial; /**< The initial Decryption Secrets Block(s) to be written, or NULL. */
+ const GArray *dsbs_growing; /**< DSBs that will be written while writing packets, or NULL.
+ This array may grow since the dumper was opened and will subsequently
+ be written before newer packets are written in wtap_dump. */
+ const GArray *sysdig_mev_growing; /**< Meta events that will be written while writing packets, or NULL.
+ This array may grow since the dumper was opened and will subsequently
+ be written before newer packets are written in wtap_dump. */
+ gboolean dont_copy_idbs; /**< XXX - don't copy IDBs; this should eventually always be the case. */
+} wtap_dump_params;
+
+/* Zero-initializer for wtap_dump_params. */
+#define WTAP_DUMP_PARAMS_INIT {.snaplen=0}
+
+struct wtap_dumper;
+
+typedef struct wtap wtap;
+typedef struct wtap_dumper wtap_dumper;
+
+typedef struct wtap_reader *FILE_T;
+
+/* Similar to the wtap_open_routine_info for open routines, the following
+ * wtap_wslua_file_info struct is used by wslua code for Lua-based file writers.
+ *
+ * This concept is necessary because when wslua goes to invoke the
+ * registered dump/write_open routine callback in Lua, it needs the ref number representing
+ * the hooked function inside Lua. This will be stored in the thing pointed to
+ * by the void* data here. This 'data' pointer will be copied into the
+ * wtap_dumper struct's 'void* data' member when calling the dump_open function,
+ * which is how wslua finally retrieves it. Unlike wtap_dumper's 'priv' member, its
+ * 'data' member is not free'd in wtap_dump_close().
+ */
+typedef struct wtap_wslua_file_info {
+ int (*wslua_can_write_encap)(int, void*); /* a can_write_encap func for wslua uses */
+ void* wslua_data; /* holds the wslua data */
+} wtap_wslua_file_info_t;
+
+/*
+ * For registering extensions used for file formats.
+ *
+ * These items are used in dialogs for opening files, so that
+ * the user can ask to see all capture files (as identified
+ * by file extension) or particular types of capture files.
+ *
+ * Each item has a human-readable description of the file types
+ * (possibly more than one!) that use all of this set of extensions,
+ * a flag indicating whether it's a capture file or just some file
+ * whose contents we can dissect, and a list of extensions files of
+ * that type might have.
+ *
+ * Note that entries in this table do *not* necessarily correspoond
+ * to single file types; for example, the entry that lists just "cap"
+ * is for several file formats, all of which use the extension ".cap".
+ *
+ * Also note that a given extension may appear in multiple entries;
+ * for example, "cap" (again!) is in an entry for some file types
+ * that use only ".cap" and in entries for file types that use
+ * ".cap" and some other extensions, and ".trc" is used both for
+ * DOS Sniffer Token Ring captures ("trc") and EyeSDN USB ISDN
+ * trace files ("tr{a}c{e}").
+ *
+ * Some entries aren't for capture file types, they're just generic types,
+ * such as "text file" or "XML file", that can be used for, among other
+ * things, captures we can read, or for file formats we can read in
+ * order to dissect the contents of the file (think of this as "Fileshark",
+ * which is a program that we really should have). Those are marked
+ * specially, because, in file section dialogs, the user should be able
+ * to select "All Capture Files" and get a set of extensions that are
+ * associated with capture file formats, but not with files in other
+ * formats that might or might not contain captured packets (such as
+ * .txt or .xml") or formats that aren't capture files but that we
+ * support as "we're being Fileshark now" (such as .jpeg). The routine
+ * that constructs a list of extensions for "All Capture Files" omits
+ * extensions for those entries.
+ */
+struct file_extension_info {
+ /* the file type description */
+ const char *name;
+
+ /* TRUE if this is a capture file type */
+ gboolean is_capture_file;
+
+ /* a semicolon-separated list of file extensions used for this type */
+ const char *extensions;
+};
+
+/*
+ * For registering file types that we can open.
+ *
+ * Each file type has an open routine.
+ *
+ * The open routine should return:
+ *
+ * WTAP_OPEN_ERROR on an I/O error;
+ *
+ * WTAP_OPEN_MINE if the file it's reading is one of the types
+ * it handles;
+ *
+ * WTAP_OPEN_NOT_MINE if the file it's reading isn't one of the
+ * types it handles.
+ *
+ * If the routine handles this type of file, it should set the
+ * "file_type_subtype" field in the "struct wtap" to the type of the file.
+ *
+ * Note that the routine does not have to free the private data pointer on
+ * error. The caller takes care of that by calling wtap_close on error.
+ * (See https://gitlab.com/wireshark/wireshark/-/issues/8518)
+ *
+ * However, the caller does have to free the private data pointer when
+ * returning WTAP_OPEN_NOT_MINE, since the next file type will be called
+ * and will likely just overwrite the pointer.
+ */
+typedef enum {
+ WTAP_OPEN_NOT_MINE = 0,
+ WTAP_OPEN_MINE = 1,
+ WTAP_OPEN_ERROR = -1
+} wtap_open_return_val;
+
+typedef wtap_open_return_val (*wtap_open_routine_t)(struct wtap*, int *,
+ char **);
+
+/*
+ * Some file formats have defined magic numbers at fixed offsets from
+ * the beginning of the file; those routines should return 1 if and
+ * only if the file has the magic number at that offset. (pcapng
+ * is a bit of a special case, as it has both the Section Header Block
+ * type field and its byte-order magic field; it checks for both.)
+ * Those file formats do not require a file name extension in order
+ * to recognize them or to avoid recognizing other file types as that
+ * type, and have no extensions specified for them.
+ *
+ * Other file formats don't have defined magic numbers at fixed offsets,
+ * so a heuristic is required. If that file format has any file name
+ * extensions used for it, a list of those extensions should be
+ * specified, so that, if the name of the file being opened has an
+ * extension, the file formats that use that extension are tried before
+ * the ones that don't, to handle the case where a file of one type
+ * might be recognized by the heuristics for a different file type.
+ */
+typedef enum {
+ OPEN_INFO_MAGIC = 0,
+ OPEN_INFO_HEURISTIC = 1
+} wtap_open_type;
+
+WS_DLL_PUBLIC void init_open_routines(void);
+
+void cleanup_open_routines(void);
+
+/*
+ * Information about a given file type that applies to all subtypes of
+ * the file type.
+ *
+ * Each file type has:
+ *
+ * a human-readable description of the file type, for use in the
+ * user interface;
+ * a wtap_open_type indication of how the open routine
+ * determines whether a file is of that type;
+ * an open routine;
+ * an optional list of extensions used for this file type;
+ * data to be passed to Lua file readers - this should be NULL for
+ * non-Lua (C) file readers.
+ *
+ * The list of file extensions is used as a hint when calling open routines
+ * to open a file; heuristic open routines whose list of extensions includes
+ * the file's extension are called before heuristic open routines whose
+ * (possibly-empty) list of extensions doesn't contain the file's extension,
+ * to reduce the chances that a file will be misidentified due to an heuristic
+ * test with a weak heuristic being done before a heuristic test for the
+ * file's type.
+ *
+ * The list of extensions should be NULL for magic-number open routines,
+ * as it will not be used for any purpose (no such hinting is done).
+ */
+struct open_info {
+ const char *name; /* Description */
+ wtap_open_type type; /* Open routine type */
+ wtap_open_routine_t open_routine; /* Open routine */
+ const char *extensions; /* List of extensions used for this file type */
+ gchar **extensions_set; /* Array of those extensions; populated using extensions member during initialization */
+ void* wslua_data; /* Data for Lua file readers */
+};
+WS_DLL_PUBLIC struct open_info *open_routines;
+
+/*
+ * Types of comments.
+ */
+#define WTAP_COMMENT_PER_SECTION 0x00000001 /* per-file/per-file-section */
+#define WTAP_COMMENT_PER_INTERFACE 0x00000002 /* per-interface */
+#define WTAP_COMMENT_PER_PACKET 0x00000004 /* per-packet */
+
+/*
+ * For a given option type in a certain block type, does a file format
+ * not support it, support only one such option, or support multiple
+ * such options?
+ */
+typedef enum {
+ OPTION_NOT_SUPPORTED,
+ ONE_OPTION_SUPPORTED,
+ MULTIPLE_OPTIONS_SUPPORTED
+} option_support_t;
+
+/*
+ * Entry in a table of supported option types.
+ */
+struct supported_option_type {
+ guint opt;
+ option_support_t support; /* OPTION_NOT_SUPPORTED allowed, equivalent to absence */
+};
+
+#define OPTION_TYPES_SUPPORTED(option_type_array) \
+ sizeof option_type_array / sizeof option_type_array[0], option_type_array
+
+#define NO_OPTIONS_SUPPORTED \
+ 0, NULL
+
+/*
+ * For a given block type, does a file format not support it, support
+ * only one such block, or support multiple such blocks?
+ */
+typedef enum {
+ BLOCK_NOT_SUPPORTED,
+ ONE_BLOCK_SUPPORTED,
+ MULTIPLE_BLOCKS_SUPPORTED
+} block_support_t;
+
+/*
+ * Entry in a table of supported block types.
+ */
+struct supported_block_type {
+ wtap_block_type_t type;
+ block_support_t support; /* BLOCK_NOT_SUPPORTED allowed, equivalent to absence */
+ size_t num_supported_options;
+ const struct supported_option_type *supported_options;
+};
+
+#define BLOCKS_SUPPORTED(block_type_array) \
+ sizeof block_type_array / sizeof block_type_array[0], block_type_array
+
+struct file_type_subtype_info {
+ /**
+ * The file type description.
+ */
+ const char *description;
+
+ /**
+ * The file type name, used to look up file types by name, e.g.
+ * looking up a file type specified as a command-line argument.
+ */
+ const char *name;
+
+ /**
+ * The default file extension, used to save this type.
+ * Should be NULL if no default extension is known.
+ */
+ const char *default_file_extension;
+
+ /**
+ * A semicolon-separated list of additional file extensions
+ * used for this type.
+ * Should be NULL if no extensions, or no extensions other
+ * than the default extension, are known.
+ */
+ const char *additional_file_extensions;
+
+ /**
+ * When writing this file format, is seeking required?
+ */
+ gboolean writing_must_seek;
+
+ /**
+ * Number of block types supported.
+ */
+ size_t num_supported_blocks;
+
+ /**
+ * Table of block types supported.
+ */
+ const struct supported_block_type *supported_blocks;
+
+ /**
+ * Can this type write this encapsulation format?
+ * Should be NULL is this file type doesn't have write support.
+ */
+ int (*can_write_encap)(int);
+
+ /**
+ * The function to open the capture file for writing.
+ * Should be NULL if this file type doesn't have write support.
+ */
+ int (*dump_open)(wtap_dumper *, int *, gchar **);
+
+ /**
+ * If can_write_encap returned WTAP_ERR_CHECK_WSLUA, then this is used instead.
+ * This should be NULL for everyone except Lua-based file writers.
+ */
+ wtap_wslua_file_info_t *wslua_info;
+};
+
+#define WTAP_TYPE_AUTO 0
+
+/**
+ * @brief Initialize the Wiretap library.
+ *
+ * @param load_wiretap_plugins Load Wiretap plugins when initializing library.
+*/
+WS_DLL_PUBLIC
+void wtap_init(gboolean load_wiretap_plugins);
+
+/** On failure, "wtap_open_offline()" returns NULL, and puts into the
+ * "int" pointed to by its second argument:
+ *
+ * @param filename Name of the file to open
+ * @param type WTAP_TYPE_AUTO for automatic recognize file format or explicit choose format type
+ * @param[out] err a positive "errno" value if the capture file can't be opened;
+ * a negative number, indicating the type of error, on other failures.
+ * @param[out] err_info for some errors, a string giving more details of
+ * the error
+ * @param do_random TRUE if random access to the file will be done,
+ * FALSE if not
+ */
+WS_DLL_PUBLIC
+struct wtap* wtap_open_offline(const char *filename, unsigned int type, int *err,
+ gchar **err_info, gboolean do_random);
+
+/**
+ * If we were compiled with zlib and we're at EOF, unset EOF so that
+ * wtap_read/gzread has a chance to succeed. This is necessary if
+ * we're tailing a file.
+ */
+WS_DLL_PUBLIC
+void wtap_cleareof(wtap *wth);
+
+/**
+ * Set callback functions to add new hostnames. Currently pcapng-only.
+ * MUST match add_ipv4_name and add_ipv6_name in addr_resolv.c.
+ */
+typedef void (*wtap_new_ipv4_callback_t) (const guint addr, const gchar *name, const gboolean static_entry);
+WS_DLL_PUBLIC
+void wtap_set_cb_new_ipv4(wtap *wth, wtap_new_ipv4_callback_t add_new_ipv4);
+
+typedef void (*wtap_new_ipv6_callback_t) (const void *addrp, const gchar *name, const gboolean static_entry);
+WS_DLL_PUBLIC
+void wtap_set_cb_new_ipv6(wtap *wth, wtap_new_ipv6_callback_t add_new_ipv6);
+
+/**
+ * Set callback function to receive new decryption secrets for a particular
+ * secrets type (as defined in secrets-types.h). Currently pcapng-only.
+ */
+typedef void (*wtap_new_secrets_callback_t)(guint32 secrets_type, const void *secrets, guint size);
+WS_DLL_PUBLIC
+void wtap_set_cb_new_secrets(wtap *wth, wtap_new_secrets_callback_t add_new_secrets);
+
+/**
+ * Set callback function to receive new sysdig meta events. Currently pcapng-only.
+ */
+typedef void (*wtap_new_sysdig_meta_event_callback_t)(uint32_t mev_type, const uint8_t *mev_data, unsigned mev_data_size);
+WS_DLL_PUBLIC
+void wtap_set_cb_new_sysdig_meta_event(wtap *wth, wtap_new_sysdig_meta_event_callback_t add_new_sysdig_meta_event);
+
+/** Read the next record in the file, filling in *phdr and *buf.
+ *
+ * @wth a wtap * returned by a call that opened a file for reading.
+ * @rec a pointer to a wtap_rec, filled in with information about the
+ * record.
+ * @buf a pointer to a Buffer, filled in with data from the record.
+ * @param err a positive "errno" value, or a negative number indicating
+ * the type of error, if the read failed.
+ * @param err_info for some errors, a string giving more details of
+ * the error
+ * @param offset a pointer to a gint64, set to the offset in the file
+ * that should be used on calls to wtap_seek_read() to reread that record,
+ * if the read succeeded.
+ * @return TRUE on success, FALSE on failure.
+ */
+WS_DLL_PUBLIC
+gboolean wtap_read(wtap *wth, wtap_rec *rec, Buffer *buf, int *err,
+ gchar **err_info, gint64 *offset);
+
+/** Read the record at a specified offset in a capture file, filling in
+ * *phdr and *buf.
+ *
+ * @wth a wtap * returned by a call that opened a file for random-access
+ * reading.
+ * @seek_off a gint64 giving an offset value returned by a previous
+ * wtap_read() call.
+ * @rec a pointer to a struct wtap_rec, filled in with information
+ * about the record.
+ * @buf a pointer to a Buffer, filled in with data from the record.
+ * @param err a positive "errno" value, or a negative number indicating
+ * the type of error, if the read failed.
+ * @param err_info for some errors, a string giving more details of
+ * the error
+ * @return TRUE on success, FALSE on failure.
+ */
+WS_DLL_PUBLIC
+gboolean wtap_seek_read(wtap *wth, gint64 seek_off, wtap_rec *rec,
+ Buffer *buf, int *err, gchar **err_info);
+
+/*** initialize a wtap_rec structure ***/
+WS_DLL_PUBLIC
+void wtap_rec_init(wtap_rec *rec);
+
+/*** Re-initialize a wtap_rec structure ***/
+WS_DLL_PUBLIC
+void wtap_rec_reset(wtap_rec *rec);
+
+/*** clean up a wtap_rec structure, freeing what wtap_rec_init() allocated */
+WS_DLL_PUBLIC
+void wtap_rec_cleanup(wtap_rec *rec);
+
+/*
+ * Types of compression for a file, including "none".
+ */
+typedef enum {
+ WTAP_UNCOMPRESSED,
+ WTAP_GZIP_COMPRESSED,
+ WTAP_ZSTD_COMPRESSED,
+ WTAP_LZ4_COMPRESSED
+} wtap_compression_type;
+
+WS_DLL_PUBLIC
+wtap_compression_type wtap_get_compression_type(wtap *wth);
+WS_DLL_PUBLIC
+const char *wtap_compression_type_description(wtap_compression_type compression_type);
+WS_DLL_PUBLIC
+const char *wtap_compression_type_extension(wtap_compression_type compression_type);
+WS_DLL_PUBLIC
+GSList *wtap_get_all_compression_type_extensions_list(void);
+
+/*** get various information snippets about the current file ***/
+
+/** Return an approximation of the amount of data we've read sequentially
+ * from the file so far. */
+WS_DLL_PUBLIC
+gint64 wtap_read_so_far(wtap *wth);
+WS_DLL_PUBLIC
+gint64 wtap_file_size(wtap *wth, int *err);
+WS_DLL_PUBLIC
+guint wtap_snapshot_length(wtap *wth); /* per file */
+WS_DLL_PUBLIC
+int wtap_file_type_subtype(wtap *wth);
+WS_DLL_PUBLIC
+int wtap_file_encap(wtap *wth);
+WS_DLL_PUBLIC
+int wtap_file_tsprec(wtap *wth);
+
+/**
+ * @brief Gets number of section header blocks.
+ * @details Returns the number of existing SHBs.
+ *
+ * @param wth The wiretap session.
+ * @return The number of existing section headers.
+ */
+WS_DLL_PUBLIC
+guint wtap_file_get_num_shbs(wtap *wth);
+
+/**
+ * @brief Gets existing section header block, not for new file.
+ * @details Returns the pointer to an existing SHB, without creating a
+ * new one. This should only be used for accessing info, not
+ * for creating a new file based on existing SHB info. Use
+ * wtap_file_get_shb_for_new_file() for that.
+ *
+ * @param wth The wiretap session.
+ * @param shb_num The ordinal number (0-based) of the section header
+ * in the file
+ * @return The specified existing section header, which must NOT be g_free'd.
+ */
+WS_DLL_PUBLIC
+wtap_block_t wtap_file_get_shb(wtap *wth, guint shb_num);
+
+/**
+ * @brief Sets or replaces the section header comment.
+ * @details The passed-in comment string is set to be the comment
+ * for the section header block. The passed-in string's
+ * ownership will be owned by the block, so it should be
+ * duplicated before passing into this function.
+ *
+ * @param wth The wiretap session.
+ * @param comment The comment string.
+ */
+WS_DLL_PUBLIC
+void wtap_write_shb_comment(wtap *wth, gchar *comment);
+
+/**
+ * @brief Gets existing interface descriptions.
+ * @details Returns a new struct containing a pointer to the existing
+ * description, without creating new descriptions internally.
+ * @note The returned pointer must be g_free'd, but its internal
+ * interface_data must not.
+ *
+ * @param wth The wiretap session.
+ * @return A new struct of the existing section descriptions, which must be g_free'd.
+ */
+WS_DLL_PUBLIC
+wtapng_iface_descriptions_t *wtap_file_get_idb_info(wtap *wth);
+
+/**
+ * @brief Gets next interface description.
+ *
+ * @details This returns the first unfetched wtap_block_t from the set
+ * of interface descriptions. Returns NULL if there are no more
+ * unfetched interface descriptions; a subsequent call after
+ * wtap_read() returns, either with a new record or an EOF, may return
+ * another interface description.
+ */
+WS_DLL_PUBLIC
+wtap_block_t wtap_get_next_interface_description(wtap *wth);
+
+/**
+ * @brief Free's a interface description block and all of its members.
+ *
+ * @details This free's all of the interface descriptions inside the passed-in
+ * struct, including their members (e.g., comments); and then free's the
+ * passed-in struct as well.
+ *
+ * @warning Do not use this for the struct returned by
+ * wtap_file_get_idb_info(), as that one did not create the internal
+ * interface descriptions; for that case you can simply g_free() the new
+ * struct.
+ */
+WS_DLL_PUBLIC
+void wtap_free_idb_info(wtapng_iface_descriptions_t *idb_info);
+
+/**
+ * @brief Gets a debug string of an interface description.
+ * @details Returns a newly allocated string of debug information about
+ * the given interface descrption, useful for debugging.
+ * @note The returned pointer must be g_free'd.
+ *
+ * @param if_descr The interface description.
+ * @param indent Number of spaces to indent each line by.
+ * @param line_end A string to append to each line (e.g., "\n" or ", ").
+ * @return A newly allocated gcahr array string, which must be g_free'd.
+ */
+WS_DLL_PUBLIC
+gchar *wtap_get_debug_if_descr(const wtap_block_t if_descr,
+ const int indent,
+ const char* line_end);
+
+/**
+ * @brief Gets existing name resolution block, not for new file.
+ * @details Returns the pointer to the existing NRB, without creating a
+ * new one. This should only be used for accessing info, not
+ * for creating a new file based on existing NRB info. Use
+ * wtap_file_get_nrb_for_new_file() for that.
+ *
+ * @param wth The wiretap session.
+ * @return The existing section header, which must NOT be g_free'd.
+ *
+ * XXX - need to be updated to handle multiple NRBs.
+ */
+WS_DLL_PUBLIC
+wtap_block_t wtap_file_get_nrb(wtap *wth);
+
+/**
+ * @brief Adds a Decryption Secrets Block to the open wiretap session.
+ * @details The passed-in DSB is added to the DSBs for the current
+ * session.
+ *
+ * @param wth The wiretap session.
+ * @param dsb The Decryption Secrets Block to add
+ */
+WS_DLL_PUBLIC
+void wtap_file_add_decryption_secrets(wtap *wth, const wtap_block_t dsb);
+
+/**
+ * Remove any decryption secret information from the per-file information;
+ * used if we're stripping decryption secrets while the file is open
+ *
+ * @param wth The wiretap session from which to remove the
+ * decryption secrets.
+ * @return TRUE if any DSBs were removed
+ */
+WS_DLL_PUBLIC
+gboolean wtap_file_discard_decryption_secrets(wtap *wth);
+
+/*** close the file descriptors for the current file ***/
+WS_DLL_PUBLIC
+void wtap_fdclose(wtap *wth);
+
+/*** reopen the random file descriptor for the current file ***/
+WS_DLL_PUBLIC
+gboolean wtap_fdreopen(wtap *wth, const char *filename, int *err);
+
+/** Close only the sequential side, freeing up memory it uses. */
+WS_DLL_PUBLIC
+void wtap_sequential_close(wtap *wth);
+
+/** Closes any open file handles and frees the memory associated with wth. */
+WS_DLL_PUBLIC
+void wtap_close(wtap *wth);
+
+/*** dump packets into a capture file ***/
+WS_DLL_PUBLIC
+gboolean wtap_dump_can_open(int filetype);
+
+/**
+ * Given a GArray of WTAP_ENCAP_ types, return the per-file encapsulation
+ * type that would be needed to write out a file with those types.
+ */
+WS_DLL_PUBLIC
+int wtap_dump_required_file_encap_type(const GArray *file_encaps);
+
+/**
+ * Return TRUE if we can write this encapsulation type in this
+ * capture file type/subtype, FALSE if not.
+ */
+WS_DLL_PUBLIC
+gboolean wtap_dump_can_write_encap(int file_type_subtype, int encap);
+
+/**
+ * Return TRUE if we can write this capture file type/subtype out in
+ * compressed form, FALSE if not.
+ */
+WS_DLL_PUBLIC
+gboolean wtap_dump_can_compress(int file_type_subtype);
+
+/**
+ * Initialize the per-file information based on an existing file. Its
+ * contents must be freed according to the requirements of wtap_dump_params.
+ * If wth does not remain valid for the duration of the session, dsbs_growing
+ * MUST be cleared after this function.
+ *
+ * @param params The parameters for wtap_dump_* to initialize.
+ * @param wth The wiretap session.
+ */
+WS_DLL_PUBLIC
+void wtap_dump_params_init(wtap_dump_params *params, wtap *wth);
+
+/**
+ * Initialize the per-file information based on an existing file, but
+ * don't copy over the interface information. Its contents must be freed
+ * according to the requirements of wtap_dump_params.
+ * If wth does not remain valid for the duration of the session, dsbs_growing
+ * MUST be cleared after this function.
+ *
+ * XXX - this should eventually become wtap_dump_params_init(), with all
+ * programs writing capture files copying IDBs over by hand, so that they
+ * handle IDBs in the middle of the file.
+ *
+ * @param params The parameters for wtap_dump_* to initialize.
+ * @param wth The wiretap session.
+ */
+WS_DLL_PUBLIC
+void wtap_dump_params_init_no_idbs(wtap_dump_params *params, wtap *wth);
+
+/**
+ * Remove any name resolution information from the per-file information;
+ * used if we're stripping name resolution as we write the file.
+ *
+ * @param params The parameters for wtap_dump_* from which to remove the
+ * name resolution..
+ */
+WS_DLL_PUBLIC
+void wtap_dump_params_discard_name_resolution(wtap_dump_params *params);
+
+/**
+ * Remove any decryption secret information from the per-file information;
+ * used if we're stripping decryption secrets as we write the file.
+ *
+ * @param params The parameters for wtap_dump_* from which to remove the
+ * decryption secrets..
+ */
+WS_DLL_PUBLIC
+void wtap_dump_params_discard_decryption_secrets(wtap_dump_params *params);
+
+/**
+ * Free memory associated with the wtap_dump_params when it is no longer in
+ * use by wtap_dumper.
+ *
+ * @param params The parameters as initialized by wtap_dump_params_init.
+ */
+WS_DLL_PUBLIC
+void wtap_dump_params_cleanup(wtap_dump_params *params);
+
+/**
+ * @brief Opens a new capture file for writing.
+ *
+ * @param filename The new file's name.
+ * @param file_type_subtype The WTAP_FILE_TYPE_SUBTYPE_XXX file type.
+ * @param compression_type Type of compression to use when writing, if any
+ * @param params The per-file information for this file.
+ * @param[out] err Will be set to an error code on failure.
+ * @param[out] err_info for some errors, a string giving more details of
+ * the error
+ * @return The newly created dumper object, or NULL on failure.
+ */
+WS_DLL_PUBLIC
+wtap_dumper* wtap_dump_open(const char *filename, int file_type_subtype,
+ wtap_compression_type compression_type, const wtap_dump_params *params,
+ int *err, gchar **err_info);
+
+/**
+ * @brief Creates a dumper for a temporary file.
+ *
+ * @param tmpdir Directory in which to create the temporary file.
+ * @param filenamep Points to a pointer that's set to point to the
+ * pathname of the temporary file; it's allocated with g_malloc()
+ * @param pfx A string to be used as the prefix for the temporary file name
+ * @param file_type_subtype The WTAP_FILE_TYPE_SUBTYPE_XXX file type.
+ * @param compression_type Type of compression to use when writing, if any
+ * @param params The per-file information for this file.
+ * @param[out] err Will be set to an error code on failure.
+ * @param[out] err_info for some errors, a string giving more details of
+ * the error
+ * @return The newly created dumper object, or NULL on failure.
+ */
+WS_DLL_PUBLIC
+wtap_dumper* wtap_dump_open_tempfile(const char *tmpdir, char **filenamep,
+ const char *pfx,
+ int file_type_subtype, wtap_compression_type compression_type,
+ const wtap_dump_params *params, int *err, gchar **err_info);
+
+/**
+ * @brief Creates a dumper for an existing file descriptor.
+ *
+ * @param fd The file descriptor for which the dumper should be created.
+ * @param file_type_subtype The WTAP_FILE_TYPE_SUBTYPE_XXX file type.
+ * @param compression_type Type of compression to use when writing, if any
+ * @param params The per-file information for this file.
+ * @param[out] err Will be set to an error code on failure.
+ * @param[out] err_info for some errors, a string giving more details of
+ * the error
+ * @return The newly created dumper object, or NULL on failure.
+ */
+WS_DLL_PUBLIC
+wtap_dumper* wtap_dump_fdopen(int fd, int file_type_subtype,
+ wtap_compression_type compression_type, const wtap_dump_params *params,
+ int *err, gchar **err_info);
+
+/**
+ * @brief Creates a dumper for the standard output.
+ *
+ * @param file_type_subtype The WTAP_FILE_TYPE_SUBTYPE_XXX file type.
+ * @param compression_type Type of compression to use when writing, if any
+ * @param params The per-file information for this file.
+ * @param[out] err Will be set to an error code on failure.
+ * @param[out] err_info for some errors, a string giving more details of
+ * the error
+ * @return The newly created dumper object, or NULL on failure.
+ */
+WS_DLL_PUBLIC
+wtap_dumper* wtap_dump_open_stdout(int file_type_subtype,
+ wtap_compression_type compression_type, const wtap_dump_params *params,
+ int *err, gchar **err_info);
+
+/*
+ * Add an IDB to the list of IDBs for a file we're writing.
+ * Makes a copy of the IDB, so it can be freed after this call is made.
+ *
+ * @param wdh handle for the file we're writing.
+ * @param idb the IDB to add
+ * @param[out] err Will be set to an error code on failure.
+ * @param[out] err_info for some errors, a string giving more details of
+ * the error.
+ * @return TRUE on success, FALSE on failure.
+ */
+WS_DLL_PUBLIC
+gboolean wtap_dump_add_idb(wtap_dumper *wdh, wtap_block_t idb, int *err,
+ gchar **err_info);
+WS_DLL_PUBLIC
+gboolean wtap_dump(wtap_dumper *, const wtap_rec *, const guint8 *,
+ int *err, gchar **err_info);
+WS_DLL_PUBLIC
+gboolean wtap_dump_flush(wtap_dumper *, int *);
+WS_DLL_PUBLIC
+int wtap_dump_file_type_subtype(wtap_dumper *wdh);
+WS_DLL_PUBLIC
+gint64 wtap_get_bytes_dumped(wtap_dumper *);
+WS_DLL_PUBLIC
+void wtap_set_bytes_dumped(wtap_dumper *wdh, gint64 bytes_dumped);
+struct addrinfo;
+WS_DLL_PUBLIC
+gboolean wtap_addrinfo_list_empty(addrinfo_lists_t *addrinfo_lists);
+WS_DLL_PUBLIC
+gboolean wtap_dump_set_addrinfo_list(wtap_dumper *wdh, addrinfo_lists_t *addrinfo_lists);
+WS_DLL_PUBLIC
+void wtap_dump_discard_name_resolution(wtap_dumper *wdh);
+WS_DLL_PUBLIC
+void wtap_dump_discard_decryption_secrets(wtap_dumper *wdh);
+
+/**
+ * Closes open file handles and frees memory associated with wdh. Note that
+ * shb_hdr and idb_inf are not freed by this routine.
+ *
+ * @param wdh handle for the file we're closing.
+ * @param[out] needs_reload if not null, points to a gboolean that will
+ * be set to TRUE if a full reload of the file would be required if
+ * this was done as part of a "Save" or "Save As" operation, FALSE
+ * if no full reload would be required.
+ * @param[out] err points to an int that will be set to an error code
+ * on failure.
+ * @param[out] err_info for some errors, points to a gchar * that will
+ * be set to a string giving more details of the error.
+ *
+ * @return TRUE on success, FALSE on failure.
+ */
+WS_DLL_PUBLIC
+gboolean wtap_dump_close(wtap_dumper *wdh, gboolean *needs_reload,
+ int *err, gchar **err_info);
+
+/**
+ * Return TRUE if we can write a file out with the given GArray of file
+ * encapsulations and the given bitmask of comment types.
+ */
+WS_DLL_PUBLIC
+gboolean wtap_dump_can_write(const GArray *file_encaps, guint32 required_comment_types);
+
+/**
+ * Generates arbitrary packet data in "exported PDU" format
+ * and appends it to buf.
+ * For filetype readers to transform non-packetized data.
+ * Calls ws_buffer_asssure_space() for you and handles padding
+ * to 4-byte boundary.
+ *
+ * @param[in,out] buf Buffer into which to write field
+ * @param epdu_tag tag ID of field to create
+ * @param data data to be written
+ * @param data_len length of data
+ */
+WS_DLL_PUBLIC
+void wtap_buffer_append_epdu_tag(Buffer *buf, guint16 epdu_tag, const guint8 *data, guint16 data_len);
+
+/**
+ * Generates packet data for an unsigned integer in "exported PDU" format.
+ * For filetype readers to transform non-packetized data.
+ *
+ * @param[in,out] buf Buffer into which to write field
+ * @param epdu_tag tag ID of field to create
+ * @param val integer value to write to buf
+ */
+WS_DLL_PUBLIC
+void wtap_buffer_append_epdu_uint(Buffer *buf, guint16 epdu_tag, guint32 val);
+
+/**
+ * Generates packet data for a string in "exported PDU" format.
+ * For filetype readers to transform non-packetized data.
+ *
+ * @param[in,out] buf Buffer into which to write field
+ * @param epdu_tag tag ID of field to create
+ * @param val string value to write to buf
+ */
+WS_DLL_PUBLIC
+void wtap_buffer_append_epdu_string(Buffer *buf, guint16 epdu_tag, const char *val);
+
+/**
+ * Close off a set of "exported PDUs" added to the buffer.
+ * For filetype readers to transform non-packetized data.
+ *
+ * @param[in,out] buf Buffer into which to write field
+ *
+ * @return Total length of buf populated to date
+ */
+WS_DLL_PUBLIC
+gint wtap_buffer_append_epdu_end(Buffer *buf);
+
+/*
+ * Sort the file types by name or by description?
+ */
+typedef enum {
+ FT_SORT_BY_NAME,
+ FT_SORT_BY_DESCRIPTION
+} ft_sort_order;
+
+/**
+ * Get a GArray of file type/subtype values for file types/subtypes
+ * that can be used to save a file of a given type with a given GArray of
+ * WTAP_ENCAP_ types and the given bitmask of comment types.
+ */
+WS_DLL_PUBLIC
+GArray *wtap_get_savable_file_types_subtypes_for_file(int file_type_subtype,
+ const GArray *file_encaps, guint32 required_comment_types,
+ ft_sort_order sort_order);
+
+/**
+ * Get a GArray of all writable file type/subtype values.
+ */
+WS_DLL_PUBLIC
+GArray *wtap_get_writable_file_types_subtypes(ft_sort_order sort_order);
+
+/*** various file type/subtype functions ***/
+WS_DLL_PUBLIC
+const char *wtap_file_type_subtype_description(int file_type_subtype);
+WS_DLL_PUBLIC
+const char *wtap_file_type_subtype_name(int file_type_subtype);
+WS_DLL_PUBLIC
+int wtap_name_to_file_type_subtype(const char *name);
+WS_DLL_PUBLIC
+int wtap_pcap_file_type_subtype(void);
+WS_DLL_PUBLIC
+int wtap_pcap_nsec_file_type_subtype(void);
+WS_DLL_PUBLIC
+int wtap_pcapng_file_type_subtype(void);
+
+/**
+ * Return an indication of whether this capture file format supports
+ * the block in question.
+ */
+WS_DLL_PUBLIC
+block_support_t wtap_file_type_subtype_supports_block(int file_type_subtype,
+ wtap_block_type_t type);
+
+/**
+ * Return an indication of whether this capture file format supports
+ * the option in queston for the block in question.
+ */
+WS_DLL_PUBLIC
+option_support_t wtap_file_type_subtype_supports_option(int file_type_subtype,
+ wtap_block_type_t type, guint opttype);
+
+/* Return a list of all extensions that are used by all capture file
+ * types, including compressed extensions, e.g. not just "pcap" but
+ * also "pcap.gz" if we can read gzipped files.
+ *
+ * "Capture files" means "include file types that correspond to
+ * collections of network packets, but not file types that
+ * store data that just happens to be transported over protocols
+ * such as HTTP but that aren't collections of network packets",
+ * so that it could be used for "All Capture Files" without picking
+ * up JPEG files or files such as that - those aren't capture files,
+ * and we *do* have them listed in the long list of individual file
+ * types, so omitting them from "All Capture Files" is the right
+ * thing to do.
+ *
+ * All strings in the list are allocated with g_malloc() and must be freed
+ * with g_free().
+ *
+ * This is used to generate a list of extensions to look for if the user
+ * chooses "All Capture Files" in a file open dialog.
+ */
+WS_DLL_PUBLIC
+GSList *wtap_get_all_capture_file_extensions_list(void);
+
+/* Return a list of all extensions that are used by all file types that
+ * we can read, including compressed extensions, e.g. not just "pcap" but
+ * also "pcap.gz" if we can read gzipped files.
+ *
+ * "File type" means "include file types that correspond to collections
+ * of network packets, as well as file types that store data that just
+ * happens to be transported over protocols such as HTTP but that aren't
+ * collections of network packets, and plain text files".
+ *
+ * All strings in the list are allocated with g_malloc() and must be freed
+ * with g_free().
+ */
+WS_DLL_PUBLIC
+GSList *wtap_get_all_file_extensions_list(void);
+
+/*
+ * Free a list returned by wtap_get_file_extension_type_extensions(),
+ * wtap_get_all_capture_file_extensions_list, wtap_get_file_extensions_list(),
+ * or wtap_get_all_file_extensions_list().
+ */
+WS_DLL_PUBLIC
+void wtap_free_extensions_list(GSList *extensions);
+
+/*
+ * Return the default file extension to use with the specified file type
+ * and subtype; that's just the extension, without any ".".
+ */
+WS_DLL_PUBLIC
+const char *wtap_default_file_extension(int file_type_subtype);
+
+/* Return a list of file extensions that are used by the specified file type
+ * and subtype.
+ *
+ * If include_compressed is TRUE, the list will include compressed
+ * extensions, e.g. not just "pcap" but also "pcap.gz" if we can read
+ * gzipped files.
+ *
+ * All strings in the list are allocated with g_malloc() and must be freed
+ * with g_free().
+ */
+WS_DLL_PUBLIC
+GSList *wtap_get_file_extensions_list(int file_type_subtype, gboolean include_compressed);
+
+WS_DLL_PUBLIC
+const char *wtap_encap_name(int encap);
+WS_DLL_PUBLIC
+const char *wtap_encap_description(int encap);
+WS_DLL_PUBLIC
+int wtap_name_to_encap(const char *short_name);
+
+WS_DLL_PUBLIC
+const char* wtap_tsprec_string(int tsprec);
+
+WS_DLL_PUBLIC
+const char *wtap_strerror(int err);
+
+/*** get available number of file types and encapsulations ***/
+WS_DLL_PUBLIC
+int wtap_get_num_file_type_extensions(void);
+WS_DLL_PUBLIC
+int wtap_get_num_encap_types(void);
+
+/*** get information for file type extension ***/
+WS_DLL_PUBLIC
+const char *wtap_get_file_extension_type_name(int extension_type);
+WS_DLL_PUBLIC
+GSList *wtap_get_file_extension_type_extensions(guint extension_type);
+
+/*** dynamically register new file types and encapsulations ***/
+WS_DLL_PUBLIC
+void wtap_register_file_type_extension(const struct file_extension_info *ei);
+
+typedef struct {
+ void (*register_wtap_module)(void); /* routine to call to register a wiretap module */
+} wtap_plugin;
+
+WS_DLL_PUBLIC
+void wtap_register_plugin(const wtap_plugin *plug);
+
+/** Returns_
+ * 0 if plugins can be loaded for libwiretap (file type).
+ * 1 if plugins are not supported by the platform.
+ * -1 if plugins were disabled in the build configuration.
+ */
+WS_DLL_PUBLIC
+int wtap_plugins_supported(void);
+
+WS_DLL_PUBLIC
+void wtap_register_open_info(struct open_info *oi, const gboolean first_routine);
+WS_DLL_PUBLIC
+gboolean wtap_has_open_info(const gchar *name);
+WS_DLL_PUBLIC
+gboolean wtap_uses_lua_filehandler(const wtap* wth);
+WS_DLL_PUBLIC
+void wtap_deregister_open_info(const gchar *name);
+
+WS_DLL_PUBLIC
+unsigned int open_info_name_to_type(const char *name);
+WS_DLL_PUBLIC
+int wtap_register_file_type_subtype(const struct file_type_subtype_info* fi);
+WS_DLL_PUBLIC
+void wtap_deregister_file_type_subtype(const int file_type_subtype);
+
+WS_DLL_PUBLIC
+int wtap_register_encap_type(const char *description, const char *name);
+
+/*** Cleanup the internal library structures */
+WS_DLL_PUBLIC
+void wtap_cleanup(void);
+
+/**
+ * Wiretap error codes.
+ */
+#define WTAP_ERR_NOT_REGULAR_FILE -1
+ /**< The file being opened for reading isn't a plain file (or pipe) */
+
+#define WTAP_ERR_RANDOM_OPEN_PIPE -2
+ /**< The file is being opened for random access and it's a pipe */
+
+#define WTAP_ERR_FILE_UNKNOWN_FORMAT -3
+ /**< The file being opened is not a capture file in a known format */
+
+#define WTAP_ERR_UNSUPPORTED -4
+ /**< Supported file type, but there's something in the file we're
+ reading that we can't support */
+
+#define WTAP_ERR_CANT_WRITE_TO_PIPE -5
+ /**< Wiretap can't save to a pipe in the specified format */
+
+#define WTAP_ERR_CANT_OPEN -6
+ /**< The file couldn't be opened, reason unknown */
+
+#define WTAP_ERR_UNWRITABLE_FILE_TYPE -7
+ /**< Wiretap can't save files in the specified format */
+
+#define WTAP_ERR_UNWRITABLE_ENCAP -8
+ /**< Wiretap can't read or save files in the specified format with the
+ specified encapsulation */
+
+#define WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED -9
+ /**< The specified format doesn't support per-packet encapsulations */
+
+#define WTAP_ERR_CANT_WRITE -10
+ /**< An attempt to read failed, reason unknown */
+
+#define WTAP_ERR_CANT_CLOSE -11
+ /**< The file couldn't be closed, reason unknown */
+
+#define WTAP_ERR_SHORT_READ -12
+ /**< An attempt to read less data than it should have */
+
+#define WTAP_ERR_BAD_FILE -13
+ /**< The file appears to be damaged or corrupted or otherwise bogus */
+
+#define WTAP_ERR_SHORT_WRITE -14
+ /**< An attempt to write wrote less data than it should have */
+
+#define WTAP_ERR_UNC_OVERFLOW -15
+ /**< Uncompressing Sniffer data would overflow buffer */
+
+#define WTAP_ERR_RANDOM_OPEN_STDIN -16
+ /**< We're trying to open the standard input for random access */
+
+#define WTAP_ERR_COMPRESSION_NOT_SUPPORTED -17
+ /**< The filetype doesn't support output compression */
+
+#define WTAP_ERR_CANT_SEEK -18
+ /**< An attempt to seek failed, reason unknown */
+
+#define WTAP_ERR_CANT_SEEK_COMPRESSED -19
+ /**< An attempt to seek on a compressed stream */
+
+#define WTAP_ERR_DECOMPRESS -20
+ /**< Error decompressing */
+
+#define WTAP_ERR_INTERNAL -21
+ /**< "Shouldn't happen" internal errors */
+
+#define WTAP_ERR_PACKET_TOO_LARGE -22
+ /**< Packet being written is larger than we support; do not use when
+ reading, use WTAP_ERR_BAD_FILE instead */
+
+#define WTAP_ERR_CHECK_WSLUA -23
+ /**< Not really an error: the file type being checked is from a Lua
+ plugin, so that the code will call wslua_can_write_encap() instead if it gets this */
+
+#define WTAP_ERR_UNWRITABLE_REC_TYPE -24
+ /**< Specified record type can't be written to that file type */
+
+#define WTAP_ERR_UNWRITABLE_REC_DATA -25
+ /**< Something in the record data can't be written to that file type */
+
+#define WTAP_ERR_DECOMPRESSION_NOT_SUPPORTED -26
+ /**< We don't support decompressing that type of compressed file */
+
+#define WTAP_ERR_TIME_STAMP_NOT_SUPPORTED -27
+ /**< We don't support writing that record's time stamp to that
+ file type */
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __WTAP_H__ */
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * vi: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */
diff --git a/wiretap/wtap_modules.h b/wiretap/wtap_modules.h
new file mode 100644
index 00000000..05320c32
--- /dev/null
+++ b/wiretap/wtap_modules.h
@@ -0,0 +1,50 @@
+/** @file
+ *
+ * Definitions for wiretap module registration
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __WTAP_MODULES_H__
+#define __WTAP_MODULES_H__
+
+#include <glib.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/*
+ * Entry in the table of built-in wiretap modules to register.
+ */
+typedef struct _wtap_module_reg {
+ const char *cb_name;
+ void (*cb_func)(void);
+} wtap_module_reg_t;
+
+extern wtap_module_reg_t wtap_module_reg[];
+
+extern const guint wtap_module_count;
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __WTAP_MODULES_H__ */
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local Variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * vi: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */
diff --git a/wiretap/wtap_opttypes.c b/wiretap/wtap_opttypes.c
new file mode 100644
index 00000000..7dfbf1a1
--- /dev/null
+++ b/wiretap/wtap_opttypes.c
@@ -0,0 +1,2269 @@
+/* wtap_opttypes.c
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 2001 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#include "config.h"
+
+#define WS_LOG_DOMAIN LOG_DOMAIN_WIRETAP
+
+#include <glib.h>
+#include <string.h>
+
+#include "wtap.h"
+#include "wtap_opttypes.h"
+#include "wtap-int.h"
+#include "pcapng_module.h"
+#include <wsutil/ws_assert.h>
+
+#include <wsutil/glib-compat.h>
+#include <wsutil/inet_ipv6.h>
+#include <wsutil/unicode-utils.h>
+
+#if 0
+#define wtap_debug(...) ws_warning(__VA_ARGS__)
+#define DEBUG_COUNT_REFS
+#else
+#define wtap_debug(...)
+#endif
+
+#define ROUND_TO_4BYTE(len) (((len) + 3) & ~3)
+
+/*
+ * Structure describing a type of block.
+ */
+typedef struct {
+ wtap_block_type_t block_type; /**< internal type code for block */
+ const char *name; /**< name of block */
+ const char *description; /**< human-readable description of block */
+ wtap_block_create_func create;
+ wtap_mand_free_func free_mand;
+ wtap_mand_copy_func copy_mand;
+ GHashTable *options; /**< hash table of known options */
+} wtap_blocktype_t;
+
+#define GET_OPTION_TYPE(options, option_id) \
+ (const wtap_opttype_t *)g_hash_table_lookup((options), GUINT_TO_POINTER(option_id))
+
+/*
+ * Structure describing a type of option.
+ */
+typedef struct {
+ const char *name; /**< name of option */
+ const char *description; /**< human-readable description of option */
+ wtap_opttype_e data_type; /**< data type of that option */
+ guint flags; /**< flags for the option */
+} wtap_opttype_t;
+
+/* Flags */
+#define WTAP_OPTTYPE_FLAG_MULTIPLE_ALLOWED 0x00000001 /* multiple instances allowed */
+
+/* Debugging reference counting */
+#ifdef DEBUG_COUNT_REFS
+static guint block_count = 0;
+static guint8 blocks_active[sizeof(guint)/8];
+
+static void rc_set(guint refnum)
+{
+ guint cellno = refnum / 8;
+ guint bitno = refnum % 8;
+ blocks_active[cellno] |= (guint8)(1 << bitno);
+}
+
+static void rc_clear(guint refnum)
+{
+ guint cellno = refnum / 8;
+ guint bitno = refnum % 8;
+ blocks_active[cellno] &= (guint8)~(1 << bitno);
+}
+
+#endif /* DEBUG_COUNT_REFS */
+
+struct wtap_block
+{
+ wtap_blocktype_t* info;
+ void* mandatory_data;
+ GArray* options;
+ gint ref_count;
+#ifdef DEBUG_COUNT_REFS
+ guint id;
+#endif
+};
+
+/* Keep track of wtap_blocktype_t's via their id number */
+static wtap_blocktype_t* blocktype_list[MAX_WTAP_BLOCK_TYPE_VALUE];
+
+static if_filter_opt_t if_filter_dup(if_filter_opt_t* filter_src)
+{
+ if_filter_opt_t filter_dest;
+
+ memset(&filter_dest, 0, sizeof(filter_dest));
+
+ /* Deep copy. */
+ filter_dest.type = filter_src->type;
+ switch (filter_src->type) {
+
+ case if_filter_pcap:
+ /* pcap filter string */
+ filter_dest.data.filter_str =
+ g_strdup(filter_src->data.filter_str);
+ break;
+
+ case if_filter_bpf:
+ /* BPF program */
+ filter_dest.data.bpf_prog.bpf_prog_len =
+ filter_src->data.bpf_prog.bpf_prog_len;
+ filter_dest.data.bpf_prog.bpf_prog =
+ (wtap_bpf_insn_t *)g_memdup2(filter_src->data.bpf_prog.bpf_prog,
+ filter_src->data.bpf_prog.bpf_prog_len * sizeof (wtap_bpf_insn_t));
+ break;
+
+ default:
+ break;
+ }
+ return filter_dest;
+}
+
+static void if_filter_free(if_filter_opt_t* filter_src)
+{
+ switch (filter_src->type) {
+
+ case if_filter_pcap:
+ /* pcap filter string */
+ g_free(filter_src->data.filter_str);
+ break;
+
+ case if_filter_bpf:
+ /* BPF program */
+ g_free(filter_src->data.bpf_prog.bpf_prog);
+ break;
+
+ default:
+ break;
+ }
+}
+
+static packet_verdict_opt_t
+packet_verdict_dup(packet_verdict_opt_t* verdict_src)
+{
+ packet_verdict_opt_t verdict_dest;
+
+ memset(&verdict_dest, 0, sizeof(verdict_dest));
+
+ /* Deep copy. */
+ verdict_dest.type = verdict_src->type;
+ switch (verdict_src->type) {
+
+ case packet_verdict_hardware:
+ /* array of octets */
+ verdict_dest.data.verdict_bytes =
+ g_byte_array_new_take((guint8 *)g_memdup2(verdict_src->data.verdict_bytes->data,
+ verdict_src->data.verdict_bytes->len),
+ verdict_src->data.verdict_bytes->len);
+ break;
+
+ case packet_verdict_linux_ebpf_tc:
+ /* eBPF TC_ACT_ value */
+ verdict_dest.data.verdict_linux_ebpf_tc =
+ verdict_src->data.verdict_linux_ebpf_tc;
+ break;
+
+ case packet_verdict_linux_ebpf_xdp:
+ /* xdp_action value */
+ verdict_dest.data.verdict_linux_ebpf_xdp =
+ verdict_src->data.verdict_linux_ebpf_xdp;
+ break;
+
+ default:
+ break;
+ }
+ return verdict_dest;
+}
+
+void wtap_packet_verdict_free(packet_verdict_opt_t* verdict)
+{
+ switch (verdict->type) {
+
+ case packet_verdict_hardware:
+ /* array of bytes */
+ g_byte_array_free(verdict->data.verdict_bytes, TRUE);
+ break;
+
+ default:
+ break;
+ }
+}
+
+static packet_hash_opt_t
+packet_hash_dup(packet_hash_opt_t* hash_src)
+{
+ packet_hash_opt_t hash_dest;
+
+ memset(&hash_dest, 0, sizeof(hash_dest));
+
+ /* Deep copy. */
+ hash_dest.type = hash_src->type;
+ /* array of octets */
+ hash_dest.hash_bytes =
+ g_byte_array_new_take((guint8 *)g_memdup2(hash_src->hash_bytes->data,
+ hash_src->hash_bytes->len),
+ hash_src->hash_bytes->len);
+ return hash_dest;
+}
+
+void wtap_packet_hash_free(packet_hash_opt_t* hash)
+{
+ /* array of bytes */
+ g_byte_array_free(hash->hash_bytes, TRUE);
+}
+
+static void wtap_opttype_block_register(wtap_blocktype_t *blocktype)
+{
+ wtap_block_type_t block_type;
+ static const wtap_opttype_t opt_comment = {
+ "opt_comment",
+ "Comment",
+ WTAP_OPTTYPE_STRING,
+ WTAP_OPTTYPE_FLAG_MULTIPLE_ALLOWED
+ };
+ static const wtap_opttype_t opt_custom = {
+ "opt_custom",
+ "Custom Option",
+ WTAP_OPTTYPE_CUSTOM,
+ WTAP_OPTTYPE_FLAG_MULTIPLE_ALLOWED
+ };
+
+ block_type = blocktype->block_type;
+
+ /* Check input */
+ ws_assert(block_type < MAX_WTAP_BLOCK_TYPE_VALUE);
+
+ /* Don't re-register. */
+ ws_assert(blocktype_list[block_type] == NULL);
+
+ /* Sanity check */
+ ws_assert(blocktype->name);
+ ws_assert(blocktype->description);
+ ws_assert(blocktype->create);
+
+ /*
+ * Initialize the set of supported options.
+ * All blocks that support options at all support
+ * OPT_COMMENT and OPT_CUSTOM.
+ *
+ * XXX - there's no "g_uint_hash()" or "g_uint_equal()",
+ * so we use "g_direct_hash()" and "g_direct_equal()".
+ */
+ blocktype->options = g_hash_table_new(g_direct_hash, g_direct_equal);
+ g_hash_table_insert(blocktype->options, GUINT_TO_POINTER(OPT_COMMENT),
+ (gpointer)&opt_comment);
+ g_hash_table_insert(blocktype->options, GUINT_TO_POINTER(OPT_CUSTOM_STR_COPY),
+ (gpointer)&opt_custom);
+ g_hash_table_insert(blocktype->options, GUINT_TO_POINTER(OPT_CUSTOM_BIN_COPY),
+ (gpointer)&opt_custom);
+ g_hash_table_insert(blocktype->options, GUINT_TO_POINTER(OPT_CUSTOM_STR_NO_COPY),
+ (gpointer)&opt_custom);
+ g_hash_table_insert(blocktype->options, GUINT_TO_POINTER(OPT_CUSTOM_BIN_NO_COPY),
+ (gpointer)&opt_custom);
+
+ blocktype_list[block_type] = blocktype;
+}
+
+static void wtap_opttype_option_register(wtap_blocktype_t *blocktype, guint opttype, const wtap_opttype_t *option)
+{
+ g_hash_table_insert(blocktype->options, GUINT_TO_POINTER(opttype),
+ (gpointer) option);
+}
+
+wtap_block_type_t wtap_block_get_type(wtap_block_t block)
+{
+ return block->info->block_type;
+}
+
+void* wtap_block_get_mandatory_data(wtap_block_t block)
+{
+ return block->mandatory_data;
+}
+
+static wtap_optval_t *
+wtap_block_get_option(wtap_block_t block, guint option_id)
+{
+ guint i;
+ wtap_option_t *opt;
+
+ if (block == NULL) {
+ return NULL;
+ }
+
+ for (i = 0; i < block->options->len; i++) {
+ opt = &g_array_index(block->options, wtap_option_t, i);
+ if (opt->option_id == option_id)
+ return &opt->value;
+ }
+
+ return NULL;
+}
+
+static wtap_optval_t *
+wtap_block_get_nth_option(wtap_block_t block, guint option_id, guint idx)
+{
+ guint i;
+ wtap_option_t *opt;
+ guint opt_idx;
+
+ if (block == NULL) {
+ return NULL;
+ }
+
+ opt_idx = 0;
+ for (i = 0; i < block->options->len; i++) {
+ opt = &g_array_index(block->options, wtap_option_t, i);
+ if (opt->option_id == option_id) {
+ if (opt_idx == idx)
+ return &opt->value;
+ opt_idx++;
+ }
+ }
+
+ return NULL;
+}
+
+wtap_block_t wtap_block_create(wtap_block_type_t block_type)
+{
+ wtap_block_t block;
+
+ if (block_type >= MAX_WTAP_BLOCK_TYPE_VALUE)
+ return NULL;
+
+ block = g_new(struct wtap_block, 1);
+ block->info = blocktype_list[block_type];
+ block->options = g_array_new(FALSE, FALSE, sizeof(wtap_option_t));
+ block->info->create(block);
+ block->ref_count = 1;
+#ifdef DEBUG_COUNT_REFS
+ block->id = block_count++;
+ rc_set(block->id);
+ wtap_debug("Created #%d %s", block->id, block->info->name);
+#endif /* DEBUG_COUNT_REFS */
+
+ return block;
+}
+
+static void wtap_block_free_option(wtap_block_t block, wtap_option_t *opt)
+{
+ const wtap_opttype_t *opttype;
+
+ if (block == NULL) {
+ return;
+ }
+
+ opttype = GET_OPTION_TYPE(block->info->options, opt->option_id);
+ switch (opttype->data_type) {
+
+ case WTAP_OPTTYPE_STRING:
+ g_free(opt->value.stringval);
+ break;
+
+ case WTAP_OPTTYPE_BYTES:
+ g_bytes_unref(opt->value.byteval);
+ break;
+
+ case WTAP_OPTTYPE_CUSTOM:
+ switch (opt->value.custom_opt.pen) {
+ case PEN_NFLX:
+ g_free(opt->value.custom_opt.data.nflx_data.custom_data);
+ break;
+ default:
+ g_free(opt->value.custom_opt.data.generic_data.custom_data);
+ break;
+ }
+ break;
+
+ case WTAP_OPTTYPE_IF_FILTER:
+ if_filter_free(&opt->value.if_filterval);
+ break;
+
+ case WTAP_OPTTYPE_PACKET_VERDICT:
+ wtap_packet_verdict_free(&opt->value.packet_verdictval);
+ break;
+
+ case WTAP_OPTTYPE_PACKET_HASH:
+ wtap_packet_hash_free(&opt->value.packet_hash);
+ break;
+
+ default:
+ break;
+ }
+}
+
+static void wtap_block_free_options(wtap_block_t block)
+{
+ guint i;
+ wtap_option_t *opt;
+
+ if (block == NULL || block->options == NULL) {
+ return;
+ }
+
+ for (i = 0; i < block->options->len; i++) {
+ opt = &g_array_index(block->options, wtap_option_t, i);
+ wtap_block_free_option(block, opt);
+ }
+ g_array_remove_range(block->options, 0, block->options->len);
+}
+
+wtap_block_t wtap_block_ref(wtap_block_t block)
+{
+ if (block == NULL) {
+ return NULL;
+ }
+
+ g_atomic_int_inc(&block->ref_count);
+#ifdef DEBUG_COUNT_REFS
+ wtap_debug("Ref #%d %s", block->id, block->info->name);
+#endif /* DEBUG_COUNT_REFS */
+ return block;
+}
+
+void wtap_block_unref(wtap_block_t block)
+{
+ if (block != NULL)
+ {
+ if (g_atomic_int_dec_and_test(&block->ref_count)) {
+#ifdef DEBUG_COUNT_REFS
+ wtap_debug("Destroy #%d %s", block->id, block->info->name);
+ rc_clear(block->id);
+#endif /* DEBUG_COUNT_REFS */
+ if (block->info->free_mand != NULL)
+ block->info->free_mand(block);
+
+ g_free(block->mandatory_data);
+ wtap_block_free_options(block);
+ g_array_free(block->options, TRUE);
+ g_free(block);
+ }
+#ifdef DEBUG_COUNT_REFS
+ else {
+ wtap_debug("Unref #%d %s", block->id, block->info->name);
+ }
+#endif /* DEBUG_COUNT_REFS */
+ }
+}
+
+void wtap_block_array_free(GArray* block_array)
+{
+ guint block;
+
+ if (block_array == NULL)
+ return;
+
+ for (block = 0; block < block_array->len; block++) {
+ wtap_block_unref(g_array_index(block_array, wtap_block_t, block));
+ }
+ g_array_free(block_array, TRUE);
+}
+
+/*
+ * Make a copy of a block.
+ */
+void
+wtap_block_copy(wtap_block_t dest_block, wtap_block_t src_block)
+{
+ guint i;
+ wtap_option_t *src_opt;
+ const wtap_opttype_t *opttype;
+
+ /*
+ * Copy the mandatory data.
+ */
+ if (dest_block->info->copy_mand != NULL)
+ dest_block->info->copy_mand(dest_block, src_block);
+
+ /* Copy the options. For now, don't remove any options that are in destination
+ * but not source.
+ */
+ for (i = 0; i < src_block->options->len; i++)
+ {
+ src_opt = &g_array_index(src_block->options, wtap_option_t, i);
+ opttype = GET_OPTION_TYPE(src_block->info->options, src_opt->option_id);
+
+ switch(opttype->data_type) {
+
+ case WTAP_OPTTYPE_UINT8:
+ wtap_block_add_uint8_option(dest_block, src_opt->option_id, src_opt->value.uint8val);
+ break;
+
+ case WTAP_OPTTYPE_UINT32:
+ wtap_block_add_uint32_option(dest_block, src_opt->option_id, src_opt->value.uint32val);
+ break;
+
+ case WTAP_OPTTYPE_UINT64:
+ wtap_block_add_uint64_option(dest_block, src_opt->option_id, src_opt->value.uint64val);
+ break;
+
+ case WTAP_OPTTYPE_INT8:
+ wtap_block_add_int8_option(dest_block, src_opt->option_id, src_opt->value.int8val);
+ break;
+
+ case WTAP_OPTTYPE_INT32:
+ wtap_block_add_int32_option(dest_block, src_opt->option_id, src_opt->value.int32val);
+ break;
+
+ case WTAP_OPTTYPE_INT64:
+ wtap_block_add_int64_option(dest_block, src_opt->option_id, src_opt->value.int64val);
+ break;
+
+ case WTAP_OPTTYPE_IPv4:
+ wtap_block_add_ipv4_option(dest_block, src_opt->option_id, src_opt->value.ipv4val);
+ break;
+
+ case WTAP_OPTTYPE_IPv6:
+ wtap_block_add_ipv6_option(dest_block, src_opt->option_id, &src_opt->value.ipv6val);
+ break;
+
+ case WTAP_OPTTYPE_STRING:
+ wtap_block_add_string_option(dest_block, src_opt->option_id, src_opt->value.stringval, strlen(src_opt->value.stringval));
+ break;
+
+ case WTAP_OPTTYPE_BYTES:
+ wtap_block_add_bytes_option_borrow(dest_block, src_opt->option_id, src_opt->value.byteval);
+ break;
+
+ case WTAP_OPTTYPE_CUSTOM:
+ switch (src_opt->value.custom_opt.pen) {
+ case PEN_NFLX:
+ wtap_block_add_nflx_custom_option(dest_block, src_opt->value.custom_opt.data.nflx_data.type, src_opt->value.custom_opt.data.nflx_data.custom_data, src_opt->value.custom_opt.data.nflx_data.custom_data_len);
+ break;
+ default:
+ wtap_block_add_custom_option(dest_block, src_opt->option_id, src_opt->value.custom_opt.pen, src_opt->value.custom_opt.data.generic_data.custom_data, src_opt->value.custom_opt.data.generic_data.custom_data_len);
+ break;
+ }
+ break;
+
+ case WTAP_OPTTYPE_IF_FILTER:
+ wtap_block_add_if_filter_option(dest_block, src_opt->option_id, &src_opt->value.if_filterval);
+ break;
+
+ case WTAP_OPTTYPE_PACKET_VERDICT:
+ wtap_block_add_packet_verdict_option(dest_block, src_opt->option_id, &src_opt->value.packet_verdictval);
+ break;
+
+ case WTAP_OPTTYPE_PACKET_HASH:
+ wtap_block_add_packet_hash_option(dest_block, src_opt->option_id, &src_opt->value.packet_hash);
+ break;
+ }
+ }
+}
+
+wtap_block_t wtap_block_make_copy(wtap_block_t block)
+{
+ wtap_block_t block_copy;
+
+ block_copy = wtap_block_create(block->info->block_type);
+ wtap_block_copy(block_copy, block);
+ return block_copy;
+}
+
+guint
+wtap_block_count_option(wtap_block_t block, guint option_id)
+{
+ guint i;
+ guint ret_val = 0;
+ wtap_option_t *opt;
+
+ if (block == NULL) {
+ return 0;
+ }
+
+ for (i = 0; i < block->options->len; i++) {
+ opt = &g_array_index(block->options, wtap_option_t, i);
+ if (opt->option_id == option_id)
+ ret_val++;
+ }
+
+ return ret_val;
+}
+
+
+gboolean wtap_block_foreach_option(wtap_block_t block, wtap_block_foreach_func func, void* user_data)
+{
+ guint i;
+ wtap_option_t *opt;
+ const wtap_opttype_t *opttype;
+
+ if (block == NULL) {
+ return TRUE;
+ }
+
+ for (i = 0; i < block->options->len; i++) {
+ opt = &g_array_index(block->options, wtap_option_t, i);
+ opttype = GET_OPTION_TYPE(block->info->options, opt->option_id);
+ if (!func(block, opt->option_id, opttype->data_type, &opt->value, user_data))
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static wtap_opttype_return_val
+wtap_block_add_option_common(wtap_block_t block, guint option_id, wtap_opttype_e type, wtap_option_t **optp)
+{
+ wtap_option_t *opt;
+ const wtap_opttype_t *opttype;
+ guint i;
+
+ if (block == NULL) {
+ return WTAP_OPTTYPE_BAD_BLOCK;
+ }
+
+ opttype = GET_OPTION_TYPE(block->info->options, option_id);
+ if (opttype == NULL) {
+ /* There's no option for this block with that option ID */
+ return WTAP_OPTTYPE_NO_SUCH_OPTION;
+ }
+
+ /*
+ * Is this an option of the specified data type?
+ */
+ if (opttype->data_type != type) {
+ /*
+ * No.
+ */
+ return WTAP_OPTTYPE_TYPE_MISMATCH;
+ }
+
+ /*
+ * Can there be more than one instance of this option?
+ */
+ if (!(opttype->flags & WTAP_OPTTYPE_FLAG_MULTIPLE_ALLOWED)) {
+ /*
+ * No. Is there already an instance of this option?
+ */
+ if (wtap_block_get_option(block, option_id) != NULL) {
+ /*
+ * Yes. Fail.
+ */
+ return WTAP_OPTTYPE_ALREADY_EXISTS;
+ }
+ }
+
+ /*
+ * Add an instance.
+ */
+ i = block->options->len;
+ g_array_set_size(block->options, i + 1);
+ opt = &g_array_index(block->options, wtap_option_t, i);
+ opt->option_id = option_id;
+ *optp = opt;
+ return WTAP_OPTTYPE_SUCCESS;
+}
+
+static wtap_opttype_return_val
+wtap_block_get_option_common(wtap_block_t block, guint option_id, wtap_opttype_e type, wtap_optval_t **optvalp)
+{
+ const wtap_opttype_t *opttype;
+ wtap_optval_t *optval;
+
+ if (block == NULL) {
+ return WTAP_OPTTYPE_BAD_BLOCK;
+ }
+
+ opttype = GET_OPTION_TYPE(block->info->options, option_id);
+ if (opttype == NULL) {
+ /* There's no option for this block with that option ID */
+ return WTAP_OPTTYPE_NO_SUCH_OPTION;
+ }
+
+ /*
+ * Is this an option of the specified data type?
+ */
+ if (opttype->data_type != type) {
+ /*
+ * No.
+ */
+ return WTAP_OPTTYPE_TYPE_MISMATCH;
+ }
+
+ /*
+ * Can there be more than one instance of this option?
+ */
+ if (opttype->flags & WTAP_OPTTYPE_FLAG_MULTIPLE_ALLOWED) {
+ /*
+ * Yes. You can't ask for "the" value.
+ */
+ return WTAP_OPTTYPE_NUMBER_MISMATCH;
+ }
+
+ optval = wtap_block_get_option(block, option_id);
+ if (optval == NULL) {
+ /* Didn't find the option */
+ return WTAP_OPTTYPE_NOT_FOUND;
+ }
+
+ *optvalp = optval;
+ return WTAP_OPTTYPE_SUCCESS;
+}
+
+static wtap_opttype_return_val
+wtap_block_get_nth_option_common(wtap_block_t block, guint option_id, wtap_opttype_e type, guint idx, wtap_optval_t **optvalp)
+{
+ const wtap_opttype_t *opttype;
+ wtap_optval_t *optval;
+
+ if (block == NULL) {
+ return WTAP_OPTTYPE_BAD_BLOCK;
+ }
+
+ opttype = GET_OPTION_TYPE(block->info->options, option_id);
+ if (opttype == NULL) {
+ /* There's no option for this block with that option ID */
+ return WTAP_OPTTYPE_NO_SUCH_OPTION;
+ }
+
+ /*
+ * Is this an option of the specified data type?
+ */
+ if (opttype->data_type != type) {
+ /*
+ * No.
+ */
+ return WTAP_OPTTYPE_TYPE_MISMATCH;
+ }
+
+ /*
+ * Can there be more than one instance of this option?
+ */
+ if (!(opttype->flags & WTAP_OPTTYPE_FLAG_MULTIPLE_ALLOWED)) {
+ /*
+ * No.
+ */
+ return WTAP_OPTTYPE_NUMBER_MISMATCH;
+ }
+
+ optval = wtap_block_get_nth_option(block, option_id, idx);
+ if (optval == NULL) {
+ /* Didn't find the option */
+ return WTAP_OPTTYPE_NOT_FOUND;
+ }
+
+ *optvalp = optval;
+ return WTAP_OPTTYPE_SUCCESS;
+}
+
+wtap_opttype_return_val
+wtap_block_add_uint8_option(wtap_block_t block, guint option_id, guint8 value)
+{
+ wtap_opttype_return_val ret;
+ wtap_option_t *opt;
+
+ ret = wtap_block_add_option_common(block, option_id, WTAP_OPTTYPE_UINT8, &opt);
+ if (ret != WTAP_OPTTYPE_SUCCESS)
+ return ret;
+ opt->value.uint8val = value;
+ return WTAP_OPTTYPE_SUCCESS;
+}
+
+wtap_opttype_return_val
+wtap_block_set_uint8_option_value(wtap_block_t block, guint option_id, guint8 value)
+{
+ wtap_opttype_return_val ret;
+ wtap_optval_t *optval;
+
+ ret = wtap_block_get_option_common(block, option_id, WTAP_OPTTYPE_UINT8, &optval);
+ if (ret != WTAP_OPTTYPE_SUCCESS)
+ return ret;
+ optval->uint8val = value;
+ return WTAP_OPTTYPE_SUCCESS;
+}
+
+wtap_opttype_return_val
+wtap_block_get_uint8_option_value(wtap_block_t block, guint option_id, guint8* value)
+{
+ wtap_opttype_return_val ret;
+ wtap_optval_t *optval;
+
+ ret = wtap_block_get_option_common(block, option_id, WTAP_OPTTYPE_UINT8, &optval);
+ if (ret != WTAP_OPTTYPE_SUCCESS)
+ return ret;
+ *value = optval->uint8val;
+ return WTAP_OPTTYPE_SUCCESS;
+}
+
+wtap_opttype_return_val
+wtap_block_add_uint32_option(wtap_block_t block, guint option_id, guint32 value)
+{
+ wtap_opttype_return_val ret;
+ wtap_option_t *opt;
+
+ ret = wtap_block_add_option_common(block, option_id, WTAP_OPTTYPE_UINT32, &opt);
+ if (ret != WTAP_OPTTYPE_SUCCESS)
+ return ret;
+ opt->value.uint32val = value;
+ return WTAP_OPTTYPE_SUCCESS;
+}
+
+wtap_opttype_return_val
+wtap_block_set_uint32_option_value(wtap_block_t block, guint option_id, guint32 value)
+{
+ wtap_opttype_return_val ret;
+ wtap_optval_t *optval;
+
+ ret = wtap_block_get_option_common(block, option_id, WTAP_OPTTYPE_UINT32, &optval);
+ if (ret != WTAP_OPTTYPE_SUCCESS)
+ return ret;
+ optval->uint32val = value;
+ return WTAP_OPTTYPE_SUCCESS;
+}
+
+wtap_opttype_return_val
+wtap_block_get_uint32_option_value(wtap_block_t block, guint option_id, guint32* value)
+{
+ wtap_opttype_return_val ret;
+ wtap_optval_t *optval;
+
+ ret = wtap_block_get_option_common(block, option_id, WTAP_OPTTYPE_UINT32, &optval);
+ if (ret != WTAP_OPTTYPE_SUCCESS)
+ return ret;
+ *value = optval->uint32val;
+ return WTAP_OPTTYPE_SUCCESS;
+}
+
+wtap_opttype_return_val
+wtap_block_add_uint64_option(wtap_block_t block, guint option_id, guint64 value)
+{
+ wtap_opttype_return_val ret;
+ wtap_option_t *opt;
+
+ ret = wtap_block_add_option_common(block, option_id, WTAP_OPTTYPE_UINT64, &opt);
+ if (ret != WTAP_OPTTYPE_SUCCESS)
+ return ret;
+ opt->value.uint64val = value;
+ return WTAP_OPTTYPE_SUCCESS;
+}
+
+wtap_opttype_return_val
+wtap_block_set_uint64_option_value(wtap_block_t block, guint option_id, guint64 value)
+{
+ wtap_opttype_return_val ret;
+ wtap_optval_t *optval;
+
+ ret = wtap_block_get_option_common(block, option_id, WTAP_OPTTYPE_UINT64, &optval);
+ if (ret != WTAP_OPTTYPE_SUCCESS)
+ return ret;
+ optval->uint64val = value;
+ return WTAP_OPTTYPE_SUCCESS;
+}
+
+wtap_opttype_return_val
+wtap_block_get_uint64_option_value(wtap_block_t block, guint option_id, guint64 *value)
+{
+ wtap_opttype_return_val ret;
+ wtap_optval_t *optval;
+
+ ret = wtap_block_get_option_common(block, option_id, WTAP_OPTTYPE_UINT64, &optval);
+ if (ret != WTAP_OPTTYPE_SUCCESS)
+ return ret;
+ *value = optval->uint64val;
+ return WTAP_OPTTYPE_SUCCESS;
+}
+
+wtap_opttype_return_val
+wtap_block_add_int8_option(wtap_block_t block, guint option_id, gint8 value)
+{
+ wtap_opttype_return_val ret;
+ wtap_option_t *opt;
+
+ ret = wtap_block_add_option_common(block, option_id, WTAP_OPTTYPE_INT8, &opt);
+ if (ret != WTAP_OPTTYPE_SUCCESS)
+ return ret;
+ opt->value.int8val = value;
+ return WTAP_OPTTYPE_SUCCESS;
+}
+
+wtap_opttype_return_val
+wtap_block_set_int8_option_value(wtap_block_t block, guint option_id, gint8 value)
+{
+ wtap_opttype_return_val ret;
+ wtap_optval_t *optval;
+
+ ret = wtap_block_get_option_common(block, option_id, WTAP_OPTTYPE_INT8, &optval);
+ if (ret != WTAP_OPTTYPE_SUCCESS)
+ return ret;
+ optval->int8val = value;
+ return WTAP_OPTTYPE_SUCCESS;
+}
+
+wtap_opttype_return_val
+wtap_block_get_int8_option_value(wtap_block_t block, guint option_id, gint8* value)
+{
+ wtap_opttype_return_val ret;
+ wtap_optval_t *optval;
+
+ ret = wtap_block_get_option_common(block, option_id, WTAP_OPTTYPE_INT8, &optval);
+ if (ret != WTAP_OPTTYPE_SUCCESS)
+ return ret;
+ *value = optval->int8val;
+ return WTAP_OPTTYPE_SUCCESS;
+}
+
+wtap_opttype_return_val
+wtap_block_add_int32_option(wtap_block_t block, guint option_id, gint32 value)
+{
+ wtap_opttype_return_val ret;
+ wtap_option_t *opt;
+
+ ret = wtap_block_add_option_common(block, option_id, WTAP_OPTTYPE_INT32, &opt);
+ if (ret != WTAP_OPTTYPE_SUCCESS)
+ return ret;
+ opt->value.int32val = value;
+ return WTAP_OPTTYPE_SUCCESS;
+}
+
+wtap_opttype_return_val
+wtap_block_set_int32_option_value(wtap_block_t block, guint option_id, gint32 value)
+{
+ wtap_opttype_return_val ret;
+ wtap_optval_t *optval;
+
+ ret = wtap_block_get_option_common(block, option_id, WTAP_OPTTYPE_INT32, &optval);
+ if (ret != WTAP_OPTTYPE_SUCCESS)
+ return ret;
+ optval->int32val = value;
+ return WTAP_OPTTYPE_SUCCESS;
+}
+
+wtap_opttype_return_val
+wtap_block_get_int32_option_value(wtap_block_t block, guint option_id, gint32* value)
+{
+ wtap_opttype_return_val ret;
+ wtap_optval_t *optval;
+
+ ret = wtap_block_get_option_common(block, option_id, WTAP_OPTTYPE_INT32, &optval);
+ if (ret != WTAP_OPTTYPE_SUCCESS)
+ return ret;
+ *value = optval->int32val;
+ return WTAP_OPTTYPE_SUCCESS;
+}
+
+wtap_opttype_return_val
+wtap_block_add_int64_option(wtap_block_t block, guint option_id, gint64 value)
+{
+ wtap_opttype_return_val ret;
+ wtap_option_t *opt;
+
+ ret = wtap_block_add_option_common(block, option_id, WTAP_OPTTYPE_INT64, &opt);
+ if (ret != WTAP_OPTTYPE_SUCCESS)
+ return ret;
+ opt->value.int64val = value;
+ return WTAP_OPTTYPE_SUCCESS;
+}
+
+wtap_opttype_return_val
+wtap_block_set_int64_option_value(wtap_block_t block, guint option_id, gint64 value)
+{
+ wtap_opttype_return_val ret;
+ wtap_optval_t *optval;
+
+ ret = wtap_block_get_option_common(block, option_id, WTAP_OPTTYPE_INT64, &optval);
+ if (ret != WTAP_OPTTYPE_SUCCESS)
+ return ret;
+ optval->int64val = value;
+ return WTAP_OPTTYPE_SUCCESS;
+}
+
+wtap_opttype_return_val
+wtap_block_get_int64_option_value(wtap_block_t block, guint option_id, gint64 *value)
+{
+ wtap_opttype_return_val ret;
+ wtap_optval_t *optval;
+
+ ret = wtap_block_get_option_common(block, option_id, WTAP_OPTTYPE_INT64, &optval);
+ if (ret != WTAP_OPTTYPE_SUCCESS)
+ return ret;
+ *value = optval->int64val;
+ return WTAP_OPTTYPE_SUCCESS;
+}
+
+wtap_opttype_return_val
+wtap_block_add_ipv4_option(wtap_block_t block, guint option_id, guint32 value)
+{
+ wtap_opttype_return_val ret;
+ wtap_option_t *opt;
+
+ ret = wtap_block_add_option_common(block, option_id, WTAP_OPTTYPE_IPv4, &opt);
+ if (ret != WTAP_OPTTYPE_SUCCESS)
+ return ret;
+ opt->value.ipv4val = value;
+ return WTAP_OPTTYPE_SUCCESS;
+}
+
+wtap_opttype_return_val
+wtap_block_set_ipv4_option_value(wtap_block_t block, guint option_id, guint32 value)
+{
+ wtap_opttype_return_val ret;
+ wtap_optval_t *optval;
+
+ ret = wtap_block_get_option_common(block, option_id, WTAP_OPTTYPE_IPv4, &optval);
+ if (ret != WTAP_OPTTYPE_SUCCESS)
+ return ret;
+ optval->ipv4val = value;
+ return WTAP_OPTTYPE_SUCCESS;
+}
+
+wtap_opttype_return_val
+wtap_block_get_ipv4_option_value(wtap_block_t block, guint option_id, guint32* value)
+{
+ wtap_opttype_return_val ret;
+ wtap_optval_t *optval;
+
+ ret = wtap_block_get_option_common(block, option_id, WTAP_OPTTYPE_IPv4, &optval);
+ if (ret != WTAP_OPTTYPE_SUCCESS)
+ return ret;
+ *value = optval->ipv4val;
+ return WTAP_OPTTYPE_SUCCESS;
+}
+
+wtap_opttype_return_val
+wtap_block_add_ipv6_option(wtap_block_t block, guint option_id, ws_in6_addr *value)
+{
+ wtap_opttype_return_val ret;
+ wtap_option_t *opt;
+
+ ret = wtap_block_add_option_common(block, option_id, WTAP_OPTTYPE_IPv6, &opt);
+ if (ret != WTAP_OPTTYPE_SUCCESS)
+ return ret;
+ opt->value.ipv6val = *value;
+ return WTAP_OPTTYPE_SUCCESS;
+}
+
+wtap_opttype_return_val
+wtap_block_set_ipv6_option_value(wtap_block_t block, guint option_id, ws_in6_addr *value)
+{
+ wtap_opttype_return_val ret;
+ wtap_optval_t *optval;
+
+ ret = wtap_block_get_option_common(block, option_id, WTAP_OPTTYPE_IPv6, &optval);
+ if (ret != WTAP_OPTTYPE_SUCCESS)
+ return ret;
+ optval->ipv6val = *value;
+ return WTAP_OPTTYPE_SUCCESS;
+}
+
+wtap_opttype_return_val
+wtap_block_get_ipv6_option_value(wtap_block_t block, guint option_id, ws_in6_addr* value)
+{
+ wtap_opttype_return_val ret;
+ wtap_optval_t *optval;
+
+ ret = wtap_block_get_option_common(block, option_id, WTAP_OPTTYPE_IPv6, &optval);
+ if (ret != WTAP_OPTTYPE_SUCCESS)
+ return ret;
+ *value = optval->ipv6val;
+ return WTAP_OPTTYPE_SUCCESS;
+}
+
+wtap_opttype_return_val
+wtap_block_add_string_option(wtap_block_t block, guint option_id, const char *value, gsize value_length)
+{
+ wtap_opttype_return_val ret;
+ wtap_option_t *opt;
+
+ ret = wtap_block_add_option_common(block, option_id, WTAP_OPTTYPE_STRING, &opt);
+ if (ret != WTAP_OPTTYPE_SUCCESS)
+ return ret;
+ opt->value.stringval = g_strndup(value, value_length);
+ WS_UTF_8_CHECK(opt->value.stringval, -1);
+ return WTAP_OPTTYPE_SUCCESS;
+}
+
+wtap_opttype_return_val
+wtap_block_add_string_option_owned(wtap_block_t block, guint option_id, char *value)
+{
+ wtap_opttype_return_val ret;
+ wtap_option_t *opt;
+
+ ret = wtap_block_add_option_common(block, option_id, WTAP_OPTTYPE_STRING, &opt);
+ if (ret != WTAP_OPTTYPE_SUCCESS)
+ return ret;
+ opt->value.stringval = value;
+ WS_UTF_8_CHECK(opt->value.stringval, -1);
+ return WTAP_OPTTYPE_SUCCESS;
+}
+
+static wtap_opttype_return_val
+wtap_block_add_string_option_vformat(wtap_block_t block, guint option_id, const char *format, va_list va)
+{
+ wtap_opttype_return_val ret;
+ wtap_option_t *opt;
+
+ ret = wtap_block_add_option_common(block, option_id, WTAP_OPTTYPE_STRING, &opt);
+ if (ret != WTAP_OPTTYPE_SUCCESS)
+ return ret;
+ opt->value.stringval = ws_strdup_vprintf(format, va);
+ WS_UTF_8_CHECK(opt->value.stringval, -1);
+ return WTAP_OPTTYPE_SUCCESS;
+}
+
+wtap_opttype_return_val
+wtap_block_add_string_option_format(wtap_block_t block, guint option_id, const char *format, ...)
+{
+ wtap_opttype_return_val ret;
+ wtap_option_t *opt;
+ va_list va;
+
+ ret = wtap_block_add_option_common(block, option_id, WTAP_OPTTYPE_STRING, &opt);
+ if (ret != WTAP_OPTTYPE_SUCCESS)
+ return ret;
+ va_start(va, format);
+ opt->value.stringval = ws_strdup_vprintf(format, va);
+ va_end(va);
+ return WTAP_OPTTYPE_SUCCESS;
+}
+
+wtap_opttype_return_val
+wtap_block_set_string_option_value(wtap_block_t block, guint option_id, const char *value, size_t value_length)
+{
+ wtap_opttype_return_val ret;
+ wtap_optval_t *optval;
+
+ ret = wtap_block_get_option_common(block, option_id, WTAP_OPTTYPE_STRING, &optval);
+ if (ret != WTAP_OPTTYPE_SUCCESS) {
+ if (ret == WTAP_OPTTYPE_NOT_FOUND) {
+ /*
+ * There's no instance to set, so just try to create a new one
+ * with the value.
+ */
+ return wtap_block_add_string_option(block, option_id, value, value_length);
+ }
+ /* Otherwise fail. */
+ return ret;
+ }
+ g_free(optval->stringval);
+ optval->stringval = g_strndup(value, value_length);
+ return WTAP_OPTTYPE_SUCCESS;
+}
+
+wtap_opttype_return_val
+wtap_block_set_nth_string_option_value(wtap_block_t block, guint option_id, guint idx, const char *value, size_t value_length)
+{
+ wtap_opttype_return_val ret;
+ wtap_optval_t *optval;
+
+ ret = wtap_block_get_nth_option_common(block, option_id, WTAP_OPTTYPE_STRING, idx, &optval);
+ if (ret != WTAP_OPTTYPE_SUCCESS)
+ return ret;
+ g_free(optval->stringval);
+ optval->stringval = g_strndup(value, value_length);
+ return WTAP_OPTTYPE_SUCCESS;
+}
+
+wtap_opttype_return_val
+wtap_block_set_string_option_value_format(wtap_block_t block, guint option_id, const char *format, ...)
+{
+ wtap_opttype_return_val ret;
+ wtap_optval_t *optval;
+ va_list va;
+
+ ret = wtap_block_get_option_common(block, option_id, WTAP_OPTTYPE_STRING, &optval);
+ if (ret != WTAP_OPTTYPE_SUCCESS) {
+ if (ret == WTAP_OPTTYPE_NOT_FOUND) {
+ /*
+ * There's no instance to set, so just try to create a new one
+ * with the formatted string.
+ */
+ va_start(va, format);
+ ret = wtap_block_add_string_option_vformat(block, option_id, format, va);
+ va_end(va);
+ return ret;
+ }
+ /* Otherwise fail. */
+ return ret;
+ }
+ g_free(optval->stringval);
+ va_start(va, format);
+ optval->stringval = ws_strdup_vprintf(format, va);
+ va_end(va);
+ return WTAP_OPTTYPE_SUCCESS;
+}
+
+wtap_opttype_return_val
+wtap_block_set_nth_string_option_value_format(wtap_block_t block, guint option_id, guint idx, const char *format, ...)
+{
+ wtap_opttype_return_val ret;
+ wtap_optval_t *optval;
+ va_list va;
+
+ ret = wtap_block_get_nth_option_common(block, option_id, WTAP_OPTTYPE_STRING, idx, &optval);
+ if (ret != WTAP_OPTTYPE_SUCCESS)
+ return ret;
+ g_free(optval->stringval);
+ va_start(va, format);
+ optval->stringval = ws_strdup_vprintf(format, va);
+ va_end(va);
+ return WTAP_OPTTYPE_SUCCESS;
+}
+
+wtap_opttype_return_val
+wtap_block_get_string_option_value(wtap_block_t block, guint option_id, char** value)
+{
+ wtap_opttype_return_val ret;
+ wtap_optval_t *optval;
+
+ ret = wtap_block_get_option_common(block, option_id, WTAP_OPTTYPE_STRING, &optval);
+ if (ret != WTAP_OPTTYPE_SUCCESS)
+ return ret;
+ *value = optval->stringval;
+ return WTAP_OPTTYPE_SUCCESS;
+}
+
+wtap_opttype_return_val
+wtap_block_get_nth_string_option_value(wtap_block_t block, guint option_id, guint idx, char** value)
+{
+ wtap_opttype_return_val ret;
+ wtap_optval_t *optval;
+
+ ret = wtap_block_get_nth_option_common(block, option_id, WTAP_OPTTYPE_STRING, idx, &optval);
+ if (ret != WTAP_OPTTYPE_SUCCESS)
+ return ret;
+ *value = optval->stringval;
+ return WTAP_OPTTYPE_SUCCESS;
+}
+
+wtap_opttype_return_val
+wtap_block_add_bytes_option(wtap_block_t block, guint option_id, const guint8 *value, gsize value_length)
+{
+ wtap_opttype_return_val ret;
+ wtap_option_t *opt;
+
+ ret = wtap_block_add_option_common(block, option_id, WTAP_OPTTYPE_BYTES, &opt);
+ if (ret != WTAP_OPTTYPE_SUCCESS)
+ return ret;
+ opt->value.byteval = g_bytes_new(value, value_length);
+ return WTAP_OPTTYPE_SUCCESS;
+}
+
+wtap_opttype_return_val
+wtap_block_add_bytes_option_borrow(wtap_block_t block, guint option_id, GBytes *value)
+{
+ wtap_opttype_return_val ret;
+ wtap_option_t *opt;
+
+ ret = wtap_block_add_option_common(block, option_id, WTAP_OPTTYPE_BYTES, &opt);
+ if (ret != WTAP_OPTTYPE_SUCCESS)
+ return ret;
+ opt->value.byteval = g_bytes_ref(value);
+ return WTAP_OPTTYPE_SUCCESS;
+}
+
+wtap_opttype_return_val
+wtap_block_set_bytes_option_value(wtap_block_t block, guint option_id, const guint8 *value, size_t value_length)
+{
+ wtap_opttype_return_val ret;
+ wtap_optval_t *optval;
+
+ ret = wtap_block_get_option_common(block, option_id, WTAP_OPTTYPE_BYTES, &optval);
+ if (ret != WTAP_OPTTYPE_SUCCESS) {
+ if (ret == WTAP_OPTTYPE_NOT_FOUND) {
+ /*
+ * There's no instance to set, so just try to create a new one
+ * with the value.
+ */
+ return wtap_block_add_bytes_option(block, option_id, value, value_length);
+ }
+ /* Otherwise fail. */
+ return ret;
+ }
+ g_bytes_unref(optval->byteval);
+ optval->byteval = g_bytes_new(value, value_length);
+ return WTAP_OPTTYPE_SUCCESS;
+}
+
+wtap_opttype_return_val
+wtap_block_set_nth_bytes_option_value(wtap_block_t block, guint option_id, guint idx, GBytes *value)
+{
+ wtap_opttype_return_val ret;
+ wtap_optval_t *optval;
+
+ ret = wtap_block_get_nth_option_common(block, option_id, WTAP_OPTTYPE_BYTES, idx, &optval);
+ if (ret != WTAP_OPTTYPE_SUCCESS)
+ return ret;
+ g_bytes_unref(optval->byteval);
+ optval->byteval = g_bytes_ref(value);
+ return WTAP_OPTTYPE_SUCCESS;
+}
+
+wtap_opttype_return_val
+wtap_block_get_bytes_option_value(wtap_block_t block, guint option_id, GBytes** value)
+{
+ wtap_opttype_return_val ret;
+ wtap_optval_t *optval;
+
+ ret = wtap_block_get_option_common(block, option_id, WTAP_OPTTYPE_BYTES, &optval);
+ if (ret != WTAP_OPTTYPE_SUCCESS)
+ return ret;
+ *value = optval->byteval;
+ return WTAP_OPTTYPE_SUCCESS;
+}
+
+wtap_opttype_return_val
+wtap_block_get_nth_bytes_option_value(wtap_block_t block, guint option_id, guint idx, GBytes** value)
+{
+ wtap_opttype_return_val ret;
+ wtap_optval_t *optval;
+
+ ret = wtap_block_get_nth_option_common(block, option_id, WTAP_OPTTYPE_BYTES, idx, &optval);
+ if (ret != WTAP_OPTTYPE_SUCCESS)
+ return ret;
+ *value = optval->byteval;
+ return WTAP_OPTTYPE_SUCCESS;
+}
+
+wtap_opttype_return_val
+wtap_block_add_nflx_custom_option(wtap_block_t block, guint32 type, const char *custom_data, gsize custom_data_len)
+{
+ wtap_opttype_return_val ret;
+ wtap_option_t *opt;
+
+ ret = wtap_block_add_option_common(block, OPT_CUSTOM_BIN_COPY, WTAP_OPTTYPE_CUSTOM, &opt);
+ if (ret != WTAP_OPTTYPE_SUCCESS)
+ return ret;
+ opt->value.custom_opt.pen = PEN_NFLX;
+ opt->value.custom_opt.data.nflx_data.type = type;
+ opt->value.custom_opt.data.nflx_data.custom_data_len = custom_data_len;
+ opt->value.custom_opt.data.nflx_data.custom_data = g_memdup2(custom_data, custom_data_len);
+ opt->value.custom_opt.data.nflx_data.use_little_endian = (block->info->block_type == WTAP_BLOCK_CUSTOM);
+ return WTAP_OPTTYPE_SUCCESS;
+}
+
+wtap_opttype_return_val
+wtap_block_get_nflx_custom_option(wtap_block_t block, guint32 nflx_type, char *nflx_custom_data _U_, gsize nflx_custom_data_len)
+{
+ const wtap_opttype_t *opttype;
+ wtap_option_t *opt;
+ guint i;
+
+ if (block == NULL) {
+ return WTAP_OPTTYPE_BAD_BLOCK;
+ }
+ opttype = GET_OPTION_TYPE(block->info->options, OPT_CUSTOM_BIN_COPY);
+ if (opttype == NULL) {
+ return WTAP_OPTTYPE_NO_SUCH_OPTION;
+ }
+ if (opttype->data_type != WTAP_OPTTYPE_CUSTOM) {
+ return WTAP_OPTTYPE_TYPE_MISMATCH;
+ }
+
+ for (i = 0; i < block->options->len; i++) {
+ opt = &g_array_index(block->options, wtap_option_t, i);
+ if ((opt->option_id == OPT_CUSTOM_BIN_COPY) &&
+ (opt->value.custom_opt.pen == PEN_NFLX) &&
+ (opt->value.custom_opt.data.nflx_data.type == nflx_type)) {
+ break;
+ }
+ }
+ if (i == block->options->len) {
+ return WTAP_OPTTYPE_NOT_FOUND;
+ }
+ if (nflx_custom_data_len < opt->value.custom_opt.data.nflx_data.custom_data_len) {
+ return WTAP_OPTTYPE_TYPE_MISMATCH;
+ }
+ switch (nflx_type) {
+ case NFLX_OPT_TYPE_VERSION: {
+ guint32 *src, *dst;
+
+ ws_assert(nflx_custom_data_len == sizeof(guint32));
+ src = (guint32 *)opt->value.custom_opt.data.nflx_data.custom_data;
+ dst = (guint32 *)nflx_custom_data;
+ *dst = GUINT32_FROM_LE(*src);
+ break;
+ }
+ case NFLX_OPT_TYPE_TCPINFO: {
+ struct nflx_tcpinfo *src, *dst;
+
+ ws_assert(nflx_custom_data_len == sizeof(struct nflx_tcpinfo));
+ src = (struct nflx_tcpinfo *)opt->value.custom_opt.data.nflx_data.custom_data;
+ dst = (struct nflx_tcpinfo *)nflx_custom_data;
+ dst->tlb_tv_sec = GUINT64_FROM_LE(src->tlb_tv_sec);
+ dst->tlb_tv_usec = GUINT64_FROM_LE(src->tlb_tv_usec);
+ dst->tlb_ticks = GUINT32_FROM_LE(src->tlb_ticks);
+ dst->tlb_sn = GUINT32_FROM_LE(src->tlb_sn);
+ dst->tlb_stackid = src->tlb_stackid;
+ dst->tlb_eventid = src->tlb_eventid;
+ dst->tlb_eventflags = GUINT16_FROM_LE(src->tlb_eventflags);
+ dst->tlb_errno = GINT32_FROM_LE(src->tlb_errno);
+ dst->tlb_rxbuf_tls_sb_acc = GUINT32_FROM_LE(src->tlb_rxbuf_tls_sb_acc);
+ dst->tlb_rxbuf_tls_sb_ccc = GUINT32_FROM_LE(src->tlb_rxbuf_tls_sb_ccc);
+ dst->tlb_rxbuf_tls_sb_spare = GUINT32_FROM_LE(src->tlb_rxbuf_tls_sb_spare);
+ dst->tlb_txbuf_tls_sb_acc = GUINT32_FROM_LE(src->tlb_txbuf_tls_sb_acc);
+ dst->tlb_txbuf_tls_sb_ccc = GUINT32_FROM_LE(src->tlb_txbuf_tls_sb_ccc);
+ dst->tlb_txbuf_tls_sb_spare = GUINT32_FROM_LE(src->tlb_txbuf_tls_sb_spare);
+ dst->tlb_state = GINT32_FROM_LE(src->tlb_state);
+ dst->tlb_starttime = GUINT32_FROM_LE(src->tlb_starttime);
+ dst->tlb_iss = GUINT32_FROM_LE(src->tlb_iss);
+ dst->tlb_flags = GUINT32_FROM_LE(src->tlb_flags);
+ dst->tlb_snd_una = GUINT32_FROM_LE(src->tlb_snd_una);
+ dst->tlb_snd_max = GUINT32_FROM_LE(src->tlb_snd_max);
+ dst->tlb_snd_cwnd = GUINT32_FROM_LE(src->tlb_snd_cwnd);
+ dst->tlb_snd_nxt = GUINT32_FROM_LE(src->tlb_snd_nxt);
+ dst->tlb_snd_recover = GUINT32_FROM_LE(src->tlb_snd_recover);
+ dst->tlb_snd_wnd = GUINT32_FROM_LE(src->tlb_snd_wnd);
+ dst->tlb_snd_ssthresh = GUINT32_FROM_LE(src->tlb_snd_ssthresh);
+ dst->tlb_srtt = GUINT32_FROM_LE(src->tlb_srtt);
+ dst->tlb_rttvar = GUINT32_FROM_LE(src->tlb_rttvar);
+ dst->tlb_rcv_up = GUINT32_FROM_LE(src->tlb_rcv_up);
+ dst->tlb_rcv_adv = GUINT32_FROM_LE(src->tlb_rcv_adv);
+ dst->tlb_flags2 = GUINT32_FROM_LE(src->tlb_flags2);
+ dst->tlb_rcv_nxt = GUINT32_FROM_LE(src->tlb_rcv_nxt);
+ dst->tlb_rcv_wnd = GUINT32_FROM_LE(src->tlb_rcv_wnd);
+ dst->tlb_dupacks = GUINT32_FROM_LE(src->tlb_dupacks);
+ dst->tlb_segqlen = GINT32_FROM_LE(src->tlb_segqlen);
+ dst->tlb_snd_numholes = GINT32_FROM_LE(src->tlb_snd_numholes);
+ dst->tlb_flex1 = GUINT32_FROM_LE(src->tlb_flex1);
+ dst->tlb_flex2 = GUINT32_FROM_LE(src->tlb_flex2);
+ dst->tlb_fbyte_in = GUINT32_FROM_LE(src->tlb_fbyte_in);
+ dst->tlb_fbyte_out = GUINT32_FROM_LE(src->tlb_fbyte_out);
+ dst->tlb_snd_scale = src->tlb_snd_scale;
+ dst->tlb_rcv_scale = src->tlb_rcv_scale;
+ for (i = 0; i < 3; i++) {
+ dst->_pad[i] = src->_pad[i];
+ }
+ dst->tlb_stackinfo_bbr_cur_del_rate = GUINT64_FROM_LE(src->tlb_stackinfo_bbr_cur_del_rate);
+ dst->tlb_stackinfo_bbr_delRate = GUINT64_FROM_LE(src->tlb_stackinfo_bbr_delRate);
+ dst->tlb_stackinfo_bbr_rttProp = GUINT64_FROM_LE(src->tlb_stackinfo_bbr_rttProp);
+ dst->tlb_stackinfo_bbr_bw_inuse = GUINT64_FROM_LE(src->tlb_stackinfo_bbr_bw_inuse);
+ dst->tlb_stackinfo_bbr_inflight = GUINT32_FROM_LE(src->tlb_stackinfo_bbr_inflight);
+ dst->tlb_stackinfo_bbr_applimited = GUINT32_FROM_LE(src->tlb_stackinfo_bbr_applimited);
+ dst->tlb_stackinfo_bbr_delivered = GUINT32_FROM_LE(src->tlb_stackinfo_bbr_delivered);
+ dst->tlb_stackinfo_bbr_timeStamp = GUINT32_FROM_LE(src->tlb_stackinfo_bbr_timeStamp);
+ dst->tlb_stackinfo_bbr_epoch = GUINT32_FROM_LE(src->tlb_stackinfo_bbr_epoch);
+ dst->tlb_stackinfo_bbr_lt_epoch = GUINT32_FROM_LE(src->tlb_stackinfo_bbr_lt_epoch);
+ dst->tlb_stackinfo_bbr_pkts_out = GUINT32_FROM_LE(src->tlb_stackinfo_bbr_pkts_out);
+ dst->tlb_stackinfo_bbr_flex1 = GUINT32_FROM_LE(src->tlb_stackinfo_bbr_flex1);
+ dst->tlb_stackinfo_bbr_flex2 = GUINT32_FROM_LE(src->tlb_stackinfo_bbr_flex2);
+ dst->tlb_stackinfo_bbr_flex3 = GUINT32_FROM_LE(src->tlb_stackinfo_bbr_flex3);
+ dst->tlb_stackinfo_bbr_flex4 = GUINT32_FROM_LE(src->tlb_stackinfo_bbr_flex4);
+ dst->tlb_stackinfo_bbr_flex5 = GUINT32_FROM_LE(src->tlb_stackinfo_bbr_flex5);
+ dst->tlb_stackinfo_bbr_flex6 = GUINT32_FROM_LE(src->tlb_stackinfo_bbr_flex6);
+ dst->tlb_stackinfo_bbr_lost = GUINT32_FROM_LE(src->tlb_stackinfo_bbr_lost);
+ dst->tlb_stackinfo_bbr_pacing_gain = GUINT16_FROM_LE(src->tlb_stackinfo_bbr_lost);
+ dst->tlb_stackinfo_bbr_cwnd_gain = GUINT16_FROM_LE(src->tlb_stackinfo_bbr_lost);
+ dst->tlb_stackinfo_bbr_flex7 = GUINT16_FROM_LE(src->tlb_stackinfo_bbr_flex7);
+ dst->tlb_stackinfo_bbr_bbr_state = src->tlb_stackinfo_bbr_bbr_state;
+ dst->tlb_stackinfo_bbr_bbr_substate = src->tlb_stackinfo_bbr_bbr_substate;
+ dst->tlb_stackinfo_bbr_inhpts = src->tlb_stackinfo_bbr_inhpts;
+ dst->tlb_stackinfo_bbr_ininput = src->tlb_stackinfo_bbr_ininput;
+ dst->tlb_stackinfo_bbr_use_lt_bw = src->tlb_stackinfo_bbr_use_lt_bw;
+ dst->tlb_stackinfo_bbr_flex8 = src->tlb_stackinfo_bbr_flex8;
+ dst->tlb_stackinfo_bbr_pkt_epoch = GUINT32_FROM_LE(src->tlb_stackinfo_bbr_pkt_epoch);
+ dst->tlb_len = GUINT32_FROM_LE(src->tlb_len);
+ break;
+ }
+ case NFLX_OPT_TYPE_DUMPINFO: {
+ struct nflx_dumpinfo *src, *dst;
+
+ ws_assert(nflx_custom_data_len == sizeof(struct nflx_dumpinfo));
+ src = (struct nflx_dumpinfo *)opt->value.custom_opt.data.nflx_data.custom_data;
+ dst = (struct nflx_dumpinfo *)nflx_custom_data;
+ dst->tlh_version = GUINT32_FROM_LE(src->tlh_version);
+ dst->tlh_type = GUINT32_FROM_LE(src->tlh_type);
+ dst->tlh_length = GUINT64_FROM_LE(src->tlh_length);
+ dst->tlh_ie_fport = src->tlh_ie_fport;
+ dst->tlh_ie_lport = src->tlh_ie_lport;
+ for (i = 0; i < 4; i++) {
+ dst->tlh_ie_faddr_addr32[i] = src->tlh_ie_faddr_addr32[i];
+ dst->tlh_ie_laddr_addr32[i] = src->tlh_ie_laddr_addr32[i];
+ }
+ dst->tlh_ie_zoneid = src->tlh_ie_zoneid;
+ dst->tlh_offset_tv_sec = GUINT64_FROM_LE(src->tlh_offset_tv_sec);
+ dst->tlh_offset_tv_usec = GUINT64_FROM_LE(src->tlh_offset_tv_usec);
+ memcpy(dst->tlh_id, src->tlh_id, 64);
+ memcpy(dst->tlh_reason, src->tlh_reason, 32);
+ memcpy(dst->tlh_tag, src->tlh_tag, 32);
+ dst->tlh_af = src->tlh_af;
+ memcpy(dst->_pad, src->_pad, 7);
+ break;
+ }
+ case NFLX_OPT_TYPE_DUMPTIME: {
+ guint64 *src, *dst;
+
+ ws_assert(nflx_custom_data_len == sizeof(guint64));
+ src = (guint64 *)opt->value.custom_opt.data.nflx_data.custom_data;
+ dst = (guint64 *)nflx_custom_data;
+ *dst = GUINT64_FROM_LE(*src);
+ break;
+ }
+ case NFLX_OPT_TYPE_STACKNAME:
+ ws_assert(nflx_custom_data_len >= 2);
+ memcpy(nflx_custom_data, opt->value.custom_opt.data.nflx_data.custom_data, nflx_custom_data_len);
+ break;
+ default:
+ return WTAP_OPTTYPE_NOT_FOUND;
+ }
+ return WTAP_OPTTYPE_SUCCESS;
+}
+
+wtap_opttype_return_val
+wtap_block_add_custom_option(wtap_block_t block, guint option_id, guint32 pen, const char *custom_data, gsize custom_data_len)
+{
+ wtap_opttype_return_val ret;
+ wtap_option_t *opt;
+
+ ret = wtap_block_add_option_common(block, option_id, WTAP_OPTTYPE_CUSTOM, &opt);
+ if (ret != WTAP_OPTTYPE_SUCCESS)
+ return ret;
+ opt->value.custom_opt.pen = pen;
+ opt->value.custom_opt.data.generic_data.custom_data_len = custom_data_len;
+ opt->value.custom_opt.data.generic_data.custom_data = g_memdup2(custom_data, custom_data_len);
+ return WTAP_OPTTYPE_SUCCESS;
+}
+
+wtap_opttype_return_val
+wtap_block_add_if_filter_option(wtap_block_t block, guint option_id, if_filter_opt_t* value)
+{
+ wtap_opttype_return_val ret;
+ wtap_option_t *opt;
+
+ ret = wtap_block_add_option_common(block, option_id, WTAP_OPTTYPE_IF_FILTER, &opt);
+ if (ret != WTAP_OPTTYPE_SUCCESS)
+ return ret;
+ opt->value.if_filterval = if_filter_dup(value);
+ return WTAP_OPTTYPE_SUCCESS;
+}
+
+wtap_opttype_return_val
+wtap_block_set_if_filter_option_value(wtap_block_t block, guint option_id, if_filter_opt_t* value)
+{
+ wtap_opttype_return_val ret;
+ wtap_optval_t *optval;
+ if_filter_opt_t prev_value;
+
+ ret = wtap_block_get_option_common(block, option_id, WTAP_OPTTYPE_IF_FILTER, &optval);
+ if (ret != WTAP_OPTTYPE_SUCCESS)
+ return ret;
+ prev_value = optval->if_filterval;
+ optval->if_filterval = if_filter_dup(value);
+ /* Free after memory is duplicated in case structure was manipulated with a "get then set" */
+ if_filter_free(&prev_value);
+
+ return WTAP_OPTTYPE_SUCCESS;
+}
+
+wtap_opttype_return_val
+wtap_block_get_if_filter_option_value(wtap_block_t block, guint option_id, if_filter_opt_t* value)
+{
+ wtap_opttype_return_val ret;
+ wtap_optval_t *optval;
+
+ ret = wtap_block_get_option_common(block, option_id, WTAP_OPTTYPE_IF_FILTER, &optval);
+ if (ret != WTAP_OPTTYPE_SUCCESS)
+ return ret;
+ *value = optval->if_filterval;
+ return WTAP_OPTTYPE_SUCCESS;
+}
+
+wtap_opttype_return_val
+wtap_block_add_packet_verdict_option(wtap_block_t block, guint option_id, packet_verdict_opt_t* value)
+{
+ wtap_opttype_return_val ret;
+ wtap_option_t *opt;
+
+ ret = wtap_block_add_option_common(block, option_id, WTAP_OPTTYPE_PACKET_VERDICT, &opt);
+ if (ret != WTAP_OPTTYPE_SUCCESS)
+ return ret;
+ opt->value.packet_verdictval = packet_verdict_dup(value);
+ return WTAP_OPTTYPE_SUCCESS;
+}
+
+wtap_opttype_return_val
+wtap_block_set_nth_packet_verdict_option_value(wtap_block_t block, guint option_id, guint idx, packet_verdict_opt_t* value)
+{
+ wtap_opttype_return_val ret;
+ wtap_optval_t *optval;
+ packet_verdict_opt_t prev_value;
+
+ ret = wtap_block_get_nth_option_common(block, option_id, WTAP_OPTTYPE_PACKET_VERDICT, idx, &optval);
+ if (ret != WTAP_OPTTYPE_SUCCESS)
+ return ret;
+ prev_value = optval->packet_verdictval;
+ optval->packet_verdictval = packet_verdict_dup(value);
+ /* Free after memory is duplicated in case structure was manipulated with a "get then set" */
+ wtap_packet_verdict_free(&prev_value);
+
+ return WTAP_OPTTYPE_SUCCESS;
+}
+
+wtap_opttype_return_val
+wtap_block_get_nth_packet_verdict_option_value(wtap_block_t block, guint option_id, guint idx, packet_verdict_opt_t* value)
+{
+ wtap_opttype_return_val ret;
+ wtap_optval_t *optval;
+
+ ret = wtap_block_get_nth_option_common(block, option_id, WTAP_OPTTYPE_STRING, idx, &optval);
+ if (ret != WTAP_OPTTYPE_SUCCESS)
+ return ret;
+ *value = optval->packet_verdictval;
+ return WTAP_OPTTYPE_SUCCESS;
+}
+
+wtap_opttype_return_val
+wtap_block_add_packet_hash_option(wtap_block_t block, guint option_id, packet_hash_opt_t* value)
+{
+ wtap_opttype_return_val ret;
+ wtap_option_t *opt;
+
+ ret = wtap_block_add_option_common(block, option_id, WTAP_OPTTYPE_PACKET_HASH, &opt);
+ if (ret != WTAP_OPTTYPE_SUCCESS)
+ return ret;
+ opt->value.packet_hash = packet_hash_dup(value);
+ return WTAP_OPTTYPE_SUCCESS;
+}
+
+wtap_opttype_return_val
+wtap_block_remove_option(wtap_block_t block, guint option_id)
+{
+ const wtap_opttype_t *opttype;
+ guint i;
+ wtap_option_t *opt;
+
+ if (block == NULL) {
+ return WTAP_OPTTYPE_BAD_BLOCK;
+ }
+
+ opttype = GET_OPTION_TYPE(block->info->options, option_id);
+ if (opttype == NULL) {
+ /* There's no option for this block with that option ID */
+ return WTAP_OPTTYPE_NO_SUCH_OPTION;
+ }
+
+ /*
+ * Can there be more than one instance of this option?
+ */
+ if (opttype->flags & WTAP_OPTTYPE_FLAG_MULTIPLE_ALLOWED) {
+ /*
+ * Yes. You can't remove "the" value.
+ */
+ return WTAP_OPTTYPE_NUMBER_MISMATCH;
+ }
+
+ for (i = 0; i < block->options->len; i++) {
+ opt = &g_array_index(block->options, wtap_option_t, i);
+ if (opt->option_id == option_id) {
+ /* Found it - free up the value */
+ wtap_block_free_option(block, opt);
+ /* Remove the option from the array of options */
+ g_array_remove_index(block->options, i);
+ return WTAP_OPTTYPE_SUCCESS;
+ }
+ }
+
+ /* Didn't find the option */
+ return WTAP_OPTTYPE_NOT_FOUND;
+}
+
+wtap_opttype_return_val
+wtap_block_remove_nth_option_instance(wtap_block_t block, guint option_id,
+ guint idx)
+{
+ const wtap_opttype_t *opttype;
+ guint i;
+ wtap_option_t *opt;
+ guint opt_idx;
+
+ if (block == NULL) {
+ return WTAP_OPTTYPE_BAD_BLOCK;
+ }
+
+ opttype = GET_OPTION_TYPE(block->info->options, option_id);
+ if (opttype == NULL) {
+ /* There's no option for this block with that option ID */
+ return WTAP_OPTTYPE_NO_SUCH_OPTION;
+ }
+
+ /*
+ * Can there be more than one instance of this option?
+ */
+ if (!(opttype->flags & WTAP_OPTTYPE_FLAG_MULTIPLE_ALLOWED)) {
+ /*
+ * No.
+ */
+ return WTAP_OPTTYPE_NUMBER_MISMATCH;
+ }
+
+ opt_idx = 0;
+ for (i = 0; i < block->options->len; i++) {
+ opt = &g_array_index(block->options, wtap_option_t, i);
+ if (opt->option_id == option_id) {
+ if (opt_idx == idx) {
+ /* Found it - free up the value */
+ wtap_block_free_option(block, opt);
+ /* Remove the option from the array of options */
+ g_array_remove_index(block->options, i);
+ return WTAP_OPTTYPE_SUCCESS;
+ }
+ opt_idx++;
+ }
+ }
+
+ /* Didn't find the option */
+ return WTAP_OPTTYPE_NOT_FOUND;
+}
+
+static void shb_create(wtap_block_t block)
+{
+ wtapng_section_mandatory_t* section_mand = g_new(wtapng_section_mandatory_t, 1);
+
+ section_mand->section_length = -1;
+
+ block->mandatory_data = section_mand;
+}
+
+static void shb_copy_mand(wtap_block_t dest_block, wtap_block_t src_block)
+{
+ memcpy(dest_block->mandatory_data, src_block->mandatory_data, sizeof(wtapng_section_mandatory_t));
+}
+
+static void nrb_create(wtap_block_t block)
+{
+ block->mandatory_data = g_new0(wtapng_nrb_mandatory_t, 1);
+}
+
+static void nrb_free_mand(wtap_block_t block)
+{
+ wtapng_nrb_mandatory_t *mand = (wtapng_nrb_mandatory_t *)block->mandatory_data;
+ g_list_free_full(mand->ipv4_addr_list, g_free);
+ g_list_free_full(mand->ipv6_addr_list, g_free);
+}
+
+#if 0
+static gpointer copy_hashipv4(gconstpointer src, gpointer user_data _U_
+{
+ hashipv4_t *src_ipv4 = (hashipv4_t*)src;
+ hashipv4_t *dst = g_new0(hashipv4_t, 1);
+ dst->addr = src_ipv4->addr;
+ (void) g_strlcpy(dst->name, src_ipv4->name, MAXNAMELEN);
+ return dst;
+}
+
+static gpointer copy_hashipv4(gconstpointer src, gpointer user_data _U_
+{
+ hashipv6_t *src_ipv6 = (hashipv6_t*)src;
+ hashipv6_t *dst = g_new0(hashipv6_t, 1);
+ dst->addr = src_ipv4->addr;
+ (void) g_strlcpy(dst->name, src_ipv4->name, MAXNAMELEN);
+ return dst;
+}
+
+static void nrb_copy_mand(wtap_block_t dest_block, wtap_block_t src_block)
+{
+ wtapng_nrb_mandatory_t *src = (wtapng_nrb_mandatory_t *)src_block->mandatory_data;
+ wtapng_nrb_mandatory_t *dst = (wtapng_nrb_mandatory_t *)dest_block->mandatory_data;
+ g_list_free_full(dst->ipv4_addr_list, g_free);
+ g_list_free_full(dst->ipv6_addr_list, g_free);
+ dst->ipv4_addr_list = g_list_copy_deep(src->ipv4_addr_list, copy_hashipv4, NULL);
+ dst->ipv6_addr_list = g_list_copy_deep(src->ipv6_addr_list, copy_hashipv6, NULL);
+}
+#endif
+
+static void isb_create(wtap_block_t block)
+{
+ block->mandatory_data = g_new0(wtapng_if_stats_mandatory_t, 1);
+}
+
+static void isb_copy_mand(wtap_block_t dest_block, wtap_block_t src_block)
+{
+ memcpy(dest_block->mandatory_data, src_block->mandatory_data, sizeof(wtapng_if_stats_mandatory_t));
+}
+
+static void idb_create(wtap_block_t block)
+{
+ block->mandatory_data = g_new0(wtapng_if_descr_mandatory_t, 1);
+}
+
+static void idb_free_mand(wtap_block_t block)
+{
+ guint j;
+ wtap_block_t if_stats;
+ wtapng_if_descr_mandatory_t* mand = (wtapng_if_descr_mandatory_t*)block->mandatory_data;
+
+ for(j = 0; j < mand->num_stat_entries; j++) {
+ if_stats = g_array_index(mand->interface_statistics, wtap_block_t, j);
+ wtap_block_unref(if_stats);
+ }
+
+ if (mand->interface_statistics)
+ g_array_free(mand->interface_statistics, TRUE);
+}
+
+static void idb_copy_mand(wtap_block_t dest_block, wtap_block_t src_block)
+{
+ guint j;
+ wtap_block_t src_if_stats, dest_if_stats;
+ wtapng_if_descr_mandatory_t *src_mand = (wtapng_if_descr_mandatory_t*)src_block->mandatory_data,
+ *dest_mand = (wtapng_if_descr_mandatory_t*)dest_block->mandatory_data;
+
+ /* Need special consideration for copying of the interface_statistics member */
+ if (dest_mand->num_stat_entries != 0)
+ g_array_free(dest_mand->interface_statistics, TRUE);
+
+ memcpy(dest_mand, src_mand, sizeof(wtapng_if_descr_mandatory_t));
+ if (src_mand->num_stat_entries != 0)
+ {
+ dest_mand->interface_statistics = g_array_new(FALSE, FALSE, sizeof(wtap_block_t));
+ for (j = 0; j < src_mand->num_stat_entries; j++)
+ {
+ src_if_stats = g_array_index(src_mand->interface_statistics, wtap_block_t, j);
+ dest_if_stats = wtap_block_make_copy(src_if_stats);
+ dest_mand->interface_statistics = g_array_append_val(dest_mand->interface_statistics, dest_if_stats);
+ }
+ }
+}
+
+static void dsb_create(wtap_block_t block)
+{
+ block->mandatory_data = g_new0(wtapng_dsb_mandatory_t, 1);
+}
+
+static void dsb_free_mand(wtap_block_t block)
+{
+ wtapng_dsb_mandatory_t *mand = (wtapng_dsb_mandatory_t *)block->mandatory_data;
+ g_free(mand->secrets_data);
+}
+
+static void dsb_copy_mand(wtap_block_t dest_block, wtap_block_t src_block)
+{
+ wtapng_dsb_mandatory_t *src = (wtapng_dsb_mandatory_t *)src_block->mandatory_data;
+ wtapng_dsb_mandatory_t *dst = (wtapng_dsb_mandatory_t *)dest_block->mandatory_data;
+ dst->secrets_type = src->secrets_type;
+ dst->secrets_len = src->secrets_len;
+ g_free(dst->secrets_data);
+ dst->secrets_data = (guint8 *)g_memdup2(src->secrets_data, src->secrets_len);
+}
+
+static void sysdig_mev_create(wtap_block_t block)
+{
+ block->mandatory_data = g_new0(wtapng_sysdig_mev_mandatory_t, 1);
+}
+
+static void sysdig_mev_free_mand(wtap_block_t block)
+{
+ wtapng_sysdig_mev_mandatory_t *mand = (wtapng_sysdig_mev_mandatory_t *)block->mandatory_data;
+ g_free(mand->mev_data);
+}
+
+static void sysdig_mev_copy_mand(wtap_block_t dest_block, wtap_block_t src_block)
+{
+ wtapng_sysdig_mev_mandatory_t *src = (wtapng_sysdig_mev_mandatory_t *)src_block->mandatory_data;
+ wtapng_sysdig_mev_mandatory_t *dst = (wtapng_sysdig_mev_mandatory_t *)dest_block->mandatory_data;
+ dst->mev_type = src->mev_type;
+ dst->mev_data_len = src->mev_data_len;
+ g_free(dst->mev_data);
+ dst->mev_data = (guint8 *)g_memdup2(src->mev_data, src->mev_data_len);
+}
+
+static void pkt_create(wtap_block_t block)
+{
+ /* Commented out for now, there's no mandatory data that isn't handled by
+ * Wireshark in other ways.
+ */
+ //block->mandatory_data = g_new0(wtapng_packet_mandatory_t, 1);
+
+ /* Ensure this is null, so when g_free is called on it, it simply returns */
+ block->mandatory_data = NULL;
+}
+
+static void sjeb_create(wtap_block_t block)
+{
+ /* Ensure this is null, so when g_free is called on it, it simply returns */
+ block->mandatory_data = NULL;
+}
+
+static void cb_create(wtap_block_t block)
+{
+ /* Ensure this is null, so when g_free is called on it, it simply returns */
+ block->mandatory_data = NULL;
+}
+
+void wtap_opttypes_initialize(void)
+{
+ static wtap_blocktype_t shb_block = {
+ WTAP_BLOCK_SECTION, /* block_type */
+ "SHB", /* name */
+ "Section Header Block", /* description */
+ shb_create, /* create */
+ NULL, /* free_mand */
+ shb_copy_mand, /* copy_mand */
+ NULL /* options */
+ };
+ static const wtap_opttype_t shb_hardware = {
+ "hardware",
+ "SHB Hardware",
+ WTAP_OPTTYPE_STRING,
+ 0
+ };
+ static const wtap_opttype_t shb_os = {
+ "os",
+ "SHB Operating System",
+ WTAP_OPTTYPE_STRING,
+ 0
+ };
+ static const wtap_opttype_t shb_userappl = {
+ "user_appl",
+ "SHB User Application",
+ WTAP_OPTTYPE_STRING,
+ 0
+ };
+
+ static wtap_blocktype_t idb_block = {
+ WTAP_BLOCK_IF_ID_AND_INFO, /* block_type */
+ "IDB", /* name */
+ "Interface Description Block", /* description */
+ idb_create, /* create */
+ idb_free_mand, /* free_mand */
+ idb_copy_mand, /* copy_mand */
+ NULL /* options */
+ };
+ static const wtap_opttype_t if_name = {
+ "name",
+ "IDB Name",
+ WTAP_OPTTYPE_STRING,
+ 0
+ };
+ static const wtap_opttype_t if_description = {
+ "description",
+ "IDB Description",
+ WTAP_OPTTYPE_STRING,
+ 0
+ };
+ static const wtap_opttype_t if_speed = {
+ "speed",
+ "IDB Speed",
+ WTAP_OPTTYPE_UINT64,
+ 0
+ };
+ static const wtap_opttype_t if_tsresol = {
+ "tsresol",
+ "IDB Time Stamp Resolution",
+ WTAP_OPTTYPE_UINT8, /* XXX - signed? */
+ 0
+ };
+ static const wtap_opttype_t if_filter = {
+ "filter",
+ "IDB Filter",
+ WTAP_OPTTYPE_IF_FILTER,
+ 0
+ };
+ static const wtap_opttype_t if_os = {
+ "os",
+ "IDB Operating System",
+ WTAP_OPTTYPE_STRING,
+ 0
+ };
+ static const wtap_opttype_t if_fcslen = {
+ "fcslen",
+ "IDB FCS Length",
+ WTAP_OPTTYPE_UINT8,
+ 0
+ };
+ static const wtap_opttype_t if_tsoffset = {
+ "tsoffset",
+ "IDB Time Stamp Offset",
+ WTAP_OPTTYPE_INT64,
+ 0
+ };
+ static const wtap_opttype_t if_hardware = {
+ "hardware",
+ "IDB Hardware",
+ WTAP_OPTTYPE_STRING,
+ 0
+ };
+
+ static wtap_blocktype_t dsb_block = {
+ WTAP_BLOCK_DECRYPTION_SECRETS,
+ "DSB",
+ "Decryption Secrets Block",
+ dsb_create,
+ dsb_free_mand,
+ dsb_copy_mand,
+ NULL
+ };
+
+ static wtap_blocktype_t nrb_block = {
+ WTAP_BLOCK_NAME_RESOLUTION, /* block_type */
+ "NRB", /* name */
+ "Name Resolution Block", /* description */
+ nrb_create, /* create */
+ nrb_free_mand, /* free_mand */
+ /* We eventually want to copy these, when dumper actually
+ * writes them out. If we're actually processing packets,
+ * as opposed to just reading and writing a file without
+ * printing (e.g., editcap), do we still want to copy all
+ * the pre-existing NRBs, or do we want to limit it to
+ * the actually used addresses, as currently?
+ */
+#if 0
+ nrb_copy_mand, /* copy_mand */
+#endif
+ NULL,
+ NULL /* options */
+ };
+ static const wtap_opttype_t ns_dnsname = {
+ "dnsname",
+ "NRB DNS server name",
+ WTAP_OPTTYPE_STRING,
+ 0
+ };
+ static const wtap_opttype_t ns_dnsIP4addr = {
+ "dnsIP4addr",
+ "NRB DNS server IPv4 address",
+ WTAP_OPTTYPE_IPv4,
+ 0
+ };
+ static const wtap_opttype_t ns_dnsIP6addr = {
+ "dnsIP6addr",
+ "NRB DNS server IPv6 address",
+ WTAP_OPTTYPE_IPv6,
+ 0
+ };
+
+ static wtap_blocktype_t isb_block = {
+ WTAP_BLOCK_IF_STATISTICS, /* block_type */
+ "ISB", /* name */
+ "Interface Statistics Block", /* description */
+ isb_create, /* create */
+ NULL, /* free_mand */
+ isb_copy_mand, /* copy_mand */
+ NULL /* options */
+ };
+ static const wtap_opttype_t isb_starttime = {
+ "starttime",
+ "ISB Start Time",
+ WTAP_OPTTYPE_UINT64,
+ 0
+ };
+ static const wtap_opttype_t isb_endtime = {
+ "endtime",
+ "ISB End Time",
+ WTAP_OPTTYPE_UINT64,
+ 0
+ };
+ static const wtap_opttype_t isb_ifrecv = {
+ "ifrecv",
+ "ISB Received Packets",
+ WTAP_OPTTYPE_UINT64,
+ 0
+ };
+ static const wtap_opttype_t isb_ifdrop = {
+ "ifdrop",
+ "ISB Dropped Packets",
+ WTAP_OPTTYPE_UINT64,
+ 0
+ };
+ static const wtap_opttype_t isb_filteraccept = {
+ "filteraccept",
+ "ISB Packets Accepted By Filter",
+ WTAP_OPTTYPE_UINT64,
+ 0
+ };
+ static const wtap_opttype_t isb_osdrop = {
+ "osdrop",
+ "ISB Packets Dropped By The OS",
+ WTAP_OPTTYPE_UINT64,
+ 0
+ };
+ static const wtap_opttype_t isb_usrdeliv = {
+ "usrdeliv",
+ "ISB Packets Delivered To The User",
+ WTAP_OPTTYPE_UINT64,
+ 0
+ };
+
+ static wtap_blocktype_t sysdig_mev_block = {
+ WTAP_BLOCK_SYSDIG_META_EVENT,
+ "Sysdig MEV",
+ "Sysdig Meta Event Block",
+ sysdig_mev_create,
+ sysdig_mev_free_mand,
+ sysdig_mev_copy_mand,
+ NULL
+ };
+
+ static wtap_blocktype_t pkt_block = {
+ WTAP_BLOCK_PACKET, /* block_type */
+ "EPB/SPB/PB", /* name */
+ "Packet Block", /* description */
+ pkt_create, /* create */
+ NULL, /* free_mand */
+ NULL, /* copy_mand */
+ NULL /* options */
+ };
+ static const wtap_opttype_t pkt_flags = {
+ "flags",
+ "Link-layer flags",
+ WTAP_OPTTYPE_UINT32,
+ 0
+ };
+ static const wtap_opttype_t pkt_dropcount = {
+ "dropcount",
+ "Packets Dropped since last packet",
+ WTAP_OPTTYPE_UINT64,
+ 0
+ };
+ static const wtap_opttype_t pkt_id = {
+ "packetid",
+ "Unique Packet Identifier",
+ WTAP_OPTTYPE_UINT64,
+ 0
+ };
+ static const wtap_opttype_t pkt_queue = {
+ "queue",
+ "Queue ID in which packet was received",
+ WTAP_OPTTYPE_UINT32,
+ 0
+ };
+ static const wtap_opttype_t pkt_hash = {
+ "hash",
+ "Hash of packet data",
+ WTAP_OPTTYPE_PACKET_HASH,
+ WTAP_OPTTYPE_FLAG_MULTIPLE_ALLOWED
+ };
+ static const wtap_opttype_t pkt_verdict = {
+ "verdict",
+ "Packet Verdict",
+ WTAP_OPTTYPE_PACKET_VERDICT,
+ WTAP_OPTTYPE_FLAG_MULTIPLE_ALLOWED
+ };
+
+ static wtap_blocktype_t journal_block = {
+ WTAP_BLOCK_SYSTEMD_JOURNAL_EXPORT, /* block_type */
+ "SJEB", /* name */
+ "systemd Journal Export Block", /* description */
+ sjeb_create, /* create */
+ NULL, /* free_mand */
+ NULL, /* copy_mand */
+ NULL /* options */
+ };
+
+ static wtap_blocktype_t cb_block = {
+ WTAP_BLOCK_CUSTOM, /* block_type */
+ "CB", /* name */
+ "Custom Block", /* description */
+ cb_create, /* create */
+ NULL, /* free_mand */
+ NULL, /* copy_mand */
+ NULL /* options */
+ };
+
+ /*
+ * Register the SHB and the options that can appear in it.
+ */
+ wtap_opttype_block_register(&shb_block);
+ wtap_opttype_option_register(&shb_block, OPT_SHB_HARDWARE, &shb_hardware);
+ wtap_opttype_option_register(&shb_block, OPT_SHB_OS, &shb_os);
+ wtap_opttype_option_register(&shb_block, OPT_SHB_USERAPPL, &shb_userappl);
+
+ /*
+ * Register the IDB and the options that can appear in it.
+ */
+ wtap_opttype_block_register(&idb_block);
+ wtap_opttype_option_register(&idb_block, OPT_IDB_NAME, &if_name);
+ wtap_opttype_option_register(&idb_block, OPT_IDB_DESCRIPTION, &if_description);
+ wtap_opttype_option_register(&idb_block, OPT_IDB_SPEED, &if_speed);
+ wtap_opttype_option_register(&idb_block, OPT_IDB_TSRESOL, &if_tsresol);
+ wtap_opttype_option_register(&idb_block, OPT_IDB_FILTER, &if_filter);
+ wtap_opttype_option_register(&idb_block, OPT_IDB_OS, &if_os);
+ wtap_opttype_option_register(&idb_block, OPT_IDB_FCSLEN, &if_fcslen);
+ wtap_opttype_option_register(&idb_block, OPT_IDB_TSOFFSET, &if_tsoffset);
+ wtap_opttype_option_register(&idb_block, OPT_IDB_HARDWARE, &if_hardware);
+
+ /*
+ * Register the NRB and the options that can appear in it.
+ */
+ wtap_opttype_block_register(&nrb_block);
+ wtap_opttype_option_register(&nrb_block, OPT_NS_DNSNAME, &ns_dnsname);
+ wtap_opttype_option_register(&nrb_block, OPT_NS_DNSIP4ADDR, &ns_dnsIP4addr);
+ wtap_opttype_option_register(&nrb_block, OPT_NS_DNSIP6ADDR, &ns_dnsIP6addr);
+
+ /*
+ * Register the ISB and the options that can appear in it.
+ */
+ wtap_opttype_block_register(&isb_block);
+ wtap_opttype_option_register(&isb_block, OPT_ISB_STARTTIME, &isb_starttime);
+ wtap_opttype_option_register(&isb_block, OPT_ISB_ENDTIME, &isb_endtime);
+ wtap_opttype_option_register(&isb_block, OPT_ISB_IFRECV, &isb_ifrecv);
+ wtap_opttype_option_register(&isb_block, OPT_ISB_IFDROP, &isb_ifdrop);
+ wtap_opttype_option_register(&isb_block, OPT_ISB_FILTERACCEPT, &isb_filteraccept);
+ wtap_opttype_option_register(&isb_block, OPT_ISB_OSDROP, &isb_osdrop);
+ wtap_opttype_option_register(&isb_block, OPT_ISB_USRDELIV, &isb_usrdeliv);
+
+ /*
+ * Register the DSB, currently no options are defined.
+ */
+ wtap_opttype_block_register(&dsb_block);
+
+ /*
+ * Register the Sysdig MEV, currently no options are defined.
+ */
+ wtap_opttype_block_register(&sysdig_mev_block);
+
+ /*
+ * Register EPB/SPB/PB and the options that can appear in it/them.
+ * NB: Simple Packet Blocks have no options.
+ * NB: obsolete Packet Blocks have dropcount as a mandatory member instead
+ * of an option.
+ */
+ wtap_opttype_block_register(&pkt_block);
+ wtap_opttype_option_register(&pkt_block, OPT_PKT_FLAGS, &pkt_flags);
+ wtap_opttype_option_register(&pkt_block, OPT_PKT_DROPCOUNT, &pkt_dropcount);
+ wtap_opttype_option_register(&pkt_block, OPT_PKT_PACKETID, &pkt_id);
+ wtap_opttype_option_register(&pkt_block, OPT_PKT_QUEUE, &pkt_queue);
+ wtap_opttype_option_register(&pkt_block, OPT_PKT_HASH, &pkt_hash);
+ wtap_opttype_option_register(&pkt_block, OPT_PKT_VERDICT, &pkt_verdict);
+
+ /*
+ * Register the SJEB and the (no) options that can appear in it.
+ */
+ wtap_opttype_block_register(&journal_block);
+
+ /*
+ * Register the CB and the options that can appear in it.
+ */
+ wtap_opttype_block_register(&cb_block);
+
+#ifdef DEBUG_COUNT_REFS
+ memset(blocks_active, 0, sizeof(blocks_active));
+#endif
+}
+
+void wtap_opttypes_cleanup(void)
+{
+ guint block_type;
+#ifdef DEBUG_COUNT_REFS
+ guint i;
+ guint cellno;
+ guint bitno;
+ guint8 mask;
+#endif /* DEBUG_COUNT_REFS */
+
+ for (block_type = (guint)WTAP_BLOCK_SECTION;
+ block_type < (guint)MAX_WTAP_BLOCK_TYPE_VALUE; block_type++) {
+ if (blocktype_list[block_type]) {
+ if (blocktype_list[block_type]->options)
+ g_hash_table_destroy(blocktype_list[block_type]->options);
+ blocktype_list[block_type] = NULL;
+ }
+ }
+
+#ifdef DEBUG_COUNT_REFS
+ for (i = 0 ; i < block_count; i++) {
+ cellno = i / 8;
+ bitno = i % 8;
+ mask = 1 << bitno;
+
+ if ((blocks_active[cellno] & mask) == mask) {
+ wtap_debug("wtap_opttypes_cleanup: orphaned block #%d", i);
+ }
+ }
+#endif /* DEBUG_COUNT_REFS */
+}
diff --git a/wiretap/wtap_opttypes.h b/wiretap/wtap_opttypes.h
new file mode 100644
index 00000000..f3d9efbd
--- /dev/null
+++ b/wiretap/wtap_opttypes.h
@@ -0,0 +1,1269 @@
+/** @file
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 2001 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef WTAP_OPT_TYPES_H
+#define WTAP_OPT_TYPES_H
+
+#include "ws_symbol_export.h"
+
+#include <wsutil/inet_ipv4.h>
+#include <wsutil/inet_ipv6.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/*
+ * We use the pcapng option codes for option type values.
+ */
+
+/* Options for all blocks */
+#define OPT_EOFOPT 0 /**< Appears in pcapng files, but not in blocks. */
+#define OPT_COMMENT 1 /**< A UTF-8 string containing a human-readable comment. */
+#define OPT_CUSTOM_STR_COPY 2988 /**< A custom option containing a UTF-8 string, copying allowed. */
+#define OPT_CUSTOM_BIN_COPY 2989 /**< A custom option containing binary data, copying allowed. */
+#define OPT_CUSTOM_STR_NO_COPY 19372 /**< A custom option containing a UTF-8 string, copying not allowed. */
+#define OPT_CUSTOM_BIN_NO_COPY 19373 /**< A custom option containing binary data, copying not allowed. */
+
+/* Section Header block (SHB) */
+#define OPT_SHB_HARDWARE 2 /**< A UTF-8 string containing the description of the
+ * hardware used to create this section.
+ */
+#define OPT_SHB_OS 3 /**< A UTF-8 string containing the
+ * name of the operating system used to create this section.
+ */
+#define OPT_SHB_USERAPPL 4 /**< A UTF-8 string containing the
+ * name of the application used to create this section.
+ */
+
+/* Interface Description block (IDB) */
+#define OPT_IDB_NAME 2 /**< A UTF-8 string containing the name
+ * of the device used to capture data.
+ * "eth0" / "\Device\NPF_{AD1CE675-96D0-47C5-ADD0-2504B9126B68}"
+ */
+#define OPT_IDB_DESCRIPTION 3 /**< A UTF-8 string containing the description
+ * of the device used to capture data.
+ * "Wi-Fi" / "Local Area Connection" /
+ * "Wireless Network Connection" /
+ * "First Ethernet Interface"
+ */
+#define OPT_IDB_IP4ADDR 4 /**< XXX: if_IPv4addr Interface network address and netmask.
+ * This option can be repeated multiple times within the same Interface Description Block
+ * when multiple IPv4 addresses are assigned to the interface.
+ * 192 168 1 1 255 255 255 0
+ */
+#define OPT_IDB_IP6ADDR 5 /**< XXX: if_IPv6addr Interface network address and prefix length (stored in the last byte).
+ * This option can be repeated multiple times within the same Interface
+ * Description Block when multiple IPv6 addresses are assigned to the interface.
+ * 2001:0db8:85a3:08d3:1319:8a2e:0370:7344/64 is written (in hex) as
+ * "20 01 0d b8 85 a3 08 d3 13 19 8a 2e 03 70 73 44 40"
+ */
+#define OPT_IDB_MACADDR 6 /**< XXX: if_MACaddr Interface Hardware MAC address (48 bits). */
+#define OPT_IDB_EUIADDR 7 /**< XXX: if_EUIaddr Interface Hardware EUI address (64 bits) */
+#define OPT_IDB_SPEED 8 /**< Interface speed (in bps). 100000000 for 100Mbps
+ */
+#define OPT_IDB_TSRESOL 9 /**< Resolution of timestamps. If the Most Significant Bit is equal to zero,
+ * the remaining bits indicates the resolution of the timestamp as a
+ * negative power of 10 (e.g. 6 means microsecond resolution, timestamps
+ * are the number of microseconds since 1/1/1970). If the Most Significant Bit
+ * is equal to one, the remaining bits indicates the resolution has a
+ * negative power of 2 (e.g. 10 means 1/1024 of second).
+ * If this option is not present, a resolution of 10^-6 is assumed
+ * (i.e. timestamps have the same resolution of the standard 'libpcap' timestamps).
+ */
+#define OPT_IDB_TZONE 10 /**< Time zone for GMT support. This option has neer been specified in
+ * greater detail and, unless it were to identify something such as
+ * an IANA time zone database timezone, would be insufficient for
+ * converting between UTC and local time. Therefore, it SHOULD NOT
+ * be used; instead, the if_iana_tzname option SHOULD be used if
+ * time zone information is to be specified. */
+#define OPT_IDB_FILTER 11 /**< The filter (e.g. "capture only TCP traffic") used to capture traffic.
+ * The first byte of the Option Data keeps a code of the filter used
+ * (e.g. if this is a libpcap string, or BPF bytecode, and more).
+ * More details about this format will be presented in Appendix XXX (TODO).
+ * (TODO: better use different options for different fields?
+ * e.g. if_filter_pcap, if_filter_bpf, ...) 00 "tcp port 23 and host 10.0.0.5"
+ */
+#define OPT_IDB_OS 12 /**< A UTF-8 string containing the name of the operating system of the
+ * machine in which this interface is installed.
+ * This can be different from the same information that can be
+ * contained by the Section Header Block
+ * (Section 3.1 (Section Header Block (mandatory))) because
+ * the capture can have been done on a remote machine.
+ * "Windows XP SP2" / "openSUSE 10.2"
+ */
+#define OPT_IDB_FCSLEN 13 /**< An integer value that specified the length of the
+ * Frame Check Sequence (in bits) for this interface.
+ * For link layers whose FCS length can change during time,
+ * the Packet Block Flags Word can be used (see Appendix A (Packet Block Flags Word))
+ */
+#define OPT_IDB_TSOFFSET 14 /**< A 64-bit signed integer value that specifies an offset (in seconds)
+ * that must be added to the timestamp of each packet to obtain
+ * the absolute timestamp of a packet. If the option is not present,
+ * an offst of 0 is assumed (i.e., timestamps in blocks are absolute
+ * timestamps).
+ *
+ * This offset is not intended to be used as an offset between local
+ * time and UTC; for this purpose, the if_iana_tzname option SHOULD be
+ * used to specify a timezone.
+ */
+#define OPT_IDB_HARDWARE 15 /**< A UTF-8 string containing the description
+ * of the hardware of the device used
+ * to capture data.
+ * "Broadcom NetXtreme" /
+ * "Intel(R) PRO/1000 MT Network Connection" /
+ * "NETGEAR WNA1000Mv2 N150 Wireless USB Micro Adapter"
+ */
+
+/*
+ * These are the flags for an EPB, but we use them for all WTAP_BLOCK_PACKET
+ */
+#define OPT_PKT_FLAGS 2
+#define OPT_PKT_HASH 3
+#define OPT_PKT_DROPCOUNT 4
+#define OPT_PKT_PACKETID 5
+#define OPT_PKT_QUEUE 6
+#define OPT_PKT_VERDICT 7
+
+/* Name Resolution Block (NRB) */
+#define OPT_NS_DNSNAME 2
+#define OPT_NS_DNSIP4ADDR 3
+#define OPT_NS_DNSIP6ADDR 4
+
+/* Interface Statistics Block (ISB) */
+#define OPT_ISB_STARTTIME 2
+#define OPT_ISB_ENDTIME 3
+#define OPT_ISB_IFRECV 4
+#define OPT_ISB_IFDROP 5
+#define OPT_ISB_FILTERACCEPT 6
+#define OPT_ISB_OSDROP 7
+#define OPT_ISB_USRDELIV 8
+
+struct wtap_block;
+typedef struct wtap_block *wtap_block_t;
+
+/*
+ * Currently supported blocks; these are not the pcapng block type values
+ * for them, they're identifiers used internally, and more than one
+ * pcapng block type may use a given block type.
+ *
+ * Note that, in a given file format, this information won't necessarily
+ * appear in the form of blocks in the file, even though they're presented
+ * to the caller of libwiretap as blocks when reading and are presented
+ * by the caller of libwiretap as blocks when writing. See, for example,
+ * the iptrace file format, in which the interface name is given as part
+ * of the packet record header; we synthesize those blocks when reading
+ * (we don't currently support writing that format, but if we did, we'd
+ * get the interface name from the block and put it in the packet record
+ * header).
+ *
+ * WTAP_BLOCK_IF_ID_AND_INFO is a block that not only gives
+ * descriptive information about an interface but *also* assigns an
+ * ID to the interface, so that every packet has either an explicit
+ * or implicit interface ID indicating on which the packet arrived.
+ *
+ * It does *not* refer to information about interfaces that does not
+ * allow identification of the interface on which a packet arrives
+ * (I'm looking at *you*, Microsoft Network Monitor...). Do *not*
+ * indicate support for that block if your capture format merely
+ * gives a list of interface information without having every packet
+ * explicitly or implicitly (as in, for example, the pcapng Simple
+ * Packet Block) indicate on which of those interfaces the packet
+ * arrived.
+ *
+ * WTAP_BLOCK_PACKET (which corresponds to the Enhanced Packet Block,
+ * the Simple Packet Block, and the deprecated Packet Block) is not
+ * currently used; it's reserved for future use. The same applies
+ * to WTAP_BLOCK_SYSTEMD_JOURNAL_EXPORT.
+ */
+typedef enum {
+ WTAP_BLOCK_SECTION = 0,
+ WTAP_BLOCK_IF_ID_AND_INFO,
+ WTAP_BLOCK_NAME_RESOLUTION,
+ WTAP_BLOCK_IF_STATISTICS,
+ WTAP_BLOCK_DECRYPTION_SECRETS,
+ WTAP_BLOCK_PACKET,
+ WTAP_BLOCK_FT_SPECIFIC_REPORT,
+ WTAP_BLOCK_FT_SPECIFIC_EVENT,
+ WTAP_BLOCK_SYSDIG_EVENT,
+ WTAP_BLOCK_SYSDIG_META_EVENT,
+ WTAP_BLOCK_SYSTEMD_JOURNAL_EXPORT,
+ WTAP_BLOCK_CUSTOM,
+ MAX_WTAP_BLOCK_TYPE_VALUE
+} wtap_block_type_t;
+
+/**
+ * Holds the required data from a WTAP_BLOCK_SECTION.
+ */
+typedef struct wtapng_section_mandatory_s {
+ guint64 section_length; /**< 64-bit value specifying the length in bytes of the
+ * following section.
+ * Section Length equal -1 (0xFFFFFFFFFFFFFFFF) means
+ * that the size of the section is not specified
+ * Note: if writing to a new file, this length will
+ * be invalid if anything changes, such as the other
+ * members of this struct, or the packets written.
+ */
+} wtapng_section_mandatory_t;
+
+/** struct holding the information to build a WTAP_BLOCK_IF_ID_AND_INFO.
+ * the interface_data array holds an array of wtap_block_t
+ * representing interfacs, one per interface.
+ */
+typedef struct wtapng_iface_descriptions_s {
+ GArray *interface_data;
+} wtapng_iface_descriptions_t;
+
+/**
+ * Holds the required data from a WTAP_BLOCK_IF_ID_AND_INFO.
+ */
+typedef struct wtapng_if_descr_mandatory_s {
+ int wtap_encap; /**< link_type translated to wtap_encap */
+ guint64 time_units_per_second;
+ int tsprecision; /**< WTAP_TSPREC_ value for this interface */
+
+ guint32 snap_len;
+
+ guint8 num_stat_entries;
+ GArray *interface_statistics; /**< An array holding the interface statistics from
+ * pcapng ISB:s or equivalent(?)*/
+} wtapng_if_descr_mandatory_t;
+
+/**
+ * Holds the required data from a WTAP_BLOCK_NAME_RESOLUTION.
+ */
+typedef struct wtapng_nrb_mandatory_s {
+ GList *ipv4_addr_list;
+ GList *ipv6_addr_list;
+} wtapng_nrb_mandatory_t;
+
+/**
+ * Holds the required data from a WTAP_BLOCK_IF_STATISTICS.
+ */
+typedef struct wtapng_if_stats_mandatory_s {
+ guint32 interface_id;
+ guint32 ts_high;
+ guint32 ts_low;
+} wtapng_if_stats_mandatory_t;
+
+/**
+ * Holds the required data from a WTAP_BLOCK_DECRYPTION_SECRETS.
+ */
+typedef struct wtapng_dsb_mandatory_s {
+ guint32 secrets_type; /** Type of secrets stored in data (see secrets-types.h) */
+ guint32 secrets_len; /** Length of the secrets data in bytes */
+ guint8 *secrets_data; /** Buffer of secrets (not NUL-terminated) */
+} wtapng_dsb_mandatory_t;
+
+/**
+ * Holds the required data from a WTAP_BLOCK_SYSDIG_META_EVENT.
+ */
+typedef struct wtapng_sysdig_mev_mandatory_s {
+ uint32_t mev_type; /** pcapng block type of the event, e.g. BLOCK_TYPE_SYSDIG_MI */
+ uint32_t mev_data_len; /** Length of the mev data in bytes */
+ uint8_t *mev_data; /** Buffer of mev data (not NUL-terminated) */
+} wtapng_sysdig_mev_mandatory_t;
+
+/**
+ * Holds the required data from a WTAP_BLOCK_PACKET.
+ * This includes Enhanced Packet Block, Simple Packet Block, and the deprecated Packet Block.
+ * NB. I'm not including the packet data here since Wireshark handles it in other ways.
+ * If we were to add it we'd need to implement copy and free routines in wtap_opttypes.c
+ */
+#if 0
+/* Commented out for now, there's no mandatory data that isn't handled by
+ * Wireshark in other ways.
+ */
+typedef struct wtapng_packet_mandatory_s {
+ guint32 interface_id;
+ guint32 ts_high;
+ guint32 ts_low;
+ guint32 captured_len;
+ guint32 orig_len;
+} wtapng_packet_mandatory_t;
+#endif
+
+/**
+ * Holds the required data from a WTAP_BLOCK_FT_SPECIFIC_REPORT.
+ */
+typedef struct wtapng_ft_specific_mandatory_s {
+ guint record_type; /* the type of record this is - file type-specific value */
+} wtapng_ft_specific_mandatory_t;
+
+/*
+ * Currently supported option types. These are not option types
+ * in the sense that each one corresponds to a particular option,
+ * they are data types for the data of an option, so, for example,
+ * all options with a 32-bit unsigned integer value have the type
+ * WTAP_OPTTYPE_UINT32.
+ */
+typedef enum {
+ WTAP_OPTTYPE_UINT8,
+ WTAP_OPTTYPE_UINT32,
+ WTAP_OPTTYPE_UINT64,
+ WTAP_OPTTYPE_STRING,
+ WTAP_OPTTYPE_BYTES,
+ WTAP_OPTTYPE_IPv4,
+ WTAP_OPTTYPE_IPv6,
+ WTAP_OPTTYPE_CUSTOM,
+ WTAP_OPTTYPE_IF_FILTER,
+ WTAP_OPTTYPE_PACKET_VERDICT,
+ WTAP_OPTTYPE_PACKET_HASH,
+ WTAP_OPTTYPE_INT8,
+ WTAP_OPTTYPE_INT32,
+ WTAP_OPTTYPE_INT64,
+} wtap_opttype_e;
+
+typedef enum {
+ WTAP_OPTTYPE_SUCCESS = 0,
+ WTAP_OPTTYPE_NO_SUCH_OPTION = -1,
+ WTAP_OPTTYPE_NOT_FOUND = -2,
+ WTAP_OPTTYPE_TYPE_MISMATCH = -3,
+ WTAP_OPTTYPE_NUMBER_MISMATCH = -4,
+ WTAP_OPTTYPE_ALREADY_EXISTS = -5,
+ WTAP_OPTTYPE_BAD_BLOCK = -6,
+} wtap_opttype_return_val;
+
+/* https://www.iana.org/assignments/enterprise-numbers/enterprise-numbers */
+#define PEN_NFLX 10949
+#define PEN_VCTR 46254
+
+/*
+ * Structure describing a custom option.
+ */
+
+typedef struct custom_opt_s {
+ guint32 pen;
+ union {
+ struct generic_custom_opt_data {
+ gsize custom_data_len;
+ gchar *custom_data;
+ } generic_data;
+ struct nflx_custom_opt_data {
+ guint32 type;
+ gsize custom_data_len;
+ gchar *custom_data;
+ gboolean use_little_endian;
+ } nflx_data;
+ } data;
+} custom_opt_t;
+
+/*
+ * Structure describing a NFLX custom option.
+ */
+typedef struct nflx_custom_opt_s {
+ gboolean nflx_use_little_endian;
+ guint32 nflx_type;
+ gsize nflx_custom_data_len;
+ gchar *nflx_custom_data;
+} nflx_custom_opt_t;
+
+/* Interface description data - if_filter option structure */
+
+/* BPF instruction */
+typedef struct wtap_bpf_insn_s {
+ guint16 code;
+ guint8 jt;
+ guint8 jf;
+ guint32 k;
+} wtap_bpf_insn_t;
+
+/*
+ * Type of filter.
+ */
+typedef enum {
+ if_filter_pcap = 0, /* pcap filter string */
+ if_filter_bpf = 1 /* BPF program */
+} if_filter_type_e;
+
+typedef struct if_filter_opt_s {
+ if_filter_type_e type;
+ union {
+ gchar *filter_str; /**< pcap filter string */
+ struct wtap_bpf_insns {
+ guint bpf_prog_len; /**< number of BPF instructions */
+ wtap_bpf_insn_t *bpf_prog; /**< BPF instructions */
+ } bpf_prog; /**< BPF program */
+ } data;
+} if_filter_opt_t;
+
+/* Packet - packet_verdict option structure */
+
+/*
+ * Type of verdict.
+ */
+typedef enum {
+ packet_verdict_hardware = 0, /* array of octets */
+ packet_verdict_linux_ebpf_tc = 1, /* 64-bit unsigned integer TC_ACT_ value */
+ packet_verdict_linux_ebpf_xdp = 2 /* 64-bit unsigned integer xdp_action value */
+} packet_verdict_type_e;
+
+typedef struct packet_verdict_opt_s {
+ packet_verdict_type_e type;
+ union {
+ GByteArray *verdict_bytes;
+ guint64 verdict_linux_ebpf_tc;
+ guint64 verdict_linux_ebpf_xdp;
+ } data;
+} packet_verdict_opt_t;
+
+typedef struct packet_hash_opt_s {
+ guint8 type;
+ GByteArray *hash_bytes;
+} packet_hash_opt_t;
+
+/*
+ * Structure describing a value of an option.
+ */
+typedef union {
+ guint8 uint8val;
+ guint32 uint32val;
+ guint64 uint64val;
+ gint8 int8val;
+ gint32 int32val;
+ gint64 int64val;
+ ws_in4_addr ipv4val; /* network byte order */
+ ws_in6_addr ipv6val;
+ char *stringval;
+ GBytes *byteval;
+ custom_opt_t custom_opt;
+ if_filter_opt_t if_filterval;
+ packet_verdict_opt_t packet_verdictval;
+ packet_hash_opt_t packet_hash;
+} wtap_optval_t;
+
+/*
+ * Structure describing an option in a block.
+ */
+typedef struct {
+ guint option_id; /**< option code for the option */
+ wtap_optval_t value; /**< value */
+} wtap_option_t;
+
+#define NFLX_OPT_TYPE_VERSION 1
+#define NFLX_OPT_TYPE_TCPINFO 2
+#define NFLX_OPT_TYPE_DUMPINFO 4
+#define NFLX_OPT_TYPE_DUMPTIME 5
+#define NFLX_OPT_TYPE_STACKNAME 6
+
+struct nflx_dumpinfo {
+ guint32 tlh_version;
+ guint32 tlh_type;
+ guint64 tlh_length;
+ guint16 tlh_ie_fport;
+ guint16 tlh_ie_lport;
+ guint32 tlh_ie_faddr_addr32[4];
+ guint32 tlh_ie_laddr_addr32[4];
+ guint32 tlh_ie_zoneid;
+ guint64 tlh_offset_tv_sec;
+ guint64 tlh_offset_tv_usec;
+ char tlh_id[64];
+ char tlh_reason[32];
+ char tlh_tag[32];
+ guint8 tlh_af;
+ guint8 _pad[7];
+};
+
+/* Flags used in tlb_eventflags */
+#define NFLX_TLB_FLAG_RXBUF 0x0001 /* Includes receive buffer info */
+#define NFLX_TLB_FLAG_TXBUF 0x0002 /* Includes send buffer info */
+#define NFLX_TLB_FLAG_HDR 0x0004 /* Includes a TCP header */
+#define NFLX_TLB_FLAG_VERBOSE 0x0008 /* Includes function/line numbers */
+#define NFLX_TLB_FLAG_STACKINFO 0x0010 /* Includes stack-specific info */
+
+/* Flags used in tlb_flags */
+#define NFLX_TLB_TF_REQ_SCALE 0x00000020 /* Sent WS option */
+#define NFLX_TLB_TF_RCVD_SCALE 0x00000040 /* Received WS option */
+
+/* Values of tlb_state */
+#define NFLX_TLB_TCPS_ESTABLISHED 4
+#define NFLX_TLB_IS_SYNCHRONIZED(state) (state >= NFLX_TLB_TCPS_ESTABLISHED)
+
+struct nflx_tcpinfo {
+ guint64 tlb_tv_sec;
+ guint64 tlb_tv_usec;
+ guint32 tlb_ticks;
+ guint32 tlb_sn;
+ guint8 tlb_stackid;
+ guint8 tlb_eventid;
+ guint16 tlb_eventflags;
+ gint32 tlb_errno;
+ guint32 tlb_rxbuf_tls_sb_acc;
+ guint32 tlb_rxbuf_tls_sb_ccc;
+ guint32 tlb_rxbuf_tls_sb_spare;
+ guint32 tlb_txbuf_tls_sb_acc;
+ guint32 tlb_txbuf_tls_sb_ccc;
+ guint32 tlb_txbuf_tls_sb_spare;
+ gint32 tlb_state;
+ guint32 tlb_starttime;
+ guint32 tlb_iss;
+ guint32 tlb_flags;
+ guint32 tlb_snd_una;
+ guint32 tlb_snd_max;
+ guint32 tlb_snd_cwnd;
+ guint32 tlb_snd_nxt;
+ guint32 tlb_snd_recover;
+ guint32 tlb_snd_wnd;
+ guint32 tlb_snd_ssthresh;
+ guint32 tlb_srtt;
+ guint32 tlb_rttvar;
+ guint32 tlb_rcv_up;
+ guint32 tlb_rcv_adv;
+ guint32 tlb_flags2;
+ guint32 tlb_rcv_nxt;
+ guint32 tlb_rcv_wnd;
+ guint32 tlb_dupacks;
+ gint32 tlb_segqlen;
+ gint32 tlb_snd_numholes;
+ guint32 tlb_flex1;
+ guint32 tlb_flex2;
+ guint32 tlb_fbyte_in;
+ guint32 tlb_fbyte_out;
+ guint8 tlb_snd_scale:4,
+ tlb_rcv_scale:4;
+ guint8 _pad[3];
+
+ /* The following fields might become part of a union */
+ guint64 tlb_stackinfo_bbr_cur_del_rate;
+ guint64 tlb_stackinfo_bbr_delRate;
+ guint64 tlb_stackinfo_bbr_rttProp;
+ guint64 tlb_stackinfo_bbr_bw_inuse;
+ guint32 tlb_stackinfo_bbr_inflight;
+ guint32 tlb_stackinfo_bbr_applimited;
+ guint32 tlb_stackinfo_bbr_delivered;
+ guint32 tlb_stackinfo_bbr_timeStamp;
+ guint32 tlb_stackinfo_bbr_epoch;
+ guint32 tlb_stackinfo_bbr_lt_epoch;
+ guint32 tlb_stackinfo_bbr_pkts_out;
+ guint32 tlb_stackinfo_bbr_flex1;
+ guint32 tlb_stackinfo_bbr_flex2;
+ guint32 tlb_stackinfo_bbr_flex3;
+ guint32 tlb_stackinfo_bbr_flex4;
+ guint32 tlb_stackinfo_bbr_flex5;
+ guint32 tlb_stackinfo_bbr_flex6;
+ guint32 tlb_stackinfo_bbr_lost;
+ guint16 tlb_stackinfo_bbr_pacing_gain;
+ guint16 tlb_stackinfo_bbr_cwnd_gain;
+ guint16 tlb_stackinfo_bbr_flex7;
+ guint8 tlb_stackinfo_bbr_bbr_state;
+ guint8 tlb_stackinfo_bbr_bbr_substate;
+ guint8 tlb_stackinfo_bbr_inhpts;
+ guint8 tlb_stackinfo_bbr_ininput;
+ guint8 tlb_stackinfo_bbr_use_lt_bw;
+ guint8 tlb_stackinfo_bbr_flex8;
+ guint32 tlb_stackinfo_bbr_pkt_epoch;
+
+ guint32 tlb_len;
+};
+
+typedef void (*wtap_block_create_func)(wtap_block_t block);
+typedef void (*wtap_mand_free_func)(wtap_block_t block);
+typedef void (*wtap_mand_copy_func)(wtap_block_t dest_block, wtap_block_t src_block);
+
+/** Initialize block types.
+ *
+ * This is currently just a placeholder as nothing needs to be
+ * initialized yet. Should handle "registration" when code is
+ * refactored to do so.
+ */
+WS_DLL_PUBLIC void
+wtap_opttypes_initialize(void);
+
+/** Create a block by type
+ *
+ * Return a newly allocated block with default options provided
+ *
+ * @param[in] block_type Block type to be created
+ * @return Newly allocated block
+ */
+WS_DLL_PUBLIC wtap_block_t
+wtap_block_create(wtap_block_type_t block_type);
+
+/** Increase reference count of a block
+ *
+ * Call when taking a copy of a block
+ *
+ * @param[in] block Block add ref to
+ * @return The block
+ */
+WS_DLL_PUBLIC wtap_block_t
+wtap_block_ref(wtap_block_t block);
+
+/** Decrease reference count of a block
+ *
+ * Needs to be called on any block once you're done with it
+ *
+ * @param[in] block Block to be deref'd
+ */
+WS_DLL_PUBLIC void
+wtap_block_unref(wtap_block_t block);
+
+/** Free an array of blocks
+ *
+ * Needs to be called to clean up blocks allocated
+ * through GArray (for multiple blocks of same type)
+ * Includes freeing the GArray
+ *
+ * @param[in] block_array Array of blocks to be freed
+ */
+WS_DLL_PUBLIC void
+wtap_block_array_free(GArray* block_array);
+
+/** Provide type of a block
+ *
+ * @param[in] block Block from which to retrieve mandatory data
+ * @return Block type.
+ */
+WS_DLL_PUBLIC wtap_block_type_t
+wtap_block_get_type(wtap_block_t block);
+
+/** Provide mandatory data of a block
+ *
+ * @param[in] block Block from which to retrieve mandatory data
+ * @return Block mandatory data. Structure varies based on block type
+ */
+WS_DLL_PUBLIC void*
+wtap_block_get_mandatory_data(wtap_block_t block);
+
+/** Count the number of times the given option appears in the block
+ *
+ * @param[in] block Block to which to add the option
+ * @param[in] option_id Identifier value for option
+ * @return guint - the number of times the option was found
+ */
+WS_DLL_PUBLIC guint
+wtap_block_count_option(wtap_block_t block, guint option_id);
+
+/** Add UINT8 option value to a block
+ *
+ * @param[in] block Block to which to add the option
+ * @param[in] option_id Identifier value for option
+ * @param[in] value Value of option
+ * @return wtap_opttype_return_val - WTAP_OPTTYPE_SUCCESS if successful,
+ * error code otherwise
+ */
+WS_DLL_PUBLIC wtap_opttype_return_val
+wtap_block_add_uint8_option(wtap_block_t block, guint option_id, guint8 value);
+
+/** Set UINT8 option value in a block
+ *
+ * @param[in] block Block in which to set the option value
+ * @param[in] option_id Identifier value for option
+ * @param[in] value New value of option
+ * @return wtap_opttype_return_val - WTAP_OPTTYPE_SUCCESS if successful,
+ * error code otherwise
+ */
+WS_DLL_PUBLIC wtap_opttype_return_val
+wtap_block_set_uint8_option_value(wtap_block_t block, guint option_id, guint8 value);
+
+/** Get UINT8 option value from a block
+ *
+ * @param[in] block Block from which to get the option value
+ * @param[in] option_id Identifier value for option
+ * @param[out] value Returned value of option
+ * @return wtap_opttype_return_val - WTAP_OPTTYPE_SUCCESS if successful,
+ * error code otherwise
+ */
+WS_DLL_PUBLIC wtap_opttype_return_val
+wtap_block_get_uint8_option_value(wtap_block_t block, guint option_id, guint8* value) G_GNUC_WARN_UNUSED_RESULT;
+
+/** Add UINT32 option value to a block
+ *
+ * @param[in] block Block to which to add the option
+ * @param[in] option_id Identifier value for option
+ * @param[in] value Value of option
+ * @return wtap_opttype_return_val - WTAP_OPTTYPE_SUCCESS if successful,
+ * error code otherwise
+ */
+WS_DLL_PUBLIC wtap_opttype_return_val
+wtap_block_add_uint32_option(wtap_block_t block, guint option_id, guint32 value);
+
+/** Set UINT32 option value in a block
+ *
+ * @param[in] block Block in which to set the option value
+ * @param[in] option_id Identifier value for option
+ * @param[in] value New value of option
+ * @return wtap_opttype_return_val - WTAP_OPTTYPE_SUCCESS if successful,
+ * error code otherwise
+ */
+WS_DLL_PUBLIC wtap_opttype_return_val
+wtap_block_set_uint32_option_value(wtap_block_t block, guint option_id, guint32 value);
+
+/** Get UINT32 option value from a block
+ *
+ * @param[in] block Block from which to get the option value
+ * @param[in] option_id Identifier value for option
+ * @param[out] value Returned value of option
+ * @return wtap_opttype_return_val - WTAP_OPTTYPE_SUCCESS if successful,
+ * error code otherwise
+ */
+WS_DLL_PUBLIC wtap_opttype_return_val
+wtap_block_get_uint32_option_value(wtap_block_t block, guint option_id, guint32* value) G_GNUC_WARN_UNUSED_RESULT;
+
+/** Add UINT64 option value to a block
+ *
+ * @param[in] block Block to which to add the option
+ * @param[in] option_id Identifier value for option
+ * @param[in] value Value of option
+ * @return wtap_opttype_return_val - WTAP_OPTTYPE_SUCCESS if successful,
+ * error code otherwise
+ */
+WS_DLL_PUBLIC wtap_opttype_return_val
+wtap_block_add_uint64_option(wtap_block_t block, guint option_id, guint64 value);
+
+/** Set UINT64 option value in a block
+ *
+ * @param[in] block Block in which to set the option value
+ * @param[in] option_id Identifier value for option
+ * @param[in] value New value of option
+ * @return wtap_opttype_return_val - WTAP_OPTTYPE_SUCCESS if successful,
+ * error code otherwise
+ */
+WS_DLL_PUBLIC wtap_opttype_return_val
+wtap_block_set_uint64_option_value(wtap_block_t block, guint option_id, guint64 value);
+
+/** Get UINT64 option value from a block
+ *
+ * @param[in] block Block from which to get the option value
+ * @param[in] option_id Identifier value for option
+ * @param[out] value Returned value of option
+ * @return wtap_opttype_return_val - WTAP_OPTTYPE_SUCCESS if successful,
+ * error code otherwise
+ */
+WS_DLL_PUBLIC wtap_opttype_return_val
+wtap_block_get_uint64_option_value(wtap_block_t block, guint option_id, guint64* value) G_GNUC_WARN_UNUSED_RESULT;
+
+/** Add INT8 option value to a block
+ *
+ * @param[in] block Block to which to add the option
+ * @param[in] option_id Identifier value for option
+ * @param[in] value Value of option
+ * @return wtap_opttype_return_val - WTAP_OPTTYPE_SUCCESS if successful,
+ * error code otherwise
+ */
+WS_DLL_PUBLIC wtap_opttype_return_val
+wtap_block_add_int8_option(wtap_block_t block, guint option_id, gint8 value);
+
+/** Set INT8 option value in a block
+ *
+ * @param[in] block Block in which to set the option value
+ * @param[in] option_id Identifier value for option
+ * @param[in] value New value of option
+ * @return wtap_opttype_return_val - WTAP_OPTTYPE_SUCCESS if successful,
+ * error code otherwise
+ */
+WS_DLL_PUBLIC wtap_opttype_return_val
+wtap_block_set_int8_option_value(wtap_block_t block, guint option_id, gint8 value);
+
+/** Get INT8 option value from a block
+ *
+ * @param[in] block Block from which to get the option value
+ * @param[in] option_id Identifier value for option
+ * @param[out] value Returned value of option
+ * @return wtap_opttype_return_val - WTAP_OPTTYPE_SUCCESS if successful,
+ * error code otherwise
+ */
+WS_DLL_PUBLIC wtap_opttype_return_val
+wtap_block_get_int8_option_value(wtap_block_t block, guint option_id, gint8* value) G_GNUC_WARN_UNUSED_RESULT;
+
+/** Add INT32 option value to a block
+ *
+ * @param[in] block Block to which to add the option
+ * @param[in] option_id Identifier value for option
+ * @param[in] value Value of option
+ * @return wtap_opttype_return_val - WTAP_OPTTYPE_SUCCESS if successful,
+ * error code otherwise
+ */
+WS_DLL_PUBLIC wtap_opttype_return_val
+wtap_block_add_int32_option(wtap_block_t block, guint option_id, gint32 value);
+
+/** Set INT32 option value in a block
+ *
+ * @param[in] block Block in which to set the option value
+ * @param[in] option_id Identifier value for option
+ * @param[in] value New value of option
+ * @return wtap_opttype_return_val - WTAP_OPTTYPE_SUCCESS if successful,
+ * error code otherwise
+ */
+WS_DLL_PUBLIC wtap_opttype_return_val
+wtap_block_set_int32_option_value(wtap_block_t block, guint option_id, gint32 value);
+
+/** Get INT32 option value from a block
+ *
+ * @param[in] block Block from which to get the option value
+ * @param[in] option_id Identifier value for option
+ * @param[out] value Returned value of option
+ * @return wtap_opttype_return_val - WTAP_OPTTYPE_SUCCESS if successful,
+ * error code otherwise
+ */
+WS_DLL_PUBLIC wtap_opttype_return_val
+wtap_block_get_int32_option_value(wtap_block_t block, guint option_id, gint32* value) G_GNUC_WARN_UNUSED_RESULT;
+
+/** Add INT64 option value to a block
+ *
+ * @param[in] block Block to which to add the option
+ * @param[in] option_id Identifier value for option
+ * @param[in] value Value of option
+ * @return wtap_opttype_return_val - WTAP_OPTTYPE_SUCCESS if successful,
+ * error code otherwise
+ */
+WS_DLL_PUBLIC wtap_opttype_return_val
+wtap_block_add_int64_option(wtap_block_t block, guint option_id, gint64 value);
+
+/** Set INT64 option value in a block
+ *
+ * @param[in] block Block in which to set the option value
+ * @param[in] option_id Identifier value for option
+ * @param[in] value New value of option
+ * @return wtap_opttype_return_val - WTAP_OPTTYPE_SUCCESS if successful,
+ * error code otherwise
+ */
+WS_DLL_PUBLIC wtap_opttype_return_val
+wtap_block_set_int64_option_value(wtap_block_t block, guint option_id, gint64 value);
+
+/** Get INT64 option value from a block
+ *
+ * @param[in] block Block from which to get the option value
+ * @param[in] option_id Identifier value for option
+ * @param[out] value Returned value of option
+ * @return wtap_opttype_return_val - WTAP_OPTTYPE_SUCCESS if successful,
+ * error code otherwise
+ */
+WS_DLL_PUBLIC wtap_opttype_return_val
+wtap_block_get_int64_option_value(wtap_block_t block, guint option_id, gint64* value) G_GNUC_WARN_UNUSED_RESULT;
+
+/** Add IPv4 address option value to a block
+ *
+ * @param[in] block Block to which to add the option
+ * @param[in] option_id Identifier value for option
+ * @param[in] value Value of option
+ * @return wtap_opttype_return_val - WTAP_OPTTYPE_SUCCESS if successful,
+ * error code otherwise
+ */
+WS_DLL_PUBLIC wtap_opttype_return_val
+wtap_block_add_ipv4_option(wtap_block_t block, guint option_id, guint32 value);
+
+/** Set IPv4 option value in a block
+ *
+ * @param[in] block Block in which to set the option value
+ * @param[in] option_id Identifier value for option
+ * @param[in] value New value of option
+ * @return wtap_opttype_return_val - WTAP_OPTTYPE_SUCCESS if successful,
+ * error code otherwise
+ */
+WS_DLL_PUBLIC wtap_opttype_return_val
+wtap_block_set_ipv4_option_value(wtap_block_t block, guint option_id, guint32 value);
+
+/** Get IPv4 option value from a block
+ *
+ * @param[in] block Block from which to get the option value
+ * @param[in] option_id Identifier value for option
+ * @param[out] value Returned value of option
+ * @return wtap_opttype_return_val - WTAP_OPTTYPE_SUCCESS if successful,
+ * error code otherwise
+ */
+WS_DLL_PUBLIC wtap_opttype_return_val
+wtap_block_get_ipv4_option_value(wtap_block_t block, guint option_id, guint32* value) G_GNUC_WARN_UNUSED_RESULT;
+
+/** Add IPv6 address option value to a block
+ *
+ * @param[in] block Block to which to add the option
+ * @param[in] option_id Identifier value for option
+ * @param[in] value Value of option
+ * @return wtap_opttype_return_val - WTAP_OPTTYPE_SUCCESS if successful,
+ * error code otherwise
+ */
+WS_DLL_PUBLIC wtap_opttype_return_val
+wtap_block_add_ipv6_option(wtap_block_t block, guint option_id, ws_in6_addr *value);
+
+/** Set IPv6 option value in a block
+ *
+ * @param[in] block Block in which to set the option value
+ * @param[in] option_id Identifier value for option
+ * @param[in] value New value of option
+ * @return wtap_opttype_return_val - WTAP_OPTTYPE_SUCCESS if successful,
+ * error code otherwise
+ */
+WS_DLL_PUBLIC wtap_opttype_return_val
+wtap_block_set_ipv6_option_value(wtap_block_t block, guint option_id, ws_in6_addr *value);
+
+/** Get IPv6 option value from a block
+ *
+ * @param[in] block Block from which to get the option value
+ * @param[in] option_id Identifier value for option
+ * @param[out] value Returned value of option
+ * @return wtap_opttype_return_val - WTAP_OPTTYPE_SUCCESS if successful,
+ * error code otherwise
+ */
+WS_DLL_PUBLIC wtap_opttype_return_val
+wtap_block_get_ipv6_option_value(wtap_block_t block, guint option_id, ws_in6_addr* value) G_GNUC_WARN_UNUSED_RESULT;
+
+/** Add a string option to a block
+ *
+ * @param[in] block Block to which to add the option
+ * @param[in] option_id Identifier value for option
+ * @param[in] value Value of option
+ * @param[in] value_length Maximum length of string to copy.
+ * @return wtap_opttype_return_val - WTAP_OPTTYPE_SUCCESS if successful,
+ * error code otherwise
+ */
+WS_DLL_PUBLIC wtap_opttype_return_val
+wtap_block_add_string_option(wtap_block_t block, guint option_id, const char *value, gsize value_length);
+
+/** Add a string option to a block taking ownership of the null-terminated string.
+ *
+ * @param[in] block Block to which to add the option
+ * @param[in] option_id Identifier value for option
+ * @param[in] value Value of option
+ * @return wtap_opttype_return_val - WTAP_OPTTYPE_SUCCESS if successful,
+ * error code otherwise
+ */
+WS_DLL_PUBLIC wtap_opttype_return_val
+wtap_block_add_string_option_owned(wtap_block_t block, guint option_id, char *value);
+
+/** Add a string option to a block with a printf-formatted string as its value
+ *
+ * @param[in] block Block to which to add the option
+ * @param[in] option_id Identifier value for option
+ * @param[in] format printf-like format string
+ * @return wtap_opttype_return_val - WTAP_OPTTYPE_SUCCESS if successful,
+ * error code otherwise
+ */
+WS_DLL_PUBLIC wtap_opttype_return_val
+wtap_block_add_string_option_format(wtap_block_t block, guint option_id, const char *format, ...)
+ G_GNUC_PRINTF(3,4);
+
+/** Set string option value in a block
+ *
+ * @param[in] block Block in which to set the option value
+ * @param[in] option_id Identifier value for option
+ * @param[in] value New value of option
+ * @param[in] value_length Maximum length of string to copy.
+ * @return wtap_opttype_return_val - WTAP_OPTTYPE_SUCCESS if successful,
+ * error code otherwise
+ */
+WS_DLL_PUBLIC wtap_opttype_return_val
+wtap_block_set_string_option_value(wtap_block_t block, guint option_id, const char* value, gsize value_length);
+
+/** Set string option value for the nth instance of a particular option
+ * in a block
+ *
+ * @param[in] block Block in which to set the option value
+ * @param[in] option_id Identifier value for option
+ * @param[in] idx Instance number of option with that ID
+ * @param[in] value New value of option
+ * @param[in] value_length Maximum length of string to copy.
+ * @return wtap_opttype_return_val - WTAP_OPTTYPE_SUCCESS if successful,
+ * error code otherwise
+ */
+WS_DLL_PUBLIC wtap_opttype_return_val
+wtap_block_set_nth_string_option_value(wtap_block_t block, guint option_id, guint idx, const char* value, gsize value_length);
+
+/** Set string option value in a block to a printf-formatted string
+ *
+ * @param[in] block Block in which to set the option value
+ * @param[in] option_id Identifier value for option
+ * @param[in] format printf-like format string
+ * @return wtap_opttype_return_val - WTAP_OPTTYPE_SUCCESS if successful,
+ * error code otherwise
+ */
+WS_DLL_PUBLIC wtap_opttype_return_val
+wtap_block_set_string_option_value_format(wtap_block_t block, guint option_id, const char *format, ...)
+ G_GNUC_PRINTF(3,4);
+
+/** Set string option value for the nth instance of a particular option
+ * in a block to a printf-formatted string
+ *
+ * @param[in] block Block in which to set the option value
+ * @param[in] option_id Identifier value for option
+ * @param[in] idx Instance number of option with that ID
+ * @param[in] format printf-like format string
+ * @return wtap_opttype_return_val - WTAP_OPTTYPE_SUCCESS if successful,
+ * error code otherwise
+ */
+WS_DLL_PUBLIC wtap_opttype_return_val
+wtap_block_set_nth_string_option_value_format(wtap_block_t block, guint option_id, guint idx, const char *format, ...)
+ G_GNUC_PRINTF(4,5);
+
+/** Get string option value from a block
+ *
+ * @param[in] block Block from which to get the option value
+ * @param[in] option_id Identifier value for option
+ * @param[out] value Returned value of option
+ * @return wtap_opttype_return_val - WTAP_OPTTYPE_SUCCESS if successful,
+ * error code otherwise
+ */
+WS_DLL_PUBLIC wtap_opttype_return_val
+wtap_block_get_string_option_value(wtap_block_t block, guint option_id, char** value) G_GNUC_WARN_UNUSED_RESULT;
+
+/** Get string option value for the nth instance of a particular option
+ * in a block
+ *
+ * @param[in] block Block from which to get the option value
+ * @param[in] option_id Identifier value for option
+ * @param[in] idx Instance number of option with that ID
+ * @param[out] value Returned value of option
+ * @return wtap_opttype_return_val - WTAP_OPTTYPE_SUCCESS if successful,
+ * error code otherwise
+ */
+WS_DLL_PUBLIC wtap_opttype_return_val
+wtap_block_get_nth_string_option_value(wtap_block_t block, guint option_id, guint idx, char** value) G_GNUC_WARN_UNUSED_RESULT;
+
+/** Add a bytes option to a block
+ *
+ * @param[in] block Block to which to add the option
+ * @param[in] option_id Identifier value for option
+ * @param[in] value Value of option to copy
+ * @param[in] value_length Number of bytes to copy
+ * @return wtap_opttype_return_val - WTAP_OPTTYPE_SUCCESS if successful,
+ * error code otherwise
+ */
+WS_DLL_PUBLIC wtap_opttype_return_val
+wtap_block_add_bytes_option(wtap_block_t block, guint option_id, const guint8 *value, gsize value_length);
+
+/** Add a bytes option to a block, borrowing the value from a GBytes
+ *
+ * @param[in] block Block to which to add the option
+ * @param[in] option_id Identifier value for option
+ * @param[in] value Value of option as a GBytes
+ * @return wtap_opttype_return_val - WTAP_OPTTYPE_SUCCESS if successful,
+ * error code otherwise
+ */
+WS_DLL_PUBLIC wtap_opttype_return_val
+wtap_block_add_bytes_option_borrow(wtap_block_t block, guint option_id, GBytes *value);
+
+/** Set bytes option value in a block
+ *
+ * @param[in] block Block in which to set the option value
+ * @param[in] option_id Identifier value for option
+ * @param[in] value New value of option
+ * @param[in] value_length Number of bytes to copy.
+ * @return wtap_opttype_return_val - WTAP_OPTTYPE_SUCCESS if successful,
+ * error code otherwise
+ */
+WS_DLL_PUBLIC wtap_opttype_return_val
+wtap_block_set_bytes_option_value(wtap_block_t block, guint option_id, const guint8* value, gsize value_length);
+
+/** Set bytes option value for nth instance of a particular option in a block
+ *
+ * @param[in] block Block in which to set the option value
+ * @param[in] option_id Identifier value for option
+ * @param[in] idx Instance number of option with that ID
+ * @param[in] value New value of option
+ * @return wtap_opttype_return_val - WTAP_OPTTYPE_SUCCESS if successful,
+ * error code otherwise
+ */
+WS_DLL_PUBLIC wtap_opttype_return_val
+wtap_block_set_nth_bytes_option_value(wtap_block_t block, guint option_id, guint idx, GBytes* value);
+
+/** Get bytes option value from a block
+ *
+ * @param[in] block Block from which to get the option value
+ * @param[in] option_id Identifier value for option
+ * @param[out] value Returned value of option
+ * @return wtap_opttype_return_val - WTAP_OPTTYPE_SUCCESS if successful,
+ * error code otherwise
+ * @note You should call g_bytes_ref() on value if you plan to keep it around
+ * (and then g_bytes_unref() when you're done with it).
+ */
+WS_DLL_PUBLIC wtap_opttype_return_val
+wtap_block_get_bytes_option_value(wtap_block_t block, guint option_id, GBytes** value) G_GNUC_WARN_UNUSED_RESULT;
+
+/** Get bytes option value for nth instance of a particular option in a block
+ *
+ * @param[in] block Block from which to get the option value
+ * @param[in] option_id Identifier value for option
+ * @param[in] idx Instance number of option with that ID
+ * @param[out] value Returned value of option
+ * @return wtap_opttype_return_val - WTAP_OPTTYPE_SUCCESS if successful,
+ * error code otherwise
+ * @note You should call g_bytes_ref() on value if you plan to keep it around
+ * (and then g_bytes_unref() when you're done with it).
+ */
+WS_DLL_PUBLIC wtap_opttype_return_val
+wtap_block_get_nth_bytes_option_value(wtap_block_t block, guint option_id, guint idx, GBytes** value) G_GNUC_WARN_UNUSED_RESULT;
+
+/** Add an NFLX custom option to a block
+ *
+ * @param[in] block Block to which to add the option
+ * @param[in] nflx_type NFLX option type
+ * @param[in] nflx_custom_data pointer to the data
+ * @param[in] nflx_custom_data_len length of custom_data
+ * @return wtap_opttype_return_val - WTAP_OPTTYPE_SUCCESS if successful,
+ * error code otherwise
+ */
+WS_DLL_PUBLIC wtap_opttype_return_val
+wtap_block_add_nflx_custom_option(wtap_block_t block, guint32 nflx_type, const char *nflx_custom_data, gsize nflx_custom_data_len);
+
+/** Get an NFLX custom option value from a block
+ *
+ * @param[in] block Block from which to get the option value
+ * @param[in] nflx_type type of the option
+ * @param[out] nflx_custom_data Returned value of NFLX custom option value
+ * @param[in] nflx_custom_data_len size of buffer provided in nflx_custom_data
+ * @return wtap_opttype_return_val - WTAP_OPTTYPE_SUCCESS if successful,
+ * error code otherwise
+ */
+WS_DLL_PUBLIC wtap_opttype_return_val
+wtap_block_get_nflx_custom_option(wtap_block_t block, guint32 nflx_type, char *nflx_custom_data, gsize nflx_custom_data_len);
+
+/** Add a custom option to a block
+ *
+ * @param[in] block Block to which to add the option
+ * @param[in] option_id Identifier value for option
+ * @param[in] pen PEN
+ * @param[in] custom_data pointer to the data
+ * @param[in] custom_data_len length of custom_data
+ * @return wtap_opttype_return_val - WTAP_OPTTYPE_SUCCESS if successful,
+ * error code otherwise
+ */
+WS_DLL_PUBLIC wtap_opttype_return_val
+wtap_block_add_custom_option(wtap_block_t block, guint option_id, guint32 pen, const char *custom_data, gsize custom_data_len);
+
+/** Add an if_filter option value to a block
+ *
+ * @param[in] block Block to which to add the option
+ * @param[in] option_id Identifier value for option
+ * @param[in] value Value of option
+ * @return wtap_opttype_return_val - WTAP_OPTTYPE_SUCCESS if successful,
+ * error code otherwise
+ */
+WS_DLL_PUBLIC wtap_opttype_return_val
+wtap_block_add_if_filter_option(wtap_block_t block, guint option_id, if_filter_opt_t* value);
+
+/** Set an if_filter option value in a block
+ *
+ * @param[in] block Block in which to set the option value
+ * @param[in] option_id Identifier value for option
+ * @param[in] value New value of option
+ * @return wtap_opttype_return_val - WTAP_OPTTYPE_SUCCESS if successful,
+ * error code otherwise
+ */
+WS_DLL_PUBLIC wtap_opttype_return_val
+wtap_block_set_if_filter_option_value(wtap_block_t block, guint option_id, if_filter_opt_t* value);
+
+/** Get an if_filter option value from a block
+ *
+ * @param[in] block Block from which to get the option value
+ * @param[in] option_id Identifier value for option
+ * @param[out] value Returned value of option value
+ * @return wtap_opttype_return_val - WTAP_OPTTYPE_SUCCESS if successful,
+ * error code otherwise
+ */
+WS_DLL_PUBLIC wtap_opttype_return_val
+wtap_block_get_if_filter_option_value(wtap_block_t block, guint option_id, if_filter_opt_t* value) G_GNUC_WARN_UNUSED_RESULT;
+
+/** Add a packet_verdict option value to a block
+ *
+ * @param[in] block Block to which to add the option
+ * @param[in] option_id Identifier value for option
+ * @param[in] value Value of option
+ * @return wtap_opttype_return_val - WTAP_OPTTYPE_SUCCESS if successful,
+ * error code otherwise
+ */
+WS_DLL_PUBLIC wtap_opttype_return_val
+wtap_block_add_packet_verdict_option(wtap_block_t block, guint option_id, packet_verdict_opt_t* value);
+
+/** Set packet_verdict option value for the nth instsance of a particular
+ * option in a block
+ *
+ * @param[in] block Block in which to set the option value
+ * @param[in] option_id Identifier value for option
+ * @param[in] idx Instance number of option with that ID
+ * @param[in] value New value of option
+ * @return wtap_opttype_return_val - WTAP_OPTTYPE_SUCCESS if successful,
+ * error code otherwise
+ */
+WS_DLL_PUBLIC wtap_opttype_return_val
+wtap_block_set_nth_packet_verdict_option_value(wtap_block_t block, guint option_id, guint idx, packet_verdict_opt_t* value);
+
+/** Get packet_verdict option value for the nth instance of a particular
+ * option in a block
+ *
+ * @param[in] block Block from which to get the option value
+ * @param[in] option_id Identifier value for option
+ * @param[in] idx Instance number of option with that ID
+ * @param[out] value Returned value of option value
+ * @return wtap_opttype_return_val - WTAP_OPTTYPE_SUCCESS if successful,
+ * error code otherwise
+ */
+WS_DLL_PUBLIC wtap_opttype_return_val
+wtap_block_get_nth_packet_verdict_option_value(wtap_block_t block, guint option_id, guint idx, packet_verdict_opt_t* value) G_GNUC_WARN_UNUSED_RESULT;
+
+WS_DLL_PUBLIC void
+wtap_packet_verdict_free(packet_verdict_opt_t* verdict);
+
+/** Add a packet_hash option value to a block
+ *
+ * @param[in] block Block to which to add the option
+ * @param[in] option_id Identifier value for option
+ * @param[in] value Value of option
+ * @return wtap_opttype_return_val - WTAP_OPTTYPE_SUCCESS if successful,
+ * error code otherwise
+ */
+WS_DLL_PUBLIC wtap_opttype_return_val
+wtap_block_add_packet_hash_option(wtap_block_t block, guint option_id, packet_hash_opt_t* value);
+
+WS_DLL_PUBLIC void
+wtap_packet_hash_free(packet_hash_opt_t* hash);
+
+/** Remove an option from a block
+ *
+ * @param[in] block Block from which to remove the option
+ * @param[in] option_id Identifier value for option
+ * @return wtap_opttype_return_val - WTAP_OPTTYPE_SUCCESS if successful,
+ * error code otherwise
+ */
+WS_DLL_PUBLIC wtap_opttype_return_val
+wtap_block_remove_option(wtap_block_t block, guint option_id);
+
+/** Remove the nth instance of an option from a block
+ *
+ * @param[in] block Block from which to remove the option instance
+ * @param[in] option_id Identifier value for option
+ * @param[in] idx Instance number of option with that ID
+ * @return wtap_opttype_return_val - WTAP_OPTTYPE_SUCCESS if successful,
+ * error code otherwise
+ */
+WS_DLL_PUBLIC wtap_opttype_return_val
+wtap_block_remove_nth_option_instance(wtap_block_t block, guint option_id, guint idx);
+
+/** Copy a block to another.
+ *
+ * Any options that are in the destination but not the source are not removed.
+ * Options that are just in source will be added to destination
+ *
+ * @param[in] dest_block Block to be copied to
+ * @param[in] src_block Block to be copied from
+ */
+WS_DLL_PUBLIC void
+wtap_block_copy(wtap_block_t dest_block, wtap_block_t src_block);
+
+/** Make a copy of a block.
+ *
+ * @param[in] block Block to be copied from
+ * @return Newly allocated copy of that block
+ */
+WS_DLL_PUBLIC wtap_block_t
+wtap_block_make_copy(wtap_block_t block);
+
+typedef gboolean (*wtap_block_foreach_func)(wtap_block_t block, guint option_id, wtap_opttype_e option_type, wtap_optval_t *option, void *user_data);
+WS_DLL_PUBLIC gboolean
+wtap_block_foreach_option(wtap_block_t block, wtap_block_foreach_func func, void* user_data);
+
+/** Cleanup the internal structures
+ */
+WS_DLL_PUBLIC void
+wtap_opttypes_cleanup(void);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* WTAP_OPT_TYPES_H */