Rename WTAP_ERR_REC_TYPE_UNSUPPORTED to WTAP_ERR_UNWRITABLE_REC_TYPE.
[metze/wireshark/wip.git] / wiretap / netscaler.c
index 897615c85a04d0ef4c8a492977ae5fcd5ed46b9b..ad2840bf827ace35596a0b9cfa49ef6c8b940c8c 100644 (file)
@@ -1,6 +1,4 @@
 /* netscaler.c
- *
- * $Id$
  *
  * Wiretap Library
  * Copyright (c) 2006 by Ravi Kondamuru <Ravi.Kondamuru@citrix.com>
@@ -25,7 +23,7 @@
 #include <string.h>
 #include "wtap-int.h"
 #include "file_wrappers.h"
-#include "buffer.h"
+#include <wsutil/buffer.h>
 #include "netscaler.h"
 
 /* Defines imported from netscaler code: nsperfrc.h */
@@ -33,6 +31,7 @@
 #define NSPR_SIGSTR_V10 "NetScaler Performance Data"
 #define NSPR_SIGSTR_V20 "NetScaler V20 Performance Data"
 #define NSPR_SIGSTR     NSPR_SIGSTR_V20
+#define NSPR_SIGSTR_V30 "Netscaler V30 Performance Data"
 /* Defined but not used */
 #define NSPR_SIGSTR_V21 "NetScaler V21 Performance Data"
 #define NSPR_SIGSTR_V22 "NetScaler V22 Performance Data"
@@ -51,6 +50,7 @@
  * other than the type and zero or more additional padding bytes).
  */
 #define NSPR_PAGESIZE   8192
+#define NSPR_PAGESIZE_TRACE (2*NSPR_PAGESIZE)
 
 /* The different record types
 ** NOTE: The Record Type is two byte fields and unused space is recognized by
@@ -63,6 +63,7 @@
 #define NSPR_UNUSEDSPACE_V20    0x00    /* rest of the page is unused */
 #define NSPR_SIGNATURE_V10      0x0101  /* signature */
 #define NSPR_SIGNATURE_V20      0x01    /* signature */
+#define NSPR_SIGNATURE_V30      NSPR_SIGNATURE_V20
 #define NSPR_ABSTIME_V10        0x0107  /* data capture time in secs from 1970*/
 #define NSPR_ABSTIME_V20        0x07    /* data capture time in secs from 1970*/
 #define NSPR_RELTIME_V10        0x0108  /* relative time in ms from last time */
@@ -111,6 +112,7 @@ typedef struct nspr_header_v10
                                    /* end of declaration */
 #define NSPR_HEADER3B_V21 NSPR_HEADER3B_V20
 #define NSPR_HEADER3B_V22 NSPR_HEADER3B_V20
+#define NSPR_HEADER3B_V30 NSPR_HEADER3B_V20
 
 typedef struct nspr_hd_v20
 {
@@ -177,6 +179,16 @@ typedef struct nspr_signature_v20
 } nspr_signature_v20_t;
 #define nspr_signature_v20_s    ((guint32)sizeof(nspr_signature_v20_t))
 
