Add tvb_get and proto_tree_add for string-encoded timestamps
authorHadriel Kaplan <hadrielk@yahoo.com>
Sat, 12 Apr 2014 04:32:20 +0000 (00:32 -0400)
committerAnders Broman <a.broman58@gmail.com>
Mon, 14 Apr 2014 11:47:39 +0000 (11:47 +0000)
This commit adds tvb_get_string_time and proto_tree_add_time_item routines for
getting nstime fields from the tvb when they are encoded in ASCII string form.

The proto_tree_add_time_item routine is also usable for normal
big/little-endian encoded time_t, and has the advantage of retrieving
the value even if there's no proto tree.

It also exposes the routines to Lua, both so that a Lua script can take
advantage of this, but also so I can write a testsuite to test the functions.

Change-Id: I955da10f68f2680e3da3a5be5ad8fdce7ed6808c
Reviewed-on: https://code.wireshark.org/review/1084
Reviewed-by: Anders Broman <a.broman58@gmail.com>
epan/proto.c
epan/proto.h
epan/tvbuff.c
epan/tvbuff.h
epan/wslua/wslua_tree.c
epan/wslua/wslua_tvb.c
test/lua/tvb.lua [new file with mode: 0644]
test/suite-wslua.sh
tools/checkAPIs.pl
wsutil/nstime.h

index a3284a07d8eea732eb3a15b9a6bc16f83a30b63a..2707f056ecc5bdfa62c4c5d6df159b4576a67d91 100644 (file)
@@ -27,6 +27,7 @@
 #include <ctype.h>
 #include <glib.h>
 #include <float.h>
+#include <errno.h>
 
 #include <wsutil/bits_ctz.h>
 #include <wsutil/bits_count_ones.h>
@@ -245,6 +246,12 @@ static expert_field ei_type_length_mismatch_error = EI_INIT;
 static expert_field ei_type_length_mismatch_warn = EI_INIT;
 static void register_type_length_mismatch(void);
 
+/* Handle number string decoding errors with expert info */
+static int proto_number_string_decoding_error = -1;
+static expert_field ei_number_string_decoding_failed_error = EI_INIT;
+static expert_field ei_number_string_decoding_erange_error = EI_INIT;
+static void register_number_string_decoding_error(void);
+
 static int proto_register_field_init(header_field_info *hfinfo, const int parent);
 
 /* special-case header field used within proto.c */
