Clean up white space.
[obnox/wireshark/wip.git] / wiretap / file_wrappers.c
index 8ca892b19635f052939c1ad663eba4e6a0fbc5c7..f8b9b8c8eb550c0d2572c9d8ed62e9d1e17b0624 100644 (file)
 #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.
  *
@@ -91,12 +95,14 @@ struct wtap_reader {
        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;
@@ -126,6 +132,7 @@ raw_read(FILE_T state, unsigned char *buf, unsigned int count, unsigned *have)
        } while (*have < count);
        if (ret < 0) {
                state->err = errno;
+               state->err_info = NULL;
                return -1;
        }
        if (ret == 0)
@@ -155,7 +162,9 @@ struct fast_seek_point {
        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 */
@@ -230,36 +239,144 @@ fast_seek_reset(FILE_T state _U_)
 
 #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
@@ -269,8 +386,9 @@ zlib_fast_seek_add(FILE_T file, struct zlib_cur_seek_point *point, int bits, gin
                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;
 
@@ -304,7 +422,9 @@ zlib_read(FILE_T state, unsigned char *buf, unsigned int count)
                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;
                }
 
@@ -315,18 +435,30 @@ zlib_read(FILE_T state, unsigned char *buf, unsigned int count)
                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) {
@@ -376,12 +508,25 @@ zlib_read(FILE_T state, unsigned char *buf, unsigned int count)
           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;
@@ -408,48 +553,72 @@ gz_head(FILE_T state)
                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));
@@ -561,6 +730,7 @@ gz_reset(FILE_T state)
 
        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 */
 }
@@ -629,6 +799,9 @@ filed_open(int fd)
                errno = ENOMEM;
                return NULL;
        }
+
+       /* for now, assume we should check the crc */
+       state->dont_check_crc = 0;
 #endif
        /* return stream */
        return state;
@@ -639,6 +812,9 @@ file_open(const char *path)
 {
        int fd;
        FILE_T ft;
+#ifdef HAVE_LIBZ
+       const char *suffixp;
+#endif
 
        /* open file and do correct filename conversions.
 
@@ -649,7 +825,7 @@ file_open(const char *path)
           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 */
@@ -659,6 +835,19 @@ file_open(const char *path)
                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;
 }
 
@@ -696,7 +885,11 @@ file_seek(FILE_T file, gint64 offset, int whence, int *err)
 
 #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;
@@ -719,26 +912,32 @@ file_seek(FILE_T file, gint64 offset, int whence, int *err)
                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) {
@@ -773,6 +972,7 @@ file_seek(FILE_T file, gint64 offset, int whence, int *err)
                file->eof = 0;
                file->seek = 0;
                file->err = 0;
+               file->err_info = NULL;
                file->avail_in = 0;
                file->pos += offset;
                return file->pos;
@@ -819,6 +1019,23 @@ file_tell(FILE_T stream)
        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)
 {
@@ -976,12 +1193,16 @@ file_eof(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
@@ -989,6 +1210,7 @@ file_clearerr(FILE_T stream)
 {
        /* clear error and end-of-file */
        stream->err = 0;
+       stream->err_info = NULL;
        stream->eof = 0;
 }
 
@@ -1007,8 +1229,9 @@ file_close(FILE_T file)
        }
        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
@@ -1040,9 +1263,9 @@ gzwfile_open(const char *path)
         return NULL;
     state = gzwfile_fdopen(fd);
     if (state == NULL) {
-       save_errno = errno;
-        close(fd);
         save_errno = errno;
+        close(fd);
+        errno = save_errno;
     }
     return state;
 }
@@ -1085,11 +1308,9 @@ gz_init(GZWFILE_T 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;
     }
 
@@ -1100,8 +1321,15 @@ gz_init(GZWFILE_T state)
     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;
     }
 
@@ -1161,7 +1389,8 @@ gz_comp(GZWFILE_T state, int flush)
         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;