Ethereal-dev: Re: [ethereal-dev] RE: Correct sniffdecomp.c attached

Note: This archive is from the project's previous web site, ethereal.com. This list is no longer active.

From: Guy Harris <gharris@xxxxxxxxxxxx>
Date: Sat, 20 May 2000 04:05:02 -0700
On Sat, Apr 22, 2000 at 03:43:44AM +0200, Joerg Mayer wrote:
> I'm currently stuck with the seek code but I hope to get some implementation
> done for this during the next two days. I *think* I got the rest right but
> without seek I can't be sure (I don't get any output). I have attached a
> diff versus 0.8.7 that contains all the changes I have made. Feel free to
> comment/make suggenstions!

I've attached a patch to Wiretap that successfully reads at least one
compressed Sniffer file here - and lets me run Ethereal on it, with at
least some random clicking on packets, so seek works at least to some
degree.

It still needs some cleanup, e.g. freeing the buffer allocated for
decompression in the initial sequential pass through the file when we
close the sequential side in "wtap_sequential_close()", and it may be
possible to implement some backward seeks by moving the pointer in the
decompressed-data buffer backward, rather than doing a zlib-style seek
back to the beginning of the compressed part of the file and skipping
forward.

(It may also be possible to remember the file offsets and
uncompressed-data-stream offsets of each blob during the initial
sequential pass through the file, and implementing forward and backward
seeks by seeking to the beginning of the blob containing the
uncompressed-data-stream offset in question and moving forward as
necessary.  In fact, it may be possible to do that for gzipped data as
well.)

It appears, at least from a very cursory test, to be able to handle
compressed Sniffer files even if they're gzipped, i.e.  it appears to
handle files that have been Sniffer-compressed and then gzipped *atop*
the Sniffer compression (given that the access to the compressed Sniffer
file goes through zlib if zlib support is enabled, this should probably
be expected to work).

There appear, at least in the compressed Sniffer file in question, to be
*three* records at the beginning of the file, *none* of which are
compressed - the type 1 (REC_VERS) record, a record with a type of 7 and
a small amount of presumably-binary data, and a record with a type of 6
and a bunch of text data plus some presumably-binary data.  That stuff
has to be read without going through the compression code.

A couple of issues came up:

	1) Can we really guarantee that no compressed blob expand to
	   more than 65536 bytes?  If not, we may have to reallocate the
	   output buffer if we don't have room to uncompress a blob.

	2) The code treats a blob with a length of 49152 as being an
	   uncompressed blob with a length of 16384; 49152 is 0xC000,
	   and 16384 is 0x4000 - is the blob length really a flag of
	   0x8000, which if set means the blob is uncompressed, and a
	   15-bit blob-size field?
Index: wiretap/file.c
===================================================================
RCS file: /usr/local/cvsroot/ethereal/wiretap/file.c,v
retrieving revision 1.52
diff -c -r1.52 wiretap/file.c
*** wiretap/file.c	2000/05/19 23:06:48	1.52
--- wiretap/file.c	2000/05/20 10:48:53
***************
*** 236,244 ****
  	{ "Novell LANalyzer", NULL,
  	  NULL, NULL },
  
! 	/* WTAP_FILE_NGSNIFFER */
  	{ "Network Associates Sniffer (DOS-based)", "ngsniffer",
  	  ngsniffer_dump_can_write_encap, ngsniffer_dump_open },
  
  	/* WTAP_FILE_SNOOP */
  	{ "Sun snoop", "snoop",
--- 236,248 ----
  	{ "Novell LANalyzer", NULL,
  	  NULL, NULL },
  
! 	/* WTAP_FILE_NGSNIFFER_UNCOMPRESSED */
  	{ "Network Associates Sniffer (DOS-based)", "ngsniffer",
  	  ngsniffer_dump_can_write_encap, ngsniffer_dump_open },
+ 
+ 	/* WTAP_FILE_NGSNIFFER_COMPRESSED */
+ 	{ "Network Associates Sniffer (DOS-based), compressed", "ngsniffer_comp",
+ 	  NULL, NULL },
  
  	/* WTAP_FILE_SNOOP */
  	{ "Sun snoop", "snoop",
Index: wiretap/ngsniffer.c
===================================================================
RCS file: /usr/local/cvsroot/ethereal/wiretap/ngsniffer.c,v
retrieving revision 1.42
diff -c -r1.42 wiretap/ngsniffer.c
*** wiretap/ngsniffer.c	2000/05/19 23:06:59	1.42
--- wiretap/ngsniffer.c	2000/05/20 10:48:56
***************
*** 84,93 ****
  
  /*
   * 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 */
--- 84,89 ----
***************
*** 96,102 ****
  	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!) */
  	guint8	timeunit;	/* timestamp units */
  	gint8	cmprs_vers;	/* compression version */
  	gint8	cmprs_level;	/* compression level */
--- 92,98 ----
  	gint16	date;		/* DOS-format date */
  	gint8	type;		/* what type of records follow */
  	guint8	network;	/* network type */
! 	gint8	format;		/* format version */
  	guint8	timeunit;	/* timestamp units */
  	gint8	cmprs_vers;	/* compression version */
  	gint8	cmprs_level;	/* compression level */
***************
*** 246,265 ****
  #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 ngsniffer_seek_read(wtap *wth, int seek_off,
      union wtap_pseudo_header *pseudo_header, u_char *pd, int packet_size);