+/* NSPR_SIGNATURE_V30 structure */
+#define NSPR_SIGSIZE_V30        sizeof(NSPR_SIGSTR_V30) /* signature value size in bytes */
+typedef struct nspr_signature_v30
+{
+    NSPR_HEADER_V20(sig);  /* short performance header */
+    guint8 sig_EndianType; /* Endian Type for the data */
+    gchar sig_Signature[NSPR_SIGSIZE_V30]; /* Signature value */
+} nspr_signature_v30_t;
+#define nspr_signature_v30_s    ((guint32)sizeof(nspr_signature_v30_t))
+
 /* NSPR_ABSTIME_V10 and NSPR_SYSTARTIME_V10 structure */
 typedef struct nspr_abstime_v10
 {
@@ -424,6 +436,32 @@ typedef struct nspr_pktracepart_v25
 #define pp_src_vmname    pp_Data
 #define pp_dst_vmname    pp_Data
 
+
+/* New full packet trace structure v30 for multipage spanning data */
+typedef struct  nspr_pktracefull_v30
+{
+    NSPR_HEADER3B_V30(fp);  /* long performance header */
+    guint8 fp_DevNo;   /* Network Device (NIC) number */
+    guint8 fp_AbsTimeHr[8];  /*High resolution absolute time in nanosec*/
+    guint8 fp_PcbDevNo[4];    /* PCB devno */
+    guint8 fp_lPcbDevNo[4];   /* link PCB devno */
+    guint8 fp_PktSizeOrg[2];  /* Original packet size */
+    guint8 fp_VlanTag[2]; /* vlan tag */
+    guint8 fp_Coreid[2]; /* coreid of the packet */
+    guint8 fp_srcNodeId[2]; /* cluster nodeid of the packet */
+    guint8 fp_destNodeId[2];
+    guint8 fp_clFlags;
+    guint8 fp_src_vmname_len;
+    guint8 fp_dst_vmname_len;
+    guint8 fp_reserved[3];
+    guint8 fp_ns_activity[4];
+    guint8 fp_reserved_32[12];
+    guint8 fp_Data[0]; /* packet data starts here */
+} nspr_pktracefull_v30_t;
+#define nspr_pktracefull_v30_s  ((guint32)(sizeof(nspr_pktracefull_v30_t)))
+#define fp_src_vmname   fp_Data
+#define fp_dst_vmname   fp_Data
+
 /* New partial packet trace structure v26 for vm info tracing */
 typedef struct nspr_pktracepart_v26
 {
@@ -475,6 +513,7 @@ typedef struct nspr_pktracepart_v26
     TRACE_V10_REC_LEN_OFF(phdr,enumprefix,structprefix,structname)
 
 #define TRACE_PART_V10_REC_LEN_OFF(phdr,enumprefix,structprefix,structname) \
+    (phdr)->rec_type = REC_TYPE_PACKET;\
     (phdr)->presence_flags |= WTAP_HAS_CAP_LEN;\
     (phdr)->len =  pletoh16(&pp->pp_PktSizeOrg) + nspr_pktracepart_v10_s;\
     (phdr)->caplen =  pletoh16(&pp->nsprRecordSize);\
@@ -516,6 +555,9 @@ typedef struct nspr_pktracepart_v26
     TRACE_V25_REC_LEN_OFF(phdr,enumprefix,structprefix,structname)\
     __TNO(phdr,enumprefix,structprefix,structname,ns_activity,ns_activity)\
 
+#define TRACE_V30_REC_LEN_OFF(phdr, enumprefix, structprefix, structname) \
+    TRACE_V26_REC_LEN_OFF(phdr,enumprefix,structprefix,structname)\
+
     TRACE_V10_REC_LEN_OFF(NULL,v10_part,pp,pktracepart_v10)
     TRACE_V10_REC_LEN_OFF(NULL,v10_full,fp,pktracefull_v10)
     TRACE_V20_REC_LEN_OFF(NULL,v20_part,pp,pktracepart_v20)
@@ -532,6 +574,7 @@ typedef struct nspr_pktracepart_v26
     TRACE_V25_REC_LEN_OFF(NULL,v25_full,fp,pktracefull_v25)
     TRACE_V26_REC_LEN_OFF(NULL,v26_part,pp,pktracepart_v26)
     TRACE_V26_REC_LEN_OFF(NULL,v26_full,fp,pktracefull_v26)
+    TRACE_V30_REC_LEN_OFF(NULL,v30_full,fp,pktracefull_v30)
 
 #undef __TNV1O
 #undef __TNV1L
@@ -575,13 +618,19 @@ static gboolean nstrace_read_v10(wtap *wth, int *err, gchar **err_info,
                                  gint64 *data_offset);
 static gboolean nstrace_read_v20(wtap *wth, int *err, gchar **err_info,
                                  gint64 *data_offset);
+static gboolean nstrace_read_v30(wtap *wth, int *err, gchar **err_info,
+                                 gint64 *data_offset);
 static gboolean nstrace_seek_read_v10(wtap *wth, gint64 seek_off,
                                       struct wtap_pkthdr *phdr,
-                                      Buffer *buf, int length,
+                                      Buffer *buf,
                                       int *err, gchar **err_info);
 static gboolean nstrace_seek_read_v20(wtap *wth, gint64 seek_off,
                                       struct wtap_pkthdr *phdr,
-                                      Buffer *buf, int length,
+                                      Buffer *buf,
+                                      int *err, gchar **err_info);
+static gboolean nstrace_seek_read_v30(wtap *wth, gint64 seek_off,
+                                      struct wtap_pkthdr *phdr,
+                                      Buffer *buf,
                                       int *err, gchar **err_info);
 static void nstrace_close(wtap *wth);
 
@@ -591,7 +640,7 @@ static gboolean nstrace_set_start_time(wtap *wth);
 static guint64 ns_hrtime2nsec(guint32 tm);
 
 static gboolean nstrace_dump(wtap_dumper *wdh, const struct wtap_pkthdr *phdr,
-                             const guint8 *pd, int *err);
+                             const guint8 *pd, int *err, gchar **err_info);
 
 
 /*
@@ -599,7 +648,7 @@ static gboolean nstrace_dump(wtap_dumper *wdh, const struct wtap_pkthdr *phdr,
  * the last page of a file can be short.
  */
 #define GET_READ_PAGE_SIZE(remaining_file_size) ((gint32)((remaining_file_size>NSPR_PAGESIZE)?NSPR_PAGESIZE:remaining_file_size))
-
+#define GET_READ_PAGE_SIZEV3(remaining_file_size) ((gint32)((remaining_file_size>NSPR_PAGESIZE_TRACE)?NSPR_PAGESIZE_TRACE:remaining_file_size))
 
 static guint64 ns_hrtime2nsec(guint32 tm)
 {
@@ -618,18 +667,16 @@ static guint64 ns_hrtime2nsec(guint32 tm)
 /*
 ** Netscaler trace format open routines
 */
-int nstrace_open(wtap *wth, int *err, gchar **err_info)
+wtap_open_return_val nstrace_open(wtap *wth, int *err, gchar **err_info)
 {
     gchar *nstrace_buf;
     gint64 file_size;
     gint32 page_size;
     nstrace_t *nstrace;
-    int bytes_read;
 
-    errno = WTAP_ERR_CANT_READ;
 
     if ((file_size = wtap_file_size(wth, err)) == -1)
-        return 0;
+        return WTAP_OPEN_NOT_MINE;
 
     nstrace_buf = (gchar *)g_malloc(NSPR_PAGESIZE);
     page_size = GET_READ_PAGE_SIZE(file_size);
@@ -644,28 +691,31 @@ int nstrace_open(wtap *wth, int *err, gchar **err_info)
         wth->file_encap = WTAP_ENCAP_NSTRACE_2_0;
         break;
 
+    case WTAP_FILE_TYPE_SUBTYPE_NETSCALER_3_0:
+        wth->file_encap = WTAP_ENCAP_NSTRACE_3_0;
+        g_free(nstrace_buf);
+        nstrace_buf = (gchar *)g_malloc(NSPR_PAGESIZE_TRACE);
+        page_size = GET_READ_PAGE_SIZEV3(file_size);
+        break;
+
     default:
-        *err = WTAP_ERR_UNSUPPORTED;
-        *err_info = g_strdup_printf("nstrace: file type %d unsupported", wth->file_type_subtype);
+        /* No known signature found, assume it's not NetScaler */
         g_free(nstrace_buf);
-        return 0;
+        return WTAP_OPEN_NOT_MINE;
     }
 
     if ((file_seek(wth->fh, 0, SEEK_SET, err)) == -1)
     {
-        *err = file_error(wth->fh, err_info);
         g_free(nstrace_buf);
-        return 0;
+        return WTAP_OPEN_ERROR;
     }
 
-    bytes_read = file_read(nstrace_buf, page_size, wth->fh);
-    if (bytes_read != page_size)
+    if (!wtap_read_bytes(wth->fh, nstrace_buf, page_size, err, err_info))
     {
-        *err = file_error(wth->fh, err_info);
         g_free(nstrace_buf);
-        if (*err != 0 && *err != WTAP_ERR_SHORT_READ)
-            return -1;
-        return 0;
+        if (*err != WTAP_ERR_SHORT_READ)
+            return WTAP_OPEN_ERROR;
+        return WTAP_OPEN_NOT_MINE;
     }
 
     switch (wth->file_type_subtype)
@@ -679,6 +729,11 @@ int nstrace_open(wtap *wth, int *err, gchar **err_info)
         wth->subtype_read = nstrace_read_v20;
         wth->subtype_seek_read = nstrace_seek_read_v20;
         break;
+
+    case WTAP_FILE_TYPE_SUBTYPE_NETSCALER_3_0:
+        wth->subtype_read = nstrace_read_v30;
+        wth->subtype_seek_read = nstrace_seek_read_v30;
+        break;
     }
     wth->subtype_close = nstrace_close;
 
@@ -701,32 +756,29 @@ int nstrace_open(wtap *wth, int *err, gchar **err_info)
         /* Reset the read pointer to start of the file. */
         if ((file_seek(wth->fh, 0, SEEK_SET, err)) == -1)
         {
-            *err = file_error(wth->fh, err_info);
             g_free(nstrace->pnstrace_buf);
             g_free(nstrace);
-            return 0;
+            return WTAP_OPEN_ERROR;
         }
 
         /* Read the first page of data */
-        bytes_read = file_read(nstrace_buf, page_size, wth->fh);
-        if (bytes_read != page_size)
+        if (!wtap_read_bytes(wth->fh, nstrace_buf, page_size, err, err_info))
         {
-            *err = file_error(wth->fh, err_info);
             g_free(nstrace->pnstrace_buf);
             g_free(nstrace);
-            return 0;
+            return WTAP_OPEN_ERROR;
         }
 
         /* reset the buffer offset */
         nstrace->nstrace_buf_offset = 0;
     }
 
