#include "file_wrappers.h"
#include <wsutil/file_util.h>
+#ifdef HAVE_LIBZ
+#include <zlib.h>
+#endif /* HAVE_LIBZ */
+
/*
* See RFC 1952 for a description of the gzip file format.
*
int seek; /* true if seek request pending */
/* error information */
int err; /* error code */
+ const char *err_info; /* additional error information string for some errors */
unsigned int avail_in; /* number of bytes available at next_in */
unsigned char *next_in; /* next input byte */
#ifdef HAVE_LIBZ
/* zlib inflate stream */
z_stream strm; /* stream structure in-place (not a pointer) */
+ int dont_check_crc; /* 1 if we aren't supposed to check the CRC */
#endif
/* fast seeking */
GPtrArray *fast_seek;
} while (*have < count);
if (ret < 0) {
state->err = errno;
+ state->err_info = NULL;
return -1;
}
if (ret == 0)
int compression;
union {
struct {
+#ifdef HAVE_INFLATEPRIME
int bits; /* number of bits (1-7) from byte at in - 1, or 0 */
+#endif
unsigned char window[ZLIB_WINSIZE]; /* preceding 32K of uncompressed data */
/* be gentle with Z_STREAM_END, 8 bytes more... Another solution would be to comment checks out */
#ifdef HAVE_LIBZ
-/* Get next byte from input, or -1 if end or error. */
-#define NEXT() ((state->avail_in == 0 && fill_in_buffer(state) == -1) ? -1 : \
+/* Get next byte from input, or -1 if end or error.
+ *
+ * Note:
+ *
+ * 1) errors from raw_read(), and thus from fill_in_buffer(), are
+ * "sticky", and fill_in_buffer() won't do any reading if there's
+ * an error;
+ *
+ * 2) GZ_GETC() returns -1 on an EOF;
+ *
+ * so it's safe to make multiple GZ_GETC() calls and only check the
+ * last one for an error. */
+#define GZ_GETC() ((state->avail_in == 0 && fill_in_buffer(state) == -1) ? -1 : \
(state->avail_in == 0 ? -1 : \
(state->avail_in--, *(state->next_in)++)))
+/* Get a one-byte integer and return 0 on success and the value in *ret.
+ Otherwise -1 is returned, state->err is set, and *ret is not modified. */
+static int
+gz_next1(FILE_T state, guint8 *ret)
+{
+ int ch;
+
+ ch = GZ_GETC();
+ if (ch == -1) {
+ if (state->err == 0) {
+ /* EOF */
+ state->err = WTAP_ERR_SHORT_READ;
+ state->err_info = NULL;
+ }
+ return -1;
+ }
+ *ret = ch;
+ return 0;
+}
+
+/* Get a two-byte little-endian integer and return 0 on success and the value
+ in *ret. Otherwise -1 is returned, state->err is set, and *ret is not
+ modified. */
+static int
+gz_next2(FILE_T state, guint16 *ret)
+{
+ guint16 val;
+ int ch;
+
+ val = GZ_GETC();
+ ch = GZ_GETC();
+ if (ch == -1) {
+ if (state->err == 0) {
+ /* EOF */
+ state->err = WTAP_ERR_SHORT_READ;
+ state->err_info = NULL;
+ }
+ return -1;
+ }
+ val += (guint16)ch << 8;
+ *ret = val;
+ return 0;
+}
+
/* Get a four-byte little-endian integer and return 0 on success and the value
- in *ret. Otherwise -1 is returned and *ret is not modified. */
+ in *ret. Otherwise -1 is returned, state->err is set, and *ret is not
+ modified. */
static int
gz_next4(FILE_T state, guint32 *ret)
{
guint32 val;
int ch;
- val = NEXT();
- val += (unsigned)NEXT() << 8;
- val += (guint32)NEXT() << 16;
- ch = NEXT();
- if (ch == -1)
+ val = GZ_GETC();
+ val += (unsigned)GZ_GETC() << 8;
+ val += (guint32)GZ_GETC() << 16;
+ ch = GZ_GETC();
+ if (ch == -1) {
+ if (state->err == 0) {
+ /* EOF */
+ state->err = WTAP_ERR_SHORT_READ;
+ state->err_info = NULL;
+ }
return -1;
+ }
val += (guint32)ch << 24;
*ret = val;
return 0;
}
+/* Skip the specified number of bytes and return 0 on success. Otherwise -1
+ is returned. */
+static int
+gz_skipn(FILE_T state, size_t n)
+{
+ while (n != 0) {
+ if (GZ_GETC() == -1) {
+ if (state->err == 0) {
+ /* EOF */
+ state->err = WTAP_ERR_SHORT_READ;
+ state->err_info = NULL;
+ }
+ return -1;
+ }
+ n--;
+ }
+ return 0;
+}
+
+/* Skip a null-terminated string and return 0 on success. Otherwise -1
+ is returned. */
+static int
+gz_skipzstr(FILE_T state)
+{
+ int ch;
+
+ /* It's null-terminated, so scan until we read a byte with
+ the value 0 or get an error. */
+ while ((ch = GZ_GETC()) > 0)
+ ;
+ if (ch == -1) {
+ if (state->err == 0) {
+ /* EOF */
+ state->err = WTAP_ERR_SHORT_READ;
+ state->err_info = NULL;
+ }
+ return -1;
+ }
+ return 0;
+}
+
static void
zlib_fast_seek_add(FILE_T file, struct zlib_cur_seek_point *point, int bits, gint64 in_pos, gint64 out_pos)
{
/* it's for sure after gzip header, so file->fast_seek->len != 0 */
struct fast_seek_point *item = file->fast_seek->pdata[file->fast_seek->len - 1];;
+#ifndef HAVE_INFLATEPRIME
+ if (bits)
+ return;
+#endif
+
/* Glib has got Balanced Binary Trees (GTree) but I couldn't find a way to do quick search for nearest (and smaller) value to seek (It's what fast_seek_find() do)
* Inserting value in middle of sorted array is expensive, so we want to add only in the end.
* It's not big deal, cause first-read don't usually invoke seeking
val->in = in_pos;
val->out = out_pos;
val->compression = ZLIB;
-
+#ifdef HAVE_INFLATEPRIME
val->data.zlib.bits = bits;
+#endif
if (point->pos != 0) {
unsigned int left = ZLIB_WINSIZE - point->pos;
if (state->avail_in == 0 && fill_in_buffer(state) == -1)
break;
if (state->avail_in == 0) {
- state->err = WTAP_ERR_ZLIB + Z_DATA_ERROR;
+ /* EOF */
+ state->err = WTAP_ERR_SHORT_READ;
+ state->err_info = NULL;
break;
}
ret = inflate(strm, Z_BLOCK);
state->avail_in = strm->avail_in;
state->next_in = strm->next_in;
- if (ret == Z_STREAM_ERROR || ret == Z_NEED_DICT) {
- state->err = WTAP_ERR_ZLIB + Z_STREAM_ERROR;
+ if (ret == Z_STREAM_ERROR) {
+ state->err = WTAP_ERR_DECOMPRESS;
+ state->err_info = strm->msg;
+ break;
+ }
+ if (ret == Z_NEED_DICT) {
+ state->err = WTAP_ERR_DECOMPRESS;
+ state->err_info = "preset dictionary needed";
break;
}
if (ret == Z_MEM_ERROR) {
- state->err = WTAP_ERR_ZLIB + Z_MEM_ERROR; /* ENOMEM? */
+ /* This means "not enough memory". */
+ state->err = ENOMEM;
+ state->err_info = NULL;
break;
}
if (ret == Z_DATA_ERROR) { /* deflate stream invalid */
- state->err = WTAP_ERR_ZLIB + Z_DATA_ERROR;
+ state->err = WTAP_ERR_DECOMPRESS;
+ state->err_info = strm->msg;
break;
}
+ /*
+ * XXX - Z_BUF_ERROR?
+ */
strm->adler = crc32(strm->adler, buf2, count2 - strm->avail_out);
if (state->fast_seek_cur) {
got before the error. The next attempt to read
something past that data will get the error. */
if (ret == Z_STREAM_END) {
- if (gz_next4(state, &crc) == -1 || gz_next4(state, &len) == -1)
- state->err = WTAP_ERR_ZLIB + Z_DATA_ERROR;
- else if (crc != strm->adler)
- state->err = WTAP_ERR_ZLIB + Z_DATA_ERROR;
- else if (len != (strm->total_out & 0xffffffffL))
- state->err = WTAP_ERR_ZLIB + Z_DATA_ERROR;
+ if (gz_next4(state, &crc) != -1 &&
+ gz_next4(state, &len) != -1) {
+ /*
+ * XXX - compressed Windows Sniffer don't
+ * all have the same CRC value; is it just
+ * random crap, or are they running the
+ * CRC on a different set of data than
+ * you're supposed to (e.g., not CRCing
+ * some of the data), or something such
+ * as that?
+ */
+ if (crc != strm->adler && !state->dont_check_crc) {
+ state->err = WTAP_ERR_DECOMPRESS;
+ state->err_info = "bad CRC";
+ } else if (len != (strm->total_out & 0xffffffffL)) {
+ state->err = WTAP_ERR_DECOMPRESS;
+ state->err_info = "length field wrong";
+ }
+ }
state->compression = UNKNOWN; /* ready for next stream, once have is 0 */
g_free(state->fast_seek_cur);
state->fast_seek_cur = NULL;
if (state->avail_in == 0 && fill_in_buffer(state) == -1)
return -1;
if (state->avail_in && state->next_in[0] == 139) {
- unsigned len;
- int flags;
+ guint8 cm;
+ guint8 flags;
+ guint16 len;
+ guint16 hcrc;
/* we have a gzip header, woo hoo! */
state->avail_in--;
state->next_in++;
- /* skip rest of header */
- if (NEXT() != 8) { /* compression method */
- state->err = WTAP_ERR_ZLIB + Z_DATA_ERROR;
+ /* read rest of header */
+
+ /* compression method (CM) */
+ if (gz_next1(state, &cm) == -1)
+ return -1;
+ if (cm != 8) {
+ state->err = WTAP_ERR_DECOMPRESS;
+ state->err_info = "unknown compression method";
return -1;
}
- flags = NEXT();
+
+ /* flags (FLG) */
+ if (gz_next1(state, &flags) == -1)
+ return -1;
if (flags & 0xe0) { /* reserved flag bits */
- state->err = WTAP_ERR_ZLIB + Z_DATA_ERROR;
+ state->err = WTAP_ERR_DECOMPRESS;
+ state->err_info = "reserved flag bits set";
return -1;
}
- NEXT(); /* modification time */
- NEXT();
- NEXT();
- NEXT();
- NEXT(); /* extra flags */
- NEXT(); /* operating system */
- if (flags & 4) { /* extra field */
- len = (unsigned)NEXT();
- len += (unsigned)NEXT() << 8;
- while (len--)
- if (NEXT() < 0)
- break;
+
+ /* modification time (MTIME) */
+ if (gz_skipn(state, 4) == -1)
+ return -1;
+
+ /* extra flags (XFL) */
+ if (gz_skipn(state, 1) == -1)
+ return -1;
+
+ /* operating system (OS) */
+ if (gz_skipn(state, 1) == -1)
+ return -1;
+
+ if (flags & 4) {
+ /* extra field - get XLEN */
+ if (gz_next2(state, &len) == -1)
+ return -1;
+
+ /* skip the extra field */
+ if (gz_skipn(state, len) == -1)
+ return -1;
}
- if (flags & 8) /* file name */
- while (NEXT() > 0)
- ;
- if (flags & 16) /* comment */
- while (NEXT() > 0)
- ;
- if (flags & 2) { /* header crc */
- NEXT();
- NEXT();
+ if (flags & 8) {
+ /* file name */
+ if (gz_skipzstr(state) == -1)
+ return -1;
+ }
+ if (flags & 16) {
+ /* comment */
+ if (gz_skipzstr(state) == -1)
+ return -1;
+ }
+ if (flags & 2) {
+ /* header crc */
+ if (gz_next2(state, &hcrc) == -1)
+ return -1;
+ /* XXX - check the CRC? */
}
- /* an unexpected end of file is not checked for here -- it will be
- noticed on the first request for uncompressed data */
/* set up for decompression */
inflateReset(&(state->strm));
state->seek = 0; /* no seek request pending */
state->err = 0; /* clear error */
+ state->err_info = NULL;
state->pos = 0; /* no uncompressed data yet */
state->avail_in = 0; /* no input data yet */
}
errno = ENOMEM;
return NULL;
}
+
+ /* for now, assume we should check the crc */
+ state->dont_check_crc = 0;
#endif
/* return stream */
return state;
{
int fd;
FILE_T ft;
+#ifdef HAVE_LIBZ
+ const char *suffixp;
+#endif
/* open file and do correct filename conversions.
here. Pre-Large File Summit UN*Xes, and possibly even some
post-LFS UN*Xes, might require O_LARGEFILE here, though.
If so, we should probably handle that in ws_open(). */
- if ((fd = ws_open(path, O_RDONLY|O_BINARY, 0666)) == -1)
+ if ((fd = ws_open(path, O_RDONLY|O_BINARY, 0000)) == -1)
return NULL;
/* open file handle */
return NULL;
}
+#ifdef HAVE_LIBZ
+ /*
+ * If this file's name ends in ".caz", it's probably a compressed
+ * Windows Sniffer file. The compression is gzip, but they don't
+ * bother filling in the CRC; we set a flag to ignore CRC errors.
+ */
+ suffixp = strrchr(path, '.');
+ if (suffixp != NULL) {
+ if (g_ascii_strcasecmp(suffixp, ".caz") == 0)
+ ft->dont_check_crc = 1;
+ }
+#endif
+
return ft;
}
#ifdef HAVE_LIBZ
if (here->compression == ZLIB) {
+#ifdef HAVE_INFLATEPRIME
off = here->in - (here->data.zlib.bits ? 1 : 0);
+#else
+ off = here->in;
+#endif
off2 = here->out;
} else if (here->compression == GZIP_AFTER_HEADER) {
off = here->in;
file->eof = 0;
file->seek = 0;
file->err = 0;
+ file->err_info = NULL;
file->avail_in = 0;
#ifdef HAVE_LIBZ
if (here->compression == ZLIB) {
z_stream *strm = &file->strm;
- FILE_T state = file;
inflateReset(strm);
strm->adler = here->data.zlib.adler;
strm->total_out = here->data.zlib.total_out;
+#ifdef HAVE_INFLATEPRIME
if (here->data.zlib.bits) {
- int ret = NEXT();
+ FILE_T state = file;
+ int ret = GZ_GETC();
if (ret == -1) {
- /* *err = ???; */
+ if (state->err == 0) {
+ /* EOF */
+ *err = WTAP_ERR_SHORT_READ;
+ } else
+ *err = state->err;
return -1;
}
-
(void)inflatePrime(strm, here->data.zlib.bits, ret >> (8 - here->data.zlib.bits));
}
+#endif
(void)inflateSetDictionary(strm, here->data.zlib.window, ZLIB_WINSIZE);
file->compression = ZLIB;
} else if (here->compression == GZIP_AFTER_HEADER) {
file->eof = 0;
file->seek = 0;
file->err = 0;
+ file->err_info = NULL;
file->avail_in = 0;
file->pos += offset;
return file->pos;
return stream->pos + (stream->seek ? stream->skip : 0);
}
+gint64
+file_tell_raw(FILE_T stream)
+{
+ return stream->raw_pos;
+}
+
+int
+file_fstat(FILE_T stream, ws_statb64 *statb, int *err)
+{
+ if (ws_fstat64(stream->fd, statb) == -1) {
+ if (err != NULL)
+ *err = errno;
+ return -1;
+ }
+ return 0;
+}
+
int
file_read(void *buf, unsigned int len, FILE_T file)
{
/*
* Routine to return a Wiretap error code (0 for no error, an errno
* for a file error, or a WTAP_ERR_ code for other errors) for an
- * I/O stream.
+ * I/O stream. Also returns an error string for some errors.
*/
int
-file_error(FILE_T fh)
+file_error(FILE_T fh, gchar **err_info)
{
- return fh->err;
+ if (fh->err != 0) {
+ *err_info = (fh->err_info == NULL) ? NULL : g_strdup(fh->err_info);
+ return fh->err;
+ }
+ return 0;
}
void
{
/* clear error and end-of-file */
stream->err = 0;
+ stream->err_info = NULL;
stream->eof = 0;
}
}
g_free(file->fast_seek_cur);
file->err = 0;
+ file->err_info = NULL;
g_free(file);
- return close(fd);
+ return ws_close(fd);
}
#ifdef HAVE_LIBZ
return NULL;
state = gzwfile_fdopen(fd);
if (state == NULL) {
- save_errno = errno;
- close(fd);
save_errno = errno;
+ close(fd);
+ errno = save_errno;
}
return state;
}
state->in = g_try_malloc(state->want);
state->out = g_try_malloc(state->want);
if (state->in == NULL || state->out == NULL) {
- if (state->out != NULL)
- g_free(state->out);
- if (state->in != NULL)
- g_free(state->in);
- state->err = WTAP_ERR_ZLIB + Z_MEM_ERROR; /* ENOMEM? */
+ g_free(state->out);
+ g_free(state->in);
+ state->err = ENOMEM;
return -1;
}
ret = deflateInit2(strm, state->level, Z_DEFLATED,
15 + 16, 8, state->strategy);
if (ret != Z_OK) {
+ g_free(state->out);
g_free(state->in);
- state->err = WTAP_ERR_ZLIB + Z_MEM_ERROR; /* ENOMEM? */
+ if (ret == Z_MEM_ERROR) {
+ /* This means "not enough memory". */
+ state->err = ENOMEM;
+ } else {
+ /* This "shouldn't happen". */
+ state->err = WTAP_ERR_INTERNAL;
+ }
return -1;
}
have = strm->avail_out;
ret = deflate(strm, flush);
if (ret == Z_STREAM_ERROR) {
- state->err = WTAP_ERR_ZLIB + Z_STREAM_ERROR;
+ /* This "shouldn't happen". */
+ state->err = WTAP_ERR_INTERNAL;
return -1;
}
have -= strm->avail_out;