@@ -445,6 +452,7 @@ proto_init(void (register_all_protocols_func)(register_cb cb, gpointer client_da
        /* Register the pseudo-protocols used for exceptions. */
        register_show_exception();
        register_type_length_mismatch();
+       register_number_string_decoding_error();
 
        /* Have each built-in dissector register its protocols, fields,
           dissector tables, and dissectors to be called through a
@@ -1261,6 +1269,137 @@ get_int_value(proto_tree *tree, tvbuff_t *tvb, gint offset, gint length, const g
        return value;
 }
 
+/* this can be called when there is no tree, so don't add that as a param */
+static void
+get_time_value(tvbuff_t *tvb, const gint start, const gint length, const guint encoding,
+              nstime_t *time_stamp, const gboolean is_relative)
+{
+       guint32     tmpsecs;
+       guint64     todsecs;
+
+       /* relative timestamps don't do TOD/NTP */
+       if (is_relative &&
+               (encoding != (ENC_TIME_TIMESPEC|ENC_BIG_ENDIAN)) &&
+               (encoding != (ENC_TIME_TIMESPEC|ENC_LITTLE_ENDIAN)) )
+       {
+               /* XXX: I think this should call REPORT_DISSECTOR_BUG(), but
+                  the existing code didn't do that, so I'm not either */
+               return;
+       }
+
+       switch (encoding) {
+
+               case ENC_TIME_TIMESPEC|ENC_BIG_ENDIAN:
+                       /*
+                        * 4-byte UNIX epoch, possibly followed by
+                        * 4-byte fractional time in nanoseconds,
+                        * both big-endian.
+                        */
+                       time_stamp->secs  = (time_t)tvb_get_ntohl(tvb, start);
+                       if (length == 8)
+                               time_stamp->nsecs = tvb_get_ntohl(tvb, start+4);
+                       else
+                               time_stamp->nsecs = 0;
+                       break;
+
+               case ENC_TIME_TIMESPEC|ENC_LITTLE_ENDIAN:
+                       /*
+                        * 4-byte UNIX epoch, possibly followed by
+                        * 4-byte fractional time in nanoseconds,
+                        * both little-endian.
+                        */
+                       time_stamp->secs  = (time_t)tvb_get_letohl(tvb, start);
+                       if (length == 8)
+                               time_stamp->nsecs = tvb_get_letohl(tvb, start+4);
+                       else
+                               time_stamp->nsecs = 0;
+                       break;
+
+               case ENC_TIME_TOD|ENC_BIG_ENDIAN:
+                       /*
+                        * TOD time stamp, big-endian.
+                        */
+/* XXX - where should this go? */
+#define TOD_BASETIME G_GUINT64_CONSTANT(2208988800)
+
+                       todsecs  = tvb_get_ntoh64(tvb, start) >> 12;
+                       time_stamp->secs = (time_t)((todsecs  / 1000000) - TOD_BASETIME);
+                       time_stamp->nsecs = (int)((todsecs  % 1000000) * 1000);
+                       break;
+
+               case ENC_TIME_TOD|ENC_LITTLE_ENDIAN:
+                       /*
+                        * TOD time stamp, big-endian.
+                        */
+                       todsecs  = tvb_get_letoh64(tvb, start) >> 12 ;
+                       time_stamp->secs = (time_t)((todsecs  / 1000000) - TOD_BASETIME);
+                       time_stamp->nsecs = (int)((todsecs  % 1000000) * 1000);
+                       break;
+
+               case ENC_TIME_NTP|ENC_BIG_ENDIAN:
+                       /*
+                        * NTP time stamp, big-endian.
+                        */
+
+/* XXX - where should this go? */
+#define NTP_BASETIME G_GUINT64_CONSTANT(2208988800)
+
+                       /* We need a temporary variable here so the unsigned math
+                        * works correctly (for years > 2036 according to RFC 2030
+                        * chapter 3).
+                        */
+                       tmpsecs  = tvb_get_ntohl(tvb, start);
+                       if (tmpsecs)
+                               time_stamp->secs = (time_t)(tmpsecs - (guint32)NTP_BASETIME);
+                       else
+                               time_stamp->secs = tmpsecs; /* 0 */
+
+                       if (length == 8) {
+                               /*
+                                * We're using nanoseconds here (and we will
+                                * display nanoseconds), but NTP's timestamps
+                                * have a precision in microseconds or greater.
+                                * Round to 1 microsecond.
+                                */
+                               time_stamp->nsecs = (int)(1000000*(tvb_get_ntohl(tvb, start+4)/4294967296.0));
+                               time_stamp->nsecs *= 1000;
+                       } else {
+                               time_stamp->nsecs = 0;
+                       }
+                       break;
+
+               case ENC_TIME_NTP|ENC_LITTLE_ENDIAN:
+                       /*
+                        * NTP time stamp, big-endian.
+                        */
+                       tmpsecs  = tvb_get_letohl(tvb, start);
+                       if (tmpsecs)
+                               time_stamp->secs = (time_t)(tmpsecs - (guint32)NTP_BASETIME);
+                       else
+                               time_stamp->secs = tmpsecs; /* 0 */
+
+                       if (length == 8) {
+                               /*
+                                * We're using nanoseconds here (and we will
+                                * display nanoseconds), but NTP's timestamps
+                                * have a precision in microseconds or greater.
+                                * Round to 1 microsecond.
+                                */
+                               time_stamp->nsecs = (int)(1000000*(tvb_get_letohl(tvb, start+4)/4294967296.0));
+                               time_stamp->nsecs *= 1000;
+                       } else {
+                               time_stamp->nsecs = 0;
+                       }
+                       break;
+
+               default:
+                       DISSECTOR_ASSERT_NOT_REACHED();
+                       time_stamp->secs = (time_t)0;
+                       time_stamp->nsecs = 0;
+                       break;
+       }
+}
+
 static void
 tree_data_add_maybe_interesting_field(tree_data_t *tree_data, field_info *fi)
 {
@@ -1300,8 +1439,6 @@ proto_tree_new_item(field_info *new_fi, proto_tree *tree,
        double      doubleval;
        const char *string;
        nstime_t    time_stamp;
-       guint32     tmpsecs;
-       guint64     todsecs;
        gboolean    length_error;
 
        switch (new_fi->hfinfo->type) {
@@ -1676,117 +1813,8 @@ proto_tree_new_item(field_info *new_fi, proto_tree *tree,
                                report_type_length_mismatch(tree, "an absolute time value", length, length_error);
                        }
 
-                       switch (encoding) {
-
-                       case ENC_TIME_TIMESPEC|ENC_BIG_ENDIAN:
-                               /*
-                                * 4-byte UNIX epoch, possibly followed by
-                                * 4-byte fractional time in nanoseconds,
-                                * both big-endian.
-                                */
-                               time_stamp.secs  = (time_t)tvb_get_ntohl(tvb, start);
-                               if (length == 8)
-                                       time_stamp.nsecs = tvb_get_ntohl(tvb, start+4);
-                               else
-                                       time_stamp.nsecs = 0;
-                               break;
-
-                       case ENC_TIME_TIMESPEC|ENC_LITTLE_ENDIAN:
-                               /*
-                                * 4-byte UNIX epoch, possibly followed by
-                                * 4-byte fractional time in nanoseconds,
-                                * both little-endian.
-                                */
-                               time_stamp.secs  = (time_t)tvb_get_letohl(tvb, start);
-                               if (length == 8)
-                                       time_stamp.nsecs = tvb_get_letohl(tvb, start+4);
-                               else
-                                       time_stamp.nsecs = 0;
-                               break;
-
-                       case ENC_TIME_TOD|ENC_BIG_ENDIAN:
-                               /*
-                                * TOD time stamp, big-endian.
-                                */
-/* XXX - where should this go? */
-#define TOD_BASETIME G_GUINT64_CONSTANT(2208988800)
-
-                               todsecs  = tvb_get_ntoh64(tvb, start) >> 12;
-                               time_stamp.secs = (time_t)((todsecs  / 1000000) - TOD_BASETIME);
-                               time_stamp.nsecs = (int)((todsecs  % 1000000) * 1000);
-                               break;
+                       get_time_value(tvb, start, length, encoding, &time_stamp, FALSE);
 
-                       case ENC_TIME_TOD|ENC_LITTLE_ENDIAN:
-                               /*
-                                * TOD time stamp, big-endian.
-                                */
-                               todsecs  = tvb_get_letoh64(tvb, start) >> 12 ;
-                               time_stamp.secs = (time_t)((todsecs  / 1000000) - TOD_BASETIME);
-                               time_stamp.nsecs = (int)((todsecs  % 1000000) * 1000);
-                               break;
-
-                       case ENC_TIME_NTP|ENC_BIG_ENDIAN:
-                               /*
-                                * NTP time stamp, big-endian.
-                                */
-
-/* XXX - where should this go? */
-#define NTP_BASETIME G_GUINT64_CONSTANT(2208988800)
-
-                               /* We need a temporary variable here so the unsigned math
-                                * works correctly (for years > 2036 according to RFC 2030
-                                * chapter 3).
-                                */
-                               tmpsecs  = tvb_get_ntohl(tvb, start);
-                               if (tmpsecs)
-                                       time_stamp.secs = (time_t)(tmpsecs - (guint32)NTP_BASETIME);
-                               else
-                                       time_stamp.secs = tmpsecs; /* 0 */
-
-                               if (length == 8) {
-                                       /*
-                                        * We're using nanoseconds here (and we will
-                                        * display nanoseconds), but NTP's timestamps
-                                        * have a precision in microseconds or greater.
-                                        * Round to 1 microsecond.
-                                        */
-                                       time_stamp.nsecs = (int)(1000000*(tvb_get_ntohl(tvb, start+4)/4294967296.0));
-                                       time_stamp.nsecs *= 1000;
-                               } else {
-                                       time_stamp.nsecs = 0;
-                               }
-                               break;
-
-                       case ENC_TIME_NTP|ENC_LITTLE_ENDIAN:
-                               /*
-                                * NTP time stamp, big-endian.
-                                */
-                               tmpsecs  = tvb_get_letohl(tvb, start);
-                               if (tmpsecs)
-                                       time_stamp.secs = (time_t)(tmpsecs - (guint32)NTP_BASETIME);
-                               else
-                                       time_stamp.secs = tmpsecs; /* 0 */
-
-                               if (length == 8) {
-                                       /*
-                                        * We're using nanoseconds here (and we will
-                                        * display nanoseconds), but NTP's timestamps
-                                        * have a precision in microseconds or greater.
-                                        * Round to 1 microsecond.
-                                        */
-                                       time_stamp.nsecs = (int)(1000000*(tvb_get_letohl(tvb, start+4)/4294967296.0));
-                                       time_stamp.nsecs *= 1000;
-                               } else {
-                                       time_stamp.nsecs = 0;
-                               }
-                               break;
-
-                       default:
-                               DISSECTOR_ASSERT_NOT_REACHED();
-                               time_stamp.secs = (time_t)0;
-                               time_stamp.nsecs = 0;
-                               break;
-                       }
                        proto_tree_set_time(new_fi, &time_stamp);
                        break;
 
@@ -1806,39 +1834,14 @@ proto_tree_new_item(field_info *new_fi, proto_tree *tree,
                         */
                        if (encoding == TRUE)
                                encoding = ENC_TIME_TIMESPEC|ENC_LITTLE_ENDIAN;
-                       switch (encoding) {
 
                        if (length != 8 && length != 4) {
                                length_error = length < 4 ? TRUE : FALSE;
                                report_type_length_mismatch(tree, "a relative time value", length, length_error);
                        }
 
-                       case ENC_TIME_TIMESPEC|ENC_BIG_ENDIAN:
-                               /*
-                                * 4-byte UNIX epoch, possibly followed by
-                                * 4-byte fractional time in nanoseconds,
-                                * both big-endian.
-                                */
-                               time_stamp.secs  = (time_t)tvb_get_ntohl(tvb, start);
-                               if (length == 8)
-                                       time_stamp.nsecs = tvb_get_ntohl(tvb, start+4);
-                               else
-                                       time_stamp.nsecs = 0;
-                               break;
+                       get_time_value(tvb, start, length, encoding, &time_stamp, TRUE);
 
-                       case ENC_TIME_TIMESPEC|ENC_LITTLE_ENDIAN:
-                               /*
-                                * 4-byte UNIX epoch, possibly followed by
-                                * 4-byte fractional time in nanoseconds,
-                                * both little-endian.
-                                */
-                               time_stamp.secs  = (time_t)tvb_get_letohl(tvb, start);
-                               if (length == 8)
-                                       time_stamp.nsecs = tvb_get_letohl(tvb, start+4);
-                               else
-                                       time_stamp.nsecs = 0;
-                               break;
-                       }
                        proto_tree_set_time(new_fi, &time_stamp);
                        break;
 
@@ -1952,6 +1955,82 @@ proto_tree_add_item(proto_tree *tree, int hfindex, tvbuff_t *tvb,
        return proto_tree_add_item_new(tree, proto_registrar_get_nth(hfindex), tvb, start, length, encoding);
 }
 
+proto_item *
+proto_tree_add_time_item(proto_tree *tree, int hfindex, tvbuff_t *tvb,
+                          const gint start, gint length, const guint encoding,
+                          nstime_t *retval, gint *endoff, gint *err)
+{
+       field_info        *new_fi;
+       nstime_t           time_stamp;
+       gint               saved_err = 0;
+       header_field_info *hfinfo = proto_registrar_get_nth(hfindex);
+
+       DISSECTOR_ASSERT_HINT(hfinfo != NULL, "Not passed hfi!");
+
+       DISSECTOR_ASSERT_HINT((hfinfo->type == FT_ABSOLUTE_TIME ||
+               hfinfo->type == FT_RELATIVE_TIME),
+               "Called proto_tree_add_time_item but not a FT_XXX_TIME");
+
+       /* length has to be -1 or > 0 regardless of encoding */
+       if (length < -1 || length == 0) {
+               REPORT_DISSECTOR_BUG(wmem_strdup_printf(wmem_packet_scope(),
+                   "Invalid length %d passed to proto_tree_add_time_item", length));
+       }
+
+       time_stamp.secs  = 0;
+       time_stamp.nsecs = 0;
+
+       if (encoding & ENC_STR_TIME_MASK) {
+               tvb_get_string_time(tvb, start, length, encoding, &time_stamp, endoff);
+               /* grab the errno now before it gets overwritten */
+               saved_err = errno;
+       }
+       else {
+               const gboolean is_relative = (hfinfo->type == FT_RELATIVE_TIME) ? TRUE : FALSE;
+
+               if (length != 8 && length != 4) {
+                       const gboolean length_error = length < 4 ? TRUE : FALSE;
+                       if (is_relative)
+                           report_type_length_mismatch(tree, "a relative time value", length, length_error);
+                       else
+                           report_type_length_mismatch(tree, "an absolute time value", length, length_error);
+               }
+
+               tvb_ensure_bytes_exist(tvb, start, length);
+               get_time_value(tvb, start, length, encoding, &time_stamp, is_relative);
+               if (endoff) *endoff = length;
+       }
+
+       if (err) *err = saved_err;
+
+       if (retval) {
+               retval->secs  = time_stamp.secs;
+               retval->nsecs = time_stamp.nsecs;
+       }
+
+       TRY_TO_FAKE_THIS_ITEM(tree, hfinfo->id, hfinfo);
+
+       new_fi = new_field_info(tree, hfinfo, tvb, start, length);
+
+       if (new_fi == NULL)
+               return NULL;
+
+       proto_tree_set_time(new_fi, &time_stamp);
+
+       if (encoding & ENC_STRING) {
+               if (saved_err == ERANGE)
+                   expert_add_info(NULL, tree, &ei_number_string_decoding_erange_error);
+               else if (saved_err == EDOM)
+                   expert_add_info(NULL, tree, &ei_number_string_decoding_failed_error);
+       }
+       else {
+               FI_SET_FLAG(new_fi,
+                       (encoding & ENC_LITTLE_ENDIAN) ? FI_LITTLE_ENDIAN : FI_BIG_ENDIAN);
+       }
+
+       return proto_tree_add_node(tree, new_fi);
+}
+
 /* Add a FT_NONE to a proto_tree */
 proto_item *
 proto_tree_add_none_format(proto_tree *tree, const int hfindex, tvbuff_t *tvb,
@@ -5253,6 +5332,38 @@ register_type_length_mismatch(void)
        proto_set_cant_toggle(proto_type_length_mismatch);
 }
 
+static void
+register_number_string_decoding_error(void)
+{
+       static ei_register_info ei[] = {
+               { &ei_number_string_decoding_failed_error,
+                       { "_ws.number_string.decoding_error.failed", PI_MALFORMED, PI_ERROR,
+                         "Failed to decode number from string", EXPFILL
+                       }
+               },
+               { &ei_number_string_decoding_erange_error,
+                       { "_ws.number_string.decoding_error.erange", PI_MALFORMED, PI_ERROR,
+                         "Decoded number from string is out of valid range", EXPFILL
+                       }
+               },
+       };
+
+       expert_module_t* expert_number_string_decoding_error;
+
+       proto_number_string_decoding_error =
+               proto_register_protocol("Number-String Decoding Error",
+                                       "Number-string decoding error",
+                                       "_ws.number_string.decoding_error");
+
+       expert_number_string_decoding_error =
+               expert_register_protocol(proto_number_string_decoding_error);
+       expert_register_field_array(expert_number_string_decoding_error, ei, array_length(ei));
+
+       /* "Number-String Decoding Error" isn't really a protocol, it's an error indication;
+          disabling them makes no sense. */
+       proto_set_cant_toggle(proto_number_string_decoding_error);
+}
+
 #define PROTO_PRE_ALLOC_HF_FIELDS_MEM (144000+PRE_ALLOC_EXPERT_FIELDS_MEM)
 static int
 proto_register_field_init(header_field_info *hfinfo, const int parent)
index 1e05072099e0376884d0edf01989d4c35a4a3f85..cde9dad3da3d1684ed0a78137ffcd7d9863e46c0 100644 (file)
@@ -332,6 +332,33 @@ WS_DLL_PUBLIC WS_MSVC_NORETURN void proto_report_dissector_bug(const char *messa
  */
 #define ENC_NA                 0x00000000
 
+/* For cases where either native type or string encodings could both be
+ * valid arguments, we need something to distinguish which one is being
+ * passed as the argument, because ENC_BIG_ENDIAN and ENC_ASCII are both
+ * 0x00000000. So we use ENC_STR_NUM or ENC_STR_HEX bit-or'ed with
+ * ENC_ASCII and its ilk.
+ */
+/* this is for strings as numbers "12345" */
+#define ENC_STR_NUM             0x01000000
+/* this is for strings as hex "1a2b3c" */
+#define ENC_STR_HEX             0x02000000
+/* a convenience macro for either of the above */
+#define ENC_STRING              0x03000000
+/* mask out ENC_STR_* and related bits - should this replace ENC_CHARENCODING_MASK? */
+#define ENC_STR_MASK            0x0000FFFE
+
+/* For cases where a string encoding contains a timestamp, use one
+ * of these (but only one). These values can collide with above, because
+ * you can't do both at the same time.
+ */
+#define ENC_ISO_8601_DATE       0x00010000
+#define ENC_ISO_8601_TIME       0x00020000
+#define ENC_ISO_8601_DATE_TIME  0x00030000
+#define ENC_RFC_822             0x00040000
+#define ENC_RFC_1123            0x00080000
+/* a convenience macro for the above - for internal use only */
+#define ENC_STR_TIME_MASK       0x000F0000
+
 /* Values for header_field_info.display */
 
 /* For integral types, the display format is a BASE_* field_display_e value
@@ -922,6 +949,42 @@ WS_DLL_PUBLIC proto_item *
 proto_tree_add_time(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start,
        gint length, const nstime_t* value_ptr);
 
+/** Get and add a FT_ABSOLUTE_TIME or FT_RELATIVE_TIME to a proto_tree.
+ The item is extracted from the tvbuff handed to it, based on the ENC_* passed
+ in for the encoding, and the retrieved value is also set to *retval so the
+ caller gets it back for other uses.
+
+ This function retrieves the value even if the passed-in tree param is NULL,
+ so that it can be used by dissectors at all times to both get the value
+ and set the tree item to it.
+
+ Like other proto_tree_add functions, if there is a tree and the value cannot
+ be decoded from the tvbuff, then an expert info error is reported. For string
+ encoding, this means that a failure to decode the time value from the string
+ results in an expert info error being added to the tree.
+
+ If encoding is string-based, it will convert using tvb_get_string_time(); see
+ that function's comments for details.
+
+ @note The nstime_t *retval must be pre-allocated as a nstime_t.
+
+ @param tree the tree to append this item to
+ @param hfindex field index
+ @param tvb the tv buffer of the current data
+ @param start start of data in tvb
+ @param length length of data in tvb
+ @param encoding data encoding (e.g, ENC_LITTLE_ENDIAN, ENC_UTF_8|ENC_ISO_8601_DATE_TIME, etc.)
+ @param[in,out] retval points to a nstime_t which will be set to the value
+ @param[in,out] endoff if not NULL, gets set to the character after those consumed.
+ @param[in,out] err if not NULL, gets set to 0 if no failure, else the errno code (e.g., EDOM, ERANGE).
+ @return the newly created item, and retval is set to the decoded value
+ */
+WS_DLL_PUBLIC proto_item *
+proto_tree_add_time_item(proto_tree *tree, int hfindex, tvbuff_t *tvb,
+    const gint start, gint length, const guint encoding,
+    nstime_t *retval, gint *endoff, gint *err);
+
+
 /** Add a formatted FT_ABSOLUTE_TIME or FT_RELATIVE_TIME to a proto_tree, with
     the format generating the string for the value and with the field name
     being included automatically.
index c9eff6d19218d637440f2d029388136a27442782..668d1c0f8bc96b5ed6ada618cfb895dcc8960203 100644 (file)
 #include "config.h"
 
 #include <string.h>
+#include <stdio.h>
+#include <errno.h>
 
 #include "wsutil/pint.h"
 #include "wsutil/sign_ext.h"
 #include "wsutil/unicode-utils.h"
+#include "wsutil/nstime.h"
 #include "tvbuff.h"
 #include "tvbuff-int.h"
 #include "strutil.h"
 #include "proto.h"     /* XXX - only used for DISSECTOR_ASSERT, probably a new header file? */
 #include "exceptions.h"
 
+/*  
+ * Just make sure we include the prototype for strptime as well 55 
+ * (needed for glibc 2.2) but make sure we do this only if not 56 
+ * yet defined. 57 
+ */ 
+#include <time.h> 
+/*#ifdef NEED_STRPTIME_H*/ 
+#ifndef strptime
+#include "wsutil/strptime.h"
+#endif 
+ /*#endif*/ 
+
 static guint64
 _tvb_get_bits64(tvbuff_t *tvb, guint bit_offset, const gint total_no_of_bits);
 
@@ -57,6 +72,9 @@ _tvb_captured_length_remaining(const tvbuff_t *tvb, const gint offset);
 static inline const guint8*
 ensure_contiguous(tvbuff_t *tvb, const gint offset, const gint length);
 
+static inline guint8 *
+tvb_get_raw_string(wmem_allocator_t *scope, tvbuff_t *tvb, const gint offset, const gint length);
+
 tvbuff_t *
 tvb_new(const struct tvb_ops *ops)
 {
@@ -1268,6 +1286,250 @@ tvb_get_letohieee_double(tvbuff_t *tvb, const int offset)
 #endif
 }
 
+static inline void
+validate_single_byte_ascii_encoding(const guint encoding)
+{
+       const guint enc = encoding & ~ENC_STR_MASK;
+
+       switch (enc) {
+           case ENC_UTF_16:
+           case ENC_UCS_2:
+           case ENC_UCS_4:
+           case ENC_3GPP_TS_23_038_7BITS:
+           case ENC_EBCDIC:
+               REPORT_DISSECTOR_BUG("Invalid string encoding type passed to tvb_get_string_XXX");
+               break;
+           default:
+               break;
+       }
+       /* make sure something valid was set */
+       if (enc == 0)
+           REPORT_DISSECTOR_BUG("No string encoding type passed to tvb_get_string_XXX");
+}
+
+/* converts a broken down date representation, relative to UTC,
+ * to a timestamp; it uses timegm() if it's available.
+ * Copied from Glib source gtimer.c
+ */
+static time_t
+mktime_utc (struct tm *tm)
+{
+  time_t retval;
+
+#ifndef HAVE_TIMEGM
+  static const gint days_before[] =
+  {
+    0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
+  };
+#endif
+
+#ifndef HAVE_TIMEGM
+  if (tm->tm_mon < 0 || tm->tm_mon > 11)
+    return (time_t) -1;
+
+  retval = (tm->tm_year - 70) * 365;
+  retval += (tm->tm_year - 68) / 4;
+  retval += days_before[tm->tm_mon] + tm->tm_mday - 1;
+
+  if (tm->tm_year % 4 == 0 && tm->tm_mon < 2)
+    retval -= 1;
+
+  retval = ((((retval * 24) + tm->tm_hour) * 60) + tm->tm_min) * 60 + tm->tm_sec;
+#else
+  retval = timegm (tm);
+#endif /* !HAVE_TIMEGM */
+
+  return retval;
+}
+
+/* support hex-encoded time values? */
+nstime_t*
+tvb_get_string_time(tvbuff_t *tvb, const gint offset, const gint length,
+                   const guint encoding, nstime_t *ns, gint *endoff)
+{
+       const gchar *begin = (gchar*) tvb_get_raw_string(wmem_packet_scope(), tvb, offset, length);
+       const gchar *ptr = begin;
+       const gchar *end = NULL;
+       struct tm tm;
+       nstime_t* retval = NULL;
+       char sign = '+';
+       int off_hr = 0;
+       int off_min = 0;
+       int num_chars = 0;
+       gboolean matched = FALSE;
+
+       errno = EDOM;
+
+       validate_single_byte_ascii_encoding(encoding);
+
+       DISSECTOR_ASSERT(ns);
+
+       memset(&tm, 0, sizeof(tm));
+       tm.tm_isdst = -1;
+       ns->secs    = 0;
+       ns->nsecs   = 0;
+
+       while (*ptr == ' ') ptr++;
+
+       if (*ptr) {
+               /* note: sscanf is known to be inconsistent across platforms with respect
+                  to whether a %n is counted as a return value or not, so we have to use
+                  '>=' a lot */
+               if ((encoding & ENC_ISO_8601_DATE_TIME) == ENC_ISO_8601_DATE_TIME) {
+                       /* TODO: using sscanf this many times is probably slow; might want
+                          to parse it by hand in the future */
+                       /* 2014-04-07T05:41:56+00:00 */
+                       if (sscanf(ptr, "%d-%d-%d%*c%d:%d:%d%c%d:%d%n",
+                           &tm.tm_year,
+                           &tm.tm_mon,
+                           &tm.tm_mday,
+                           &tm.tm_hour,
+                           &tm.tm_min,
+                           &tm.tm_sec,
+                           &sign,
+                           &off_hr,
+                           &off_min,
+                           &num_chars) >= 9)
+                       {
+                               matched = TRUE;
+                       }
+                       /* no seconds is ok */
+                       else if (sscanf(ptr, "%d-%d-%d%*c%d:%d%c%d:%d%n",
+                           &tm.tm_year,
+                           &tm.tm_mon,
+                           &tm.tm_mday,
+                           &tm.tm_hour,
+                           &tm.tm_min,
+                           &sign,
+                           &off_hr,
+                           &off_min,
+                           &num_chars) >= 8)
+                       {
+                               matched = TRUE;
+                       }
+                       /* 2007-04-05T14:30:56Z */
+                       else if (sscanf(ptr, "%d-%d-%d%*c%d:%d:%dZ%n",
+                           &tm.tm_year,
+                           &tm.tm_mon,
+                           &tm.tm_mday,
+                           &tm.tm_hour,
+                           &tm.tm_min,
+                           &tm.tm_sec,
+                           &num_chars) >= 6)
+                       {
+                               matched = TRUE;
+                               off_hr = 0;
+                               off_min = 0;
+                       }
+                       /* 2007-04-05T14:30Z no seconds is ok */
+                       else if (sscanf(ptr, "%d-%d-%d%*c%d:%dZ%n",
+                           &tm.tm_year,
+                           &tm.tm_mon,
+                           &tm.tm_mday,
+                           &tm.tm_hour,
+                           &tm.tm_min,
+                           &num_chars) >= 5)
+                       {
+                               matched = TRUE;
+                               off_hr = 0;
+                               off_min = 0;
+                       }
+
+                       if (matched) {
+                               errno = 0;
+                               end = ptr + num_chars;
+                               tm.tm_mon--;
+                               if (tm.tm_year > 1900) tm.tm_year -= 1900;
+                               if (sign == '-') off_hr = -off_hr;
+                       }
+               }
+               else if (encoding & ENC_ISO_8601_DATE) {
+                       /* 2014-04-07 */
+                       if (sscanf(ptr, "%d-%d-%d%n",
+                           &tm.tm_year,
+                           &tm.tm_mon,
+                           &tm.tm_mday,
+                           &num_chars) >= 3)
+                       {
+                               errno = 0;
+                               end = ptr + num_chars;
+                               tm.tm_mon--;
+                               if (tm.tm_year > 1900) tm.tm_year -= 1900;
+                       }
+               }
+               else if (encoding & ENC_ISO_8601_TIME) {
+                       /* 2014-04-07 */
+                       if (sscanf(ptr, "%d:%d:%d%n",
+                           &tm.tm_hour,
+                           &tm.tm_min,
+                           &tm.tm_sec,
+                           &num_chars) >= 2)
+                       {
+                               /* what should we do about day/month/year? */
+                               /* setting it to "now" for now */
+                               time_t time_now = time(NULL);
+                               struct tm *tm_now = gmtime(&time_now);
+                               tm.tm_year = tm_now->tm_year;
+                               tm.tm_mon  = tm_now->tm_mon;
+                               tm.tm_mday = tm_now->tm_mday;
+                               end = ptr + num_chars;
+                               errno = 0;
+
+                       }
+               }
+               else if (encoding & ENC_RFC_822 || encoding & ENC_RFC_1123) {
+                       if (encoding & ENC_RFC_822) {
+                               /* this will unfortunately match ENC_RFC_1123 style
+                                  strings too, partially - probably need to do this the long way */
+                               end = strptime(ptr, "%a, %d %b %y %H:%M:%S", &tm);
+                               if (!end) end = strptime(ptr, "%a, %d %b %y %H:%M", &tm);
+                               if (!end) end = strptime(ptr, "%d %b %y %H:%M:%S", &tm);
+                               if (!end) end = strptime(ptr, "%d %b %y %H:%M", &tm);
+                       }
+                       else if (encoding & ENC_RFC_1123) {
+                               end = strptime(ptr, "%a, %d %b %Y %H:%M:%S", &tm);
+                               if (!end) end = strptime(ptr, "%a, %d %b %Y %H:%M", &tm);
+                               if (!end) end = strptime(ptr, "%d %b %Y %H:%M:%S", &tm);
+                               if (!end) end = strptime(ptr, "%d %b %Y %H:%M", &tm);
+                       }
+                       if (end) {
+                               errno = 0;
+                               if (*end == ' ') end++;
+                               if (g_ascii_strncasecmp(end, "UT", 2) == 0)
+                               {
+                                       end += 2;
+                               }
+                               else if (g_ascii_strncasecmp(end, "GMT", 3) == 0)
+                               {
+                                       end += 3;
+                               }
+                               else if (sscanf(end, "%c%2d%2d%n",
+                                   &sign,
+                                   &off_hr,
+                                   &off_min,
+                                   &num_chars) < 3)
+                               {
+                                       errno = ERANGE;
+                               }
+                               if (sign == '-') off_hr = -off_hr;
+                       }
+               }
+       }
+
+       if (errno == 0) {
+               ns->secs = mktime_utc (&tm);
+               if (off_hr > 0)
+                       ns->secs += (off_hr * 3600) + (off_min * 60);
+               else if (off_hr < 0)
+                       ns->secs -= ((-off_hr) * 3600) + (off_min * 60);
+               retval = ns;
+               if (endoff)
+                   *endoff = (gint)(offset + (end - begin));
+       }
+
+       return retval;
+}
+
 /* Fetch an IPv4 address, in network byte order.
  * We do *not* convert them to host byte order; we leave them in
  * network byte order. */
@@ -1897,6 +2159,35 @@ tvb_get_utf_8_string(wmem_allocator_t *scope, tvbuff_t *tvb, const gint offset,
        return strbuf;
 }
 
+/*
+ * Given a tvbuff, an offset, and a length, treat the string of bytes
+ * referred to by them as a raw string, and return a pointer to that
+ * string. This means a null is appended at the end, but no replacement
+ * checking is done otherwise. Currently tvb_get_utf_8_string() does
+ * not replace either, but it might in the future.
+ *
+ * Also, this one allows a length of -1 to mean get all, but does not
+ * allow a negative offset.
+ */
+static inline guint8 *
+tvb_get_raw_string(wmem_allocator_t *scope, tvbuff_t *tvb, const gint offset, const gint length)
+{
+       guint8       *strbuf;
+       gint          abs_length = length;
+
+       DISSECTOR_ASSERT(offset     >=  0);
+       DISSECTOR_ASSERT(abs_length >= -1);
+
+       if (abs_length < 0)
+               abs_length = tvb->length - offset;
+
+       tvb_ensure_bytes_exist(tvb, offset, abs_length);
+       strbuf = (guint8 *)wmem_alloc(scope, abs_length + 1);
+       tvb_memcpy(tvb, strbuf, offset, abs_length);
+       strbuf[abs_length] = '\0';
+       return strbuf;
+}
+
 /*
  * Given a tvbuff, an offset, and a length, treat the string of bytes
  * referred to by them as an ISO 8859/1 string, with all bytes with the
index a365276ee1d4b95962d4ec842899d4145b6e91e7..14789d318b708056b338ca49fc093eef1b96abb6 100644 (file)
@@ -55,6 +55,7 @@ struct tvbuff;
 typedef struct tvbuff tvbuff_t;
 
 struct e_in6_addr; /* ipv6-utils.h */
+struct nstime_t;   /* nstime.h */
 
 /** @defgroup tvbuff Testy, Virtual(-izable) Buffers
  *
@@ -335,6 +336,37 @@ WS_DLL_PUBLIC gdouble tvb_get_letohieee_double(tvbuff_t *tvb,
 #error "Unsupported byte order"
 #endif
 
+
+/* Fetch a time value from an ASCII-style string in the tvb.
+ *
+ * @param[in] offset The beginning offset in the tvb (cannot be negative)
+ * @param[in] length The field's length in the tvb (or -1 for remaining)
+ * @param[in] encoding The ENC_* that defines the format (e.g., ENC_ISO_8601_DATE_TIME)
+ * @param[in,out] ns The pre-allocated nstime_t that will be set to the decoded value
+ * @param[out] endoff if not NULL, should point to a gint that this
+ *     routine will then set to be the offset to the character after
+ *     the last character used in the conversion. This is useful because
+ *     they may not consume the whole section.
+ *
+ * @return a pointer to the nstime_t passed-in, or NULL on failure; if no
+ *    valid conversion could be performed, *endoff is set to 0, and errno will be
+ *    EDOM or ERANGE, and the nstime_t* passed-in will be cleared.
+ *
+ * @note The conversion ignores leading spaces, and will succeed even if it does
+ *    not consume the entire string. If you care about such things, always compare
+ *    the *endoff to where you expect it to be (namely, offset+length).
+ *
+ * This routine will not throw an error unless the passed-in arguments are
+ * invalid (e.g., offset is beyond the tvb's length).
+ *
+ * @warning This only works for string encodings which encode ASCII characters in
+ * a single byte: ENC_ASCII, ENC_UTF_8, ENC_ISO_8859_*, etc. It does NOT work
+ * for purely multi-byte encodings such as ENC_UTF_16, ENC_UCS_*, etc.
+ */
+WS_DLL_PUBLIC
+struct nstime_t* tvb_get_string_time(tvbuff_t *tvb, const gint offset, const gint length,
+                              const guint encoding, struct nstime_t* ns, gint *endoff);
+
 /**
  * Fetch an IPv4 address, in network byte order.
  * We do *not* convert it to host byte order; we leave it in
index 5eee9e561efe972da47b0fa05edd1051c0a60292..356054f518b8581538b8024162fe962616cfd213 100644 (file)
@@ -32,6 +32,8 @@
 /* WSLUA_MODULE Tree Adding information to the dissection tree */
 
 #include "wslua.h"
+#include <epan/exceptions.h>
+#include <epan/show_exception.h>
 
 static gint wslua_ett = -1;
 
@@ -50,19 +52,113 @@ WSLUA_CLASS_DEFINE(TreeItem,FAIL_ON_NULL_OR_EXPIRED("TreeItem"),NOP);
 /* `TreeItem`s represent information in the packet-details pane.
    A root `TreeItem` is passed to dissectors as the third argument. */
 
+/* the following is used by TreeItem_add_packet_field() - this can THROW errors */
+static proto_item *
+try_add_packet_field(lua_State *L, TreeItem tree_item, TvbRange tvbr, const int hfid,
+                     const ftenum_t type, const guint encoding, gint *ret_err)
+{
+    gint err = 0;
+    proto_item* item = NULL;
+    gint endoff = 0;
+
+    switch(type) {
+        case FT_ABSOLUTE_TIME:
+        case FT_RELATIVE_TIME:
+            {
+               /* nstime_t will be g_free'd by Lua */
+                nstime_t *nstime = (nstime_t *) g_malloc0(sizeof(nstime_t));
+                item = proto_tree_add_time_item(tree_item->tree, hfid, tvbr->tvb->ws_tvb,
+                                                   tvbr->offset, tvbr->len, encoding,
+                                                   nstime, &endoff, &err);
+                if (err == 0) {
+                    pushNSTime(L,nstime);
+                    lua_pushinteger(L, endoff);
+                }
+            }
+            break;
+
+        /* anything else just needs to be done the old fashioned way */
+        default:
+            item = proto_tree_add_item(tree_item->tree, hfid, tvbr->tvb->ws_tvb, tvbr->offset, tvbr->len, encoding);
+            lua_pushnil(L);
+            lua_pushnil(L);
+            break;
+    }
+
+    if (ret_err) *ret_err = err;
+
+    return item;
+}
+
 WSLUA_METHOD TreeItem_add_packet_field(lua_State *L) {
     /*
-     Adds an child item to a given item, returning the child.
-     tree_item:add_packet_field([proto_field], [tvbrange], [encoding], ...)
+     Adds a new child tree for the given `ProtoField` object to this tree item,
+     returning the new child `TreeItem`.
+
+     Unlike `TreeItem:add()` and `TreeItem:add_le()`, the `ProtoField` argument
+     is not optional, and cannot be a `Proto` object. Instead, this function always
+     uses the `ProtoField` to determine the type of field to extract from the
+     passed-in `TvbRange`, highlighting the relevant bytes in the Packet Bytes pane
+     of the GUI (if there is a GUI), etc.  If no `TvbRange` is given, no bytes are
+     highlighted and the field's value cannot be determined; the `ProtoField` must
+     have been defined/created not to have a length in such a case, or an error will
+     occur.  For backwards-compatibility reasons the `encoding` argument, however,
+     must still be given.
+
+     Unlike `TreeItem:add()` and `TreeItem:add_le()`, this function performs both
+     big-endian and little-endian decoding, by setting the `encoding` argument to
+     be `ENC_BIG_ENDIAN` or `ENC_LITTLE_ENDIAN`.
+
+     The signature of this function:
+     @code
+     tree_item:add_packet_field(proto_field [,tvbrange], encoding, ...)
+     @endcode
+
+     In Wireshark version 1.11.3, this function was changed to return more than
+     just the new child `TreeItem`. The child is the first return value, so that
+     function chaining will still work as before; but it now also returns the value
+     of the extracted field (i.e., a number, `UInt64`, `Address`, etc.). If the
+     value could not be extracted from the `TvbRange`, the child `TreeItem` is still
+     returned, but the second returned value is `nil`.
+
+     Another new feature added to this function in Wireshark version 1.11.3 is the
+     ability to extract native number `ProtoField`s from string encoding in the
+     `TvbRange`, for ASCII-based and similar string encodings. For example, a
+     `ProtoField` of as `ftypes.UINT32` type can be extracted from a `TvbRange`
+     containing the ASCII string "123", and it will correctly decode the ASCII to
+     the number `123`, both in the tree as well as for the second return value of
+     this function. To do so, you must set the `encoding` argument of this function
+     to the appropriate string `ENC_*` value, bitwise-or'd with the `ENC_STRING`
+     value (see `init.lua`). `ENC_STRING` is guaranteed to be a unique bit flag, and
+     thus it can added instead of bitwise-or'ed as well. Only single-byte ASCII digit
+     string encoding types can be used for this, such as `ENC_ASCII` and `ENC_UTF_8`.
+
+     For example, assuming the `Tvb` named "`tvb`" contains the string "123":
+     @code
+     -- this is done earlier in the script
+     local myfield = ProtoField.new("Transaction ID", "myproto.trans_id", ftypes.UINT16)
+
+     -- this is done inside a dissector, post-dissector, or heuristic function
+     -- child will be the created child tree, and value will be the number 123 or nil on failure
+     local child, value = tree:add_packet_field(myfield, tvb:range(0,3), ENC_UTF_8 + ENC_STRING)
+     @encode
+
     */
+#define WSLUA_ARG_TreeItem_add_packet_field_PROTOFIELD 2 /* The ProtoField field object to add to the tree. */
+#define WSLUA_OPTARG_TreeItem_add_packet_field_TVBRANGE 3 /* The `TvbRange` of bytes in the packet this tree item covers/represents. */
+#define WSLUA_ARG_TreeItem_add_packet_field_ENCODING 4 /* The field's encoding in the `TvbRange`. */
+#define WSLUA_OPTARG_TreeItem_add_packet_field_LABEL 5 /* One or more strings to append to the created `TreeItem`. */
     TvbRange tvbr;
     ProtoField field;
     int hfid;
     int ett;
     ftenum_t type;
-    TreeItem tree_item  = shiftTreeItem(L,1);
+    TreeItem tree_item = shiftTreeItem(L,1);
     guint encoding;
     proto_item* item = NULL;
+    int nargs;
+    gint err = 0;
+    const char *volatile error = NULL;
 
     if (!tree_item) {
         return luaL_error(L,"not a TreeItem!");
@@ -92,6 +188,14 @@ WSLUA_METHOD TreeItem_add_packet_field(lua_State *L) {
 
     encoding = wslua_checkguint(L,1);
     lua_remove(L,1);
+
+    /* get the number of additional args before we add more to the stack */
+    nargs = lua_gettop(L);
+
+    /* XXX: why is this being done? If the length was -1, FT_STRINGZ figures out
+     * the right length in tvb_get_stringz_enc(); if it was 0, it should remain zero;
+     * if it was greater than zero, then it's the length the caller wanted.
+     */
     if (type == FT_STRINGZ) {
         switch (encoding & ENC_CHARENCODING_MASK) {
 
@@ -105,13 +209,29 @@ WSLUA_METHOD TreeItem_add_packet_field(lua_State *L) {
             break;
         }
     }
-    item = proto_tree_add_item(tree_item->tree, hfid, tvbr->tvb->ws_tvb, tvbr->offset, tvbr->len, encoding);
 
-    while(lua_gettop(L)) {
+    TRY {
+
+        item = try_add_packet_field(L, tree_item, tvbr, hfid, type, encoding, &err);
+
+    } CATCH_ALL {
+        show_exception(tvbr->tvb->ws_tvb, lua_pinfo, tree_item->tree, EXCEPT_CODE, GET_MESSAGE);
+        error = "Lua programming error";
+    } ENDTRY;
+
+    if (error) { WSLUA_ERROR(TreeItem_add_packet_field,error); }
+
+    if (err != 0) {
+        lua_pushnil(L);
+        lua_pushnil(L);
+    }
+
+    while(nargs) {
         const gchar* s;
         s = lua_tostring(L,1);
         if (s) proto_item_append_text(item, " %s", s);
         lua_remove(L,1);
+        nargs--;
     }
 
     tree_item = (TreeItem)g_malloc(sizeof(struct _wslua_treeitem));
@@ -121,7 +241,10 @@ WSLUA_METHOD TreeItem_add_packet_field(lua_State *L) {
 
     PUSH_TREEITEM(L,tree_item);
 
-    WSLUA_RETURN(1); /* The new child TreeItem. */
+    /* move the tree object before the field value */
+    lua_insert(L, 1);
+
+    WSLUA_RETURN(3); /* The new child `TreeItem`, the field's extracted value or nil, and offset or nil. */
 }
 
 static int TreeItem_add_item_any(lua_State *L, gboolean little_endian) {
index bfad06c51ea4123dc34c30c9b3cb26cea59ae1d9..9746ff77a48b0c1f3676b7ce8f24b3c59add3537 100644 (file)
@@ -1055,8 +1055,10 @@ WSLUA_METHOD TvbRange_ether(lua_State* L) {
 
 WSLUA_METHOD TvbRange_nstime(lua_State* L) {
        /* Obtain a time_t structure from a `TvbRange`, as an `NSTime` object. */
+#define WSLUA_OPTARG_TvbRange_nstime_ENCODING 2 /* An optional ENC_* encoding value to use */
     TvbRange tvbr = checkTvbRange(L,1);
     NSTime nstime;
+    const guint encoding = luaL_optint(L, WSLUA_OPTARG_TvbRange_nstime_ENCODING, 0);
 
     if ( !(tvbr && tvbr->tvb)) return 0;
     if (tvbr->tvb->expired) {
@@ -1066,21 +1068,41 @@ WSLUA_METHOD TvbRange_nstime(lua_State* L) {
 
     nstime = g_new(nstime_t,1);
 
-    if (tvbr->len == 4) {
-      nstime->secs = tvb_get_ntohl(tvbr->tvb->ws_tvb, tvbr->offset);
-      nstime->nsecs = 0;
-    } else if (tvbr->len == 8) {
-      nstime->secs = tvb_get_ntohl(tvbr->tvb->ws_tvb, tvbr->offset);
-      nstime->nsecs = tvb_get_ntohl(tvbr->tvb->ws_tvb, tvbr->offset + 4);
-    } else {
-      g_free(nstime);
-      WSLUA_ERROR(TvbRange_nstime,"The range must be 4 or 8 bytes long");
-      return 0;
+    if (encoding == 0) {
+        if (tvbr->len == 4) {
+          nstime->secs = tvb_get_ntohl(tvbr->tvb->ws_tvb, tvbr->offset);
+          nstime->nsecs = 0;
+        } else if (tvbr->len == 8) {
+          nstime->secs = tvb_get_ntohl(tvbr->tvb->ws_tvb, tvbr->offset);
+          nstime->nsecs = tvb_get_ntohl(tvbr->tvb->ws_tvb, tvbr->offset + 4);
+        } else {
+          g_free(nstime);
+          WSLUA_ERROR(TvbRange_nstime,"The range must be 4 or 8 bytes long");
+          return 0;
+        }
+        pushNSTime(L, nstime);
+        lua_pushinteger(L, tvbr->len);
+    }
+    else if (encoding & ~ENC_STR_TIME_MASK) {
+        WSLUA_OPTARG_ERROR(TvbRange_nstime, ENCODING, "invalid encoding value");
+    }
+    else {
+        gint endoff = 0;
+        nstime_t *retval = tvb_get_string_time(tvbr->tvb->ws_tvb, tvbr->offset, tvbr->len,
+                                               encoding, nstime, &endoff);
+        if (!retval || endoff == 0) {
+            g_free(nstime);
+            /* push nil nstime and offset */
+            lua_pushnil(L);
+            lua_pushnil(L);
+        }
+        else {
+            pushNSTime(L, nstime);
+            lua_pushinteger(L, endoff);
+        }
     }
 
-    pushNSTime(L, nstime);
-
-    WSLUA_RETURN(1); /* The `NSTime` object. */
+    WSLUA_RETURN(2); /* The `NSTime` object and number of bytes used, or nil on failure. */
 }
 
 WSLUA_METHOD TvbRange_le_nstime(lua_State* L) {
diff --git a/test/lua/tvb.lua b/test/lua/tvb.lua
new file mode 100644 (file)
index 0000000..52f271b
--- /dev/null
@@ -0,0 +1,582 @@
+----------------------------------------
+-- script-name: tvb.lua
+-- This tests the Tvb/TvbRange and proto_add_XXX_item API.
+----------------------------------------
+
+------------- general test helper funcs ------------
+local FRAME = "frame"
+local OTHER = "other"
+
+local total_tests = 0
+local function getTotal()
+    return total_tests
+end
+
+
+local packet_counts = {}
+local function incPktCount(name)
+    if not packet_counts[name] then
+        packet_counts[name] = 1
+    else
+        packet_counts[name] = packet_counts[name] + 1
+    end
+end
+local function getPktCount(name)
+    return packet_counts[name] or 0
+end
+
+local passed = {}
+local function setPassed(name)
+    if not passed[name] then
+        passed[name] = 1
+    else
+        passed[name] = passed[name] + 1
+    end
+    total_tests = total_tests + 1
+end
+
+local fail_count = 0
+local function setFailed(name)
+    fail_count = fail_count + 1
+    total_tests = total_tests + 1
+end
+
+-- expected number of runs per type
+--
+-- CHANGE THIS TO MATCH HOW MANY TESTS THERE ARE
+--
+local taptests = { [FRAME]=4, [OTHER]=247 }
+
+local function getResults()
+    print("\n-----------------------------\n")
+    for k,v in pairs(taptests) do
+        -- each frame run executes the same test again, so multiply by #frames
+        if k ~= "frame" and v ~= 0 then v = (v * taptests.frame) end
+
+        if v ~= 0 and passed[k] ~= v then
+            print("Something didn't run or ran too much... tests failed!")
+            print("Dissector type " .. k ..
+                  " expected: " .. v ..
+                  " (" .. ( v / taptests.frame) .. ")" ..
+                  ", but got: " .. tostring(passed[k]) ..
+                  " (" .. (tonumber(passed[k] or 0) / taptests.frame) .. ")" )
+            return false
+        end
+    end
+    print("All tests passed!\n\n")
+    return true
+end
+
+
+local function testing(type,...)
+    print("\n-------- Testing " .. tostring(...) ..
+          " ---- for packet # " .. getPktCount(type) ..
+          " --------\n")
+end
+
+local function execute(type,name, ...)
+    io.stdout:write("test --> "..name.."-"..getTotal().."-"..getPktCount(type).."...")
+    local results = { ... }
+    if #results > 0 and results[1] == true then
+        setPassed(type)
+        io.stdout:write("passed\n")
+        return true
+    else
+        setFailed(type)
+        io.stdout:write("failed!\n")
+        if #results > 1 then
+            print("Got the following error: '" .. tostring(results[2]) .. "'")
+        end
+        error(name.." test failed!")
+    end
+end
+
+---------
+-- the following are so we can use pcall (which needs a function to call)
+local function callFunc(func,...)
+    func(...)
+end
+
+local function callObjFuncGetter(vart,varn,tobj,name,...)
+    vart[varn] = tobj[name](...)
+end
+
+local function setValue(tobj,name,value)
+    tobj[name] = value
+end
+
+local function getValue(tobj,name)
+    local foo = tobj[name]
+end
+
+------------- test script ------------
+
+----------------------------------
+-- modify original test function for now, kinda sorta
+local orig_execute = execute
+execute = function (...)
+    return orig_execute(OTHER,...)
+end
+
+----------------------------------------
+-- creates a Proto object for our testing
+local test_proto = Proto("test","Test Protocol")
+
+local numinits = 0
+function test_proto.init()
+    numinits = numinits + 1
+    if numinits == 2 then
+        getResults()
+    end
+end
+
+
+----------------------------------------
+-- a table of all of our Protocol's fields and test input and expected output
+local testfield =
+{
+    basic =
+    {
+        STRING  = ProtoField.string ("test.basic.string",  "Basic string"),
+        BOOLEAN = ProtoField.bool   ("test.basic.boolean", "Basic boolean", 16, {"yes","no"}, 0x0001),
+        UINT16  = ProtoField.uint16 ("test.basic.uint16",  "Basic uint16")
+    },
+
+    time =
+    {
+        ABSOLUTE_LOCAL = ProtoField.absolute_time("test.time.absolute.local","Time absolute local"),
+        ABSOLUTE_UTC   = ProtoField.absolute_time("test.time.absolute.utc",  "Time absolute utc", 1001),
+    },
+
+}
+
+-- create a flat array table of the above that can be registered
+local pfields = {}
+for _,t in pairs(testfield) do
+    for k,v in pairs(t) do
+        pfields[#pfields+1] = v
+    end
+end
+
+-- register them
+test_proto.fields = pfields
+
+print("test_proto ProtoFields registered")
+
+
+local getfield =
+{
+    basic =
+    {
+        STRING  = Field.new ("test.basic.string"),
+        BOOLEAN = Field.new ("test.basic.boolean"),
+        UINT16  = Field.new ("test.basic.uint16")
+    },
+
+    time =
+    {
+        ABSOLUTE_LOCAL = Field.new ("test.time.absolute.local"),
+        ABSOLUTE_UTC   = Field.new ("test.time.absolute.utc"),
+    },
+
+}
+
+local function addMatchFields(match_fields, ... )
+    match_fields[#match_fields + 1] = { ... }
+end
+
+local function getFieldInfos(name)
+    local base, field = name:match("([^.]+)%.(.+)")
+    if not base or not field then
+        error("failed to get base.field from '" .. name .. "'")
+    end
+    local t = { getfield[base][field]() }
+    return t
+end
+
+local function verifyFields(name, match_fields)
+    local finfos = getFieldInfos(name)
+
+    execute ("verify-fields-size-" .. name, #finfos == #match_fields,
+             "#finfos=" .. #finfos .. ", #match_fields=" .. #match_fields)
+
+    for i, t in ipairs(match_fields) do
+        if type(t) ~= 'table' then
+            error("verifyFields didn't get a table inside the matches table")
+        end
+        if #t ~= 1 then
+            error("verifyFields matches table's table is not size 1")
+        end
+        local result = finfos[i]()
+        local value  = t[1]
+        print(
+                name .. " got:",
+                "\n\tfinfos [" .. i .. "]='" .. tostring( result ) .. "'",
+                "\n\tmatches[" .. i .. "]='" .. tostring( value  ) .. "'"
+             )
+        execute ( "verify-fields-value-" .. name .. "-" .. i, result == value )
+    end
+end
+
+
+local function addMatchValues(match_values, ... )
+    match_values[#match_values + 1] = { ... }
+end
+
+local function addMatchFieldValues(match_fields, match_values, match_both, ...)
+    addMatchFields(match_fields, match_both)
+    addMatchValues(match_values, match_both, ...)
+end
+
+local result_values = {}
+local function resetResults()
+    result_values = {}
+end
+
+local function treeAddPField(...)
+    local t = { pcall ( TreeItem.add_packet_field, ... ) }
+    if t[1] == nil then
+        return nil, t[2]
+    end
+    -- it gives back a TreeItem, then the results
+    if typeof(t[2]) ~= 'TreeItem' then
+        return nil, "did not get a TreeItem returned from TreeItem.add_packet_field, "..
+                    "got a '" .. typeof(t[2]) .."'"
+    end
+
+    if #t ~= 4 then
+        return nil, "did not get 3 return values from TreeItem.add_packet_field"
+    end
+
+    result_values[#result_values + 1] = { t[3], t[4] }
+
+    return true
+end
+
+local function verifyResults(name, match_values)
+    execute ("verify-results-size-" .. name, #result_values == #match_values,
+             "#result_values=" .. #result_values ..
+             ", #match_values=" .. #match_values)
+
+    for j, t in ipairs(match_values) do
+        if type(t) ~= 'table' then
+            error("verifyResults didn't get a table inside the matches table")
+        end
+        for i, match in ipairs(t) do
+            local r = result_values[j][i]
+            print(
+                    name .. " got:",
+                    "\n\tresults[" .. j .. "][" .. i .. "]='" .. tostring( r ) .. "'",
+                    "\n\tmatches[" .. j .. "][" .. i .. "]='" .. tostring( match ) .. "'"
+                 )
+            local result_type, match_type
+            if type(match) == 'userdata' then
+                match_type = typeof(match)
+            else
+                match_type = type(match)
+            end
+            if type(r) == 'userdata' then
+                result_type = typeof(r)
+            else
+                result_type = type(r)
+            end
+            execute ( "verify-results-type-" .. name .. "-" .. i, result_type == match_type )
+            execute ( "verify-results-value-" .. name .. "-" .. i, r == match )
+        end
+    end
+end
+
+-- Compute the difference in seconds between local time and UTC
+-- from http://lua-users.org/wiki/TimeZone
+local function get_timezone()
+  local now = os.time()
+  return os.difftime(now, os.time(os.date("!*t", now)))
+end
+local timezone = get_timezone()
+print ("timezone = " .. timezone)
+
+----------------------------------------
+-- The following creates the callback function for the dissector.
+-- The 'tvbuf' is a Tvb object, 'pktinfo' is a Pinfo object, and 'root' is a TreeItem object.
+function test_proto.dissector(tvbuf,pktinfo,root)
+
+    incPktCount(FRAME)
+    incPktCount(OTHER)
+
+    testing(OTHER, "Basic")
+
+    local tree = root:add(test_proto, tvbuf:range(0,tvbuf:len()))
+
+    -- create a fake Tvb to use for testing
+    local teststring = "this is the string for the first test"
+    local bytearray = ByteArray.new(teststring, true)
+    local tvb = bytearray:tvb("Basic")
+
+    local function callTreeAdd(tree,...)
+        tree:add(...)
+    end
+
+    local string_match_fields = {}
+
+    execute ("basic-string", tree:add(testfield.basic.STRING, tvb:range(0,tvb:len())) ~= nil )
+    addMatchFields(string_match_fields, teststring)
+
+    execute ("basic-string", pcall (callTreeAdd, tree, testfield.basic.STRING, tvb:range() ) )
+    addMatchFields(string_match_fields, teststring)
+
+    verifyFields("basic.STRING", string_match_fields)
+
+    tvb = ByteArray.new("00FF 0001 8000"):tvb("Basic")
+    local bool_match_fields = {}
+
+    execute ("basic-boolean", pcall (callTreeAdd, tree, testfield.basic.BOOLEAN, tvb:range(0,2)) )
+    addMatchFields(bool_match_fields, true)
+
+    execute ("basic-boolean", pcall (callTreeAdd, tree, testfield.basic.BOOLEAN, tvb:range(2,2)) )
+    addMatchFields(bool_match_fields, true)
+
+    execute ("basic-boolean", pcall (callTreeAdd, tree, testfield.basic.BOOLEAN, tvb:range(4,2)) )
+    addMatchFields(bool_match_fields, false)
+
+    verifyFields("basic.BOOLEAN", bool_match_fields )
+
+    local uint16_match_fields = {}
+
+    execute ("basic-uint16", pcall (callTreeAdd, tree, testfield.basic.UINT16, tvb:range(0,2)) )
+    addMatchFields(uint16_match_fields, 255)
+
+    execute ("basic-uint16", pcall (callTreeAdd, tree, testfield.basic.UINT16, tvb:range(2,2)) )
+    addMatchFields(uint16_match_fields, 1)
+
+    execute ("basic-uint16", pcall (callTreeAdd, tree, testfield.basic.UINT16, tvb:range(4,2)) )
+    addMatchFields(uint16_match_fields, 32768)
+
+    verifyFields("basic.UINT16", uint16_match_fields)
+
+    local function callTreeAddLE(tree,...)
+        tree:add_le(...)
+    end
+
+    execute ("basic-uint16-le", pcall (callTreeAddLE, tree, testfield.basic.UINT16, tvb:range(0,2)) )
+    addMatchFields(uint16_match_fields, 65280)
+
+    execute ("basic-uint16-le", pcall (callTreeAddLE, tree, testfield.basic.UINT16, tvb:range(2,2)) )
+    addMatchFields(uint16_match_fields, 256)
+
+    execute ("basic-uint16-le", pcall (callTreeAddLE, tree, testfield.basic.UINT16, tvb:range(4,2)) )
+    addMatchFields(uint16_match_fields, 128)
+
+    verifyFields("basic.UINT16", uint16_match_fields)
+
+
+----------------------------------------
+    testing(OTHER, "tree:add Time")
+
+    tvb = ByteArray.new("00000000 00000000 0000FF0F 00FF000F"):tvb("Time")
+    local ALOCAL = testfield.time.ABSOLUTE_LOCAL
+    local alocal_match_fields = {}
+
+    execute ("time-local",    pcall (callTreeAdd,   tree, ALOCAL, tvb:range(0,8)) )
+    addMatchFields(alocal_match_fields, NSTime())
+
+    execute ("time-local",    pcall (callTreeAdd,   tree, ALOCAL, tvb:range(8,8)) )
+    addMatchFields(alocal_match_fields, NSTime( 0x0000FF0F, 0x00FF000F) )
+
+    execute ("time-local-le", pcall (callTreeAddLE, tree, ALOCAL, tvb:range(0,8)) )
+    addMatchFields(alocal_match_fields, NSTime())
+
+    execute ("time-local-le", pcall (callTreeAddLE, tree, ALOCAL, tvb:range(8,8)) )
+    addMatchFields(alocal_match_fields, NSTime( 0x0FFF0000, 0x0F00FF00 ) )
+
+    verifyFields("time.ABSOLUTE_LOCAL", alocal_match_fields)
+
+    local AUTC = testfield.time.ABSOLUTE_UTC
+    local autc_match_fields = {}
+
+    execute ("time-utc",    pcall (callTreeAdd,   tree, AUTC, tvb:range(0,8)) )
+    addMatchFields(autc_match_fields, NSTime())
+
+    execute ("time-utc",    pcall (callTreeAdd,   tree, AUTC, tvb:range(8,8)) )
+    addMatchFields(autc_match_fields, NSTime( 0x0000FF0F, 0x00FF000F) )
+
+    execute ("time-utc-le", pcall (callTreeAddLE, tree, AUTC, tvb:range(0,8)) )
+    addMatchFields(autc_match_fields, NSTime())
+
+    execute ("time-utc-le", pcall (callTreeAddLE, tree, AUTC, tvb:range(8,8)) )
+    addMatchFields(autc_match_fields, NSTime( 0x0FFF0000, 0x0F00FF00 ) )
+
+    verifyFields("time.ABSOLUTE_UTC", autc_match_fields )
+
+----------------------------------------
+    testing(OTHER, "tree:add_packet_field Time bytes")
+
+    resetResults()
+    local autc_match_values = {}
+
+    -- something to make this easier to read
+    local function addMatch(...)
+        addMatchFieldValues(autc_match_fields, autc_match_values, ...)
+    end
+
+    -- tree:add_packet_field(ALOCAL, tvb:range(0,8), ENC_BIG_ENDIAN)
+    execute ("add_pfield-time-bytes-local",    treeAddPField ( tree, AUTC, tvb:range(0,8), ENC_BIG_ENDIAN) )
+    addMatch( NSTime(), 8)
+
+    execute ("add_pfield-time-bytes-local",    treeAddPField ( tree, AUTC, tvb:range(8,8), ENC_BIG_ENDIAN) )
+    addMatch( NSTime( 0x0000FF0F, 0x00FF000F), 8)
+
+    execute ("add_pfield-time-bytes-local-le", treeAddPField ( tree, AUTC, tvb:range(0,8), ENC_LITTLE_ENDIAN) )
+    addMatch( NSTime(), 8)
+
+    execute ("add_pfield-time-bytes-local-le", treeAddPField ( tree, AUTC, tvb:range(8,8), ENC_LITTLE_ENDIAN) )
+    addMatch( NSTime( 0x0FFF0000, 0x0F00FF00 ), 8)
+
+    verifyFields("time.ABSOLUTE_UTC", autc_match_fields)
+
+    verifyResults("add_pfield-time-bytes-local", autc_match_values)
+
+----------------------------------------
+    testing(OTHER, "tree:add_packet_field Time string ENC_ISO_8601_DATE_TIME")
+
+    resetResults()
+    autc_match_values = {}
+
+    local datetimestring1 =   "2013-03-01T22:14:48+00:00" -- this is 1362176088 seconds epoch time
+    local tvb1 = ByteArray.new(datetimestring1, true):tvb("Date_Time string 1")
+    local datetimestring2 = "  2013-03-01T17:14:48+05:00" -- this is 1362176088 seconds epoch time
+    local tvb2 = ByteArray.new(datetimestring2 .. "  foobar", true):tvb("Date_Time string 2")
+    local datetimestring3 = "  2013-03-01T16:44+05:30"    -- this is 1362176040 seconds epoch time
+    local tvb3 = ByteArray.new(datetimestring3, true):tvb("Date_Time string 3")
+    local datetimestring4 =   "2013-03-02T01:44:00-03:30" -- this is 1362176040 seconds epoch time
+    local tvb4 = ByteArray.new(datetimestring4, true):tvb("Date_Time string 4")
+    local datetimestring5 =   "2013-03-01T22:14:48Z"      -- this is 1362176088 seconds epoch time
+    local tvb5 = ByteArray.new(datetimestring5, true):tvb("Date_Time string 5")
+    local datetimestring6 =   "2013-03-01T22:14Z"         -- this is 1362176040 seconds epoch time
+    local tvb6 = ByteArray.new(datetimestring6, true):tvb("Date_Time string 6")
+
+    execute ("add_pfield-datetime-local", treeAddPField ( tree, AUTC, tvb1:range(), ENC_ISO_8601_DATE_TIME) )
+    addMatch( NSTime( 1362176088, 0), string.len(datetimestring1))
+
+    execute ("add_pfield-datetime-local", treeAddPField ( tree, AUTC, tvb2:range(), ENC_ISO_8601_DATE_TIME) )
+    addMatch( NSTime( 1362176088, 0), string.len(datetimestring2))
+
+    execute ("add_pfield-datetime-local", treeAddPField ( tree, AUTC, tvb3:range(), ENC_ISO_8601_DATE_TIME) )
+    addMatch( NSTime( 1362176040, 0), string.len(datetimestring3))
+
+    execute ("add_pfield-datetime-local", treeAddPField ( tree, AUTC, tvb4:range(), ENC_ISO_8601_DATE_TIME) )
+    addMatch( NSTime( 1362176040, 0), string.len(datetimestring4))
+
+    execute ("add_pfield-datetime-local", treeAddPField ( tree, AUTC, tvb5:range(), ENC_ISO_8601_DATE_TIME) )
+    addMatch( NSTime( 1362176088, 0), string.len(datetimestring5))
+
+    execute ("add_pfield-datetime-local", treeAddPField ( tree, AUTC, tvb6:range(), ENC_ISO_8601_DATE_TIME) )
+    addMatch( NSTime( 1362176040, 0), string.len(datetimestring6))
+
+    verifyFields("time.ABSOLUTE_UTC", autc_match_fields)
+
+    verifyResults("add_pfield-datetime-local", autc_match_values)
+
+----------------------------------------
+    testing(OTHER, "tree:add_packet_field Time string ENC_ISO_8601_DATE")
+
+    resetResults()
+    autc_match_values = {}
+
+    local datestring1 =   "2013-03-01"  -- this is 1362096000 seconds epoch time
+    local d_tvb1 = ByteArray.new(datestring1, true):tvb("Date string 1")
+    local datestring2 = "  2013-03-01"  -- this is 1362096000 seconds epoch time
+    local d_tvb2 = ByteArray.new(datestring2 .. "  foobar", true):tvb("Date string 2")
+
+    execute ("add_pfield-date-local", treeAddPField ( tree, AUTC, d_tvb1:range(), ENC_ISO_8601_DATE) )
+    addMatch( NSTime( 1362096000, 0), string.len(datestring1))
+
+    execute ("add_pfield-date-local", treeAddPField ( tree, AUTC, d_tvb2:range(), ENC_ISO_8601_DATE) )
+    addMatch( NSTime( 1362096000, 0), string.len(datestring2))
+
+    verifyFields("time.ABSOLUTE_UTC", autc_match_fields)
+
+    verifyResults("add_pfield-date-local", autc_match_values)
+
+----------------------------------------
+    testing(OTHER, "tree:add_packet_field Time string ENC_ISO_8601_TIME")
+
+    resetResults()
+    autc_match_values = {}
+
+    local timestring1 =   "22:14:48"  -- this is 80088 seconds
+    local t_tvb1 = ByteArray.new(timestring1, true):tvb("Time string 1")
+    local timestring2 = "  22:14:48"  -- this is 80088 seconds
+    local t_tvb2 = ByteArray.new(timestring2 .. "  foobar", true):tvb("Time string 2")
+
+    local now = os.date("!*t")
+    now.hour = 22
+    now.min  = 14
+    now.sec  = 48
+    local timebase = os.time( now )
+    timebase = timebase + timezone
+    print ("timebase = " .. tostring(timebase) .. ", timezone=" .. timezone)
+
+    execute ("add_pfield-time-local", treeAddPField ( tree, AUTC, t_tvb1:range(), ENC_ISO_8601_TIME) )
+    addMatch( NSTime( timebase, 0), string.len(timestring1))
+
+    execute ("add_pfield-time-local", treeAddPField ( tree, AUTC, t_tvb2:range(), ENC_ISO_8601_TIME) )
+    addMatch( NSTime( timebase, 0), string.len(timestring2))
+
+    verifyFields("time.ABSOLUTE_UTC", autc_match_fields)
+
+    verifyResults("add_pfield-time-local", autc_match_values)
+
+----------------------------------------
+    testing(OTHER, "tree:add_packet_field Time string ENC_RFC_822")
+
+    resetResults()
+    autc_match_values = {}
+
+    local rfc822string1 =   "Fri, 01 Mar 13 22:14:48 GMT"  -- this is 1362176088 seconds epoch time
+    local rfc822_tvb1 = ByteArray.new(rfc822string1, true):tvb("RFC 822 Time string 1")
+    local rfc822string2 = "  Fri, 01 Mar 13 22:14:48 GMT"  -- this is 1362176088 seconds epoch time
+    local rfc822_tvb2 = ByteArray.new(rfc822string2 .. "  foobar", true):tvb("RFC 822 Time string 2")
+
+    execute ("add_pfield-time-local", treeAddPField ( tree, AUTC, rfc822_tvb1:range(), ENC_RFC_822) )
+    addMatch( NSTime( 1362176088, 0), string.len(rfc822string1))
+
+    execute ("add_pfield-time-local", treeAddPField ( tree, AUTC, rfc822_tvb2:range(), ENC_RFC_822) )
+    addMatch( NSTime( 1362176088, 0), string.len(rfc822string2))
+
+    verifyFields("time.ABSOLUTE_UTC", autc_match_fields)
+
+    verifyResults("add_pfield-rfc822-local", autc_match_values)
+
+----------------------------------------
+    testing(OTHER, "tree:add_packet_field Time string ENC_RFC_1123")
+
+    resetResults()
+    autc_match_values = {}
+
+    local rfc1123string1 =   "Fri, 01 Mar 2013 22:14:48 GMT"  -- this is 1362176088 seconds epoch time
+    local rfc1123_tvb1 = ByteArray.new(rfc1123string1, true):tvb("RFC 1123 Time string 1")
+    local rfc1123string2 = "  Fri, 01 Mar 2013 22:14:48 GMT"  -- this is 1362176088 seconds epoch time
+    local rfc1123_tvb2 = ByteArray.new(rfc1123string2 .. "  foobar", true):tvb("RFC 1123 Time string 2")
+
+    execute ("add_pfield-time-local", treeAddPField ( tree, AUTC, rfc1123_tvb1:range(), ENC_RFC_1123) )
+    addMatch( NSTime( 1362176088, 0), string.len(rfc1123string1))
+
+    execute ("add_pfield-time-local", treeAddPField ( tree, AUTC, rfc1123_tvb2:range(), ENC_RFC_1123) )
+    addMatch( NSTime( 1362176088, 0), string.len(rfc1123string2))
+
+    verifyFields("time.ABSOLUTE_UTC", autc_match_fields)
+
+    verifyResults("add_pfield-rfc1123-local", autc_match_values)
+
+----------------------------------------
+
+    setPassed(FRAME)
+end
+
+----------------------------------------
+-- we want to have our protocol dissection invoked for a specific UDP port,
+-- so get the udp dissector table and add our protocol to it
+DissectorTable.get("udp.port"):add(65333, test_proto)
+DissectorTable.get("udp.port"):add(65346, test_proto)
+
+print("test_proto dissector registered")
index 5afdc9a516c64ac1b227607cc291fabce5a5a411..fdeb5f0f2fef8299493f94819067a1c5fb79112c 100755 (executable)
@@ -364,6 +364,22 @@ wslua_step_struct_test() {
        fi
 }
 
+wslua_step_tvb_test() {
+       if [ $HAVE_LUA -ne 0 ]; then
+               test_step_skipped
+               return
+       fi
+
+       # Tshark catches lua script failures, so we have to parse the output.
+       $TSHARK -r $CAPTURE_DIR/dns_port.pcap -X lua_script:$TESTS_DIR/lua/tvb.lua > testout.txt 2>&1
+       if grep -q "All tests passed!" testout.txt; then
+               test_step_ok
+       else
+               cat testout.txt
+               test_step_failed "didn't find pass marker"
+       fi
+}
+
 wslua_cleanup_step() {
        rm -f ./testout.txt
        rm -f ./testin.txt
@@ -385,6 +401,7 @@ wslua_suite() {
        test_step_add "wslua proto/protofield" wslua_step_proto_test
        test_step_add "wslua script arguments" wslua_step_args_test
        test_step_add "wslua struct" wslua_step_struct_test
+       test_step_add "wslua tvb" wslua_step_tvb_test
 }
 #
 # Editor modelines  -  http://www.wireshark.org/tools/modelines.html
index 17750194d86d6123e119618397eebe6358a45102..1b87dd6cdbb7cf19c53ae77e530da45367a1ff73 100755 (executable)
@@ -1625,7 +1625,7 @@ sub check_proto_tree_add_XXX_encoding($$)
                 $args =~ s/\(.*\)//sg;
 
                 if ($args =~ /,\s*ENC_/xos) {
-                        if (!($func =~ /proto_tree_add_(item|bitmask|bits_item|bits_ret_val)/xos)
+                        if (!($func =~ /proto_tree_add_(time|item|bitmask|bits_item|bits_ret_val)/xos)
                            ) {
                                 print STDERR "Error: ".$filename." uses $func with ENC_*.\n";
                                 $errorCount++;
index 8def5de2505c32a00f5bb69fd292dc112844630f..3222ed18baa2d501e970ad06ed406ffab3f91a61 100644 (file)
@@ -36,7 +36,7 @@ extern "C" {
  */
 
 /** data structure to hold time values with nanosecond resolution*/
-typedef struct {
+typedef struct nstime_t {
        time_t  secs;
        int     nsecs;
 } nstime_t;