diff options
Diffstat (limited to 'wiretap/ngsniffer.c')
-rw-r--r-- | wiretap/ngsniffer.c | 2935 |
1 files changed, 2935 insertions, 0 deletions
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: + */ |