-    wth->tsprecision = WTAP_FILE_TSPREC_NSEC;
+    wth->file_tsprec = WTAP_TSPREC_NSEC;
     wth->phdr.ts.secs = nstrace->nspm_curtime;
     wth->phdr.ts.nsecs = 0;
 
     *err = 0;
-    return 1;
+    return WTAP_OPEN_MINE;
 }
 
 
@@ -737,12 +789,15 @@ int nstrace_open(wtap *wth, int *err, gchar **err_info)
 
 nspm_signature_func(10)
 nspm_signature_func(20)
+nspm_signature_func(30)
 
 /*
-** Check signature and return the version number of the signature.
-** If not found, it returns 0. At the time of return from this function
-** we might not be at the first page. So after a call to this function, there
-** has to be a file seek to return to the start of the first page.
+** Check signature and return the file type and subtype for files with
+** that signature.  If it finds no signature that it recognizes, it
+** returns WTAP_FILE_TYPE_SUBTYPE_UNKNOWN. At the time of return from
+** this function we might not be at the first page. So after a call to
+** this function, there has to be a file seek to return to the start
+** of the first page.
 */
 static guint32
 nspm_signature_version(wtap *wth, gchar *nstrace_buf, gint32 len)
@@ -766,19 +821,23 @@ nspm_signature_version(wtap *wth, gchar *nstrace_buf, gint32 len)
 #define sigv20p    ((nspr_signature_v20_t*)dp)
             if ((sigv20p->sig_RecordType == NSPR_SIGNATURE_V20) &&
                 (sigv20p->sig_RecordSize <= len) &&
-                ((gint32)sizeof(NSPR_SIGSTR_V20) <= len) &&
-                (!nspm_signature_isv20(sigv20p->sig_Signature)))
-                return WTAP_FILE_TYPE_SUBTYPE_NETSCALER_2_0;
+                ((gint32)sizeof(NSPR_SIGSTR_V20) <= len))
+            {
+                if (!nspm_signature_isv20(sigv20p->sig_Signature))
+                    return WTAP_FILE_TYPE_SUBTYPE_NETSCALER_2_0;
+                else if (!nspm_signature_isv30(sigv20p->sig_Signature))
+                    return WTAP_FILE_TYPE_SUBTYPE_NETSCALER_3_0;
+            }
 #undef    sigv20p
         }
     }
 
-    return 0;    /* no version found */
+    return WTAP_FILE_TYPE_SUBTYPE_UNKNOWN;    /* no version found */
 }
 
