diff options
Diffstat (limited to 'wiretap')
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, ¤t_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, ¤t_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(¶ms->rec->ts_rel_cap, ¶ms->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, ðheader, sizeof(ethheader), err, err_info)) { + ws_debug("not enough bytes for ethernet frame header in file"); + return FALSE; + } + fix_endianness_blf_ethernetframeheader(ðheader); + + /* + * 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, ðheader, 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(ðheader); + + 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, ðernet_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(ðernet_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(×tamp); + 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(¶ms); + + 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, ×tamp, &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,ð_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, ð_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, ×tamp, &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, ¶ms, 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, ¶ms, err, + err_info); + } else if (out_filename) { + pdh = wtap_dump_open(out_filename, file_type, WTAP_UNCOMPRESSED, + ¶ms, err, err_info); + } else { + pdh = wtap_dump_open_stdout(file_type, WTAP_UNCOMPRESSED, ¶ms, 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 */ |