! static int ngsniffer_read_rec_header(FILE_T fh, guint16 *typep,
!     guint16 *lengthp, int *err);
! static int ngsniffer_read_frame2(FILE_T fh, 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(FILE_T fh, 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_rec_data(FILE_T fh, char *pd, int length, int *err);
  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);
--- 242,269 ----
  #define NUM_NGSNIFF_TIMEUNITS 7
  static double Usec[] = { 15.0, 0.838096, 15.0, 0.5, 2.0, 1.0, 0.1 };
  
+ static int ng_file_read(void *buffer, size_t elementsize, size_t numelements,
+     wtap *wth, FILE_T infile, ngsniffer_comp_stream_t *comp_stream, int *err);
+ static long ng_file_seek(wtap *wth, FILE_T infile,
+     ngsniffer_comp_stream_t *comp_stream, long offset, int whence);
+ static int SnifferDecompress( unsigned char * inbuf, size_t inlen,
+         unsigned char * outbuf, size_t outlen, int *err );
  static int ngsniffer_read(wtap *wth, int *err);
  static int ngsniffer_seek_read(wtap *wth, int seek_off,
      union wtap_pseudo_header *pseudo_header, u_char *pd, int packet_size);
! static int ngsniffer_read_rec_header(wtap *wth, FILE_T fh,
!     ngsniffer_comp_stream_t *comp_stream, guint16 *typep, guint16 *lengthp,
      int *err);
+ static int ngsniffer_read_frame2(wtap *wth, FILE_T fh,
+     ngsniffer_comp_stream_t *comp_stream, 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, FILE_T fh,
!     ngsniffer_comp_stream_t *comp_stream, 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_rec_data(wtap *wth, FILE_T fh,
!     ngsniffer_comp_stream_t *comp_stream, char *pd, int length, int *err);
  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);
***************
*** 271,277 ****
  	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;
  	struct vers_rec version;
  	guint16	start_date;
--- 275,281 ----
  	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;
  	struct vers_rec version;
  	guint16	start_date;
***************
*** 343,355 ****
  	}
  	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 both LAPB and PPP captures
--- 347,352 ----
***************
*** 380,388 ****
  		return -1;
  	}
  
  	/* This is a ngsniffer file */
- 	wth->file_type = WTAP_FILE_NGSNIFFER;
  	wth->capture.ngsniffer = g_malloc(sizeof(ngsniffer_t));
  	wth->subtype_read = ngsniffer_read;
  	wth->subtype_seek_read = ngsniffer_seek_read;
  	wth->subtype_close = ngsniffer_close;
--- 377,471 ----
  		return -1;
  	}
  
