/* ngsniffer.c
*
- * $Id: ngsniffer.c,v 1.24 1999/09/30 20:34:26 guy Exp $
+ * $Id: ngsniffer.c,v 1.85 2002/08/28 20:30:45 jmayer Exp $
*
* Wiretap Library
- * Copyright (c) 1998 by Gilbert Ramirez <gram@verdict.uthscsa.edu>
- *
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
- *
+ *
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
+ *
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
*/
/* The code in ngsniffer.c that decodes the time fields for each packet in the
#include <stdlib.h>
#include <errno.h>
-#include <time.h>
-#include "wtap.h"
-#include "file.h"
+#include <string.h>
+#include "wtap-int.h"
+#include "file_wrappers.h"
#include "buffer.h"
#include "ngsniffer.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 serial numbers? */
+#define REC_HEADER2 7 /* Header containing ??? */
+#define REC_V2DESC 8 /* In version 2 sniffer traces contains
+ * infos about this capturing session.
+ * 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 version record format.
- *
- * XXX - the Sniffer documentation doesn't say what the compression stuff
- * means. The manual says "IMPORTANT: You must save the file uncompressed
- * to use this format specification."
*/
struct vers_rec {
gint16 maj_vers; /* major version number */
gint16 date; /* DOS-format date */
gint8 type; /* what type of records follow */
guint8 network; /* network type */
- gint8 format; /* format version (we only support version 1!) */
+ gint8 format; /* format version */
guint8 timeunit; /* timestamp units */
gint8 cmprs_vers; /* compression version */
gint8 cmprs_level; /* compression level */
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 */
+ gint8 time_high; /* high part of time stamp */
+ gint8 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 */
+ guint8 chemical_x[22]; /* ? */
+};
+
+/*
+ * Network type values in type 7 records.
+ */
+#define NET_SDLC 0
+#define NET_HDLC 1
+#define NET_FRAME_RELAY 2
+#define NET_ROUTER 3 /* what's this? */
+#define NET_PPP 4
+#define NET_SMDS 5
+
/* values for V.timeunit */
#define NUM_NGSNIFF_TIMEUNITS 7
static double Usec[] = { 15.0, 0.838096, 15.0, 0.5, 2.0, 1.0, 0.1 };
-static int ngsniffer_read(wtap *wth, int *err);
+static int skip_header_records(wtap *wth, int *err, gint16 version);
+static gboolean ngsniffer_read(wtap *wth, int *err, long *data_offset);
+static gboolean ngsniffer_seek_read(wtap *wth, long seek_off,
+ union wtap_pseudo_header *pseudo_header, guchar *pd, int packet_size,
+ int *err);
+static int ngsniffer_read_rec_header(wtap *wth, gboolean is_random,
+ guint16 *typep, guint16 *lengthp, int *err);
+static gboolean ngsniffer_read_frame2(wtap *wth, gboolean is_random,
+ struct frame2_rec *frame2, int *err);
+static void set_pseudo_header_frame2(union wtap_pseudo_header *pseudo_header,
+ struct frame2_rec *frame2);
+static gboolean ngsniffer_read_frame4(wtap *wth, gboolean is_random,
+ struct frame4_rec *frame4, int *err);
+static void set_pseudo_header_frame4(union wtap_pseudo_header *pseudo_header,
+ struct frame4_rec *frame4);
+static gboolean ngsniffer_read_frame6(wtap *wth, gboolean is_random,
+ struct frame6_rec *frame6, int *err);
+static void set_pseudo_header_frame6(union wtap_pseudo_header *pseudo_header,
+ struct frame6_rec *frame6);
+static gboolean ngsniffer_read_rec_data(wtap *wth, gboolean is_random,
+ guchar *pd, int length, int *err);
+static void fix_pseudo_header(wtap *wth,
+ 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 struct wtap_pkthdr *phdr,
+ const union wtap_pseudo_header *pseudo_header, const guchar *pd, int *err);
+static gboolean ngsniffer_dump_close(wtap_dumper *wdh, int *err);
+static int SnifferDecompress( unsigned char * inbuf, size_t inlen,
+ unsigned char * outbuf, size_t outlen, int *err );
+static int ng_file_read(void *buffer, size_t elementsize, size_t numelements,
+ wtap *wth, gboolean is_random, int *err);
+static int read_blob(FILE_T infile, ngsniffer_comp_stream_t *comp_stream,
+ int *err);
+static long ng_file_seek_seq(wtap *wth, long offset, int whence, int *err);
+static long ng_file_seek_rand(wtap *wth, long offset, int whence, int *err);
int ngsniffer_open(wtap *wth, int *err)
{
int bytes_read;
- char magic[18];
+ 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, length = 0;
+ the last 2 are "reserved" and are thrown away */
+ guint16 type, length;
struct vers_rec version;
guint16 start_date;
guint16 start_time;
static const int sniffer_encap[] = {
- WTAP_ENCAP_TR,
+ 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_LAPB, /* Internetwork analyzer */
- WTAP_ENCAP_UNKNOWN, /* type 8 not defined in Sniffer */
+ WTAP_ENCAP_UNKNOWN, /* Internetwork analyzer (synchronous) */
+ WTAP_ENCAP_UNKNOWN, /* Internetwork analyzer (asynchronous) */
WTAP_ENCAP_FDDI_BITSWAPPED,
- WTAP_ENCAP_ATM_SNIFFER /* ATM */
+ WTAP_ENCAP_ATM_SNIFFER
};
#define NUM_NGSNIFF_ENCAPS (sizeof sniffer_encap / sizeof sniffer_encap[0])
struct tm tm;
/* Read in the string that should be at the start of a Sniffer file */
- file_seek(wth->fh, 0, SEEK_SET);
- wth->data_offset = 0;
errno = WTAP_ERR_CANT_READ;
- bytes_read = file_read(magic, 1, 17, wth->fh);
- if (bytes_read != 17) {
- if (file_error(wth->fh)) {
- *err = errno;
+ bytes_read = file_read(magic, 1, sizeof magic, wth->fh);
+ if (bytes_read != sizeof magic) {
+ *err = file_error(wth->fh);
+ if (*err != 0)
return -1;
- }
return 0;
}
- wth->data_offset += 17;
-
- magic[17] = 0;
+ wth->data_offset += sizeof magic;
- if (strcmp(magic, "TRSNIFF data \x1a")) {
+ if (memcmp(magic, ngsniffer_magic, sizeof ngsniffer_magic)) {
return 0;
}
bytes_read = file_read(record_type, 1, 2, wth->fh);
bytes_read += file_read(record_length, 1, 4, wth->fh);
if (bytes_read != 6) {
- if (file_error(wth->fh)) {
- *err = errno;
+ *err = file_error(wth->fh);
+ if (*err != 0)
return -1;
- }
return 0;
}
wth->data_offset += 6;
errno = WTAP_ERR_CANT_READ;
bytes_read = file_read(&version, 1, sizeof version, wth->fh);
if (bytes_read != sizeof version) {
- if (file_error(wth->fh)) {
- *err = errno;
+ *err = file_error(wth->fh);
+ if (*err != 0)
return -1;
- }
return 0;
}
wth->data_offset += sizeof version;
- /* Make sure this is an uncompressed Sniffer file */
- if (version.format != 1) {
- g_message("ngsniffer: Compressed Sniffer files are not supported");
- *err = WTAP_ERR_UNSUPPORTED;
- return -1;
- }
+ /* Check the data link type.
+ If "version.network" is 7, that's "Internetwork analyzer";
+ Sniffers appear to write out LAPB, LAPD and PPP captures
+ (and perhaps other types of captures) in that fashion,
+ and, so far, the only way we know of distinguishing them
+ is to look at the first byte of the packet - if it's 0xFF,
+ it's PPP, otherwise if it's odd, it's LAPB else it's LAPD.
+ Therefore, we treat it as WTAP_ENCAP_UNKNOWN for now, but
+ don't treat that as an error.
- /* Check the data link type */
+ In one PPP capture, the two 16-bit words of the "rsvd" field
+ were 1 and 3, respectively, and in one X.25 capture, they
+ were both 0. That's too small a sample from which to
+ conclude anything, however.... */
if (version.network >= NUM_NGSNIFF_ENCAPS
- || sniffer_encap[version.network] == WTAP_ENCAP_UNKNOWN) {
+ || (sniffer_encap[version.network] == WTAP_ENCAP_UNKNOWN
+ && version.network != 7)) {
g_message("ngsniffer: network type %u unknown or unsupported",
version.network);
- *err = WTAP_ERR_UNSUPPORTED;
+ *err = WTAP_ERR_UNSUPPORTED_ENCAP;
return -1;
}
return -1;
}
+ /* compressed or uncompressed Sniffer file? */
+ if (version.format != 1) {
+ wth->file_type = WTAP_FILE_NGSNIFFER_COMPRESSED;
+
+ } else {
+ wth->file_type = WTAP_FILE_NGSNIFFER_UNCOMPRESSED;
+ }
+
+ /* 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.
+ */
+ if (skip_header_records(wth, err, version.maj_vers) < 0)
+ return -1;
+
+ /*
+ * 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, wth->data_offset, SEEK_SET, err) == -1)
+ return -1;
+ }
+
/* This is a ngsniffer file */
- wth->file_type = WTAP_FILE_NGSNIFFER;
wth->capture.ngsniffer = g_malloc(sizeof(ngsniffer_t));
+
+ /* We haven't allocated any uncompression buffers yet. */
+ wth->capture.ngsniffer->seq.buf = NULL;
+ wth->capture.ngsniffer->rand.buf = NULL;
+
+ /* Set the current file offset; the offset in the compressed file
+ and in the uncompressed data stream currently the same. */
+ wth->capture.ngsniffer->seq.uncomp_offset = wth->data_offset;
+ wth->capture.ngsniffer->seq.comp_offset = wth->data_offset;
+ wth->capture.ngsniffer->rand.uncomp_offset = wth->data_offset;
+ wth->capture.ngsniffer->rand.comp_offset = wth->data_offset;
+
+ /* We don't yet have any list of compressed blobs. */
+ wth->capture.ngsniffer->first_blob = NULL;
+ wth->capture.ngsniffer->last_blob = NULL;
+ wth->capture.ngsniffer->current_blob = NULL;
+
wth->subtype_read = ngsniffer_read;
- wth->snapshot_length = 16384; /* not available in header, only in frame */
+ 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 */
wth->capture.ngsniffer->timeunit = Usec[version.timeunit];
- wth->file_encap = sniffer_encap[version.network];
wth->capture.ngsniffer->is_atm =
(wth->file_encap == WTAP_ENCAP_ATM_SNIFFER);
return 1;
}
-/* Read the next packet */
-static int ngsniffer_read(wtap *wth, int *err)
+static int
+skip_header_records(wtap *wth, int *err, gint16 version)
{
- int bytes_read;
- char record_type[2];
- char record_length[4]; /* only 1st 2 bytes are length */
- guint16 type, length;
- struct frame2_rec frame2;
- struct frame4_rec frame4;
- double t;
- guint16 time_low, time_med, time_high, true_size, size;
- int data_offset;
+ int bytes_read;
+ 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, length;
+ int bytes_to_read;
+ unsigned char buffer[32];
for (;;) {
- /*
- * Read the record header.
- */
errno = WTAP_ERR_CANT_READ;
bytes_read = file_read(record_type, 1, 2, wth->fh);
if (bytes_read != 2) {
- if (file_error(wth->fh)) {
- *err = errno;
+ *err = file_error(wth->fh);
+ if (*err != 0)
return -1;
- }
if (bytes_read != 0) {
*err = WTAP_ERR_SHORT_READ;
return -1;
}
+ return 0; /* EOF */
+ }
+
+ type = pletohs(record_type);
+ if ((type != REC_HEADER1) && (type != REC_HEADER2)
+ && (type != REC_HEADER3) && (type != REC_HEADER4)
+ && (type != REC_HEADER5) && (type != REC_HEADER6)
+ && (type != REC_HEADER7)
+ && ((type != REC_V2DESC) || (version > 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;
}
- wth->data_offset += 2;
+
errno = WTAP_ERR_CANT_READ;
bytes_read = file_read(record_length, 1, 4, wth->fh);
if (bytes_read != 4) {
- if (file_error(wth->fh))
- *err = errno;
- else
+ *err = file_error(wth->fh);
+ if (*err == 0)
*err = WTAP_ERR_SHORT_READ;
return -1;
}
- wth->data_offset += 4;
+ wth->data_offset += 6;
- type = pletohs(record_type);
length = pletohs(record_length);
+ /*
+ * Is this a REC_HEADER2 record, and do we not yet know
+ * the encapsulation type (i.e., is this is an
+ * "Internetwork analyzer" capture?
+ *
+ * If so, the 5th byte of the record appears to specify
+ * the particular type of network we're on.
+ */
+ if (type == REC_HEADER2 &&
+ wth->file_encap == WTAP_ENCAP_UNKNOWN) {
+ /*
+ * Yes, get the first 32 bytes of the record
+ * data.
+ */
+ bytes_to_read = MIN(length, sizeof buffer);
+ bytes_read = file_read(buffer, 1, bytes_to_read,
+ wth->fh);
+ if (bytes_read != bytes_to_read) {
+ *err = file_error(wth->fh);
+ if (*err == 0) {
+ *err = WTAP_ERR_SHORT_READ;
+ return -1;
+ }
+ }
+ /*
+ * Skip the rest of the record.
+ */
+ if (length > sizeof buffer) {
+ if (file_seek(wth->fh, length - sizeof buffer,
+ SEEK_CUR, err) == -1)
+ return -1;
+ }
+
+ /*
+ * XXX - what about LAPB and LAPD? At least one
+ * X.25 capture has a type of NET_HDLC, but one
+ * might also consider LAPD to be an HDLC
+ * variant; if it also has a type of NET_HDLC,
+ * we'd have to look at some other data to
+ * distinguish them.
+ *
+ * I have no LAPD captures, so I can't check
+ * various fields of this record (and I'd
+ * need multiple captures of both LAPB/X.25
+ * and LAPD/ISDN to be reasonable certain
+ * where the magic key is).
+ *
+ * So, for now, we don't set the encapsulation
+ * for NET_HDLC.
+ */
+ switch (buffer[4]) {
+
+ case NET_FRAME_RELAY:
+ wth->file_encap = WTAP_ENCAP_FRELAY;
+ break;
+
+ case NET_PPP:
+ wth->file_encap = WTAP_ENCAP_PPP;
+ break;
+ }
+
+ } else {
+ /* Nope, just skip over the data. */
+ if (file_seek(wth->fh, length, SEEK_CUR, err) == -1)
+ return -1;
+ }
+ wth->data_offset += length;
+ }
+}
+
+/* Read the next packet */
+static gboolean ngsniffer_read(wtap *wth, int *err, long *data_offset)
+{
+ int ret;
+ guint16 type, length;
+ struct frame2_rec frame2;
+ struct frame4_rec frame4;
+ struct frame6_rec frame6;
+ double t;
+ guint16 time_low, time_med, time_high, true_size, size;
+ guchar *pd;
+
+ for (;;) {
+ /*
+ * Read the record header.
+ */
+ *data_offset = wth->data_offset;
+ ret = ngsniffer_read_rec_header(wth, FALSE, &type, &length,
+ err);
+ if (ret <= 0) {
+ /* Read error or EOF */
+ return FALSE;
+ }
+ wth->data_offset += 6;
+
switch (type) {
case REC_FRAME2:
*/
g_message("ngsniffer: REC_FRAME2 record in an ATM Sniffer file");
*err = WTAP_ERR_BAD_RECORD;
- return -1;
+ return FALSE;
}
/* Read the f_frame2_struct */
- errno = WTAP_ERR_CANT_READ;
- bytes_read = file_read(&frame2, 1, sizeof frame2, wth->fh);
- if (bytes_read != sizeof frame2) {
- if (file_error(wth->fh))
- *err = errno;
- else
- *err = WTAP_ERR_SHORT_READ;
- return -1;
+ if (!ngsniffer_read_frame2(wth, FALSE, &frame2, err)) {
+ /* Read error */
+ return FALSE;
}
wth->data_offset += sizeof frame2;
time_low = pletohs(&frame2.time_low);
t = (double)time_low+(double)(time_med)*65536.0 +
(double)time_high*4294967296.0;
- wth->phdr.pseudo_header.x25.flags = frame2.fs & 0x80;
-
+ set_pseudo_header_frame2(&wth->pseudo_header, &frame2);
goto found;
case REC_FRAME4:
*/
g_message("ngsniffer: REC_FRAME4 record in a non-ATM Sniffer file");
*err = WTAP_ERR_BAD_RECORD;
- return -1;
+ return FALSE;
}
/* Read the f_frame4_struct */
- errno = WTAP_ERR_CANT_READ;
- bytes_read = file_read(&frame4, 1, sizeof frame4, wth->fh);
- if (bytes_read != sizeof frame4) {
- if (file_error(wth->fh))
- *err = errno;
- else
- *err = WTAP_ERR_SHORT_READ;
- return -1;
+ if (!ngsniffer_read_frame4(wth, FALSE, &frame4, err)) {
+ /* Read error */
+ return FALSE;
}
wth->data_offset += sizeof frame4;
time_low = pletohs(&frame4.time_low);
t = (double)time_low+(double)(time_med)*65536.0 +
(double)time_high*4294967296.0;
- wth->phdr.pseudo_header.ngsniffer_atm.AppTrafType =
- frame4.atm_info.AppTrafType;
- wth->phdr.pseudo_header.ngsniffer_atm.AppHLType =
- frame4.atm_info.AppHLType;
- wth->phdr.pseudo_header.ngsniffer_atm.Vpi =
- pletohs(&frame4.atm_info.Vpi);
- wth->phdr.pseudo_header.ngsniffer_atm.Vci =
- pletohs(&frame4.atm_info.Vci);
- wth->phdr.pseudo_header.ngsniffer_atm.channel =
- pletohs(&frame4.atm_info.channel);
- wth->phdr.pseudo_header.ngsniffer_atm.cells =
- pletohs(&frame4.atm_info.cells);
- wth->phdr.pseudo_header.ngsniffer_atm.aal5t_u2u =
- pletohs(&frame4.atm_info.Trailer.aal5t_u2u);
- wth->phdr.pseudo_header.ngsniffer_atm.aal5t_len =
- pletohs(&frame4.atm_info.Trailer.aal5t_len);
- wth->phdr.pseudo_header.ngsniffer_atm.aal5t_chksum =
- pletohl(&frame4.atm_info.Trailer.aal5t_chksum);
+ set_pseudo_header_frame4(&wth->pseudo_header, &frame4);
+ goto found;
+
+ case REC_FRAME6:
+ /* XXX - Is this test valid? */
+ if (wth->capture.ngsniffer->is_atm) {
+ g_message("ngsniffer: REC_FRAME6 record in an ATM Sniffer file");
+ *err = WTAP_ERR_BAD_RECORD;
+ return FALSE;
+ }
+
+ /* Read the f_frame6_struct */
+ if (!ngsniffer_read_frame6(wth, FALSE, &frame6, err)) {
+ /* Read error */
+ return FALSE;
+ }
+ wth->data_offset += sizeof frame6;
+ time_low = pletohs(&frame6.time_low);
+ time_med = pletohs(&frame6.time_med);
+ time_high = frame6.time_high;
+ size = pletohs(&frame6.size);
+ true_size = pletohs(&frame6.true_size);
+
+ length -= sizeof frame6; /* we already read that much */
+
+ /*
+ * XXX - use the "time_day" field? Is that for captures
+ * that take a *really* long time?
+ */
+ t = (double)time_low+(double)(time_med)*65536.0 +
+ (double)time_high*4294967296.0;
+
+ set_pseudo_header_frame6(&wth->pseudo_header, &frame6);
goto found;
case REC_EOF:
/*
* End of file. Return an EOF indication.
*/
- return 0;
+ *err = 0; /* EOF, not error */
+ return FALSE;
default:
break; /* unknown type, skip it */
* it is but can't handle it. Skip past the data
* portion, and keep looping.
*/
- file_seek(wth->fh, length, SEEK_CUR);
+ if (ng_file_seek_seq(wth, length, SEEK_CUR, err) == -1)
+ return FALSE;
wth->data_offset += length;
}
found:
+ /*
+ * OK, is the frame data size greater than than what's left of the
+ * record?
+ */
+ if (size > length) {
+ /*
+ * Yes - treat this as an error.
+ */
+ g_message("ngsniffer: Record length is less than packet size");
+ *err = WTAP_ERR_BAD_RECORD;
+ return FALSE;
+ }
+
wth->phdr.len = true_size ? true_size : size;
wth->phdr.caplen = size;
* Read the packet data.
*/
buffer_assure_space(wth->frame_buffer, length);
- data_offset = wth->data_offset;
- errno = WTAP_ERR_CANT_READ;
- bytes_read = file_read(buffer_start_ptr(wth->frame_buffer), 1,
- length, wth->fh);
+ pd = buffer_start_ptr(wth->frame_buffer);
+ if (!ngsniffer_read_rec_data(wth, FALSE, pd, length, err))
+ return FALSE; /* Read error */
+ wth->data_offset += length;
- if (bytes_read != length) {
- if (file_error(wth->fh))
- *err = errno;
- else
- *err = WTAP_ERR_SHORT_READ;
- return -1;
+ if (wth->file_encap == WTAP_ENCAP_UNKNOWN) {
+ /*
+ * OK, this 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 byte of the packet,
+ * and figure out whether it's LAPB, LAPD, PPP, or
+ * Frame Relay.
+ */
+ if (pd[0] == 0xFF) {
+ /*
+ * PPP.
+ */
+ wth->file_encap = WTAP_ENCAP_PPP;
+ } else if (pd[0] == 0x34 || pd[0] == 0x28) {
+ /*
+ * Frame Relay.
+ */
+ wth->file_encap = WTAP_ENCAP_FRELAY;
+ } else if (pd[0] & 1) {
+ /*
+ * LAPB.
+ */
+ wth->file_encap = WTAP_ENCAP_LAPB;
+ } else {
+ /*
+ * LAPD.
+ */
+ wth->file_encap = WTAP_ENCAP_LAPD;
+ }
}
- wth->data_offset += length;
+
+ /*
+ * Fix up the pseudo-header; we may have set "x25.flags",
+ * but, for some traffic, we should set "p2p.sent" instead.
+ */
+ fix_pseudo_header(wth, &wth->pseudo_header);
t = t/1000000.0 * wth->capture.ngsniffer->timeunit; /* t = # of secs */
t += wth->capture.ngsniffer->start;
wth->phdr.ts.tv_usec = (unsigned long)((t-(double)(wth->phdr.ts.tv_sec))
*1.0e6);
wth->phdr.pkt_encap = wth->file_encap;
- return data_offset;
+ return TRUE;
+}
+
+static gboolean ngsniffer_seek_read(wtap *wth, long seek_off,
+ union wtap_pseudo_header *pseudo_header, guchar *pd, int packet_size,
+ int *err)
+{
+ int ret;
+ guint16 type, length;
+ struct frame2_rec frame2;
+ struct frame4_rec frame4;
+ struct frame6_rec frame6;
+
+ if (ng_file_seek_rand(wth, seek_off, SEEK_SET, err) == -1)
+ return FALSE;
+
+ ret = ngsniffer_read_rec_header(wth, TRUE, &type, &length, err);
+ if (ret <= 0) {
+ /* Read error or EOF */
+ if (ret == 0) {
+ /* EOF means "short read" in random-access mode */
+ *err = WTAP_ERR_SHORT_READ;
+ }
+ return FALSE;
+ }
+
+ switch (type) {
+
+ case REC_FRAME2:
+ /* Read the f_frame2_struct */
+ if (!ngsniffer_read_frame2(wth, TRUE, &frame2, err)) {
+ /* Read error */
+ return FALSE;
+ }
+
+ length -= sizeof frame2; /* we already read that much */
+
+ set_pseudo_header_frame2(pseudo_header, &frame2);
+ break;
+
+ case REC_FRAME4:
+ /* Read the f_frame4_struct */
+ if (!ngsniffer_read_frame4(wth, TRUE, &frame4, err)) {
+ /* Read error */
+ return FALSE;
+ }
+
+ length -= sizeof frame4; /* we already read that much */
+
+ set_pseudo_header_frame4(pseudo_header, &frame4);
+ break;
+
+ case REC_FRAME6:
+ /* Read the f_frame6_struct */
+ if (!ngsniffer_read_frame6(wth, TRUE, &frame6, err)) {
+ /* Read error */
+ return FALSE;
+ }
+
+ length -= sizeof frame6; /* we already read that much */
+
+ set_pseudo_header_frame6(pseudo_header, &frame6);
+ break;
+
+ default:
+ /*
+ * "Can't happen".
+ */
+ g_assert_not_reached();
+ return FALSE;
+ }
+
+ /*
+ * Fix up the pseudo-header; we may have set "x25.flags",
+ * but, for some traffic, we should set "p2p.sent" instead.
+ */
+ fix_pseudo_header(wth, pseudo_header);
+
+ /*
+ * Got the pseudo-header (if any), now get the data.
+ */
+ return ngsniffer_read_rec_data(wth, TRUE, pd, packet_size, err);
+}
+
+static int ngsniffer_read_rec_header(wtap *wth, gboolean is_random,
+ guint16 *typep, guint16 *lengthp, int *err)
+{
+ int bytes_read;
+ char record_type[2];
+ char record_length[4]; /* only 1st 2 bytes are length */
+
+ /*
+ * Read the record header.
+ */
+ bytes_read = ng_file_read(record_type, 1, 2, wth, is_random, err);
+ if (bytes_read != 2) {
+ if (*err != 0)
+ return -1;
+ if (bytes_read != 0) {
+ *err = WTAP_ERR_SHORT_READ;
+ return -1;
+ }
+ return 0;
+ }
+ bytes_read = ng_file_read(record_length, 1, 4, wth, is_random, err);
+ if (bytes_read != 4) {
+ if (*err == 0)
+ *err = WTAP_ERR_SHORT_READ;
+ return -1;
+ }
+
+ *typep = pletohs(record_type);
+ *lengthp = pletohs(record_length);
+ return 1; /* success */
+}
+
+static gboolean ngsniffer_read_frame2(wtap *wth, gboolean is_random,
+ struct frame2_rec *frame2, int *err)
+{
+ int bytes_read;
+
+ /* Read the f_frame2_struct */
+ bytes_read = ng_file_read(frame2, 1, sizeof *frame2, wth, is_random,
+ err);
+ if (bytes_read != sizeof *frame2) {
+ if (*err == 0)
+ *err = WTAP_ERR_SHORT_READ;
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static void set_pseudo_header_frame2(union wtap_pseudo_header *pseudo_header,
+ struct frame2_rec *frame2)
+{
+ /*
+ * In one PPP "Internetwork analyzer" capture,
+ * the only bit seen in "fs" is the 0x80 bit,
+ * which probably indicates the packet's
+ * direction; all other bits were zero.
+ * All bits in "frame2.flags" were zero.
+ *
+ * In one X.25 "Interenetwork analyzer" capture,
+ * the only bit seen in "fs" is the 0x80 bit,
+ * which probably indicates the packet's
+ * direction; all other bits were zero.
+ * "frame2.flags" was always 0x18.
+ *
+ * In one Ethernet capture, "fs" was always 0,
+ * and "flags" was either 0 or 0x18, with no
+ * obvious correlation with anything.
+ *
+ * In one Token Ring capture, "fs" was either 0
+ * or 0xcc, and "flags" was either 0 or 0x18,
+ * with no obvious correlation with anything.
+ */
+ pseudo_header->x25.flags = (frame2->fs & 0x80) ? 0x00 : FROM_DCE;
+}
+
+static gboolean ngsniffer_read_frame4(wtap *wth, gboolean is_random,
+ struct frame4_rec *frame4, int *err)
+{
+ int bytes_read;
+
+ /* Read the f_frame4_struct */
+ bytes_read = ng_file_read(frame4, 1, sizeof *frame4, wth, is_random,
+ err);
+ if (bytes_read != sizeof *frame4) {
+ if (*err == 0)
+ *err = WTAP_ERR_SHORT_READ;
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static void set_pseudo_header_frame4(union wtap_pseudo_header *pseudo_header,
+ struct frame4_rec *frame4)
+{
+ guint8 aal_type, hl_type;
+ guint16 vpi, vci;
+
+ aal_type = frame4->atm_info.AppTrafType & ATT_AALTYPE;
+ hl_type = frame4->atm_info.AppTrafType & ATT_HLTYPE;
+ vpi = pletohs(&frame4->atm_info.Vpi);
+ vci = pletohs(&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 = pletohs(&frame4->atm_info.channel);
+ pseudo_header->atm.cells = pletohs(&frame4->atm_info.cells);
+ pseudo_header->atm.aal5t_u2u = pletohs(&frame4->atm_info.Trailer.aal5t_u2u);
+ pseudo_header->atm.aal5t_len = pletohs(&frame4->atm_info.Trailer.aal5t_len);
+ pseudo_header->atm.aal5t_chksum = pletohl(&frame4->atm_info.Trailer.aal5t_chksum);
+}
+
+static gboolean ngsniffer_read_frame6(wtap *wth, gboolean is_random,
+ struct frame6_rec *frame6, int *err)
+{
+ int bytes_read;
+
+ /* Read the f_frame6_struct */
+ bytes_read = ng_file_read(frame6, 1, sizeof *frame6, wth, is_random,
+ err);
+ if (bytes_read != sizeof *frame6) {
+ if (*err == 0)
+ *err = WTAP_ERR_SHORT_READ;
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static void set_pseudo_header_frame6(
+ union wtap_pseudo_header *pseudo_header _U_,
+ struct frame6_rec *frame6 _U_)
+{
+ /* XXX - Once the frame format is divined, something will most likely go here */
+}
+
+static gboolean ngsniffer_read_rec_data(wtap *wth, gboolean is_random,
+ guchar *pd, int length, int *err)
+{
+ int bytes_read;
+
+ bytes_read = ng_file_read(pd, 1, length, wth, is_random, err);
+
+ if (bytes_read != length) {
+ if (*err == 0)
+ *err = WTAP_ERR_SHORT_READ;
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static void fix_pseudo_header(wtap *wth,
+ union wtap_pseudo_header *pseudo_header)
+{
+ switch (wth->file_encap) {
+
+ case WTAP_ENCAP_LAPD:
+ if (pseudo_header->x25.flags == 0x00)
+ pseudo_header->p2p.sent = TRUE;
+ else
+ pseudo_header->p2p.sent = FALSE;
+ break;
+ }
+}
+
+/* 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)
+{
+ if (wth->capture.ngsniffer->seq.buf != NULL) {
+ g_free(wth->capture.ngsniffer->seq.buf);
+ wth->capture.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)
+{
+ if (wth->capture.ngsniffer->rand.buf != NULL)
+ g_free(wth->capture.ngsniffer->rand.buf);
+ if (wth->capture.ngsniffer->first_blob != NULL) {
+ g_list_foreach(wth->capture.ngsniffer->first_blob, free_blob, NULL);
+ g_list_free(wth->capture.ngsniffer->first_blob);
+ }
+ g_free(wth->capture.ngsniffer);
+}
+
+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_ATM_RFC1483 */
+ -1, /* WTAP_ENCAP_LINUX_ATM_CLIP */
+ 7, /* WTAP_ENCAP_LAPB -> Internetwork analyzer (synchronous) */
+ -1, /* WTAP_ENCAP_ATM_SNIFFER */
+ -1 /* WTAP_ENCAP_NULL -> unsupported */
+};
+#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. */
+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_UNSUPPORTED_ENCAP;
+
+ return 0;
+}
+
+/* Returns TRUE on success, FALSE on failure; sets "*err" to an error code on
+ failure */
+gboolean ngsniffer_dump_open(wtap_dumper *wdh, gboolean cant_seek _U_, int *err)
+{
+ size_t nwritten;
+ char buf[6] = {REC_VERS, 0x00, 0x12, 0x00, 0x00, 0x00}; /* version record */
+
+ /* This is a sniffer file */
+ wdh->subtype_write = ngsniffer_dump;
+ wdh->subtype_close = ngsniffer_dump_close;
+
+ wdh->dump.ngsniffer = g_malloc(sizeof(ngsniffer_dump_t));
+ wdh->dump.ngsniffer->first_frame = TRUE;
+ wdh->dump.ngsniffer->start = 0;
+
+ /* Write the file header. */
+ nwritten = fwrite(ngsniffer_magic, 1, sizeof ngsniffer_magic, wdh->fh);
+ if (nwritten != sizeof ngsniffer_magic) {
+ if (nwritten == 0 && ferror(wdh->fh))
+ *err = errno;
+ else
+ *err = WTAP_ERR_SHORT_WRITE;
+ return FALSE;
+ }
+ nwritten = fwrite(buf, 1, 6, wdh->fh);
+ if (nwritten != 6) {
+ if (nwritten == 0 && ferror(wdh->fh))
+ *err = errno;
+ else
+ *err = WTAP_ERR_SHORT_WRITE;
+ 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 struct wtap_pkthdr *phdr,
+ const union wtap_pseudo_header *pseudo_header, const guchar *pd, int *err)
+{
+ ngsniffer_dump_t *priv = wdh->dump.ngsniffer;
+ struct frame2_rec rec_hdr;
+ size_t nwritten;
+ char buf[6];
+ double t;
+ guint16 t_low, t_med, t_high;
+ struct vers_rec version;
+ gint16 maj_vers, min_vers;
+ guint16 start_date;
+ struct tm *tm;
+
+ /* 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 (priv->first_frame) {
+ priv->first_frame=FALSE;
+ tm = localtime(&phdr->ts.tv_sec);
+ if (tm != NULL) {
+ start_date = (tm->tm_year - (1980 - 1900)) << 9;
+ start_date |= (tm->tm_mon + 1) << 5;
+ start_date |= tm->tm_mday;
+ /* record the start date, not the start time */
+ priv->start = phdr->ts.tv_sec - (3600*tm->tm_hour + 60*tm->tm_min + tm->tm_sec);
+ } else {
+ start_date = 0;
+ priv->start = 0;
+ }
+
+ /* "sniffer" version ? */
+ maj_vers = 4;
+ min_vers = 0;
+ version.maj_vers = htoles(maj_vers);
+ version.min_vers = htoles(min_vers);
+ version.time = 0;
+ version.date = htoles(start_date);
+ version.type = 4;
+ version.network = wtap_encap[wdh->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;
+ nwritten = fwrite(&version, 1, sizeof version, wdh->fh);
+ if (nwritten != sizeof version) {
+ if (nwritten == 0 && ferror(wdh->fh))
+ *err = errno;
+ else
+ *err = WTAP_ERR_SHORT_WRITE;
+ return FALSE;
+ }
+ }
+
+ buf[0] = REC_FRAME2;
+ buf[1] = 0x00;
+ buf[2] = (char)((phdr->caplen + sizeof(struct frame2_rec))%256);
+ buf[3] = (char)((phdr->caplen + sizeof(struct frame2_rec))/256);
+ buf[4] = 0x00;
+ buf[5] = 0x00;
+ nwritten = fwrite(buf, 1, 6, wdh->fh);
+ if (nwritten != 6) {
+ if (nwritten == 0 && ferror(wdh->fh))
+ *err = errno;
+ else
+ *err = WTAP_ERR_SHORT_WRITE;
+ return FALSE;
+ }
+ t = (double)phdr->ts.tv_sec + (double)phdr->ts.tv_usec/1.0e6; /* # of secs */
+ t = (t - priv->start)*1.0e6 / Usec[1]; /* timeunit = 1 */
+ t_low = (guint16)(t-(double)((guint32)(t/65536.0))*65536.0);
+ t_med = (guint16)((guint32)(t/65536.0) % 65536);
+ t_high = (guint16)(t/4294967296.0);
+ rec_hdr.time_low = htoles(t_low);
+ rec_hdr.time_med = htoles(t_med);
+ rec_hdr.time_high = htoles(t_high);
+ rec_hdr.size = htoles(phdr->caplen);
+ if (wdh->encap == WTAP_ENCAP_LAPB || wdh->encap == WTAP_ENCAP_PPP)
+ rec_hdr.fs = (pseudo_header->x25.flags & FROM_DCE) ? 0x00 : 0x80;
+ else
+ rec_hdr.fs = 0;
+ rec_hdr.flags = 0;
+ rec_hdr.true_size = phdr->len != phdr->caplen ? htoles(phdr->len) : 0;
+ rec_hdr.rsvd = 0;
+ nwritten = fwrite(&rec_hdr, 1, sizeof rec_hdr, wdh->fh);
+ if (nwritten != sizeof rec_hdr) {
+ if (nwritten == 0 && ferror(wdh->fh))
+ *err = errno;
+ else
+ *err = WTAP_ERR_SHORT_WRITE;
+ return FALSE;
+ }
+ nwritten = fwrite(pd, 1, phdr->caplen, wdh->fh);
+ if (nwritten != phdr->caplen) {
+ if (nwritten == 0 && ferror(wdh->fh))
+ *err = errno;
+ else
+ *err = WTAP_ERR_SHORT_WRITE;
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/* Finish writing to a dump file.
+ Returns TRUE on success, FALSE on failure. */
+static gboolean ngsniffer_dump_close(wtap_dumper *wdh, int *err)
+{
+ /* EOF record */
+ char buf[6] = {REC_EOF, 0x00, 0x00, 0x00, 0x00, 0x00};
+ size_t nwritten;
+
+ nwritten = fwrite(buf, 1, 6, wdh->fh);
+ if (nwritten != 6) {
+ if (err != NULL) {
+ if (nwritten == 0 && ferror(wdh->fh))
+ *err = errno;
+ else
+ *err = WTAP_ERR_SHORT_WRITE;
+ }
+ 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
+ outbuf - decompressed contents, could contain a partial Sniffer
+ record at the end.
+ outlen - length of outbuf.
+
+ Return value is the number of bytes in outbuf on return.
+*/
+static int
+SnifferDecompress( unsigned char * inbuf, size_t inlen,
+ unsigned char * outbuf, size_t outlen, int *err )
+{
+ 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 */
+
+ bit_mask = 0; /* don't have any bits yet */
+ while (1)
+ {
+ /* Shift down the bit mask we use to see whats encoded */
+ bit_mask = bit_mask >> 1;
+
+ /* If there are no bits left, time to get another 16 bits */
+ if ( 0 == bit_mask )
+ {
+ bit_mask = 0x8000; /* start with the high bit */
+ bit_value = pletohs(pin); /* get the next 16 bits */
+ pin += 2; /* skip over what we just grabbed */
+ if ( pin >= pin_end )
+ {
+ *err = WTAP_ERR_UNC_TRUNCATED; /* data was oddly truncated */
+ return ( -1 );
+ }
+ }
+
+ /* 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 */
+ *(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 */
+ if ( pin >= pin_end )
+ {
+ *err = WTAP_ERR_UNC_TRUNCATED; /* data was oddly truncated */
+ return ( -1 );
+ }
+
+ /* 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;
+ /* If length would put us past end of output, avoid overflow */
+ if ( pout + length > pout_end )
+ {
+ *err = WTAP_ERR_UNC_OVERFLOW;
+ return ( -1 );
+ }
+
+ /* generate the repeated series of bytes */
+ memset( pout, *pin++, length );
+ pout += 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.
+ */
+ length = code_low + ((unsigned int)(*pin++) << 4) + 19;
+ /* If we are already at end of input, there is no byte
+ to repeat */
+ if ( pin >= pin_end )
+ {
+ *err = WTAP_ERR_UNC_TRUNCATED; /* data was oddly truncated */
+ return ( -1 );
+ }
+ /* If length would put us past end of output, avoid overflow */
+ if ( pout + length > pout_end )
+ {
+ *err = WTAP_ERR_UNC_OVERFLOW;
+ return ( -1 );
+ }
+
+ /* generate the repeated series of bytes */
+ memset( pout, *pin++, length );
+ pout += 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.
+ */
+ offset = code_low + ((unsigned int)(*pin++) << 4) + 3;
+ /* If we are already at end of input, there is no byte
+ to repeat */
+ if ( pin >= pin_end )
+ {
+ *err = WTAP_ERR_UNC_TRUNCATED; /* data was oddly truncated */
+ return ( -1 );
+ }
+ /* Check if offset would put us back past begin of buffer */
+ if ( pout - offset < outbuf )
+ {
+ *err = WTAP_ERR_UNC_BAD_OFFSET;
+ return ( -1 );
+ }
+
+ /* get length from next byte, make sure it won't overrun buf */
+ length = (unsigned int)(*pin++) + 16;
+ if ( pout + length > pout_end )
+ {
+ *err = WTAP_ERR_UNC_OVERFLOW;
+ return ( -1 );
+ }
+
+ /* Copy the string from previous text to output position,
+ advance output pointer */
+ memcpy( pout, pout - offset, length );
+ pout += 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.
+ */
+ offset = code_low + ((unsigned int)(*pin++) << 4) + 3;
+ /* Check if offset would put us back past begin of buffer */
+ if ( pout - offset < outbuf )
+ {
+ *err = WTAP_ERR_UNC_BAD_OFFSET;
+ return ( -1 );
+ }
+
+ /* get length from code_type, make sure it won't overrun buf */
+ length = code_type;
+ if ( pout + length > pout_end )
+ {
+ *err = WTAP_ERR_UNC_OVERFLOW;
+ return ( -1 );
+ }
+
+ /* Copy the string from previous text to output position,
+ advance output pointer */
+ memcpy( pout, pout - offset, length );
+ pout += length;
+ break;
+ }
+ }
+
+ /* If we've consumed all the input, we are done */
+ if ( pin >= pin_end )
+ break;
+ }
+
+ return ( pout - outbuf ); /* return length of expanded text */
+}
+
+/*
+ * XXX - is there any guarantee that this is big enough to hold the
+ * uncompressed data from any blob?
+ */
+#define OUTBUF_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 {
+ long blob_comp_offset;
+ long blob_uncomp_offset;
+} blob_info_t;
+
+static int
+ng_file_read(void *buffer, size_t elementsize, size_t numelements, wtap *wth,
+ gboolean is_random, int *err)
+{
+ FILE_T infile;
+ ngsniffer_comp_stream_t *comp_stream;
+ int copybytes = elementsize * numelements; /* bytes left to be copied */
+ int copied_bytes = 0; /* bytes already copied */
+ unsigned char *outbuffer = buffer; /* where to write next decompressed data */
+ blob_info_t *blob;
+ int bytes_to_copy;
+ int bytes_left;
+
+ if (is_random) {
+ infile = wth->random_fh;
+ comp_stream = &wth->capture.ngsniffer->rand;
+ } else {
+ infile = wth->fh;
+ comp_stream = &wth->capture.ngsniffer->seq;
+ }
+
+ if (wth->file_type == WTAP_FILE_NGSNIFFER_UNCOMPRESSED) {
+ errno = WTAP_ERR_CANT_READ;
+ copied_bytes = file_read(buffer, 1, copybytes, infile);
+ if (copied_bytes != copybytes)
+ *err = file_error(infile);
+ return copied_bytes;
+ }
+
+ /* Allocate the stream buffer if it hasn't already been allocated. */
+ if (comp_stream->buf == NULL) {
+ comp_stream->buf = 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. */
+ wth->capture.ngsniffer->current_blob =
+ wth->capture.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) {
+ g_assert(wth->capture.ngsniffer->first_blob == NULL);
+ blob = g_malloc(sizeof (blob_info_t));
+ blob->blob_comp_offset = comp_stream->comp_offset;
+ blob->blob_uncomp_offset = comp_stream->uncomp_offset;
+ wth->capture.ngsniffer->first_blob =
+ g_list_append(wth->capture.ngsniffer->first_blob, blob);
+ wth->capture.ngsniffer->last_blob =
+ wth->capture.ngsniffer->first_blob;
+ }
+ }
+
+ /* Now read the first blob into the buffer. */
+ if (read_blob(infile, comp_stream, err) < 0)
+ return -1;
+ }
+ while (copybytes > 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. */
+ wth->capture.ngsniffer->current_blob =
+ g_list_next(wth->capture.ngsniffer->current_blob);
+ blob = wth->capture.ngsniffer->current_blob->data;
+ } 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_malloc(sizeof (blob_info_t));
+ blob->blob_comp_offset = comp_stream->comp_offset;
+ blob->blob_uncomp_offset = comp_stream->uncomp_offset;
+ wth->capture.ngsniffer->last_blob =
+ g_list_append(wth->capture.ngsniffer->last_blob, blob);
+ }
+ }
+
+ if (read_blob(infile, comp_stream, err) < 0)
+ return -1;
+ bytes_left = comp_stream->nbytes - comp_stream->nextout;
+ }
+
+ bytes_to_copy = copybytes;
+ if (bytes_to_copy > bytes_left)
+ bytes_to_copy = bytes_left;
+ memcpy(outbuffer, &comp_stream->buf[comp_stream->nextout],
+ bytes_to_copy);
+ copybytes -= bytes_to_copy;
+ copied_bytes += bytes_to_copy;
+ outbuffer += bytes_to_copy;
+ comp_stream->nextout += bytes_to_copy;
+ comp_stream->uncomp_offset += bytes_to_copy;
+ }
+ return copied_bytes;
+}
+
+/* Read a blob from a compressed stream.
+ Return -1 and set "*err" on error, otherwise return 0. */
+static int
+read_blob(FILE_T infile, ngsniffer_comp_stream_t *comp_stream, int *err)
+{
+ size_t in_len;
+ size_t read_len;
+ unsigned short blob_len;
+ gint16 blob_len_host;
+ gboolean uncompressed;
+ unsigned char file_inbuf[65536];
+ int out_len;
+
+ /* Read one 16-bit word which is length of next compressed blob */
+ errno = WTAP_ERR_CANT_READ;
+ read_len = file_read(&blob_len, 1, 2, infile);
+ if (2 != read_len) {
+ *err = file_error(infile);
+ return -1;
+ }
+ comp_stream->comp_offset += 2;
+ blob_len_host = pletohs(&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;
+ }
+
+ /* Read the blob */
+ errno = WTAP_ERR_CANT_READ;
+ read_len = file_read(file_inbuf, 1, in_len, infile);
+ if (in_len != read_len) {
+ *err = file_error(infile);
+ return -1;
+ }
+ 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);
+ if (out_len < 0)
+ return -1;
+ }
+ comp_stream->nextout = 0;
+ comp_stream->nbytes = out_len;
+ return 0;
+}
+
+/* Seek in the sequential data stream; we can only seek forward, and we
+ do it on compressed files by skipping forward. */
+static long
+ng_file_seek_seq(wtap *wth, long offset, int whence, int *err)
+{
+ long delta;
+ char buf[65536];
+ long amount_to_read;
+
+ if (wth->file_type == WTAP_FILE_NGSNIFFER_UNCOMPRESSED)
+ return file_seek(wth->fh, offset, whence, err);
+
+ switch (whence) {
+
+ case SEEK_SET:
+ break; /* "offset" is the target offset */
+
+ case SEEK_CUR:
+ offset += wth->capture.ngsniffer->seq.uncomp_offset;
+ break; /* "offset" is relative to the current offset */
+
+ case SEEK_END:
+ g_assert_not_reached(); /* "offset" is relative to the end of the file... */
+ break; /* ...but we don't know where that is. */
+ }
+
+ delta = offset - wth->capture.ngsniffer->seq.uncomp_offset;
+ g_assert(delta >= 0);
+
+ /* Ok, now read and discard "delta" bytes. */
+ while (delta != 0) {
+ amount_to_read = delta;
+ if ((unsigned long)amount_to_read > sizeof buf)
+ amount_to_read = sizeof buf;
+ if (ng_file_read(buf, 1, amount_to_read, wth, FALSE, err) < 0)
+ return -1; /* error */
+ delta -= amount_to_read;
+ }
+ return offset;
+}
+
+/* Seek 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 long
+ng_file_seek_rand(wtap *wth, long offset, int whence, int *err)
+{
+ ngsniffer_t *ngsniffer;
+ long delta;
+ GList *new, *next;
+ blob_info_t *next_blob, *new_blob;
+
+ if (wth->file_type == WTAP_FILE_NGSNIFFER_UNCOMPRESSED)
+ return file_seek(wth->random_fh, offset, whence, err);
+
+ ngsniffer = wth->capture.ngsniffer;
+
+ switch (whence) {
+
+ case SEEK_SET:
+ break; /* "offset" is the target offset */
+
+ case SEEK_CUR:
+ offset += ngsniffer->rand.uncomp_offset;
+ break; /* "offset" is relative to the current offset */
+
+ case SEEK_END:
+ g_assert_not_reached(); /* "offset" is relative to the end of the file... */
+ break; /* ...but we don't know where that is. */
+ }
+
+ 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 = 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, starting with the blob
+ following the current blob. */
+ new = g_list_next(ngsniffer->current_blob);
+ for (;;) {
+ next = g_list_next(new);
+ if (next == NULL) {
+ /* No more blobs; the current one is it. */
+ break;
+ }
+
+ next_blob = next->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 = next;
+ }
+ }
+ } 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, starting with the blob
+ preceding the current blob. */
+ new = g_list_previous(ngsniffer->current_blob);
+ for (;;) {
+ /* Does this blob start at or before the target offset?
+ If so, the current blob is the one we want. */
+ new_blob = new->data;
+ if (new_blob->blob_uncomp_offset <= offset)
+ break;
+
+ /* It doesn't - skip to the previous blob. */
+ new = g_list_previous(new);
+ }
+ }
+ }
+
+ if (new != NULL) {
+ /* The place to which we're seeking isn't in the current buffer;
+ move to a new blob. */
+ new_blob = new->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 -1;
+
+ /* Make the blob we found the current one. */
+ ngsniffer->current_blob = new;
+
+ /* 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) < 0)
+ return -1;
+
+ /* 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;
+ g_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 += delta;
+ ngsniffer->rand.uncomp_offset += delta;
+
+ return offset;
}