-#define nspr_getv10recordtype(hdp) (pletoh16(&hdp->nsprRecordType))
-#define nspr_getv10recordsize(hdp) (pletoh16(&hdp->nsprRecordSize))
-#define nspr_getv20recordtype(hdp) (hdp->phd_RecordType)
+#define nspr_getv10recordtype(hdp) (pletoh16(&(hdp)->nsprRecordType))
+#define nspr_getv10recordsize(hdp) (pletoh16(&(hdp)->nsprRecordSize))
+#define nspr_getv20recordtype(hdp) ((hdp)->phd_RecordType)
 #define nspr_getv20recordsize(hdp) \
     (((hdp)->phd_RecordSizeLow & NSPR_V20RECORDSIZE_2BYTES)? \
         (((hdp)->phd_RecordSizeHigh * NSPR_V20RECORDSIZE_2BYTES)+ \
@@ -825,7 +884,6 @@ nstrace_set_start_time_ver(20)
 
 #undef nspr_getv10recordtype
 #undef nspr_getv20recordtype
-#undef nspr_getv10recordsize
 
 /*
 ** Set the start time of the trace file. We look for the first ABSTIME record. We use that
@@ -839,7 +897,8 @@ static gboolean nstrace_set_start_time(wtap *wth)
         return nstrace_set_start_time_v10(wth);
     else if (wth->file_type_subtype == WTAP_FILE_TYPE_SUBTYPE_NETSCALER_2_0)
         return nstrace_set_start_time_v20(wth);
-
+    else if (wth->file_type_subtype == WTAP_FILE_TYPE_SUBTYPE_NETSCALER_3_0)
+        return nstrace_set_start_time_v20(wth);
     return FALSE;
 }
 
@@ -889,13 +948,14 @@ static gboolean nstrace_read_v10(wtap *wth, int *err, gchar **err_info, gint64 *
              * as the time stamps in the records are relative to\
              * the previous packet.\
              */\
+            (phdr)->rec_type = REC_TYPE_PACKET;\
             (phdr)->presence_flags = WTAP_HAS_TS;\
             nsg_creltime += ns_hrtime2nsec(pletoh32(&fp->fp_RelTimeHr));\
             (phdr)->ts.secs = nstrace->nspm_curtime + (guint32) (nsg_creltime / 1000000000);\
             (phdr)->ts.nsecs = (guint32) (nsg_creltime % 1000000000);\
             TRACE_FULL_V##type##_REC_LEN_OFF(phdr,v##type##_full,fp,pktracefull_v##type);\
-            buffer_assure_space(wth->frame_buffer, (phdr)->caplen);\
-            memcpy(buffer_start_ptr(wth->frame_buffer), fp, (phdr)->caplen);\
+            ws_buffer_assure_space(wth->frame_buffer, (phdr)->caplen);\
+            memcpy(ws_buffer_start_ptr(wth->frame_buffer), fp, (phdr)->caplen);\
             *data_offset = nstrace->xxx_offset + nstrace_buf_offset;\
             nstrace->nstrace_buf_offset = nstrace_buf_offset + (phdr)->len;\
             nstrace->nstrace_buflen = nstrace_buflen;\
@@ -912,13 +972,14 @@ static gboolean nstrace_read_v10(wtap *wth, int *err, gchar **err_info, gint64 *
              * as the time stamps in the records are relative to\
              * the previous packet.\
              */\
+            (phdr)->rec_type = REC_TYPE_PACKET;\
             (phdr)->presence_flags = WTAP_HAS_TS;\
             nsg_creltime += ns_hrtime2nsec(pletoh32(&pp->pp_RelTimeHr));\
             (phdr)->ts.secs = nstrace->nspm_curtime + (guint32) (nsg_creltime / 1000000000);\
             (phdr)->ts.nsecs = (guint32) (nsg_creltime % 1000000000);\
             TRACE_PART_V##type##_REC_LEN_OFF(phdr,v##type##_part,pp,pktracepart_v##type);\
-            buffer_assure_space(wth->frame_buffer, (phdr)->caplen);\
-            memcpy(buffer_start_ptr(wth->frame_buffer), pp, (phdr)->caplen);\
+            ws_buffer_assure_space(wth->frame_buffer, (phdr)->caplen);\
+            memcpy(ws_buffer_start_ptr(wth->frame_buffer), pp, (phdr)->caplen);\
             *data_offset = nstrace->xxx_offset + nstrace_buf_offset;\
             nstrace->nstrace_buf_offset = nstrace_buf_offset + (phdr)->caplen;\
             nstrace->nsg_creltime = nsg_creltime;\
@@ -970,6 +1031,7 @@ static gboolean nstrace_read_v10(wtap *wth, int *err, gchar **err_info, gint64 *
 
 #define TIMEDEFV20(fp,type) \
     do {\
+        wth->phdr.rec_type = REC_TYPE_PACKET;\
         wth->phdr.presence_flags |= WTAP_HAS_TS;\
         nsg_creltime += ns_hrtime2nsec(pletoh32(fp->type##_RelTimeHr));\
         wth->phdr.ts.secs = nstrace->nspm_curtime + (guint32) (nsg_creltime / 1000000000);\
@@ -978,6 +1040,17 @@ static gboolean nstrace_read_v10(wtap *wth, int *err, gchar **err_info, gint64 *
 
 #define TIMEDEFV23(fp,type) \
     do {\
+        wth->phdr.rec_type = REC_TYPE_PACKET;\
+        wth->phdr.presence_flags |= WTAP_HAS_TS;\
+        /* access _AbsTimeHr as a 64bit value */\
+        nsg_creltime = pletoh64(fp->type##_AbsTimeHr);\
+        wth->phdr.ts.secs = (guint32) (nsg_creltime / 1000000000);\
+        wth->phdr.ts.nsecs = (guint32) (nsg_creltime % 1000000000);\
+    }while(0)
+
+#define TIMEDEFV30(fp,type) \
+    do {\
+        wth->phdr.rec_type = REC_TYPE_PACKET;\
         wth->phdr.presence_flags |= WTAP_HAS_TS;\
         /* access _AbsTimeHr as a 64bit value */\
         nsg_creltime = pletoh64(fp->type##_AbsTimeHr);\
@@ -990,8 +1063,10 @@ static gboolean nstrace_read_v10(wtap *wth, int *err, gchar **err_info, gint64 *
 #define TIMEDEFV24(fp,type) TIMEDEFV23(fp,type)
 #define TIMEDEFV25(fp,type) TIMEDEFV24(fp,type)
 #define TIMEDEFV26(fp,type) TIMEDEFV24(fp,type)
+
 #define PPSIZEDEFV20(phdr,pp,ver) \
     do {\
+        (phdr)->rec_type = REC_TYPE_PACKET;\
         (phdr)->presence_flags |= WTAP_HAS_CAP_LEN;\
         (phdr)->len = pletoh16(&pp->pp_PktSizeOrg) + nspr_pktracepart_v##ver##_s;\
         (phdr)->caplen = nspr_getv20recordsize((nspr_hd_v20_t *)pp);\
@@ -1017,6 +1092,14 @@ static gboolean nstrace_read_v10(wtap *wth, int *err, gchar **err_info, gint64 *
 #define FPSIZEDEFV25(phdr,fp,ver) FPSIZEDEFV20(phdr,fp,ver)
 #define FPSIZEDEFV26(phdr,fp,ver) FPSIZEDEFV20(phdr,fp,ver)
 
+#define FPSIZEDEFV30(phdr,fp,ver)\
+    do {\
+        (phdr)->rec_type = REC_TYPE_PACKET;\
+        (phdr)->presence_flags |= WTAP_HAS_CAP_LEN;\
+        (phdr)->len = pletoh16(&fp->fp_PktSizeOrg) + nspr_pktracefull_v##ver##_s;\
+        (phdr)->caplen = nspr_getv20recordsize((nspr_hd_v20_t *)fp);\
+    }while(0)
+
 #define PACKET_DESCRIBE(phdr,FPTIMEDEF,SIZEDEF,ver,enumprefix,type,structname,TYPE)\
     do {\
         nspr_##structname##_t *fp= (nspr_##structname##_t*)&nstrace_buf[nstrace_buf_offset];\
@@ -1024,8 +1107,8 @@ static gboolean nstrace_read_v10(wtap *wth, int *err, gchar **err_info, gint64 *
         SIZEDEF##ver((phdr),fp,ver);\
         TRACE_V##ver##_REC_LEN_OFF((phdr),enumprefix,type,structname);\
         (phdr)->pseudo_header.nstr.rec_type = NSPR_HEADER_VERSION##TYPE;\
-        buffer_assure_space(wth->frame_buffer, (phdr)->caplen);\
-        memcpy(buffer_start_ptr(wth->frame_buffer), fp, (phdr)->caplen);\
+        ws_buffer_assure_space(wth->frame_buffer, (phdr)->caplen);\
+        memcpy(ws_buffer_start_ptr(wth->frame_buffer), fp, (phdr)->caplen);\
         *data_offset = nstrace->xxx_offset + nstrace_buf_offset;\
         nstrace->nstrace_buf_offset = nstrace_buf_offset + nspr_getv20recordsize((nspr_hd_v20_t *)fp);\
         nstrace->nstrace_buflen = nstrace_buflen;\
@@ -1142,12 +1225,120 @@ static gboolean nstrace_read_v20(wtap *wth, int *err, gchar **err_info, gint64 *
 
 #undef PACKET_DESCRIBE
 
+#define PACKET_DESCRIBE(phdr,FPTIMEDEF,SIZEDEF,ver,enumprefix,type,structname,TYPE)\
+    do {\
+    nspr_##structname##_t *fp = (nspr_##structname##_t *) &nstrace_buf[nstrace_buf_offset];\
+    TIMEDEFV##ver(fp,type);\
+    SIZEDEF##ver((phdr),fp,ver);\
+    TRACE_V##ver##_REC_LEN_OFF((phdr),enumprefix,type,structname);\
+    (phdr)->pseudo_header.nstr.rec_type = NSPR_HEADER_VERSION##TYPE;\
+    ws_buffer_assure_space(wth->frame_buffer, (phdr)->caplen);\
+    *data_offset = nstrace->xxx_offset + nstrace_buf_offset;\
+    while (nstrace_tmpbuff_off < nspr_##structname##_s) {\
+        nstrace_tmpbuff[nstrace_tmpbuff_off++] = nstrace_buf[nstrace_buf_offset++];\
+    }\
+    nst_dataSize = nspr_getv20recordsize(hdp);\
+    rec_size = nst_dataSize - nstrace_tmpbuff_off;\
+    nsg_nextPageOffset = ((nstrace_buf_offset + rec_size) >= NSPR_PAGESIZE_TRACE) ?\
+    ((nstrace_buf_offset + rec_size) - (NSPR_PAGESIZE_TRACE - 1)) : 0;\
+    while (nsg_nextPageOffset) {\
+        while (nstrace_buf_offset < NSPR_PAGESIZE_TRACE) {\
+            nstrace_tmpbuff[nstrace_tmpbuff_off++] = nstrace_buf[nstrace_buf_offset++];\
+        }\
+        nstrace_buflen = NSPR_PAGESIZE_TRACE;\
+        nstrace->xxx_offset += nstrace_buflen;\
+        bytes_read = file_read(nstrace_buf, NSPR_PAGESIZE_TRACE, wth->fh);\
+        if (bytes_read != NSPR_PAGESIZE_TRACE) {\
+            return FALSE;\
+        } else {\
+            nstrace_buf_offset = 0;\
+        }\
+        rec_size = nst_dataSize - nstrace_tmpbuff_off;\
+        nsg_nextPageOffset = ((nstrace_buf_offset + rec_size) >= NSPR_PAGESIZE_TRACE) ?\
+        ((nstrace_buf_offset + rec_size) - (NSPR_PAGESIZE_TRACE- 1)): 0;\
+    } \
+    while (nstrace_tmpbuff_off < nst_dataSize) {\
+        nstrace_tmpbuff[nstrace_tmpbuff_off++] = nstrace_buf[nstrace_buf_offset++];\
+    }\
+    memcpy(ws_buffer_start_ptr(wth->frame_buffer), nstrace_tmpbuff, (phdr)->caplen);\
+    nstrace->nstrace_buf_offset = nstrace_buf_offset;\
+    nstrace->nstrace_buflen = nstrace_buflen = ((gint32)NSPR_PAGESIZE_TRACE);\
+    nstrace->nsg_creltime = nsg_creltime;\
+    return TRUE;\
+} while(0)
+
+static gboolean nstrace_read_v30(wtap *wth, int *err, gchar **err_info, gint64 *data_offset)
+{
+    nstrace_t *nstrace = (nstrace_t *)wth->priv;
+    guint64 nsg_creltime;
+    gchar *nstrace_buf = nstrace->pnstrace_buf;
+    gint32 nstrace_buf_offset = nstrace->nstrace_buf_offset;
+    gint32 nstrace_buflen = nstrace->nstrace_buflen;
+    guint8 nstrace_tmpbuff[65536];
+    guint32 nstrace_tmpbuff_off=0,nst_dataSize=0,rec_size=0,nsg_nextPageOffset=0;
+    nspr_hd_v20_t *hdp;
+    int bytes_read;
+    *err = 0;
+    *err_info = NULL;
+
+    do
+    {
+        while ((nstrace_buf_offset < NSPR_PAGESIZE_TRACE) &&
+            nstrace_buf[nstrace_buf_offset])
+        {
+            hdp = (nspr_hd_v20_t *) &nstrace_buf[nstrace_buf_offset];
+            switch (hdp->phd_RecordType)
+            {
+
+#define GENERATE_CASE_FULL_V30(phdr,type,acttype) \
+        case NSPR_PDPKTRACEFULLTX_V##type:\
+        case NSPR_PDPKTRACEFULLTXB_V##type:\
+        case NSPR_PDPKTRACEFULLRX_V##type:\
+        case NSPR_PDPKTRACEFULLNEWRX_V##type:\
+            PACKET_DESCRIBE(phdr,TIMEDEF,FPSIZEDEFV,type,v##type##_full,fp,pktracefull_v##type,acttype);
+
+                GENERATE_CASE_FULL_V30(&wth->phdr,30,300);
+
+#undef GENERATE_CASE_FULL_V30
+
+                case NSPR_ABSTIME_V20:
+                {
+                    nstrace_buf_offset += nspr_getv20recordsize(hdp);
+                    ns_setabstime(nstrace, pletoh32(&((nspr_abstime_v20_t *) &nstrace_buf[nstrace_buf_offset])->abs_Time), pletoh16(&((nspr_abstime_v20_t *) &nstrace_buf[nstrace_buf_offset])->abs_RelTime));
+                    break;
+                }
+
+                case NSPR_RELTIME_V20:
+                {
+                    ns_setrelativetime(nstrace, pletoh16(&((nspr_abstime_v20_t *) &nstrace_buf[nstrace_buf_offset])->abs_RelTime));
+                    nstrace_buf_offset += nspr_getv20recordsize(hdp);
+                    break;
+                }
+
+                default:
+                {
+                    nstrace_buf_offset += nspr_getv20recordsize(hdp);
+                    break;
+                }
+            }
+        }
+        nstrace_buf_offset = 0;
+        nstrace->xxx_offset += nstrace_buflen;
+        nstrace_buflen = NSPR_PAGESIZE_TRACE;
+    } while((nstrace_buflen > 0) && (bytes_read = file_read(nstrace_buf, nstrace_buflen, wth->fh)) && (bytes_read == nstrace_buflen));
+
+    return FALSE;
+}
+
+#undef PACKET_DESCRIBE
+
 static gboolean nstrace_seek_read_v10(wtap *wth, gint64 seek_off,
-    struct wtap_pkthdr *phdr, Buffer *buf, int length,
-    int *err, gchar **err_info)
+    struct wtap_pkthdr *phdr, Buffer *buf, int *err, gchar **err_info)
 {
+    nspr_hd_v10_t hdr;
+    guint record_length;
     guint8 *pd;
-    int bytes_read;
+    unsigned int bytes_to_read;
     nspr_pktracefull_v10_t *fp;
     nspr_pktracepart_v10_t *pp;
 
@@ -1157,18 +1348,33 @@ static gboolean nstrace_seek_read_v10(wtap *wth, gint64 seek_off,
         return FALSE;
 
     /*
-    ** Read the packet data.
+    ** Read the record header.
     */
-    buffer_assure_space(buf, length);
-    pd = buffer_start_ptr(buf);
-    bytes_read = file_read(pd, length, wth->random_fh);
-    if (bytes_read != length) {
-        *err = file_error(wth->random_fh, err_info);
-        if (*err == 0)
-            *err = WTAP_ERR_SHORT_READ;
+    if (!wtap_read_bytes(wth->random_fh, (void *)&hdr, sizeof hdr,
+                         err, err_info))
         return FALSE;
+
+    /*
+    ** Get the record length.
+    */
+    record_length = nspr_getv10recordsize(&hdr);
+
+    /*
+    ** Copy the header to the buffer and read the rest of the record..
+    */
+    ws_buffer_assure_space(buf, record_length);
+    pd = ws_buffer_start_ptr(buf);
+    memcpy(pd, (void *)&hdr, sizeof hdr);
+    if (record_length > sizeof hdr) {
+        bytes_to_read = (unsigned int)(record_length - sizeof hdr);
+        if (!wtap_read_bytes(wth->random_fh, pd + sizeof hdr, bytes_to_read,
+                             err, err_info))
+            return FALSE;
     }
 
+    /*
+    ** Fill in what part of the struct wtap_pkthdr we can.
+    */
 #define GENERATE_CASE_FULL(phdr,type,acttype) \
         case NSPR_PDPKTRACEFULLTX_V##type:\
         case NSPR_PDPKTRACEFULLTXB_V##type:\
@@ -1209,11 +1415,13 @@ static gboolean nstrace_seek_read_v10(wtap *wth, gint64 seek_off,
     }while(0)
 
 static gboolean nstrace_seek_read_v20(wtap *wth, gint64 seek_off,
-    struct wtap_pkthdr *phdr, Buffer *buf, int length,
-    int *err, gchar **err_info)
+    struct wtap_pkthdr *phdr, Buffer *buf, int *err, gchar **err_info)
 {
+    nspr_hd_v20_t hdr;
+    guint record_length;
+    guint hdrlen;
     guint8 *pd;
-    int bytes_read;
+    unsigned int bytes_to_read;
 
     *err = 0;
 
@@ -1221,16 +1429,38 @@ static gboolean nstrace_seek_read_v20(wtap *wth, gint64 seek_off,
         return FALSE;
 
     /*
-    ** Read the packet data.
+    ** Read the first 2 bytes of the record header.
     */
-    buffer_assure_space(buf, length);
-    pd = buffer_start_ptr(buf);
-    bytes_read = file_read(pd, length, wth->random_fh);
-    if (bytes_read != length) {
-        *err = file_error(wth->random_fh, err_info);
-        if (*err == 0)
-            *err = WTAP_ERR_SHORT_READ;
+    if (!wtap_read_bytes(wth->random_fh, (void *)&hdr, 2, err, err_info))
         return FALSE;
+    hdrlen = 2;
+
+    /*
+    ** Is there a third byte?  If so, read it.
+    */
+    if (hdr.phd_RecordSizeLow & NSPR_V20RECORDSIZE_2BYTES) {
+        if (!wtap_read_bytes(wth->random_fh, (void *)&hdr.phd_RecordSizeHigh, 1,
+                             err, err_info))
+            return FALSE;
+        hdrlen = 3;
+    }
+
+    /*
+    ** Get the record length.
+    */
+    record_length = nspr_getv20recordsize(&hdr);
+
+    /*
+    ** Copy the header to the buffer and read the rest of the record..
+    */
+    ws_buffer_assure_space(buf, record_length);
+    pd = ws_buffer_start_ptr(buf);
+    memcpy(pd, (void *)&hdr, hdrlen);
+    if (record_length > hdrlen) {
+        bytes_to_read = (unsigned int)(record_length - hdrlen);
+        if (!wtap_read_bytes(wth->random_fh, pd + hdrlen, bytes_to_read,
+                             err, err_info))
+            return FALSE;
     }
 
 #define GENERATE_CASE_FULL(phdr,type,acttype) \
@@ -1285,6 +1515,73 @@ static gboolean nstrace_seek_read_v20(wtap *wth, gint64 seek_off,
     return TRUE;
 }
 
+
+static gboolean nstrace_seek_read_v30(wtap *wth, gint64 seek_off,
+    struct wtap_pkthdr *phdr, Buffer *buf, int *err, gchar **err_info)
+{
+    nspr_hd_v20_t hdr;
+    guint record_length;
+    guint hdrlen;
+    guint8 *pd;
+    unsigned int bytes_to_read;
+
+    *err = 0;
+
+    if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
+        return FALSE;
+    /*
+    ** Read the first 2 bytes of the record header.
+    */
+    if (!wtap_read_bytes(wth->random_fh, (void *)&hdr, 2, err, err_info))
+        return FALSE;
+    hdrlen = 2;
+
+    /*
+    ** Is there a third byte?  If so, read it.
+    */
+    if (hdr.phd_RecordSizeLow & NSPR_V20RECORDSIZE_2BYTES) {
+        if (!wtap_read_bytes(wth->random_fh, (void *)&hdr.phd_RecordSizeHigh, 1,
+                             err, err_info))
+            return FALSE;
+        hdrlen = 3;
+    }
+
+    /*
+    ** Get the record length.
+    */
+    record_length = nspr_getv20recordsize(&hdr);
+
+    /*
+    ** Copy the header to the buffer and read the rest of the record..
+    */
+    ws_buffer_assure_space(buf, record_length);
+    pd = ws_buffer_start_ptr(buf);
+    memcpy(pd, (void *)&hdr, hdrlen);
+    if (record_length > hdrlen) {
+        bytes_to_read = (unsigned int)(record_length - hdrlen);
+        if (!wtap_read_bytes(wth->random_fh, pd + hdrlen, bytes_to_read,
+                             err, err_info))
+            return FALSE;
+    }
+
+#define GENERATE_CASE_V30(phdr,type,acttype) \
+    case NSPR_PDPKTRACEFULLTX_V##type:\
+    case NSPR_PDPKTRACEFULLTXB_V##type:\
+    case NSPR_PDPKTRACEFULLRX_V##type:\
+    case NSPR_PDPKTRACEFULLNEWRX_V##type:\
+    TRACE_V##type##_REC_LEN_OFF((phdr),v##type##_full,fp,pktracefull_v##type);\
+        (phdr)->pseudo_header.nstr.rec_type = NSPR_HEADER_VERSION##acttype;\
+        break;
+
+        switch ((( nspr_hd_v20_t*)pd)->phd_RecordType)
+        {
+            GENERATE_CASE_V30(phdr,30, 300);
+        }
+
+    return TRUE;
+}
+
+
 /*
 ** Netscaler trace format close routines.
 */
@@ -1309,7 +1606,7 @@ int nstrace_10_dump_can_write_encap(int encap)
     if (encap == WTAP_ENCAP_NSTRACE_1_0)
         return 0;
 
-    return WTAP_ERR_UNSUPPORTED_ENCAP;
+    return WTAP_ERR_UNWRITABLE_ENCAP;
 }
 
 
@@ -1320,9 +1617,18 @@ int nstrace_20_dump_can_write_encap(int encap)
     if (encap == WTAP_ENCAP_NSTRACE_2_0)
         return 0;
 
-    return WTAP_ERR_UNSUPPORTED_ENCAP;
+    return WTAP_ERR_UNWRITABLE_ENCAP;
 }
 
+/* Returns 0 if we could write the specified encapsulation type,
+** an error indication otherwise. */
+int nstrace_30_dump_can_write_encap(int encap)
+{
+    if (encap == WTAP_ENCAP_NSTRACE_3_0)
+        return 0;
+
+    return WTAP_ERR_UNWRITABLE_ENCAP;
+}
 
 /* Returns TRUE on success, FALSE on failure; sets "*err" to an error code on
 ** failure */
@@ -1383,6 +1689,21 @@ static gboolean nstrace_add_signature(wtap_dumper *wdh, int *err)
         /* Move forward the page offset */
         nstrace->page_offset += (guint16) sig20.sig_RecordSize;
 
+    } else if (wdh->file_type_subtype == WTAP_FILE_TYPE_SUBTYPE_NETSCALER_3_0)
+    {
+        nspr_signature_v30_t sig30;
+
+        sig30.sig_RecordType = NSPR_SIGNATURE_V30;
+        sig30.sig_RecordSize = nspr_signature_v30_s;
+        memcpy(sig30.sig_Signature, NSPR_SIGSTR_V30, sizeof(NSPR_SIGSTR_V30));
+
+        /* Write the record into the file */
+        if (!wtap_dump_file_write(wdh, &sig30, sig30.sig_RecordSize,
+            err))
+            return FALSE;
+
+        /* Move forward the page offset */
+        nstrace->page_offset += (guint16) sig30.sig_RecordSize;
     } else
     {
         g_assert_not_reached();
@@ -1427,8 +1748,8 @@ nstrace_add_abstime(wtap_dumper *wdh, const struct wtap_pkthdr *phdr,
         /* Move forward the page offset */
         nstrace->page_offset += nspr_abstime_v10_s;
 
-    } else if (wdh->file_type_subtype == WTAP_FILE_TYPE_SUBTYPE_NETSCALER_2_0)
-    {
+    } else if ((wdh->file_type_subtype == WTAP_FILE_TYPE_SUBTYPE_NETSCALER_2_0) ||
+        (wdh->file_type_subtype == WTAP_FILE_TYPE_SUBTYPE_NETSCALER_3_0))    {
         guint32 reltime;
         guint64 abstime;
         nspr_abstime_v20_t abs20;
@@ -1463,10 +1784,16 @@ nstrace_add_abstime(wtap_dumper *wdh, const struct wtap_pkthdr *phdr,
 /* Write a record for a packet to a dump file.
    Returns TRUE on success, FALSE on failure. */
 static gboolean nstrace_dump(wtap_dumper *wdh, const struct wtap_pkthdr *phdr,
-    const guint8 *pd, int *err)
+    const guint8 *pd, int *err, gchar **err_info _U_)
 {
     nstrace_dump_t *nstrace = (nstrace_dump_t *)wdh->priv;
 
+    /* We can only write packet records. */
+    if (phdr->rec_type != REC_TYPE_PACKET) {
+        *err = WTAP_ERR_UNWRITABLE_REC_TYPE;
+        return FALSE;
+    }
+
     if (nstrace->page_offset == 0)
     {
         /* Add the signature record and abs time record */
@@ -1476,6 +1803,11 @@ static gboolean nstrace_dump(wtap_dumper *wdh, const struct wtap_pkthdr *phdr,
                 !nstrace_add_abstime(wdh, phdr, pd, err))
                 return FALSE;
         } else if (wdh->file_type_subtype == WTAP_FILE_TYPE_SUBTYPE_NETSCALER_2_0)
+        {
+            if (!nstrace_add_signature(wdh, err) ||
+                !nstrace_add_abstime(wdh, phdr, pd, err))
+                return FALSE;
+        } else if (wdh->file_type_subtype == WTAP_FILE_TYPE_SUBTYPE_NETSCALER_3_0)
         {
             if (!nstrace_add_signature(wdh, err) ||
                 !nstrace_add_abstime(wdh, phdr, pd, err))
@@ -1513,7 +1845,7 @@ static gboolean nstrace_dump(wtap_dumper *wdh, const struct wtap_pkthdr *phdr,
             nstrace->page_offset += (guint16) phdr->caplen;
         } else if (wdh->file_type_subtype == WTAP_FILE_TYPE_SUBTYPE_NETSCALER_2_0)
         {
-            *err = WTAP_ERR_UNSUPPORTED_FILE_TYPE;
+            *err = WTAP_ERR_UNWRITABLE_FILE_TYPE;
             return FALSE;
         }
 
@@ -1528,7 +1860,7 @@ static gboolean nstrace_dump(wtap_dumper *wdh, const struct wtap_pkthdr *phdr,
     case NSPR_HEADER_VERSION206:
         if (wdh->file_type_subtype == WTAP_FILE_TYPE_SUBTYPE_NETSCALER_1_0)
         {
-            *err = WTAP_ERR_UNSUPPORTED_FILE_TYPE;
+            *err = WTAP_ERR_UNWRITABLE_FILE_TYPE;
             return FALSE;
         } else if (wdh->file_type_subtype == WTAP_FILE_TYPE_SUBTYPE_NETSCALER_2_0)
         {
@@ -1554,6 +1886,42 @@ static gboolean nstrace_dump(wtap_dumper *wdh, const struct wtap_pkthdr *phdr,
 
         break;
 
+    case NSPR_HEADER_VERSION300:
+        if (wdh->file_type_subtype == WTAP_FILE_TYPE_SUBTYPE_NETSCALER_1_0)
+        {
+            *err = WTAP_ERR_UNWRITABLE_FILE_TYPE;
+            return FALSE;
+        } else if (wdh->file_type_subtype == WTAP_FILE_TYPE_SUBTYPE_NETSCALER_2_0)
+        {
+            *err = WTAP_ERR_UNWRITABLE_FILE_TYPE;
+            return FALSE;
+        } else if (wdh->file_type_subtype == WTAP_FILE_TYPE_SUBTYPE_NETSCALER_3_0)
+        {
+            if (nstrace->page_offset + phdr->caplen >= nstrace->page_len)
+            {
+                /* Start on the next page */
+                if (wtap_dump_file_seek(wdh, (nstrace->page_len - nstrace->page_offset), SEEK_CUR, err) == -1)
+                    return FALSE;
+
+                nstrace->page_offset = 0;
+
+                /* Possibly add signature and abstime records and increment offset */
+                if (!nstrace_add_signature(wdh, err))
+                    return FALSE;
+            }
+
+            /* Write the actual record as is */
+            if (!wtap_dump_file_write(wdh, pd, phdr->caplen, err))
+                return FALSE;
+
+            nstrace->page_offset += (guint16) phdr->caplen;
+        } else
+        {
+            g_assert_not_reached();
+            return FALSE;
+        }
+        break;
+
     default:
         g_assert_not_reached();
         return FALSE;
@@ -1561,4 +1929,3 @@ static gboolean nstrace_dump(wtap_dumper *wdh, const struct wtap_pkthdr *phdr,
 
     return TRUE;
 }
-