+ 	/*
+ 	 * Read the second record, which, at least if I remember
+ 	 * correctly the captures I've seen, is a "type 7" record,
+ 	 * containing some binary data, and which appears not to be
+ 	 * compressed, even in a compressed capture file.
+ 	 */
+ 	errno = WTAP_ERR_CANT_READ;
+ 	bytes_read = file_read(record_type, 1, 2, wth->fh);
+ 	bytes_read += file_read(record_length, 1, 4, wth->fh);
+ 	if (bytes_read != 6) {
+ 		*err = file_error(wth->fh);
+ 		if (*err != 0)
+ 			return -1;
+ 		return 0;
+ 	}
+ 	wth->data_offset += 6;
+ 
+ 	type = pletohs(record_type);
+ 	length = pletohs(record_length);
+ 
+ 	if (type != 7) {
+ 		g_message("ngsniffer: Second record in Sniffer file isn't a type 7 record");
+ 		*err = WTAP_ERR_BAD_RECORD;
+ 		return -1;
+ 	}
+ 
+ 	/* OK, now skip over it. */
+ 	file_seek(wth->fh, length, SEEK_CUR);
+ 	wth->data_offset += length;
+ 
+ 	/*
+ 	 * Read the third record, which, at least if I remember
+ 	 * correctly the captures I've seen, is a "type 6" record,
+ 	 * containing a bunch of text and some binary data, and which
+ 	 * appears not to be compressed even in a compressed capture file.
+ 	 */
+ 	errno = WTAP_ERR_CANT_READ;
+ 	bytes_read = file_read(record_type, 1, 2, wth->fh);
+ 	bytes_read += file_read(record_length, 1, 4, wth->fh);
+ 	if (bytes_read != 6) {
+ 		*err = file_error(wth->fh);
+ 		if (*err != 0)
+ 			return -1;
+ 		return 0;
+ 	}
+ 	wth->data_offset += 6;
+ 
+ 	type = pletohs(record_type);
+ 	length = pletohs(record_length);
+ 
+ 	if (type != 6) {
+ 		g_message("ngsniffer: Third record in Sniffer file isn't a type 6 record");
+ 		*err = WTAP_ERR_BAD_RECORD;
+ 		return -1;
+ 	}
+ 
+ 	/* OK, now skip over it. */
+ 	file_seek(wth->fh, length, SEEK_CUR);
+ 	wth->data_offset += length;
+ 
+ 	/*
+ 	 * Now position the random stream 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()".
+ 	 */
+ 	file_seek(wth->random_fh, wth->data_offset, SEEK_SET);
+ 
  	/* This is a ngsniffer file */
  	wth->capture.ngsniffer = g_malloc(sizeof(ngsniffer_t));
+ 	/* compressed or uncompressed Sniffer file? */
+ 	if (version.format != 1) {
+ 		wth->file_type = WTAP_FILE_NGSNIFFER_COMPRESSED;
+ 	} else {
+ 		wth->file_type = WTAP_FILE_NGSNIFFER_UNCOMPRESSED;
+ 	}
+ 
+ 	/* We haven't allocated any uncompression buffers yet. */
+ 	wth->capture.ngsniffer->seq.file_outbuf = NULL;
+ 	wth->capture.ngsniffer->rand.file_outbuf = NULL;
+ 
+ 	/* Set the current file offset. */
+ 	wth->capture.ngsniffer->data_offset = wth->data_offset;
+ 	wth->capture.ngsniffer->seq.offset = wth->data_offset;
+ 	wth->capture.ngsniffer->rand.offset = wth->data_offset;
+ 
  	wth->subtype_read = ngsniffer_read;
  	wth->subtype_seek_read = ngsniffer_seek_read;
  	wth->subtype_close = ngsniffer_close;
***************
*** 443,449 ****
  		 * Read the record header.
  		 */
  		record_offset = wth->data_offset;
! 		ret = ngsniffer_read_rec_header(wth->fh, &type, &length, err);
  		if (ret <= 0) {
  			/* Read error or EOF */
  			return ret;
--- 526,533 ----
  		 * Read the record header.
  		 */
  		record_offset = wth->data_offset;
! 		ret = ngsniffer_read_rec_header(wth, wth->fh,
! 		    &wth->capture.ngsniffer->seq, &type, &length, err);
  		if (ret <= 0) {
  			/* Read error or EOF */
  			return ret;
***************
*** 464,470 ****
  			}
  
  			/* Read the f_frame2_struct */
! 			ret = ngsniffer_read_frame2(wth->fh, &frame2, err);
  			if (ret < 0) {
  				/* Read error */
  				return ret;
--- 548,555 ----
  			}
  
  			/* Read the f_frame2_struct */
! 			ret = ngsniffer_read_frame2(wth, wth->fh,
! 			    &wth->capture.ngsniffer->seq, &frame2, err);
  			if (ret < 0) {
  				/* Read error */
  				return ret;
***************
*** 496,502 ****
  			}
  
  			/* Read the f_frame4_struct */
! 			ret = ngsniffer_read_frame4(wth->fh, &frame4, err);
  			wth->data_offset += sizeof frame4;
  			time_low = pletohs(&frame4.time_low);
  			time_med = pletohs(&frame4.time_med);
--- 581,588 ----
  			}
  
  			/* Read the f_frame4_struct */
