Removed trailing whitespaces from .h and .c files using the
[obnox/wireshark/wip.git] / wiretap / ngsniffer.c
index 4bd358cc91493d1937c2fce49c2dd2d8d8ba68f3..8d45b07666049efe0c3c733512a2ae399a7b289c 100644 (file)
@@ -1,24 +1,23 @@
 /* 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 */
@@ -89,7 +105,7 @@ struct vers_rec {
        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 */
@@ -235,56 +251,114 @@ struct frame4_rec {
        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;
        }
 
@@ -296,10 +370,9 @@ int ngsniffer_open(wtap *wth, int *err)
        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;
@@ -316,27 +389,33 @@ int ngsniffer_open(wtap *wth, int *err)
        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;
        }
 
@@ -347,13 +426,67 @@ int ngsniffer_open(wtap *wth, int *err)
                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);
 
@@ -391,51 +524,156 @@ int ngsniffer_open(wtap *wth, int *err)
        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:
@@ -446,18 +684,13 @@ static int ngsniffer_read(wtap *wth, int *err)
                                 */
                                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);
@@ -471,8 +704,7 @@ static int ngsniffer_read(wtap *wth, int *err)
                        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:
@@ -483,18 +715,13 @@ static int ngsniffer_read(wtap *wth, int *err)
                                 */
                                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);
@@ -512,31 +739,47 @@ static int ngsniffer_read(wtap *wth, int *err)
                        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 */
@@ -547,11 +790,25 @@ static int ngsniffer_read(wtap *wth, int *err)
                 * 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;
 
@@ -559,19 +816,49 @@ found:
         * 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;
@@ -579,5 +866,1237 @@ found:
        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;
 }