summaryrefslogtreecommitdiff
path: root/epan
diff options
context:
space:
mode:
authorHadriel Kaplan <hadrielk@yahoo.com>2014-04-12 00:32:20 -0400
committerAnders Broman <a.broman58@gmail.com>2014-04-14 11:47:39 +0000
commitdd002649c32a0f16720236b34fe5a7aefe04c457 (patch)
treed03bdc6f0f5ac36bb2d43a68f0cc8bcb72c27c3a /epan
parent92b501303bdeb163cec55825da8b683138517e4d (diff)
downloadwireshark-dd002649c32a0f16720236b34fe5a7aefe04c457.tar.gz
Add tvb_get and proto_tree_add for string-encoded timestamps
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>
Diffstat (limited to 'epan')
-rw-r--r--epan/proto.c387
-rw-r--r--epan/proto.h63
-rw-r--r--epan/tvbuff.c291
-rw-r--r--epan/tvbuff.h32
-rw-r--r--epan/wslua/wslua_tree.c135
-rw-r--r--epan/wslua/wslua_tvb.c48
6 files changed, 799 insertions, 157 deletions
diff --git a/epan/proto.c b/epan/proto.c
index a3284a07d8..2707f056ec 100644
--- a/epan/proto.c
+++ b/epan/proto.c
@@ -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)
diff --git a/epan/proto.h b/epan/proto.h
index 1e05072099..cde9dad3da 100644
--- a/epan/proto.h
+++ b/epan/proto.h
@@ -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.
diff --git a/epan/tvbuff.c b/epan/tvbuff.c
index c9eff6d192..668d1c0f8b 100644
--- a/epan/tvbuff.c
+++ b/epan/tvbuff.c
@@ -36,10 +36,13 @@
#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"
@@ -48,6 +51,18 @@
#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. */
@@ -1899,6 +2161,35 @@ tvb_get_utf_8_string(wmem_allocator_t *scope, tvbuff_t *tvb, const gint offset,
/*
* 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
* high-order bit set being invalid, and return a pointer to a UTF-8
* string.
diff --git a/epan/tvbuff.h b/epan/tvbuff.h
index a365276ee1..14789d318b 100644
--- a/epan/tvbuff.h
+++ b/epan/tvbuff.h
@@ -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
diff --git a/epan/wslua/wslua_tree.c b/epan/wslua/wslua_tree.c
index 5eee9e561e..356054f518 100644
--- a/epan/wslua/wslua_tree.c
+++ b/epan/wslua/wslua_tree.c
@@ -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) {
diff --git a/epan/wslua/wslua_tvb.c b/epan/wslua/wslua_tvb.c
index bfad06c51e..9746ff77a4 100644
--- a/epan/wslua/wslua_tvb.c
+++ b/epan/wslua/wslua_tvb.c
@@ -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) {