! 			ret = ngsniffer_read_frame4(wth, wth->fh,
! 			    &wth->capture.ngsniffer->seq, &frame4, err);
  			wth->data_offset += sizeof frame4;
  			time_low = pletohs(&frame4.time_low);
  			time_med = pletohs(&frame4.time_med);
***************
*** 531,537 ****
  		 * it is but can't handle it.  Skip past the data
  		 * portion, and keep looping.
  		 */
! 		file_seek(wth->fh, length, SEEK_CUR);
  		wth->data_offset += length;
  	}
  
--- 617,624 ----
  		 * it is but can't handle it.  Skip past the data
  		 * portion, and keep looping.
  		 */
! 		ng_file_seek(wth, wth->fh, &wth->capture.ngsniffer->seq,
! 		    length, SEEK_CUR);
  		wth->data_offset += length;
  	}
  
***************
*** 544,550 ****
  	 */
  	buffer_assure_space(wth->frame_buffer, length);
  	pd = buffer_start_ptr(wth->frame_buffer);
! 	if (ngsniffer_read_rec_data(wth->fh, pd, length, err) < 0)
  		return -1;	/* Read error */
  	wth->data_offset += length;
  
--- 631,638 ----
  	 */
  	buffer_assure_space(wth->frame_buffer, length);
  	pd = buffer_start_ptr(wth->frame_buffer);
! 	if (ngsniffer_read_rec_data(wth, wth->fh,
! 	    &wth->capture.ngsniffer->seq, pd, length, err) < 0)
  		return -1;	/* Read error */
  	wth->data_offset += length;
  
***************
*** 585,593 ****
  	struct frame2_rec frame2;
  	struct frame4_rec frame4;
  
! 	file_seek(wth->random_fh, seek_off, SEEK_SET);
  
