fix doxygen generation
[obnox/wireshark/wip.git] / wiretap / ngsniffer.c
index c0d1d9e6a6ad43cd4f669c4987e79915fd69a730..1d7768250d1a67ecc2886b5388b3fa544b053ecd 100644 (file)
@@ -1,20 +1,20 @@
 /* ngsniffer.c
  *
- * $Id: ngsniffer.c,v 1.75 2002/03/05 05:58:40 guy Exp $
+ * $Id$
  *
  * Wiretap Library
  * 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.
@@ -64,6 +64,7 @@
 #include "wtap-int.h"
 #include "file_wrappers.h"
 #include "buffer.h"
+#include "atm.h"
 #include "ngsniffer.h"
 
 /* Magic number in Sniffer files. */
@@ -114,6 +115,33 @@ struct vers_rec {
 
 /*
  * Sniffer type 2 data record format - followed by frame data.
+ *
+ * The manual at
+ *
+ *     http://download.nai.com/products/media/sniffer/support/sdos/operation.pdf
+ *
+ * documents some of the values used in "fs" and "flags".  "flags" don't
+ * look as if they'd be of much interest to us, as those are internal
+ * flags for state used by the Sniffer, but "fs" gives various status
+ * bits including error indications *and*:
+ *
+ *     ISDN channel information for ISDN;
+ *
+ *     PPP vs. SLIP information for Async.
+ *
+ * In that section it also refers to "FDDI analyzers using the NPI PCI
+ * FDDI adapter" and "FDDI analyzers using the NPI ISA FDDI adapter",
+ * referring to the first as "F1SNIFF" and the second as "FDSNIFF";
+ * those sound as if they *could* be replacements for "TRSNIFF" in
+ * the file header, but that manual says, earlier, that the header
+ * starts with "TRSNIFF data, no matter where the frames were
+ * collected".
+ *
+ * It also says that "time_high" is really "tstamp_high" and "tstamp_day";
+ * did some older manual have it as a 16-bit "tstamp_high", so that perhaps
+ * it depends on the version number in the file, or is it "tstamp_high"
+ * plus "tstamp_day" in all versions?  (I forget whether this came purely
+ * from tcpview, or if I saw any of it in an NAI document.)
  */
 struct frame2_rec {
        guint16 time_low;       /* low part of time stamp */
@@ -129,8 +157,10 @@ struct frame2_rec {
 /*
  * Sniffer type 4 data record format - followed by frame data.
  *
- * XXX - the manual says that the "flags" field holds "buffer flags;
- * BF_xxxx", but doesn't say what the BF_xxxx flags are.
+ * The ATM Sniffer manual says that the "flags" field holds "buffer flags;
+ * BF_xxxx", but doesn't say what the BF_xxxx flags are.  They may
+ * be the same as they are in a type 2 record, in which case they're
+ * probably not of much interest to us.
  *
  * XXX - the manual also says there's an 8-byte "ATMTimeStamp" driver
  * time stamp at the end of "ATMSaveInfo", but, from an ATM Sniffer capture
@@ -144,7 +174,7 @@ struct frame2_rec {
 typedef struct _ATM_AAL5Trailer {
        guint16 aal5t_u2u;      /* user-to-user indicator */
        guint16 aal5t_len;      /* length of the packet */
-       guint32 aal5t_chksum; /* checksum for AAL5 packet */
+       guint32 aal5t_chksum;   /* checksum for AAL5 packet */
 } ATM_AAL5Trailer;
 
 typedef struct _ATMTimeStamp {
@@ -263,53 +293,100 @@ struct frame6_rec {
        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 */
+       guint8  fs;             /* frame error status bits */
+       guint8  flags;          /* buffer flags */
        gint16  true_size;      /* size of original frame, in bytes */
        guint8  chemical_x[22]; /* ? */
 };
 
 /*
- * Network type values in type 7 records.
+ * Network type values in some type 7 records.
+ *
+ * Captures with a major version number of 2 appear to have type 7
+ * records with text in them (at least one I have does).
+ *
+ * Captures with a major version of 4, and at least some captures with
+ * a major version of 5, have type 7 records with those values in the
+ * 5th byte.
+ *
+ * However, some captures with a major version number of 5 appear not to
+ * have type 7 records at all (at least one I have doesn't), but do appear
+ * to put non-zero values in the "rsvd" field of the version header (at
+ * least one I have does) - at least some other captures with smaller version
+ * numbers appear to put 0 there, so *maybe* that's where the network
+ * (sub)type is hidden in those captures.  The version 5 captures I've seen
+ * that *do* have type 7 records put 0 there, so it's not as if *all* V5
+ * captures have something in the "rsvd" field, however.
+ *
+ * The semantics of these network types is inferred from the Sniffer
+ * documentation, as they correspond to types described in the UI;
+ * in particular, see
+ *
+ *     http://download.nai.com/products/media/sniffer/support/sdos/operation.pdf
+ *
+ * starting at page 3-10 (56 of 496).
+ *
+ * XXX - I've seen X.25 captures with NET_ROUTER, and I've seen bridge/
+ * router captures with NET_HDLC.  Sigh....
  */
-#define NET_SDLC       0
-#define NET_HDLC       1
+#define NET_SDLC       0       /* Probably "SDLC then SNA" */
+#define NET_HDLC       1       /* Used for X.25; is it used for other
+                                  things as well, or is it "HDLC then
+                                  X.25", as referred to by the document
+                                  cited above, and only used for X.25? */
 #define NET_FRAME_RELAY        2
-#define NET_ROUTER     3       /* what's this? */
-#define NET_PPP                4
-#define NET_SMDS       5
+#define NET_ROUTER     3       /* Probably "Router/Bridge", for various
+                                  point-to-point protocols for use between
+                                  bridges and routers, including PPP as well
+                                  as various proprietary protocols; also
+                                  used for ISDN, for reasons not obvious
+                                  to me, given that a Sniffer knows
+                                  whether it's using a WAN or an ISDN pod */
+#define NET_PPP                4       /* "Asynchronous", which includes SLIP too */
+#define NET_SMDS       5       /* Not mentioned in the document, but
+                                  that's a document for version 5.50 of
+                                  the Sniffer, and that version might use
+                                  version 5 in the file format and thus
+                                  might not be using type 7 records */
 
 /* values for V.timeunit */
 #define NUM_NGSNIFF_TIMEUNITS 7
 static double Usec[] = { 15.0, 0.838096, 15.0, 0.5, 2.0, 1.0, 0.1 };
 
-static int skip_header_records(wtap *wth, int *err, gint16 version);
-static gboolean ngsniffer_read(wtap *wth, int *err, long *data_offset);
-static int ngsniffer_seek_read(wtap *wth, long seek_off,
-    union wtap_pseudo_header *pseudo_header, u_char *pd, int packet_size,
-    int *err);
+static int process_header_records(wtap *wth, int *err, gchar **err_info,
+    gint16 maj_vers);
+static int process_rec_header2_v2(wtap *wth, unsigned char *buffer,
+    guint16 length, int *err, gchar **err_info);
+static int process_rec_header2_v145(wtap *wth, unsigned char *buffer,
+    guint16 length, gint16 maj_vers, int *err, gchar **err_info);
+static gboolean ngsniffer_read(wtap *wth, int *err, gchar **err_info,
+    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, gchar **err_info);
 static int ngsniffer_read_rec_header(wtap *wth, gboolean is_random,
     guint16 *typep, guint16 *lengthp, int *err);
-static int ngsniffer_read_frame2(wtap *wth, gboolean is_random,
+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 int ngsniffer_read_frame4(wtap *wth, gboolean is_random,
+static void set_pseudo_header_frame2(wtap *wth,
+    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 int ngsniffer_read_frame6(wtap *wth, gboolean is_random,
+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 int ngsniffer_read_rec_data(wtap *wth, gboolean is_random, u_char *pd,
-    int length, int *err);
-static void fix_pseudo_header(wtap *wth,
+static void set_pseudo_header_frame6(wtap *wth,
+    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 int infer_pkt_encap(const guint8 *pd, int len);
+static int fix_pseudo_header(int encap, const guint8 *pd, int len,
     union wtap_pseudo_header *pseudo_header);
 static void ngsniffer_sequential_close(wtap *wth);
 static void ngsniffer_close(wtap *wth);
 static gboolean ngsniffer_dump(wtap_dumper *wdh, const struct wtap_pkthdr *phdr,
-       const union wtap_pseudo_header *pseudo_header, const u_char *pd, int *err);
+       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 );
@@ -320,7 +397,7 @@ static int read_blob(FILE_T infile, ngsniffer_comp_stream_t *comp_stream,
 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 ngsniffer_open(wtap *wth, int *err, gchar **err_info)
 {
        int bytes_read;
        char magic[sizeof ngsniffer_magic];
@@ -329,6 +406,7 @@ int ngsniffer_open(wtap *wth, int *err)
                                  the last 2 are "reserved" and are thrown away */
        guint16 type, length;
        struct vers_rec version;
+       guint16 maj_vers;
        guint16 start_date;
        guint16 start_time;
        static const int sniffer_encap[] = {
@@ -339,10 +417,10 @@ int ngsniffer_open(wtap *wth, int *err)
                WTAP_ENCAP_UNKNOWN,     /* PC Network broadband */
                WTAP_ENCAP_UNKNOWN,     /* LocalTalk */
                WTAP_ENCAP_UNKNOWN,     /* Znet */
-               WTAP_ENCAP_UNKNOWN,     /* Internetwork analyzer (synchronous) */
-               WTAP_ENCAP_UNKNOWN,     /* Internetwork analyzer (asynchronous) */
+               WTAP_ENCAP_PER_PACKET,  /* Internetwork analyzer (synchronous) */
+               WTAP_ENCAP_PER_PACKET,  /* Internetwork analyzer (asynchronous) */
                WTAP_ENCAP_FDDI_BITSWAPPED,
-               WTAP_ENCAP_ATM_SNIFFER
+               WTAP_ENCAP_ATM_PDUS
        };
        #define NUM_NGSNIFF_ENCAPS (sizeof sniffer_encap / sizeof sniffer_encap[0])
        struct tm tm;
@@ -381,8 +459,8 @@ int ngsniffer_open(wtap *wth, int *err)
        length = pletohs(record_length);
 
        if (type != REC_VERS) {
-               g_message("ngsniffer: Sniffer file doesn't start with a version record");
                *err = WTAP_ERR_BAD_RECORD;
+               *err_info = g_strdup_printf("ngsniffer: Sniffer file doesn't start with a version record");
                return -1;
        }
 
@@ -396,33 +474,19 @@ int ngsniffer_open(wtap *wth, int *err)
        }
        wth->data_offset += sizeof version;
 
-       /* 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.
-
-          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.... */
+       /* Check the data link type. */
        if (version.network >= NUM_NGSNIFF_ENCAPS
-           || (sniffer_encap[version.network] == WTAP_ENCAP_UNKNOWN
-              && version.network != 7)) {
-               g_message("ngsniffer: network type %u unknown or unsupported",
-                   version.network);
+           || sniffer_encap[version.network] == WTAP_ENCAP_UNKNOWN) {
                *err = WTAP_ERR_UNSUPPORTED_ENCAP;
+               *err_info = g_strdup_printf("ngsniffer: network type %u unknown or unsupported",
+                   version.network);
                return -1;
        }
 
        /* Check the time unit */
        if (version.timeunit >= NUM_NGSNIFF_TIMEUNITS) {
-               g_message("ngsniffer: Unknown timeunit %u", version.timeunit);
                *err = WTAP_ERR_UNSUPPORTED;
+               *err_info = g_strdup_printf("ngsniffer: Unknown timeunit %u", version.timeunit);
                return -1;
        }
 
@@ -444,9 +508,50 @@ int ngsniffer_open(wtap *wth, int *err)
         * so we just skip them - except for REC_HEADER2 records, which
         * we look at, for "Internetwork analyzer" captures, to attempt to
         * determine what the link-layer encapsulation is.
+        *
+        * XXX - in some version 1.16 internetwork analyzer files
+        * generated by the Windows Sniffer when saving Windows
+        * Sniffer files as DOS Sniffer files, there's no REC_HEADER2
+        * record, but the first "rsvd" word is 1 for PRI ISDN files, 2
+        * for BRI ISDN files, and 0 for non-ISDN files; is that something
+        * the DOS Sniffer understands?
         */
-       if (skip_header_records(wth, err, version.maj_vers) < 0)
+       maj_vers = pletohs(&version.maj_vers);
+       if (process_header_records(wth, err, err_info, maj_vers) < 0)
                return -1;
+       if (wth->file_encap == WTAP_ENCAP_PER_PACKET) {
+               /*
+                * Well, we haven't determined the internetwork analyzer
+                * subtype yet...
+                */
+               switch (maj_vers) {
+
+               case 1:
+                       /*
+                        * ... and this is a version 1 capture; look
+                        * at the first "rsvd" word.
+                        */
+                       switch (pletohs(&version.rsvd[0])) {
+
+                       case 1:
+                       case 2:
+                               wth->file_encap = WTAP_ENCAP_ISDN;
+                               break;
+                       }
+                       break;
+
+               case 3:
+                       /*
+                        * ...and this is a version 3 capture; we've
+                        * seen nothing in those that obviously
+                        * indicates the capture type, but the only
+                        * one we've seen is a Frame Relay capture,
+                        * so mark it as Frame Relay for now.
+                        */
+                       wth->file_encap = WTAP_ENCAP_FRELAY_WITH_PHDR;
+                       break;
+               }
+       }
 
        /*
         * Now, if we have a random stream open, position it to the same
@@ -458,14 +563,14 @@ int ngsniffer_open(wtap *wth, int *err)
         * "ngsniffer_read()".
         */
        if (wth->random_fh != NULL) {
-               if (file_seek(wth->random_fh, wth->data_offset, SEEK_SET) == -1) {
-                       *err = file_error(wth->random_fh);
+               if (file_seek(wth->random_fh, wth->data_offset, SEEK_SET, err) == -1)
                        return -1;
-               }
        }
 
        /* This is a ngsniffer file */
        wth->capture.ngsniffer = g_malloc(sizeof(ngsniffer_t));
+       wth->capture.ngsniffer->maj_vers = maj_vers;
+       wth->capture.ngsniffer->min_vers = pletohs(&version.min_vers);
 
        /* We haven't allocated any uncompression buffers yet. */
        wth->capture.ngsniffer->seq.buf = NULL;
@@ -490,7 +595,7 @@ int ngsniffer_open(wtap *wth, int *err)
        wth->snapshot_length = 0;       /* not available in header, only in frame */
        wth->capture.ngsniffer->timeunit = Usec[version.timeunit];
        wth->capture.ngsniffer->is_atm =
-           (wth->file_encap == WTAP_ENCAP_ATM_SNIFFER);
+           (wth->file_encap == WTAP_ENCAP_ATM_PDUS);
 
        /* Get capture start time */
        start_time = pletohs(&version.time);
@@ -527,7 +632,7 @@ int ngsniffer_open(wtap *wth, int *err)
 }
 
 static int
-skip_header_records(wtap *wth, int *err, gint16 version)
+process_header_records(wtap *wth, int *err, gchar **err_info, gint16 maj_vers)
 {
        int bytes_read;
        char record_type[2];
@@ -535,7 +640,7 @@ skip_header_records(wtap *wth, int *err, gint16 version)
                                  the last 2 are "reserved" and are thrown away */
        guint16 type, length;
        int bytes_to_read;
-       unsigned char buffer[32];
+       unsigned char buffer[256];
 
        for (;;) {
                errno = WTAP_ERR_CANT_READ;
@@ -556,7 +661,7 @@ skip_header_records(wtap *wth, int *err, gint16 version)
                        && (type != REC_HEADER3) && (type != REC_HEADER4)
                        && (type != REC_HEADER5) && (type != REC_HEADER6)
                        && (type != REC_HEADER7)
-                       && ((type != REC_V2DESC) || (version > 2)) ) {
+                       && ((type != REC_V2DESC) || (maj_vers > 2)) ) {
                        /*
                         * Well, this is either some unknown header type
                         * (we ignore this case), an uncompressed data
@@ -564,10 +669,8 @@ skip_header_records(wtap *wth, int *err, gint16 version)
                         * which implies data. Seek backwards over the
                         * two bytes we read, and return.
                         */
-                       if (file_seek(wth->fh, -2, SEEK_CUR) == -1) {
-                               *err = file_error(wth->fh);
+                       if (file_seek(wth->fh, -2, SEEK_CUR, err) == -1)
                                return -1;
-                       }
                        return 0;
                }
 
@@ -584,18 +687,18 @@ skip_header_records(wtap *wth, int *err, gint16 version)
                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?
+                * Do we not yet know the encapsulation type (i.e., is
+                * this is an "Internetwork analyzer" capture?), and
+                * is this a REC_HEADER2 record?
                 *
-                * If so, the 5th byte of the record appears to specify
-                * the particular type of network we're on.
+                * If so, it appears to specify the particular type
+                * of network we're on.
                 */
-               if (type == REC_HEADER2 &&
-                   wth->file_encap == WTAP_ENCAP_UNKNOWN) {
+               if (wth->file_encap == WTAP_ENCAP_PER_PACKET &&
+                   type == REC_HEADER2) {
                        /*
-                        * Yes, get the first 32 bytes of the record
-                        * data.
+                        * Yes, get the first up-to-256 bytes of the
+                        * record data.
                         */
                        bytes_to_read = MIN(length, sizeof buffer);
                        bytes_read = file_read(buffer, 1, bytes_to_read,
@@ -607,58 +710,187 @@ skip_header_records(wtap *wth, int *err, gint16 version)
                                        return -1;
                                }
                        }
+
+                       switch (maj_vers) {
+
+                       case 2:
+                               if (process_rec_header2_v2(wth, buffer,
+                                   length, err, err_info) < 0)
+                                       return -1;
+                               break;
+
+                       case 1:
+                       case 4:
+                       case 5:
+                               if (process_rec_header2_v145(wth, buffer,
+                                   length, maj_vers, err, err_info) < 0)
+                                       return -1;
+                               break;
+                       }
+
                        /*
                         * Skip the rest of the record.
                         */
                        if (length > sizeof buffer) {
                                if (file_seek(wth->fh, length - sizeof buffer,
-                                   SEEK_CUR) == -1) {
-                                       *err = file_error(wth->fh);
+                                   SEEK_CUR, err) == -1)
                                        return -1;
-                               }
                        }
+               } else {
+                       /* Nope, just skip over the data. */
+                       if (file_seek(wth->fh, length, SEEK_CUR, err) == -1)
+                               return -1;
+               }
+               wth->data_offset += length;
+       }
+}
 
-                       /*
-                        * 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]) {
+static int
+process_rec_header2_v2(wtap *wth, unsigned char *buffer, guint16 length,
+    int *err, gchar **err_info)
+{
+       static const char x_25_str[] = "HDLC\nX.25\n";
 
-                       case NET_FRAME_RELAY:
-                               wth->file_encap = WTAP_ENCAP_FRELAY;
-                               break;
+       /*
+        * There appears to be a string in a REC_HEADER2 record, with
+        * a list of protocols.  In one X.25 capture I've seen, the
+        * string was "HDLC\nX.25\nCLNP\nISO_TP\nSESS\nPRES\nVTP\nACSE".
+        * Presumably CLNP and everything else is per-packet, but
+        * we assume "HDLC\nX.25\n" indicates that it's an X.25 capture.
+        */
+       if (length < sizeof x_25_str - 1) {
+               /*
+                * There's not enough data to compare.
+                */
+               *err = WTAP_ERR_UNSUPPORTED_ENCAP;
+               *err_info = g_strdup_printf("ngsniffer: WAN capture has too-short protocol list");
+               return -1;
+       }
 
-                       case NET_PPP:
-                               wth->file_encap = WTAP_ENCAP_PPP;
-                               break;
-                       }
+       if (strncmp((char *)buffer, x_25_str, sizeof x_25_str - 1) == 0) {
+               /*
+                * X.25.
+                */
+               wth->file_encap = WTAP_ENCAP_LAPB;
+       } else {
+               *err = WTAP_ERR_UNSUPPORTED_ENCAP;
+               *err_info = g_strdup_printf("ngsniffer: WAN capture protocol string %.*s unknown",
+                   length, buffer);
+               return -1;
+       }
+       return 0;
+}
 
-               } else {
-                       /* Nope, just skip over the data. */
-                       if (file_seek(wth->fh, length, SEEK_CUR) == -1) {
-                               *err = file_error(wth->fh);
+static int
+process_rec_header2_v145(wtap *wth, unsigned char *buffer, guint16 length,
+    gint16 maj_vers, int *err, gchar **err_info)
+{
+       /*
+        * The 5th byte of the REC_HEADER2 record appears to be a
+        * network type.
+        */
+       if (length < 5) {
+               /*
+                * There is no 5th byte; give up.
+                */
+               *err = WTAP_ERR_UNSUPPORTED_ENCAP;
+               *err_info = g_strdup("ngsniffer: WAN capture has no network subtype");
+               return -1;
+       }
+
+       /*
+        * The X.25 captures I've seen have a type of NET_HDLC, and the
+        * Sniffer documentation seems to imply that it's used for
+        * X.25, although it could be used for other purposes as well.
+        *
+        * NET_ROUTER is used for all sorts of point-to-point protocols,
+        * including ISDN.  It appears, from the documentation, that the
+        * Sniffer attempts to infer the particular protocol by looking
+        * at the traffic; it's not clear whether it stores in the file
+        * an indication of the protocol it inferred was being used.
+        *
+        * Unfortunately, it also appears that NET_HDLC is used for
+        * stuff other than X.25 as well, so we can't just interpret
+        * it unconditionally as X.25.
+        *
+        * For now, we interpret both NET_HDLC and NET_ROUTER as "per-packet
+        * encapsulation".  We remember that we saw NET_ROUTER, though,
+        * as it appears that we can infer whether a packet is PPP or
+        * ISDN based on the channel number subfield of the frame error
+        * status bits - if it's 0, it's PPP, otherwise it's ISDN and
+        * the channel number indicates which channel it is.  We assume
+        * NET_HDLC isn't used for ISDN.
+        */
+       switch (buffer[4]) {
+
+       case NET_SDLC:
+               wth->file_encap = WTAP_ENCAP_SDLC;
+               break;
+
+       case NET_HDLC:
+               wth->file_encap = WTAP_ENCAP_PER_PACKET;
+               break;
+
+       case NET_FRAME_RELAY:
+               wth->file_encap = WTAP_ENCAP_FRELAY_WITH_PHDR;
+               break;
+
+       case NET_ROUTER:
+               /*
+                * For most of the version 4 capture files I've seen,
+                * 0xfa in buffer[1] means the file is an ISDN capture,
+                * but there's one PPP file with 0xfa there; does that
+                * mean that the 0xfa has nothing to do with ISDN,
+                * or is that just an ISDN file with no D channel
+                * packets?  (The channel number is not 0 in any
+                * of the packets, so perhaps it is.)
+                *
+                * For one version 5 ISDN capture I've seen, there's
+                * a 0x01 in buffer[6]; none of the non-ISDN version
+                * 5 captures have it.
+                */
+               wth->file_encap = WTAP_ENCAP_PER_PACKET;
+               switch (maj_vers) {
+
+               case 4:
+                       if (buffer[1] == 0xfa)
+                               wth->file_encap = WTAP_ENCAP_ISDN;
+                       break;
+
+               case 5:
+                       if (length < 7) {
+                               /*
+                                * There is no 5th byte; give up.
+                                */
+                               *err = WTAP_ERR_UNSUPPORTED_ENCAP;
+                               *err_info = g_strdup("ngsniffer: WAN bridge/router capture has no ISDN flag");
                                return -1;
                        }
+                       if (buffer[6] == 0x01)
+                               wth->file_encap = WTAP_ENCAP_ISDN;
+                       break;
                }
-               wth->data_offset += length;
+               break;
+
+       case NET_PPP:
+               wth->file_encap = WTAP_ENCAP_PPP_WITH_PHDR;
+               break;
+
+       default:
+               /*
+                * Reject these until we can figure them out.
+                */
+               *err = WTAP_ERR_UNSUPPORTED_ENCAP;
+               *err_info = g_strdup_printf("ngsniffer: WAN network subtype %u unknown or unsupported",
+                   buffer[4]);
+               return -1;
        }
+       return 0;
 }
 
 /* Read the next packet */
-static gboolean ngsniffer_read(wtap *wth, int *err, long *data_offset)
+static gboolean ngsniffer_read(wtap *wth, int *err, gchar **err_info,
+    long *data_offset)
 {
        int     ret;
        guint16 type, length;
@@ -667,7 +899,7 @@ static gboolean ngsniffer_read(wtap *wth, int *err, long *data_offset)
        struct frame6_rec frame6;
        double  t;
        guint16 time_low, time_med, time_high, true_size, size;
-       u_char  *pd;
+       guchar  *pd;
 
        for (;;) {
                /*
@@ -690,14 +922,13 @@ static gboolean ngsniffer_read(wtap *wth, int *err, long *data_offset)
                                 * We shouldn't get a frame2 record in
                                 * an ATM capture.
                                 */
-                               g_message("ngsniffer: REC_FRAME2 record in an ATM Sniffer file");
                                *err = WTAP_ERR_BAD_RECORD;
+                               *err_info = g_strdup("ngsniffer: REC_FRAME2 record in an ATM Sniffer file");
                                return FALSE;
                        }
 
                        /* Read the f_frame2_struct */
-                       ret = ngsniffer_read_frame2(wth, FALSE, &frame2, err);
-                       if (ret < 0) {
+                       if (!ngsniffer_read_frame2(wth, FALSE, &frame2, err)) {
                                /* Read error */
                                return FALSE;
                        }
@@ -713,7 +944,8 @@ static gboolean ngsniffer_read(wtap *wth, int *err, long *data_offset)
                        t = (double)time_low+(double)(time_med)*65536.0 +
                            (double)time_high*4294967296.0;
 
-                       set_pseudo_header_frame2(&wth->pseudo_header, &frame2);
+                       set_pseudo_header_frame2(wth, &wth->pseudo_header,
+                           &frame2);
                        goto found;
 
                case REC_FRAME4:
@@ -722,13 +954,16 @@ static gboolean ngsniffer_read(wtap *wth, int *err, long *data_offset)
                                 * We shouldn't get a frame2 record in
                                 * a non-ATM capture.
                                 */
-                               g_message("ngsniffer: REC_FRAME4 record in a non-ATM Sniffer file");
                                *err = WTAP_ERR_BAD_RECORD;
+                               *err_info = g_strdup("ngsniffer: REC_FRAME4 record in a non-ATM Sniffer file");
                                return FALSE;
                        }
 
                        /* Read the f_frame4_struct */
-                       ret = ngsniffer_read_frame4(wth, FALSE, &frame4, err);
+                       if (!ngsniffer_read_frame4(wth, FALSE, &frame4, err)) {
+                               /* Read error */
+                               return FALSE;
+                       }
                        wth->data_offset += sizeof frame4;
                        time_low = pletohs(&frame4.time_low);
                        time_med = pletohs(&frame4.time_med);
@@ -736,7 +971,19 @@ static gboolean ngsniffer_read(wtap *wth, int *err, long *data_offset)
                        size = pletohs(&frame4.size);
                        true_size = pletohs(&frame4.true_size);
 
-                       length -= sizeof frame4;        /* we already read that much */
+                       /*
+                        * XXX - it looks as if version 4 captures have
+                        * a bogus record length, based on the assumption
+                        * that the record is a frame2 record.
+                        */
+                       if (wth->capture.ngsniffer->maj_vers >= 5)
+                               length -= sizeof frame4;        /* we already read that much */
+                       else {
+                               if (wth->capture.ngsniffer->min_vers >= 95)
+                                       length -= sizeof frame2;
+                               else
+                                       length -= sizeof frame4;
+                       }
 
                        /*
                         * XXX - use the "time_day" field?  Is that for captures
@@ -749,15 +996,11 @@ static gboolean ngsniffer_read(wtap *wth, int *err, long *data_offset)
                        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;
+                       /* Read the f_frame6_struct */
+                       if (!ngsniffer_read_frame6(wth, FALSE, &frame6, err)) {
+                               /* Read error */
                                return FALSE;
                        }
-
-                       /* Read the f_frame6_struct */
-                       ret = ngsniffer_read_frame6(wth, FALSE, &frame6, err);
                        wth->data_offset += sizeof frame6;
                        time_low = pletohs(&frame6.time_low);
                        time_med = pletohs(&frame6.time_med);
@@ -774,7 +1017,8 @@ static gboolean ngsniffer_read(wtap *wth, int *err, long *data_offset)
                        t = (double)time_low+(double)(time_med)*65536.0 +
                            (double)time_high*4294967296.0;
 
-                       set_pseudo_header_frame6(&wth->pseudo_header, &frame6);
+                       set_pseudo_header_frame6(wth, &wth->pseudo_header,
+                           &frame6);
                        goto found;
 
                case REC_EOF:
@@ -807,8 +1051,8 @@ found:
                /*
                 * Yes - treat this as an error.
                 */
-               g_message("ngsniffer: Record length is less than packet size");
                *err = WTAP_ERR_BAD_RECORD;
+               *err_info = g_strdup("ngsniffer: Record length is less than packet size");
                return FALSE;
        }
 
@@ -820,61 +1064,24 @@ found:
         */
        buffer_assure_space(wth->frame_buffer, length);
        pd = buffer_start_ptr(wth->frame_buffer);
-       if (ngsniffer_read_rec_data(wth, FALSE, pd, length, err) < 0)
+       if (!ngsniffer_read_rec_data(wth, FALSE, pd, length, err))
                return FALSE;   /* Read error */
        wth->data_offset += length;
 
-       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;
-               }
-       }
-
-       /*
-        * 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);
+       wth->phdr.pkt_encap = fix_pseudo_header(wth->file_encap, pd, length,
+           &wth->pseudo_header);
 
        t = t/1000000.0 * wth->capture.ngsniffer->timeunit; /* t = # of secs */
        t += wth->capture.ngsniffer->start;
        wth->phdr.ts.tv_sec = (long)t;
        wth->phdr.ts.tv_usec = (unsigned long)((t-(double)(wth->phdr.ts.tv_sec))
                        *1.0e6);
-       wth->phdr.pkt_encap = wth->file_encap;
        return TRUE;
 }
 
-static int ngsniffer_seek_read(wtap *wth, long seek_off,
-    union wtap_pseudo_header *pseudo_header, u_char *pd, int packet_size,
-    int *err)
+static gboolean ngsniffer_seek_read(wtap *wth, long seek_off,
+    union wtap_pseudo_header *pseudo_header, guchar *pd, int packet_size,
+    int *err, gchar **err_info _U_)
 {
        int     ret;
        guint16 type, length;
@@ -883,7 +1090,7 @@ static int ngsniffer_seek_read(wtap *wth, long seek_off,
        struct frame6_rec frame6;
 
        if (ng_file_seek_rand(wth, seek_off, SEEK_SET, err) == -1)
-               return -1;
+               return FALSE;
 
        ret = ngsniffer_read_rec_header(wth, TRUE, &type, &length, err);
        if (ret <= 0) {
@@ -892,27 +1099,29 @@ static int ngsniffer_seek_read(wtap *wth, long seek_off,
                        /* EOF means "short read" in random-access mode */
                        *err = WTAP_ERR_SHORT_READ;
                }
-               return -1;
+               return FALSE;
        }
 
        switch (type) {
 
        case REC_FRAME2:
                /* Read the f_frame2_struct */
-               ret = ngsniffer_read_frame2(wth, TRUE, &frame2, err);
-               if (ret < 0) {
+               if (!ngsniffer_read_frame2(wth, TRUE, &frame2, err)) {
                        /* Read error */
-                       return ret;
+                       return FALSE;
                }
 
                length -= sizeof frame2;        /* we already read that much */
 
-               set_pseudo_header_frame2(pseudo_header, &frame2);
+               set_pseudo_header_frame2(wth, pseudo_header, &frame2);
                break;
 
        case REC_FRAME4:
                /* Read the f_frame4_struct */
-               ret = ngsniffer_read_frame4(wth, TRUE, &frame4, err);
+               if (!ngsniffer_read_frame4(wth, TRUE, &frame4, err)) {
+                       /* Read error */
+                       return FALSE;
+               }
 
                length -= sizeof frame4;        /* we already read that much */
 
@@ -921,11 +1130,14 @@ static int ngsniffer_seek_read(wtap *wth, long seek_off,
 
        case REC_FRAME6:
                /* Read the f_frame6_struct */
-               ret = ngsniffer_read_frame6(wth, TRUE, &frame6, err);
+               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);
+               set_pseudo_header_frame6(wth, pseudo_header, &frame6);
                break;
 
        default:
@@ -933,19 +1145,18 @@ static int ngsniffer_seek_read(wtap *wth, long seek_off,
                 * "Can't happen".
                 */
                g_assert_not_reached();
-               return -1;
+               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);
+       if (!ngsniffer_read_rec_data(wth, TRUE, pd, packet_size, err))
+               return FALSE;
+
+       fix_pseudo_header(wth->file_encap, pd, packet_size, pseudo_header);
+
+       return TRUE;
 }
 
 static int ngsniffer_read_rec_header(wtap *wth, gboolean is_random,
@@ -980,7 +1191,7 @@ static int ngsniffer_read_rec_header(wtap *wth, gboolean is_random,
        return 1;       /* success */
 }
 
-static int ngsniffer_read_frame2(wtap *wth, gboolean is_random,
+static gboolean ngsniffer_read_frame2(wtap *wth, gboolean is_random,
     struct frame2_rec *frame2, int *err)
 {
        int bytes_read;
@@ -991,39 +1202,104 @@ static int ngsniffer_read_frame2(wtap *wth, gboolean is_random,
        if (bytes_read != sizeof *frame2) {
                if (*err == 0)
                        *err = WTAP_ERR_SHORT_READ;
-               return -1;
+               return FALSE;
        }
-       return 0;
+       return TRUE;
 }
 
-static void set_pseudo_header_frame2(union wtap_pseudo_header *pseudo_header,
-    struct frame2_rec *frame2)
+static void set_pseudo_header_frame2(wtap *wth,
+    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 PPP "Internetwork analyzer" capture:
+        *
+        *      The only bit seen in "frame2.fs" is the 0x80 bit, which
+        *      probably indicates the packet's direction; all other
+        *      bits were zero.  The Expert Sniffer Network Analyzer
+        *      5.50 Operations manual says that bit is the FS_DTE bit
+        *      for async/PPP data.  The other bits are error bits
+        *      plus bits indicating whether the frame is PPP or SLIP,
+        *      but the PPP bit isn't set.
+        *
+        *      All bits in "frame2.flags" were zero.
+        *
+        * In one X.25 "Internetwork analyzer" capture:
+        *
+        *      The only bit seen in "frame2.fs" is the 0x80 bit, which
+        *      probably indicates the packet's direction; all other
+        *      bits were zero.
+        *
+        *      "frame2.flags" was always 0x18; however, the Sniffer
+        *      manual says that just means that a display filter was
+        *      calculated for the frame, and it should be displayed,
+        *      so perhaps that's just a quirk of that particular capture.
         *
-        * In one 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:
         *
-        * In one Ethernet capture, "fs" was always 0,
-        * and "flags" was either 0 or 0x18, with no
-        * obvious correlation with anything.
+        *      "frame2.fs" was always 0; the Sniffer manual says they're
+        *      error bits of various sorts.
         *
-        * In one Token Ring capture, "fs" was either 0
-        * or 0xcc, and "flags" was either 0 or 0x18,
-        * with no obvious correlation with anything.
+        *      "frame2.flags" was either 0 or 0x18, with no obvious
+        *      correlation with anything.  See previous comment
+        *      about display filters.
+        *
+        * In one Token Ring capture:
+        *
+        *      "frame2.fs" was either 0 or 0xcc; the Sniffer manual says
+        *      nothing about those bits for Token Ring captures.
+        *
+        *      "frame2.flags" was either 0 or 0x18, with no obvious
+        *      correlation with anything.  See previous comment
+        *      about display filters.
         */
-       pseudo_header->x25.flags = (frame2->fs & 0x80) ? 0x00 : 0x80;
+       switch (wth->file_encap) {
+
+       case WTAP_ENCAP_ETHERNET:
+               /*
+                * XXX - do we ever have an FCS?  If not, why do we often
+                * have 4 extra bytes of stuff at the end?  Do some
+                * PC Ethernet interfaces report the length including the
+                * FCS but not store the FCS in the packet, or do some
+                * Ethernet drivers work that way?
+                */
+               pseudo_header->eth.fcs_len = 0;
+               break;
+
+       case WTAP_ENCAP_PPP_WITH_PHDR:
+       case WTAP_ENCAP_SDLC:
+               pseudo_header->p2p.sent = (frame2->fs & 0x80) ? TRUE : FALSE;
+               break;
+
+       case WTAP_ENCAP_LAPB:
+       case WTAP_ENCAP_FRELAY_WITH_PHDR:
+       case WTAP_ENCAP_PER_PACKET:
+               pseudo_header->x25.flags = (frame2->fs & 0x80) ? 0x00 : FROM_DCE;
+               break;
+
+       case WTAP_ENCAP_ISDN:
+               pseudo_header->isdn.uton = (frame2->fs & 0x80) ? FALSE : TRUE;
+               switch (frame2->fs & 0x18) {
+
+               case 0x18:
+                       pseudo_header->isdn.channel = 0;        /* D-channel */
+                       break;
+
+               case 0x08:
+                       pseudo_header->isdn.channel = 1;        /* B1-channel */
+                       break;
+
+               case 0x10:
+                       pseudo_header->isdn.channel = 2;        /* B1-channel */
+                       break;
+
+               default:
+                       pseudo_header->isdn.channel = 30;       /* XXX */
+                       break;
+               }
+       }
 }
 
-static int ngsniffer_read_frame4(wtap *wth, gboolean is_random,
+static gboolean ngsniffer_read_frame4(wtap *wth, gboolean is_random,
     struct frame4_rec *frame4, int *err)
 {
        int bytes_read;
@@ -1034,26 +1310,265 @@ static int ngsniffer_read_frame4(wtap *wth, gboolean is_random,
        if (bytes_read != sizeof *frame4) {
                if (*err == 0)
                        *err = WTAP_ERR_SHORT_READ;
-               return -1;
+               return FALSE;
        }
-       return 0;
+       return TRUE;
 }
 
 static void set_pseudo_header_frame4(union wtap_pseudo_header *pseudo_header,
     struct frame4_rec *frame4)
 {
-       pseudo_header->ngsniffer_atm.AppTrafType = frame4->atm_info.AppTrafType;
-       pseudo_header->ngsniffer_atm.AppHLType = frame4->atm_info.AppHLType;
-       pseudo_header->ngsniffer_atm.Vpi = pletohs(&frame4->atm_info.Vpi);
-       pseudo_header->ngsniffer_atm.Vci = pletohs(&frame4->atm_info.Vci);
-       pseudo_header->ngsniffer_atm.channel = pletohs(&frame4->atm_info.channel);
-       pseudo_header->ngsniffer_atm.cells = pletohs(&frame4->atm_info.cells);
-       pseudo_header->ngsniffer_atm.aal5t_u2u = pletohs(&frame4->atm_info.Trailer.aal5t_u2u);
-       pseudo_header->ngsniffer_atm.aal5t_len = pletohs(&frame4->atm_info.Trailer.aal5t_len);
-       pseudo_header->ngsniffer_atm.aal5t_chksum = pletohl(&frame4->atm_info.Trailer.aal5t_chksum);
+       guint32 StatusWord;
+       guint8 aal_type, hl_type;
+       guint16 vpi, vci;
+
+       /*
+        * Map flags from frame4.atm_info.StatusWord.
+        */
+       pseudo_header->atm.flags = 0;
+       StatusWord = pletohl(&frame4->atm_info.StatusWord);
+       if (StatusWord & SW_RAW_CELL)
+               pseudo_header->atm.flags |= ATM_RAW_CELL;
+
+       aal_type = frame4->atm_info.AppTrafType & ATT_AALTYPE;
+       hl_type = frame4->atm_info.AppTrafType & ATT_HLTYPE;
+       vpi = 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 = pntohl(&frame4->atm_info.Trailer.aal5t_chksum);
 }
 
-static int ngsniffer_read_frame6(wtap *wth, gboolean is_random,
+static gboolean ngsniffer_read_frame6(wtap *wth, gboolean is_random,
     struct frame6_rec *frame6, int *err)
 {
        int bytes_read;
@@ -1064,20 +1579,28 @@ static int ngsniffer_read_frame6(wtap *wth, gboolean is_random,
        if (bytes_read != sizeof *frame6) {
                if (*err == 0)
                        *err = WTAP_ERR_SHORT_READ;
-               return -1;
+               return FALSE;
        }
-       return 0;
+       return TRUE;
 }
 
-static void set_pseudo_header_frame6(
-       union wtap_pseudo_header *pseudo_header _U_,
+static void set_pseudo_header_frame6(wtap *wth,
+       union wtap_pseudo_header *pseudo_header,
        struct frame6_rec *frame6 _U_)
 {
        /* XXX - Once the frame format is divined, something will most likely go here */
+
+       switch (wth->file_encap) {
+
+       case WTAP_ENCAP_ETHERNET:
+               /* XXX - is there an FCS? */
+               pseudo_header->eth.fcs_len = -1;
+               break;
+       }
 }
 
-static int ngsniffer_read_rec_data(wtap *wth, gboolean is_random, u_char *pd,
-    int length, int *err)
+static gboolean ngsniffer_read_rec_data(wtap *wth, gboolean is_random,
+    guchar *pd, int length, int *err)
 {
        int     bytes_read;
 
@@ -1086,23 +1609,188 @@ static int ngsniffer_read_rec_data(wtap *wth, gboolean is_random, u_char *pd,
        if (bytes_read != length) {
                if (*err == 0)
                        *err = WTAP_ERR_SHORT_READ;
-               return -1;
+               return FALSE;
        }
-       return 0;
+       return TRUE;
 }
 
-static void fix_pseudo_header(wtap *wth,
+/*
+ * OK, this capture is from an "Internetwork analyzer", and we either
+ * didn't see a type 7 record or it had a network type such as NET_HDLC
+ * that doesn't tell us which *particular* HDLC derivative this is;
+ * let's look at the first few bytes of the packet, a pointer to which
+ * was passed to us as an argument, and see whether it looks like PPP,
+ * Frame Relay, Wellfleet HDLC, Cisco HDLC, or LAPB - or, if it's none
+ * of those, assume it's LAPD.
+ *
+ * (XXX - are there any "Internetwork analyzer" captures that don't
+ * have type 7 records?  If so, is there some other field that will
+ * tell us what type of capture it is?)
+ */
+static int infer_pkt_encap(const guint8 *pd, int len)
+{
+       int i;
+
+       if (len <= 0) {
+               /*
+                * Nothing to infer, but it doesn't matter how you
+                * dissect an empty packet.  Let's just say PPP.
+                */
+               return WTAP_ENCAP_PPP_WITH_PHDR;
+       }
+
+       if (pd[0] == 0xFF) {
+               /*
+                * PPP.  (XXX - check for 0xFF 0x03?)
+                */
+               return WTAP_ENCAP_PPP_WITH_PHDR;
+       }
+
+       if (len >= 2) {
+               if (pd[0] == 0x07 && pd[1] == 0x03) {
+                       /*
+                        * Wellfleet HDLC.
+                        */
+                       return WTAP_ENCAP_WFLEET_HDLC;
+               } else if ((pd[0] == 0x0F && pd[1] == 0x00) ||
+                          (pd[0] == 0x8F && pd[1] == 0x00)) {
+                       /*
+                        * Cisco HDLC.
+                        */
+                       return WTAP_ENCAP_CHDLC_WITH_PHDR;
+               }
+
+               /*
+                * Check for Frame Relay.  Look for packets with at least
+                * 3 bytes of header - 2 bytes of DLCI followed by 1 byte
+                * of control, which, for now, we require to be 0x03 (UI),
+                * although there might be other frame types as well.
+                * Scan forward until we see the last DLCI byte, with
+                * the low-order bit being 1, and then check the next
+                * byte to see if it's a control byte.
+                *
+                * XXX - in version 4 and 5 captures, wouldn't this just
+                * have a capture subtype of NET_FRAME_RELAY?  Or is this
+                * here only to handle other versions of the capture
+                * file, where we might just not yet have found where
+                * the subtype is specified in the capture?
+                *
+                * Bay^H^H^HNortel Networks has a mechanism in the Optivity
+                * software for some of their routers to save captures
+                * in Sniffer format; they use a version number of 4.9, but
+                * don't put out any header records before the first FRAME2
+                * record.  That means we have to use heuristics to guess
+                * what type of packet we have.
+                */
+               for (i = 0; i < len && (pd[i] & 0x01) == 0; i++)
+                       ;
+               i++;    /* advance to the byte after the last DLCI byte */
+               if (i == len) {
+                       /*
+                        * No control byte.
+                        */
+                       return WTAP_ENCAP_LAPB;
+               }
+               if (pd[i] == 0x03)
+                       return WTAP_ENCAP_FRELAY_WITH_PHDR;
+       }
+
+       /*
+        * Assume LAPB, for now.  If we support other HDLC encapsulations,
+        * we can check whether the low-order bit of the first byte is
+        * set (as it should be for LAPB) if no other checks pass.
+        *
+        * Or, if it's truly impossible to distinguish ISDN from non-ISDN
+        * captures, we could assume it's ISDN if it's not anything
+        * else.
+        */
+       return WTAP_ENCAP_LAPB;
+}
+
+static int fix_pseudo_header(int encap, const guint8 *pd, int len,
     union wtap_pseudo_header *pseudo_header)
 {
-       switch (wth->file_encap) {
+       switch (encap) {
 
-       case WTAP_ENCAP_LAPD:
-               if (pseudo_header->x25.flags == 0x00)
-                       pseudo_header->p2p.sent = TRUE;
-               else
-                       pseudo_header->p2p.sent = FALSE;
+       case WTAP_ENCAP_PER_PACKET:
+               /*
+                * Infer the packet type from the first two bytes.
+                */
+               encap = infer_pkt_encap(pd, len);
+
+               /*
+                * Fix up the pseudo-header to match the new
+                * encapsulation type.
+                */
+               switch (encap) {
+
+               case WTAP_ENCAP_WFLEET_HDLC:
+               case WTAP_ENCAP_CHDLC_WITH_PHDR:
+               case WTAP_ENCAP_PPP_WITH_PHDR:
+                       if (pseudo_header->x25.flags == 0)
+                               pseudo_header->p2p.sent = TRUE;
+                       else
+                               pseudo_header->p2p.sent = FALSE;
+                       break;
+
+               case WTAP_ENCAP_ISDN:
+                       if (pseudo_header->x25.flags == 0x00)
+                               pseudo_header->isdn.uton = FALSE;
+                       else
+                               pseudo_header->isdn.uton = TRUE;
+
+                       /*
+                        * XXX - this is currently a per-packet
+                        * encapsulation type, and we can't determine
+                        * whether a capture is an ISDN capture before
+                        * seeing any packets, and B-channel PPP packets
+                        * look like PPP packets and are given
+                        * WTAP_ENCAP_PPP_WITH_PHDR, not WTAP_ENCAP_ISDN,
+                        * so we assume this is a D-channel packet and
+                        * thus give it a channel number of 0.
+                        */
+                       pseudo_header->isdn.channel = 0;
+                       break;
+               }
+               break;
+
+       case WTAP_ENCAP_ATM_PDUS:
+               /*
+                * If the Windows Sniffer writes out one of its ATM
+                * capture files in DOS Sniffer format, it doesn't
+                * distinguish between LE Control and LANE encapsulated
+                * LAN frames, it just marks them as LAN frames,
+                * so we fix that up here.
+                *
+                * I've also seen DOS Sniffer captures claiming that
+                * LANE packets that *don't* start with FF 00 are
+                * marked as LE Control frames, so we fix that up
+                * as well.
+                */
+               if (pseudo_header->atm.type == TRAF_LANE && len >= 2) {
+                       if (pd[0] == 0xff && pd[1] == 0x00) {
+                               /*
+                                * This must be LE Control.
+                                */
+                               pseudo_header->atm.subtype =
+                                   TRAF_ST_LANE_LE_CTRL;
+                       } else {
+                               /*
+                                * This can't be LE Control.
+                                */
+                               if (pseudo_header->atm.subtype ==
+                                   TRAF_ST_LANE_LE_CTRL) {
+                                       /*
+                                        * XXX - Ethernet or Token Ring?
+                                        */
+                                       pseudo_header->atm.subtype =
+                                           TRAF_ST_LANE_802_3;
+                               }
+                       }
+               }
                break;
        }
+       return encap;
 }
 
 /* Throw away the buffers used by the sequential I/O stream, but not
@@ -1120,10 +1808,12 @@ 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->seq.buf != NULL)
-               g_free(wth->capture.ngsniffer->seq.buf);
        if (wth->capture.ngsniffer->rand.buf != NULL)
                g_free(wth->capture.ngsniffer->rand.buf);
        if (wth->capture.ngsniffer->first_blob != NULL) {
@@ -1146,8 +1836,12 @@ static const int wtap_encap[] = {
     -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 */
+    -1,                /* WTAP_ENCAP_ATM_PDUS */
+    -1,                /* WTAP_ENCAP_NULL -> unsupported */
+    -1,                /* WTAP_ENCAP_ASCEND -> unsupported */
+    -1,                /* WTAP_ENCAP_ISDN -> unsupported */
+    -1,                /* WTAP_ENCAP_IP_OVER_FC -> unsupported */
+    7,         /* WTAP_ENCAP_PPP_WITH_PHDR -> Internetwork analyzer (synchronous) FIXME ! */
 };
 #define NUM_WTAP_ENCAPS (sizeof wtap_encap / sizeof wtap_encap[0])
 
@@ -1167,7 +1861,7 @@ int ngsniffer_dump_can_write_encap(int encap)
 
 /* Returns TRUE on success, FALSE on failure; sets "*err" to an error code on
    failure */
-gboolean ngsniffer_dump_open(wtap_dumper *wdh, int *err)
+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 */
@@ -1204,7 +1898,7 @@ gboolean ngsniffer_dump_open(wtap_dumper *wdh, int *err)
 /* 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 u_char *pd, int *err)
+    const union wtap_pseudo_header *pseudo_header, const guchar *pd, int *err)
 {
     ngsniffer_dump_t *priv = wdh->dump.ngsniffer;
     struct frame2_rec rec_hdr;
@@ -1283,8 +1977,8 @@ static gboolean ngsniffer_dump(wtap_dumper *wdh, const struct wtap_pkthdr *phdr,
     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 & 0x80) ? 0x00 : 0x80;
+    if (wdh->encap == WTAP_ENCAP_LAPB || wdh->encap == WTAP_ENCAP_PPP_WITH_PHDR)
+       rec_hdr.fs = (pseudo_header->x25.flags & FROM_DCE) ? 0x00 : 0x80;
     else
        rec_hdr.fs = 0;
     rec_hdr.flags = 0;
@@ -1319,10 +2013,12 @@ static gboolean ngsniffer_dump_close(wtap_dumper *wdh, int *err)
 
     nwritten = fwrite(buf, 1, 6, wdh->fh);
     if (nwritten != 6) {
-       if (nwritten == 0 && ferror(wdh->fh))
-           *err = errno;
-       else
-           *err = WTAP_ERR_SHORT_WRITE;
+       if (err != NULL) {
+           if (nwritten == 0 && ferror(wdh->fh))
+               *err = errno;
+           else
+               *err = WTAP_ERR_SHORT_WRITE;
+       }
        return FALSE;
     }
     return TRUE;
@@ -1345,7 +2041,7 @@ static gboolean ngsniffer_dump_close(wtap_dumper *wdh, int *err)
    Return value is the number of bytes in outbuf on return.
 */
 static int
-SnifferDecompress( unsigned char * inbuf, size_t inlen, 
+SnifferDecompress( unsigned char * inbuf, size_t inlen,
                        unsigned char * outbuf, size_t outlen, int *err )
 {
    unsigned char * pin = inbuf;
@@ -1406,7 +2102,7 @@ SnifferDecompress( unsigned char * inbuf, size_t inlen,
                     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 )
@@ -1421,12 +2117,12 @@ SnifferDecompress( unsigned char * inbuf, size_t inlen,
                 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 
+                    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 */
@@ -1448,12 +2144,12 @@ SnifferDecompress( unsigned char * inbuf, size_t inlen,
                 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 
+                    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 */
@@ -1484,12 +2180,12 @@ SnifferDecompress( unsigned char * inbuf, size_t inlen,
                 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 
+                    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 )
@@ -1627,7 +2323,7 @@ ng_file_read(void *buffer, size_t elementsize, size_t numelements, wtap *wth,
                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;
@@ -1704,17 +2400,12 @@ 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)
 {
-    long ret;
     long delta;
     char buf[65536];
     long amount_to_read;
 
-    if (wth->file_type == WTAP_FILE_NGSNIFFER_UNCOMPRESSED) {
-       ret = file_seek(wth->fh, offset, whence);
-       if (ret == -1)
-               *err = file_error(wth->fh);
-       return ret;
-    }
+    if (wth->file_type == WTAP_FILE_NGSNIFFER_UNCOMPRESSED)
+       return file_seek(wth->fh, offset, whence, err);
 
     switch (whence) {
 
@@ -1756,18 +2447,13 @@ 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)
 {
-    long ret;
     ngsniffer_t *ngsniffer;
     long delta;
     GList *new, *next;
     blob_info_t *next_blob, *new_blob;
 
-    if (wth->file_type == WTAP_FILE_NGSNIFFER_UNCOMPRESSED) {
-       ret = file_seek(wth->random_fh, offset, whence);
-       if (ret == -1)
-               *err = file_error(wth->random_fh);
-       return ret;
-    }
+    if (wth->file_type == WTAP_FILE_NGSNIFFER_UNCOMPRESSED)
+       return file_seek(wth->random_fh, offset, whence, err);
 
     ngsniffer = wth->capture.ngsniffer;
 
@@ -1793,7 +2479,7 @@ ng_file_seek_rand(wtap *wth, long offset, int whence, int *err)
     if (delta > 0) {
        /* We're going forwards.
           Is the place to which we're seeking within the current buffer? */
-       if ((unsigned)ngsniffer->rand.nextout + delta >= ngsniffer->rand.nbytes) {
+       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. */
@@ -1842,10 +2528,8 @@ ng_file_seek_rand(wtap *wth, long offset, int whence, int *err)
 
        /* 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) == -1) {
-           *err = file_error(wth->random_fh);
+       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;