! 	ret = ngsniffer_read_rec_header(wth->random_fh, &type, &length, &err);
  	if (ret <= 0) {
  		/* Read error or EOF */
  		return ret;
--- 673,683 ----
  	struct frame2_rec frame2;
  	struct frame4_rec frame4;
  
! 	ng_file_seek(wth, wth->random_fh, &wth->capture.ngsniffer->rand,
! 	    seek_off, SEEK_SET);
  
! 	ret = ngsniffer_read_rec_header(wth, wth->random_fh,
! 	    &wth->capture.ngsniffer->rand, &type, &length, &err);
  	if (ret <= 0) {
  		/* Read error or EOF */
  		return ret;
***************
*** 597,603 ****
  
  	case REC_FRAME2:
  		/* Read the f_frame2_struct */
! 		ret = ngsniffer_read_frame2(wth->random_fh, &frame2, &err);
  		if (ret < 0) {
  			/* Read error */
  			return ret;
--- 687,694 ----
  
  	case REC_FRAME2:
  		/* Read the f_frame2_struct */
! 		ret = ngsniffer_read_frame2(wth, wth->random_fh,
! 		    &wth->capture.ngsniffer->rand, &frame2, &err);
  		if (ret < 0) {
  			/* Read error */
  			return ret;
***************
*** 610,616 ****
  
  	case REC_FRAME4:
  		/* Read the f_frame4_struct */
! 		ret = ngsniffer_read_frame4(wth->random_fh, &frame4, &err);
  
  		length -= sizeof frame4;	/* we already read that much */
  
--- 701,708 ----
  
  	case REC_FRAME4:
  		/* Read the f_frame4_struct */
! 		ret = ngsniffer_read_frame4(wth, wth->random_fh,
! 		    &wth->capture.ngsniffer->rand, &frame4, &err);
  
  		length -= sizeof frame4;	/* we already read that much */
  
***************
*** 628,638 ****
  	/*
  	 * Got the pseudo-header (if any), now get the data.
  	 */
! 	return ngsniffer_read_rec_data(wth->random_fh, pd, packet_size, &err);
  }
  
! static int ngsniffer_read_rec_header(FILE_T fh, guint16 *typep,
!     guint16 *lengthp, int *err)
  {
  	int	bytes_read;
  	char	record_type[2];
--- 720,732 ----
  	/*
  	 * Got the pseudo-header (if any), now get the data.
  	 */
! 	return ngsniffer_read_rec_data(wth, wth->random_fh,
! 	    &wth->capture.ngsniffer->rand, pd, packet_size, &err);
  }
  
! static int ngsniffer_read_rec_header(wtap *wth, FILE_T fh,
!     ngsniffer_comp_stream_t *comp_stream, guint16 *typep, guint16 *lengthp,
!     int *err)
  {
  	int	bytes_read;
  	char	record_type[2];
***************
*** 641,650 ****
  	/*
  	 * Read the record header.
  	 */
! 	errno = WTAP_ERR_CANT_READ;
! 	bytes_read = file_read(record_type, 1, 2, fh);
  	if (bytes_read != 2) {
- 		*err = file_error(fh);
  		if (*err != 0)
  			return -1;
  		if (bytes_read != 0) {
--- 735,742 ----
  	/*
  	 * Read the record header.
  	 */
! 	bytes_read = ng_file_read(record_type, 1, 2, wth, fh, comp_stream, err);
  	if (bytes_read != 2) {
  		if (*err != 0)
  			return -1;
  		if (bytes_read != 0) {
***************
*** 653,662 ****
  		}
  		return 0;
  	}
! 	errno = WTAP_ERR_CANT_READ;
! 	bytes_read = file_read(record_length, 1, 4, fh);
  	if (bytes_read != 4) {
- 		*err = file_error(fh);
  		if (*err == 0)
  			*err = WTAP_ERR_SHORT_READ;
  		return -1;
--- 745,753 ----
  		}
  		return 0;
  	}
! 	bytes_read = ng_file_read(record_length, 1, 4, wth, fh, comp_stream,
! 	    err);
  	if (bytes_read != 4) {
  		if (*err == 0)
  			*err = WTAP_ERR_SHORT_READ;
  		return -1;
***************
*** 667,682 ****
  	return 1;	/* success */
  }
  
! static int ngsniffer_read_frame2(FILE_T fh, struct frame2_rec *frame2,
!     int *err)
  {
  	int bytes_read;
  
  	/* Read the f_frame2_struct */
! 	errno = WTAP_ERR_CANT_READ;
! 	bytes_read = file_read(frame2, 1, sizeof *frame2, fh);
  	if (bytes_read != sizeof *frame2) {
- 		*err = file_error(fh);
  		if (*err == 0)
  			*err = WTAP_ERR_SHORT_READ;
  		return -1;
--- 758,772 ----
  	return 1;	/* success */
  }
  
! static int ngsniffer_read_frame2(wtap *wth, FILE_T fh,
!     ngsniffer_comp_stream_t *comp_stream, struct frame2_rec *frame2, int *err)
  {
  	int bytes_read;
  
  	/* Read the f_frame2_struct */
! 	bytes_read = ng_file_read(frame2, 1, sizeof *frame2, wth, fh,
! 	    comp_stream, err);
  	if (bytes_read != sizeof *frame2) {
  		if (*err == 0)
  			*err = WTAP_ERR_SHORT_READ;
  		return -1;
***************
*** 711,726 ****
  	pseudo_header->x25.flags = (frame2->fs & 0x80) ? 0x00 : 0x80;
  }
  
! static int ngsniffer_read_frame4(FILE_T fh, struct frame4_rec *frame4,
!     int *err)
  {
  	int bytes_read;
  
  	/* Read the f_frame4_struct */
! 	errno = WTAP_ERR_CANT_READ;
! 	bytes_read = file_read(frame4, 1, sizeof *frame4, fh);
  	if (bytes_read != sizeof *frame4) {
- 		*err = file_error(fh);
  		if (*err == 0)
  			*err = WTAP_ERR_SHORT_READ;
  		return -1;
--- 801,815 ----
  	pseudo_header->x25.flags = (frame2->fs & 0x80) ? 0x00 : 0x80;
  }
  
! static int ngsniffer_read_frame4(wtap *wth, FILE_T fh,
!     ngsniffer_comp_stream_t *comp_stream, struct frame4_rec *frame4, int *err)
  {
  	int bytes_read;
  
  	/* Read the f_frame4_struct */
! 	bytes_read = ng_file_read(frame4, 1, sizeof *frame4, wth, fh,
! 	    comp_stream, err);
  	if (bytes_read != sizeof *frame4) {
  		if (*err == 0)
  			*err = WTAP_ERR_SHORT_READ;
  		return -1;
***************
*** 742,756 ****
  	pseudo_header->ngsniffer_atm.aal5t_chksum = pletohl(&frame4->atm_info.Trailer.aal5t_chksum);
  }
  
! static int ngsniffer_read_rec_data(FILE_T fh, char *pd, int length, int *err)
  {
  	int	bytes_read;
  
! 	errno = WTAP_ERR_CANT_READ;
! 	bytes_read = file_read(pd, 1, length, fh);
  
  	if (bytes_read != length) {
- 		*err = file_error(fh);
  		if (*err == 0)
  			*err = WTAP_ERR_SHORT_READ;
  		return -1;
--- 831,844 ----
  	pseudo_header->ngsniffer_atm.aal5t_chksum = pletohl(&frame4->atm_info.Trailer.aal5t_chksum);
  }
  
! static int ngsniffer_read_rec_data(wtap *wth, FILE_T fh,
!     ngsniffer_comp_stream_t *comp_stream, char *pd, int length, int *err)
  {
  	int	bytes_read;
  
! 	bytes_read = ng_file_read(pd, 1, length, wth, fh, comp_stream, err);
  
  	if (bytes_read != length) {
  		if (*err == 0)
  			*err = WTAP_ERR_SHORT_READ;
  		return -1;
***************
*** 760,765 ****
--- 848,859 ----
  
  static void ngsniffer_close(wtap *wth)
  {
+ 	/* XXX - we want to free the sequential buffers when we close
+ 	   the sequential side. */
+ 	if (wth->capture.ngsniffer->seq.file_outbuf != NULL)
+ 		g_free(wth->capture.ngsniffer->seq.file_outbuf);
+ 	if (wth->capture.ngsniffer->rand.file_outbuf != NULL)
+ 		g_free(wth->capture.ngsniffer->rand.file_outbuf);
  	g_free(wth->capture.ngsniffer);
  }
  
***************
*** 951,954 ****
--- 1045,1417 ----
  	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
+ 
+ static int
+ ng_file_read(void *buffer, size_t elementsize, size_t numelements, wtap *wth,
+     FILE_T infile, ngsniffer_comp_stream_t *comp_stream, int *err)
+ {
+     unsigned char file_inbuf[65536];
+     unsigned char *file_outbuf;
+     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 */
+     size_t in_len;
+     size_t read_len;
+     gboolean uncompressed;
+     unsigned short blob_len;
+     int out_len;
+     int bytes_to_copy;
+ 
+     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->file_outbuf == NULL) {
+ 	comp_stream->file_outbuf = g_malloc(OUTBUF_SIZE);
+ 	comp_stream->nextout = comp_stream->file_outbuf;
+ 	comp_stream->outbuf_nbytes = 0;
+     }
+     file_outbuf = comp_stream->file_outbuf;
+     while (copybytes > 0) {
+    	if (comp_stream->outbuf_nbytes == 0) {
+ 	    /* There's no decompressed stuff to copy; get some more. */
+ 
+ 	    /* 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 );
+ 	    }
+ 	    in_len = (size_t)pletohs(&blob_len);
+ 
+ 	    /* Compressed or uncompressed?
+ 	       XXX - 49152 is 0xC000, and 16384 is 0x4000;
+ 	       is the high-order bit of the blob length a flag that's
+ 	       set if the blob is uncompressed and clear if it's
+ 	       compressed, and does this mean the biggest blob is
+ 	       32767 bytes long?  Does it also mean you can have an
+ 	       uncompressed blob that's not 16384 bytes long? */
+ 	    uncompressed = FALSE;
+ 	    if ( 49152 == in_len ) {
+ 		in_len = 16384;
+ 		uncompressed = TRUE;
+ 	    }
+ 
+ 	    /* 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 );
+ 	    }
+ 
+ 	    if ( uncompressed ) {
+ 		memcpy( file_outbuf, file_inbuf, in_len );
+ 		out_len = in_len;
+ 	    } else {
+ 		/* Decompress the blob */
+ 		out_len = SnifferDecompress( file_inbuf, in_len,
+ 						file_outbuf, OUTBUF_SIZE, err );
+ 		if (out_len < 0)
+ 		    return( -1 );
+ 	    }
+ 	    comp_stream->nextout = file_outbuf;
+ 	    comp_stream->outbuf_nbytes = out_len;
+ 	}
+    	    
+ 	bytes_to_copy = copybytes;
+ 	if (bytes_to_copy > comp_stream->outbuf_nbytes)
+ 	    bytes_to_copy = comp_stream->outbuf_nbytes;
+ 	memcpy( outbuffer, 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->outbuf_nbytes -= bytes_to_copy;
+ 	comp_stream->offset += bytes_to_copy;
+     }
+     return( copied_bytes );
+ }
+ 
+ static long
+ ng_file_seek(wtap *wth, FILE_T infile, ngsniffer_comp_stream_t *comp_stream,
+     long offset, int whence)
+ {
+    long delta;
+    char buf[65536];
+    long amount_to_read;
+    int err;
+ 
+    if ( wth->file_type == WTAP_FILE_NGSNIFFER_UNCOMPRESSED )
+ 	return file_seek(infile, offset, whence);
+ 
+    /* OK, seeking in a compressed data stream is a pain - especially
+       given that the compressed Sniffer data stream we're reading
+       may actually be further compressed by gzip.
+ 
+       For now, we implement random access the same way zlib does:
+ 
+ 	compute the target position (we don't support relative-to-end);
+ 
+ 	if the target position is ahead of where we are, read and throw
+ 	away the number of bytes ahead it is;
+ 
+ 	if the target position is behind where we are, seek backward
+ 	to the beginning of the compressed part of the data (i.e.,
+ 	seek backward to the stuff after the header), and then recompute
+ 	the relative position based on the new position and seek forward
+ 	by reading and throwing away data. */
+ 
+     switch (whence) {
+ 
+     case SEEK_SET:
+     	break;		/* "offset" is the target offset */
+ 
+     case SEEK_CUR:
+ 	offset += comp_stream->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 - comp_stream->offset;
+     if (delta < 0) {
+ 	/* Oh, dear, we're going backwards.  That's a pain.
+ 	   Seek back to the beginning of the compressed data. */
+ 	if (file_seek(infile, wth->capture.ngsniffer->data_offset, SEEK_SET) == -1)
+ 	    return -1;
+ 	comp_stream->offset = wth->capture.ngsniffer->data_offset;
+ 	delta = offset - comp_stream->offset;
+ 	if (delta < 0) {
+ 	    /* "I'm sorry, Dave, I can't do that."
+ 	       After doing a seek, we can only read stuff from the
+ 	       possibly-compressed region, as we expect compressed
+ 	       blob lengths. */
+ 	    g_assert_not_reached();
+ 	}
+ 
+ 	/* Reset the output buffer. */
+ 	comp_stream->nextout = comp_stream->file_outbuf;
+ 	comp_stream->outbuf_nbytes = 0;
+     }
+ 
+     /* Ok, now read and discard "delta" bytes. */
+     while (delta != 0) {
+ 	amount_to_read = delta;
+ 	if (amount_to_read > sizeof buf)
+ 	    amount_to_read = sizeof buf;
+ 	if (ng_file_read(buf, 1, amount_to_read, wth, infile, comp_stream, &err) < 0)
+ 	    return -1;	/* error */
+ 	delta -= amount_to_read;
+     }
+     return offset;
  }
Index: wiretap/wtap-int.h
===================================================================
RCS file: /usr/local/cvsroot/ethereal/wiretap/wtap-int.h,v
retrieving revision 1.1
diff -c -r1.1 wiretap/wtap-int.h
*** wiretap/wtap-int.h	2000/05/19 23:07:04	1.1
--- wiretap/wtap-int.h	2000/05/20 10:48:56
***************
*** 47,56 ****
--- 47,67 ----
  
  #include "wtap.h"
  
+ /* Information for a compressed Sniffer data stream. */
  typedef struct {
+ 	unsigned char *file_outbuf;
+ 	unsigned char *nextout;
+ 	size_t	outbuf_nbytes;
+ 	long	offset;
+ } ngsniffer_comp_stream_t;
+ 
+ typedef struct {
  	double	timeunit;
  	time_t	start;
  	int	is_atm;
+ 	ngsniffer_comp_stream_t seq;	/* sequential access */
+ 	ngsniffer_comp_stream_t rand;	/* random access */
+ 	long	data_offset;		/* start of possibly-compressed stuff */
  } ngsniffer_t;
  
  typedef struct {
Index: wiretap/wtap.c
===================================================================
RCS file: /usr/local/cvsroot/ethereal/wiretap/wtap.c,v
retrieving revision 1.43
diff -c -r1.43 wiretap/wtap.c
*** wiretap/wtap.c	2000/05/19 23:07:04	1.43
--- wiretap/wtap.c	2000/05/20 10:48:57
***************
*** 155,161 ****
  	NULL,
  	"Less data was read than was expected",
  	"File contains a record that's not valid",
! 	"Less data was written than was requested"
  };
  #define	WTAP_ERRLIST_SIZE	(sizeof wtap_errlist / sizeof wtap_errlist[0])
  
--- 155,164 ----
  	NULL,
  	"Less data was read than was expected",
  	"File contains a record that's not valid",
! 	"Less data was written than was requested",
! 	"Uncompression error: data oddly truncated",
! 	"Uncompression error: data would overflow buffer",
! 	"Uncompression error: bad LZ77 offset",
  };
  #define	WTAP_ERRLIST_SIZE	(sizeof wtap_errlist / sizeof wtap_errlist[0])
  
Index: wiretap/wtap.h
===================================================================
RCS file: /usr/local/cvsroot/ethereal/wiretap/wtap.h,v
retrieving revision 1.71
diff -c -r1.71 wiretap/wtap.h
*** wiretap/wtap.h	2000/05/19 23:07:04	1.71
--- wiretap/wtap.h	2000/05/20 10:48:57
***************
*** 106,128 ****
  #define WTAP_FILE_PCAP_MODIFIED			3
  #define WTAP_FILE_PCAP_RH_6_1			4
  #define WTAP_FILE_LANALYZER			5
! #define WTAP_FILE_NGSNIFFER			6
! #define WTAP_FILE_SNOOP				7
! #define WTAP_FILE_IPTRACE_1_0			8
! #define WTAP_FILE_IPTRACE_2_0			9
! #define WTAP_FILE_NETMON_1_x			10
! #define WTAP_FILE_NETMON_2_x			11
! #define WTAP_FILE_NETXRAY_1_0			12
! #define WTAP_FILE_NETXRAY_1_1			13
! #define WTAP_FILE_NETXRAY_2_001			14
! #define WTAP_FILE_RADCOM			15
! #define WTAP_FILE_ASCEND			16
! #define WTAP_FILE_NETTL				17
! #define WTAP_FILE_TOSHIBA			18
! #define WTAP_FILE_I4BTRACE			19
  
  /* last WTAP_FILE_ value + 1 */
! #define WTAP_NUM_FILE_TYPES			20
  
  /*
   * Maximum packet size we'll support.
--- 106,129 ----
  #define WTAP_FILE_PCAP_MODIFIED			3
  #define WTAP_FILE_PCAP_RH_6_1			4
  #define WTAP_FILE_LANALYZER			5
! #define WTAP_FILE_NGSNIFFER_UNCOMPRESSED	6
! #define WTAP_FILE_NGSNIFFER_COMPRESSED		7
! #define WTAP_FILE_SNOOP				8
! #define WTAP_FILE_IPTRACE_1_0			9
! #define WTAP_FILE_IPTRACE_2_0			10
! #define WTAP_FILE_NETMON_1_x			11
! #define WTAP_FILE_NETMON_2_x			12
! #define WTAP_FILE_NETXRAY_1_0			13
! #define WTAP_FILE_NETXRAY_1_1			14
! #define WTAP_FILE_NETXRAY_2_001			15
! #define WTAP_FILE_RADCOM			16
! #define WTAP_FILE_ASCEND			17
! #define WTAP_FILE_NETTL				18
! #define WTAP_FILE_TOSHIBA			19
! #define WTAP_FILE_I4BTRACE			20
  
  /* last WTAP_FILE_ value + 1 */
! #define WTAP_NUM_FILE_TYPES			21
  
  /*
   * Maximum packet size we'll support.
***************
*** 336,341 ****
--- 337,348 ----
  	/* We read an invalid record */
  #define	WTAP_ERR_SHORT_WRITE			-12
  	/* An attempt to write wrote less data than it should have */
+ #define	WTAP_ERR_UNC_TRUNCATED			-13
+ 	/* Sniffer compressed data was oddly truncated */
+ #define	WTAP_ERR_UNC_OVERFLOW			-14
+ 	/* Uncompressing Sniffer data would overflow buffer */
+ #define	WTAP_ERR_UNC_BAD_OFFSET			-15
+ 	/* LZ77 compressed data has bad offset to string */
  
  /* Errors from zlib; zlib error Z_xxx turns into Wiretap error
     WTAP_ERR_ZLIB + Z_xxx.