From f52626cc83fa6362a286f3ffbda1d3d67392ac3f Mon Sep 17 00:00:00 2001 From: Hadriel Kaplan Date: Sat, 12 Apr 2014 23:20:15 -0400 Subject: [PATCH] Add tvb_get and proto_tree_add for string-encoded byte arrays This commit adds tvb_get_string_bytes and proto_tree_add_bytes_item routines for getting GByteArrays fields from the tvb when they are encoded in ASCII hex string form. The proto_tree_add_bytes_item routine is also usable for normal binary encoded byte arrays, and has the advantage of retrieving the array values 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: I112a038653df6482a5d0ebe7c95708f207319e20 Reviewed-on: https://code.wireshark.org/review/1158 Reviewed-by: Hadriel Kaplan Reviewed-by: Anders Broman --- doc/README.dissector | 46 +++++++ epan/proto.c | 159 ++++++++++++++++++++++- epan/proto.h | 54 ++++++++ epan/strutil.c | 129 ++++++++++++++++++- epan/strutil.h | 30 ++++- epan/tvbuff.c | 30 +++++ epan/tvbuff.h | 12 ++ epan/wslua/wslua_field.c | 19 ++- epan/wslua/wslua_tree.c | 22 ++++ epan/wslua/wslua_tvb.c | 67 +++++++++- test/lua/tvb.lua | 263 +++++++++++++++++++++++++++++++++++---- test/suite-wslua.sh | 8 ++ 12 files changed, 802 insertions(+), 37 deletions(-) diff --git a/doc/README.dissector b/doc/README.dissector index 1ca8107cc0..0d5d9aef66 100644 --- a/doc/README.dissector +++ b/doc/README.dissector @@ -320,6 +320,16 @@ gchar *tvb_bytes_to_str_punct(tvbuff_t *tvb, gint offset, gint len, gchar punct) This function is similar to tvb_bytes_to_str(...) except that 'punct' is inserted between the hex representation of each byte. +GByteArray *tvb_get_string_bytes(tvbuff_t *tvb, const gint offset, const gint length, + const guint encoding, GByteArray* bytes, gint *endoff) + +Given a tvbuff, an offset into the tvbuff, and a length that starts +at that offset (which may be -1 for "all the way to the end of the +tvbuff"), fetch the hex-decoded byte values of the tvbuff into the +passed-in 'bytes' array, based on the passed-in encoding. In other +words, convert from a hex-ascii string in tvbuff, into the supplied +GByteArray. + gchar *tvb_bcd_dig_to_wmem_packet_str(tvbuff_t *tvb, const gint offset, const gint len, dgt_set_t *dgt, gboolean skip_first); Given a tvbuff, an offset into the tvbuff, and a length that starts @@ -1097,6 +1107,10 @@ protocol or field labels to the proto_tree: proto_item * proto_tree_add_bytes(tree, id, tvb, start, length, start_ptr); + proto_item * + proto_tree_add_bytes_item(tree, id, tvb, start, length, encoding, + retval, endoff, err); + proto_item * proto_tree_add_bytes_format(tree, id, tvb, start, length, start_ptr, format, ...); @@ -1108,6 +1122,10 @@ protocol or field labels to the proto_tree: proto_item * proto_tree_add_time(tree, id, tvb, start, length, value_ptr); + proto_item * + proto_tree_add_time_item(tree, id, tvb, start, length, encoding, + retval, endoff, err); + proto_item * proto_tree_add_time_format(tree, id, tvb, start, length, value_ptr, format, ...); @@ -1476,6 +1494,34 @@ Subarea Nodes. The user does not have to shift the value of the FID to the high nibble of the byte ("sna.th.fid == 0xf0") as was necessary in the past. +proto_tree_add_XXX_item() +--------------------- +proto_tree_add_XXX_item is used when you wish to do no special formatting, +but also either wish for the retrieved value from the tvbuff to be handed +back (to avoid doing tvb_get_...), and/or wish to have the value be decoded +from the tvbuff in a string-encoded format. + +The item added to the GUI tree will contain the name (as passed in the +proto_register_*() function) and a value. The value will be fetched +from the tvbuff, based on the type of the XXX name and the encoding of +the value as specified by the "encoding" argument. + +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 hex value from the string +results in an expert info error being added to the tree. + +For string-decoding, the passed-in encoding argument needs to specify the +string encoding (e.g., ENC_ASCII, ENC_UTF_8) as well as the format. For +some XXX types, the format is constrained - for example for the encoding format +for proto_tree_add_time_item() can only be one of the ENC_ISO_8601_* ones +or ENC_RFC_822 or ENC_RFC_1123. For proto_tree_add_bytes_item() it can only +be ENC_STR_HEX bit-or'ed with one or more of the ENC_SEP_* separator types. + proto_tree_add_protocol_format() -------------------------------- proto_tree_add_protocol_format is used to add the top-level item for the diff --git a/epan/proto.c b/epan/proto.c index 2707f056ec..ac73a2604b 100644 --- a/epan/proto.c +++ b/epan/proto.c @@ -87,8 +87,9 @@ struct ptvcursor { @param tree the tree to append this item to @param hfindex field index @param hfinfo header_field + @param free_block a code block to call to free resources if this returns @return the header field matching 'hfinfo' */ -#define TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo) \ +#define TRY_TO_FAKE_THIS_ITEM_OR_FREE(tree, hfindex, hfinfo, free_block) \ /* If this item is not referenced we dont have to do much work \ at all but we should still return a node so that field items \ below this node (think proto_item_add_subtree()) will still \ @@ -100,10 +101,13 @@ struct ptvcursor { We fake FT_PROTOCOL unless some clients have requested us \ not to do so. \ */ \ - if (!tree) \ + if (!tree) { \ + free_block; \ return NULL; \ + } \ PTREE_DATA(tree)->count++; \ if (PTREE_DATA(tree)->count > MAX_TREE_ITEMS) { \ + free_block; \ if (getenv("WIRESHARK_ABORT_ON_TOO_MANY_ITEMS") != NULL) \ g_error("More than %d items in the tree -- possible infinite loop", MAX_TREE_ITEMS); \ /* Let the exception handler add items to the tree */ \ @@ -117,12 +121,22 @@ struct ptvcursor { if ((hfinfo->ref_type != HF_REF_TYPE_DIRECT) \ && (hfinfo->type != FT_PROTOCOL || \ PTREE_DATA(tree)->fake_protocols)) { \ + free_block; \ /* just return tree back to the caller */\ return tree; \ } \ } \ } +/** See inlined comments. + @param tree the tree to append this item to + @param hfindex field index + @param hfinfo header_field + @return the header field matching 'hfinfo' */ +#define TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo) \ + TRY_TO_FAKE_THIS_ITEM_OR_FREE(tree, hfindex, hfinfo, ((void)0)) + + /** See inlined comments. @param pi the created protocol item we're about to return */ #define TRY_TO_FAKE_THIS_REPR(pi) \ @@ -182,6 +196,8 @@ proto_tree_set_bytes(field_info *fi, const guint8* start_ptr, gint length); static void proto_tree_set_bytes_tvb(field_info *fi, tvbuff_t *tvb, gint offset, gint length); static void +proto_tree_set_bytes_gbytearray(field_info *fi, const GByteArray *value); +static void proto_tree_set_time(field_info *fi, const nstime_t *value_ptr); static void proto_tree_set_string(field_info *fi, const char* value); @@ -1955,6 +1971,133 @@ 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); } +/* which FT_ types can use proto_tree_add_bytes_item() */ +static inline gboolean +validate_proto_tree_add_bytes_ftype(const enum ftenum type) +{ + return (type == FT_BYTES || + type == FT_UINT_BYTES || + type == FT_OID || + type == FT_REL_OID || + type == FT_SYSTEM_ID ); +} + +/* Note: this does no validation that the byte array of an FT_OID or + FT_REL_OID is actually valid; and neither does proto_tree_add_item(), + so I think it's ok to continue not validating it? + */ +proto_item * +proto_tree_add_bytes_item(proto_tree *tree, int hfindex, tvbuff_t *tvb, + const gint start, gint length, const guint encoding, + GByteArray *retval, gint *endoff, gint *err) +{ + field_info *new_fi; + GByteArray *bytes = retval; + GByteArray *created_bytes = NULL; + gint saved_err = 0; + guint32 n = 0; + header_field_info *hfinfo = proto_registrar_get_nth(hfindex); + gboolean generate = (bytes || tree) ? TRUE : FALSE; + + DISSECTOR_ASSERT_HINT(hfinfo != NULL, "Not passed hfi!"); + + DISSECTOR_ASSERT_HINT(validate_proto_tree_add_bytes_ftype(hfinfo->type), + "Called proto_tree_add_bytes_item but not a bytes-based FT_XXX type"); + + /* length has to be -1 or > 0 regardless of encoding */ + /* invalid FT_UINT_BYTES length is caught in get_uint_value() */ + if (length < -1 || length == 0) { + REPORT_DISSECTOR_BUG(wmem_strdup_printf(wmem_packet_scope(), + "Invalid length %d passed to proto_tree_add_bytes_item for %s", + length, ftype_name(hfinfo->type))); + } + + if (encoding & ENC_STR_NUM) { + REPORT_DISSECTOR_BUG("Decoding number strings for byte arrays is not supported"); + } + + if (generate && (encoding & ENC_STR_HEX)) { + if (hfinfo->type == FT_UINT_BYTES) { + /* can't decode FT_UINT_BYTES from strings */ + REPORT_DISSECTOR_BUG("proto_tree_add_bytes_item called for " + "FT_UINT_BYTES type, but as ENC_STR_HEX"); + } + + if (!bytes) { + /* caller doesn't care about return value, but we need it to + call tvb_get_string_bytes() and set the tree later */ + bytes = created_bytes = g_byte_array_new(); + } + + /* bytes might be NULL after this, but can't add expert error until later */ + bytes = tvb_get_string_bytes(tvb, start, length, encoding, bytes, endoff); + + /* grab the errno now before it gets overwritten */ + saved_err = errno; + } + else if (generate) { + tvb_ensure_bytes_exist(tvb, start, length); + + if (!bytes) { + /* caller doesn't care about return value, but we need it to + call tvb_get_string_bytes() and set the tree later */ + bytes = created_bytes = g_byte_array_new(); + } + + if (hfinfo->type == FT_UINT_BYTES) { + n = length; /* n is now the "header" length */ + length = get_uint_value(tree, tvb, start, n, encoding); + /* length is now the value's length; only store the value in the array */ + g_byte_array_append(bytes, tvb_get_ptr(tvb, start + n, length), length); + } + else if (length > 0) { + g_byte_array_append(bytes, tvb_get_ptr(tvb, start, length), length); + } + + if (endoff) + *endoff = start + n + length; + } + + if (err) *err = saved_err; + + TRY_TO_FAKE_THIS_ITEM_OR_FREE(tree, hfinfo->id, hfinfo, + { + if (created_bytes) + g_byte_array_free(created_bytes, TRUE); + created_bytes = NULL; + bytes = NULL; + } ); + + /* n will be zero except when it's a FT_UINT_BYTES */ + new_fi = new_field_info(tree, hfinfo, tvb, start, n + length); + + if (new_fi == NULL) + return NULL; + + if (encoding & ENC_STRING) { + if (saved_err == ERANGE) + expert_add_info(NULL, tree, &ei_number_string_decoding_erange_error); + else if (!bytes || saved_err != 0) + expert_add_info(NULL, tree, &ei_number_string_decoding_failed_error); + + if (bytes) + proto_tree_set_bytes_gbytearray(new_fi, bytes); + + if (created_bytes) + g_byte_array_free(created_bytes, TRUE); + } + else { + /* n will be zero except when it's a FT_UINT_BYTES */ + proto_tree_set_bytes_tvb(new_fi, tvb, start + n, length); + + FI_SET_FLAG(new_fi, + (encoding & ENC_LITTLE_ENDIAN) ? FI_LITTLE_ENDIAN : FI_BIG_ENDIAN); + } + + return proto_tree_add_node(tree, new_fi); +} + + proto_item * proto_tree_add_time_item(proto_tree *tree, int hfindex, tvbuff_t *tvb, const gint start, gint length, const guint encoding, @@ -2205,6 +2348,18 @@ proto_tree_set_bytes_tvb(field_info *fi, tvbuff_t *tvb, gint offset, gint length proto_tree_set_bytes(fi, tvb_get_ptr(tvb, offset, length), length); } +static void +proto_tree_set_bytes_gbytearray(field_info *fi, const GByteArray *value) +{ + GByteArray *bytes; + + DISSECTOR_ASSERT(value != NULL); + + bytes = byte_array_dup(value); + + fvalue_set_byte_array(&fi->value, bytes); +} + /* Add a FT_*TIME to a proto_tree */ proto_item * proto_tree_add_time(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, diff --git a/epan/proto.h b/epan/proto.h index d17242990e..eac413478a 100644 --- a/epan/proto.h +++ b/epan/proto.h @@ -347,6 +347,22 @@ WS_DLL_PUBLIC WS_MSVC_NORETURN void proto_report_dissector_bug(const char *messa /* mask out ENC_STR_* and related bits - should this replace ENC_CHARENCODING_MASK? */ #define ENC_STR_MASK 0x0000FFFE +/* for cases where the number is allowed to have a leading '+'/'-' */ +/* this can't collide with ENC_SEP_* because they can be used simultaneously */ +#define ENC_NUM_PREF 0x00200000 + +/* For cases where a string encoding contains hex, bit-or one or more + * of these for the allowed separator(s), as well as with ENC_STR_HEX. + * See hex_str_to_bytes_encoding() in epan/strutil.h for details. + */ +#define ENC_SEP_NONE 0x00010000 +#define ENC_SEP_COLON 0x00020000 +#define ENC_SEP_DASH 0x00040000 +#define ENC_SEP_DOT 0x00080000 +#define ENC_SEP_SPACE 0x00100000 +/* a convenience macro for the above */ +#define ENC_SEP_MASK 0x001F0000 + /* 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. @@ -905,6 +921,44 @@ WS_DLL_PUBLIC proto_item * proto_tree_add_bytes(proto_tree *tree, int hfindex, tvbuff_t *tvb, gint start, gint length, const guint8* start_ptr); +/** Get and add a byte-array-based FT_* to a proto_tree. + + Supported: FT_BYTES, FT_UINT_BYTES, FT_OID, FT_REL_OID, and FT_SYSTEM_ID. + + The item is extracted from the tvbuff handed to it, based on the ENC_* passed + in for the encoding, and the retrieved byte array 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 hex 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_bytes(); see + that function's comments for details. + + @note The GByteArray retval must be pre-constructed using g_byte_array_new(). + + @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, or ENC_UTF_8|ENC_STR_HEX) + @param[in,out] retval points to a GByteArray which will be set to the bytes from the Tvb. + @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_bytes_item(proto_tree *tree, int hfindex, tvbuff_t *tvb, + const gint start, gint length, const guint encoding, + GByteArray *retval, gint *endoff, gint *err); + /** Add a formatted FT_BYTES 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/strutil.c b/epan/strutil.c index 567d072abd..d9b8c5a553 100644 --- a/epan/strutil.c +++ b/epan/strutil.c @@ -30,6 +30,7 @@ #include "emem.h" #include +#include #ifdef _WIN32 #include @@ -545,6 +546,132 @@ hex_str_to_bytes(const char *hex_str, GByteArray *bytes, gboolean force_separato return TRUE; } +static inline gchar +get_valid_byte_sep(gchar c, const guint encoding) +{ + gchar retval = -1; /* -1 means failure */ + + switch (c) { + case ':': + if (encoding & ENC_SEP_COLON) + retval = c; + break; + case '-': + if (encoding & ENC_SEP_DASH) + retval = c; + break; + case '.': + if (encoding & ENC_SEP_DOT) + retval = c; + break; + case ' ': + if (encoding & ENC_SEP_SPACE) + retval = c; + break; + case '\0': + /* we were given the end of the string, so it's fine */ + retval = 0; + break; + default: + if (isxdigit(c) && (encoding & ENC_SEP_NONE)) + retval = 0; + /* anything else means we've got a failure */ + break; + } + + return retval; +} + +/* Turn a string of hex digits with optional separators (defined by is_byte_sep()) + * into a byte array. Unlike hex_str_to_bytes(), this will read as many hex-char + * pairs as possible and not error if it hits a non-hex-char; instead it just ends + * there. (i.e., like strtol()/atoi()/etc.) Unless fail_if_partial is TRUE. + * + * The **endptr, if not NULL, is set to the char after the last hex character. + */ +gboolean +hex_str_to_bytes_encoding(const gchar *hex_str, GByteArray *bytes, const gchar **endptr, + const guint encoding, const gboolean fail_if_partial) +{ + gchar c, d; + guint8 val; + const gchar *end = hex_str; + gboolean retval = FALSE; + gchar sep = -1; + + /* a map from ASCII hex chars to their value */ + static const gchar str_to_nibble[256] = { + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1, + -1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 + }; + + /* we must see two hex chars at the beginning, or fail */ + if (bytes && *end && isxdigit(*end) && isxdigit(*(end+1))) { + retval = TRUE; + + /* set the separator character we'll allow; if this returns a -1, it means something's + * invalid after the hex, but we'll let the while-loop grab the first hex-pair anyway + */ + sep = get_valid_byte_sep(*(end+2), encoding); + + while (*end) { + c = str_to_nibble[(int)*end]; + if (c < 0) { + if (fail_if_partial) retval = FALSE; + break; + } + ++end; + + d = str_to_nibble[(int)*end]; + if (d < 0) { + if (fail_if_partial) retval = FALSE; + break; + } + val = ((guint8)c * 16) + d; + g_byte_array_append(bytes, &val, 1); + ++end; + + /* check for separator and peek at next char to make sure we should keep going */ + if (sep > 0 && *end == sep && str_to_nibble[(int)*(end+1)] > -1) { + /* yes, it's the right sep and followed by more hex, so skip the sep */ + ++end; + } else if (sep != 0 && *end) { + /* we either need a separator, but we don't see one; or the get_valid_byte_sep() + earlier didn't find a valid one to begin with */ + if (fail_if_partial) retval = FALSE; + break; + } + /* otherwise, either no separator allowed, or *end is null, or *end is an invalid + * sep, or *end is a valid sep but after it is not a hex char - in all those + * cases, just loop back up and let it fail later naturally. + */ + } + } + + if (!retval) { + if (bytes) g_byte_array_set_size(bytes, 0); + end = hex_str; + } + + if (endptr) *endptr = end; + + return retval; +} + /* * Turn an RFC 3986 percent-encoded string into a byte array. * XXX - We don't check for reserved characters. @@ -666,7 +793,7 @@ format_uri(const GByteArray *bytes, const gchar *reserved_chars) * */ GByteArray * -byte_array_dup(GByteArray *ba) { +byte_array_dup(const GByteArray *ba) { GByteArray *new_ba; if (!ba) diff --git a/epan/strutil.h b/epan/strutil.h index b9e8bdfcb7..f92136bbcd 100644 --- a/epan/strutil.h +++ b/epan/strutil.h @@ -112,6 +112,34 @@ WS_DLL_PUBLIC gboolean hex_str_to_bytes(const char *hex_str, GByteArray *bytes, gboolean force_separators); +/* Turn a string of hex digits with optional separators (defined by encoding) + * into a byte array. Unlike hex_str_to_bytes(), this will read as many hex-char + * pairs as possible and not error if it hits a non-hex-char; instead it just ends + * there. (i.e., like strtol()/atoi()/etc.) But it must see two hex chars at the + * beginning or it will return FALSE. + * + * @param hex_str The string of hex digits. + * @param bytes The GByteArray that will receive the bytes. This + * must be initialized by the caller. + * @param endptr if not NULL, is set to the char after the last hex character consumed. + * @param encoding set to one or more bitwise-or'ed ENC_SEP_* (see proto.h) + * @param fail_if_partial If set to TRUE, then the conversion fails if the whole + * hex_str is not consumed. + * @return FALSE only if no bytes were generated; or if fail_if_partial is TRUE + * and the entire hex_str was not consumed. + * + * If no ENC_SEP_* is set, then no separators are allowed. If multiple ENC_SEP_* are + * bit-or'ed, any of them can be a separator, but once the separator is seen then + * only its same type is accepted for the rest of the string. (i.e., it won't convert + * a "01:23-4567" even if ENC_SEP_COLON|ENC_SEP_DASH|ENC_SEP_NONE is passed in) + * + * This is done this way because it's likely a malformed scenario if they're mixed, + * and this routine is used by dissectors via tvb_get_string_XXX routines. + */ +WS_DLL_PUBLIC +gboolean hex_str_to_bytes_encoding(const char *hex_str, GByteArray *bytes, const char **endptr, + const guint encoding, const gboolean fail_if_partial); + /** Turn an RFC 3986 percent-encoded string into a byte array. * * @param uri_str The string of hex digits. @@ -169,7 +197,7 @@ gboolean oid_str_to_bytes(const char *oid_str, GByteArray *bytes); * @todo - Should this be in strutil.c? */ WS_DLL_PUBLIC -GByteArray *byte_array_dup(GByteArray *ba); +GByteArray *byte_array_dup(const GByteArray *ba); /** * Compare the contents of two GByteArrays diff --git a/epan/tvbuff.c b/epan/tvbuff.c index 668d1c0f8b..8612921bd5 100644 --- a/epan/tvbuff.c +++ b/epan/tvbuff.c @@ -1307,6 +1307,36 @@ validate_single_byte_ascii_encoding(const guint encoding) REPORT_DISSECTOR_BUG("No string encoding type passed to tvb_get_string_XXX"); } +GByteArray* +tvb_get_string_bytes(tvbuff_t *tvb, const gint offset, const gint length, + const guint encoding, GByteArray *bytes, gint *endoff) +{ + const gchar *ptr = (gchar*) tvb_get_raw_string(wmem_packet_scope(), tvb, offset, length); + const gchar *begin = ptr; + const gchar *end = NULL; + GByteArray* retval = NULL; + + errno = EDOM; + + validate_single_byte_ascii_encoding(encoding); + + if (endoff) *endoff = 0; + + while (*begin == ' ') begin++; + + if (*begin && bytes) { + if (hex_str_to_bytes_encoding(begin, bytes, &end, encoding, FALSE)) { + if (bytes->len > 0) { + if (endoff) *endoff = offset + (gint)(end - ptr); + errno = 0; + retval = bytes; + } + } + } + + return retval; +} + /* 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 diff --git a/epan/tvbuff.h b/epan/tvbuff.h index 14789d318b..e68095759a 100644 --- a/epan/tvbuff.h +++ b/epan/tvbuff.h @@ -367,6 +367,18 @@ 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); +/* Similar to above, but returns a GByteArray based on the case-insensitive + * hex-char strings with optional separators, and with optional leading spaces. + * The separators allowed are based on the ENC_SEP_* passed in the encoding param. + * + * The passed-in bytes is set to the values, and its pointer is also the return + * value or NULL on error. The GByteArray bytes must be pre-constructed with + * g_byte_array_new(). + */ +WS_DLL_PUBLIC +GByteArray* tvb_get_string_bytes(tvbuff_t *tvb, const gint offset, const gint length, + const guint encoding, GByteArray* bytes, 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_field.c b/epan/wslua/wslua_field.c index 0720edf591..72dc551d7f 100644 --- a/epan/wslua/wslua_field.c +++ b/epan/wslua/wslua_field.c @@ -75,7 +75,16 @@ WSLUA_METAMETHOD FieldInfo__unm(lua_State* L) { /* WSLUA_ATTRIBUTE FieldInfo_value RO The value of this field. */ WSLUA_METAMETHOD FieldInfo__call(lua_State* L) { /* - Obtain the Value of the field + Obtain the Value of the field. + + Previous to 1.11.4, this function retrieved the value for most field types, + but for `ftypes.UINT_BYTES` it retrieved the `ByteArray` of the field's entire `TvbRange`. + In other words, it returned a `ByteArray` that included the leading length byte(s), + instead of just the *value* bytes. That was a bug, and has been changed in 1.11.4. + Furthermore, it retrieved an `ftypes.GUID` as a `ByteArray`, which is also incorrect. + + If you wish to still get a `ByteArray` of the `TvbRange`, use `FieldInfo:get_range()` + to get the `TvbRange`, and then use `Tvb:bytes()` to convert it to a `ByteArray`. */ FieldInfo fi = checkFieldInfo(L,1); @@ -165,16 +174,18 @@ WSLUA_METAMETHOD FieldInfo__call(lua_State* L) { /* FALLTHROUGH */ case FT_BYTES: case FT_UINT_BYTES: - case FT_GUID: case FT_PROTOCOL: case FT_REL_OID: case FT_SYSTEM_ID: - case FT_OID: { + case FT_OID: + { ByteArray ba = g_byte_array_new(); - g_byte_array_append(ba, (const guint8 *)tvb_memdup(wmem_packet_scope(),fi->ws_fi->ds_tvb,fi->ws_fi->start,fi->ws_fi->length),fi->ws_fi->length); + g_byte_array_append(ba, (const guint8 *) fvalue_get(&fi->ws_fi->value), + fvalue_length(&fi->ws_fi->value)); pushByteArray(L,ba); return 1; } + case FT_GUID: default: luaL_error(L,"FT_ not yet supported"); return 1; diff --git a/epan/wslua/wslua_tree.c b/epan/wslua/wslua_tree.c index 78c4390e41..afd5feef90 100644 --- a/epan/wslua/wslua_tree.c +++ b/epan/wslua/wslua_tree.c @@ -62,6 +62,25 @@ try_add_packet_field(lua_State *L, TreeItem tree_item, TvbRange tvbr, const int gint endoff = 0; switch(type) { + /* these all generate ByteArrays */ + case FT_BYTES: + case FT_UINT_BYTES: + case FT_OID: + case FT_REL_OID: + case FT_SYSTEM_ID: + { + /* GByteArray and its data will be g_free'd by Lua */ + GByteArray *gba = g_byte_array_new(); + item = proto_tree_add_bytes_item(tree_item->tree, hfid, tvbr->tvb->ws_tvb, + tvbr->offset, tvbr->len, encoding, + gba, &endoff, &err); + if (err == 0) { + pushByteArray(L, gba); + lua_pushinteger(L, endoff); + } + } + break; + case FT_ABSOLUTE_TIME: case FT_RELATIVE_TIME: { @@ -77,6 +96,9 @@ try_add_packet_field(lua_State *L, TreeItem tree_item, TvbRange tvbr, const int } break; + /* XXX: what about these? */ + case FT_NONE: + case FT_PROTOCOL: /* 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); diff --git a/epan/wslua/wslua_tvb.c b/epan/wslua/wslua_tvb.c index a48e48f32c..d40de58436 100644 --- a/epan/wslua/wslua_tvb.c +++ b/epan/wslua/wslua_tvb.c @@ -106,6 +106,27 @@ WSLUA_METAMETHOD ByteArray__concat(lua_State* L) { WSLUA_RETURN(1); /* The new composite `ByteArray`. */ } +WSLUA_METAMETHOD ByteArray__eq(lua_State* L) { + /* Compares two ByteArray values. + + @since 1.11.4 + */ +#define WSLUA_ARG_ByteArray__eq_FIRST 1 /* First array. */ +#define WSLUA_ARG_ByteArray__eq_SECOND 2 /* Second array. */ + ByteArray ba1 = checkByteArray(L,WSLUA_ARG_ByteArray__eq_FIRST); + ByteArray ba2 = checkByteArray(L,WSLUA_ARG_ByteArray__eq_SECOND); + gboolean result = FALSE; + + if (ba1->len == ba2->len) { + if (memcmp(ba1->data, ba2->data, ba1->len) == 0) + result = TRUE; + } + + lua_pushboolean(L,result); + + return 1; +} + WSLUA_METHOD ByteArray_prepend(lua_State* L) { /* Prepend a `ByteArray` to this `ByteArray`. */ #define WSLUA_ARG_ByteArray_prepend_PREPENDED 2 /* `ByteArray` to be prepended. */ @@ -325,6 +346,7 @@ WSLUA_METHODS ByteArray_methods[] = { WSLUA_META ByteArray_meta[] = { WSLUA_CLASS_MTREG(ByteArray,tostring), WSLUA_CLASS_MTREG(ByteArray,concat), + WSLUA_CLASS_MTREG(ByteArray,eq), {"__call",ByteArray_subset}, { NULL, NULL } }; @@ -1309,9 +1331,25 @@ WSLUA_METHOD TvbRange_le_ustringz(lua_State* L) { } WSLUA_METHOD TvbRange_bytes(lua_State* L) { - /* Obtain a `ByteArray` from a `TvbRange`. */ + /* Obtain a `ByteArray` from a `TvbRange`. + + Starting in 1.11.4, this function also takes an optional `encoding` argument, + which can be set to `ENC_STR_HEX` to decode a hex-string from the `TvbRange` + into the returned `ByteArray`. The `encoding` can be bitwise-or'ed with one + or more separator encodings, such as `ENC_SEP_COLON`, to allow separators + to occur between each pair of hex characters. + + The return value also now returns the number of bytes used as a second return value. + + On failure or error, nil is returned for both return values. + + @note The encoding type of the hex string should also be set, for example + `ENC_ASCII` or `ENC_UTF_8`, along with `ENC_STR_HEX`. + */ +#define WSLUA_OPTARG_TvbRange_bytes_ENCODING 2 /* An optional ENC_* encoding value to use */ TvbRange tvbr = checkTvbRange(L,1); GByteArray* ba; + const guint encoding = luaL_optint(L, WSLUA_OPTARG_TvbRange_bytes_ENCODING, 0); if ( !(tvbr && tvbr->tvb)) return 0; if (tvbr->tvb->expired) { @@ -1320,11 +1358,32 @@ WSLUA_METHOD TvbRange_bytes(lua_State* L) { } ba = g_byte_array_new(); - g_byte_array_append(ba,(const guint8 *)tvb_memdup(wmem_packet_scope(),tvbr->tvb->ws_tvb,tvbr->offset,tvbr->len),tvbr->len); - pushByteArray(L,ba); + if (encoding == 0) { + g_byte_array_append(ba,(const guint8 *)tvb_memdup(wmem_packet_scope(),tvbr->tvb->ws_tvb,tvbr->offset,tvbr->len),tvbr->len); + pushByteArray(L,ba); + lua_pushinteger(L, tvbr->len); + } + else if ((encoding & ENC_STR_HEX) == 0) { + WSLUA_OPTARG_ERROR(TvbRange_nstime, ENCODING, "invalid encoding value"); + } + else { + gint endoff = 0; + GByteArray* retval = tvb_get_string_bytes(tvbr->tvb->ws_tvb, tvbr->offset, tvbr->len, + encoding, ba, &endoff); + if (!retval || endoff == 0) { + g_byte_array_free(ba, TRUE); + /* push nil nstime and offset */ + lua_pushnil(L); + lua_pushnil(L); + } + else { + pushByteArray(L,ba); + lua_pushinteger(L, endoff); + } + } - WSLUA_RETURN(1); /* The `ByteArray` object. */ + WSLUA_RETURN(2); /* The `ByteArray` object or nil, and number of bytes consumed or nil. */ } WSLUA_METHOD TvbRange_bitfield(lua_State* L) { diff --git a/test/lua/tvb.lua b/test/lua/tvb.lua index 52f271b9da..25c0daacf3 100644 --- a/test/lua/tvb.lua +++ b/test/lua/tvb.lua @@ -45,7 +45,7 @@ end -- -- CHANGE THIS TO MATCH HOW MANY TESTS THERE ARE -- -local taptests = { [FRAME]=4, [OTHER]=247 } +local taptests = { [FRAME]=4, [OTHER]=312 } local function getResults() print("\n-----------------------------\n") @@ -132,14 +132,21 @@ end ---------------------------------------- --- a table of all of our Protocol's fields and test input and expected output +-- a table of all of our Protocol's fields 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") + 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"), + BYTES = ProtoField.bytes ("test.basic.bytes", "Basic Bytes"), + UINT_BYTES = ProtoField.ubytes ("test.basic.ubytes", "Basic Uint Bytes"), + OID = ProtoField.oid ("test.basic.oid", "Basic OID"), + REL_OID = ProtoField.rel_oid("test.basic.rel_oid", "Basic Relative OID"), + ABSOLUTE_LOCAL = ProtoField.absolute_time("test.basic.absolute.local","Basic absolute local"), + ABSOLUTE_UTC = ProtoField.absolute_time("test.basic.absolute.utc", "Basic absolute utc", 1001), + -- GUID = ProtoField.guid ("test.basic.guid", "Basic GUID"), }, time = @@ -148,6 +155,14 @@ local testfield = ABSOLUTE_UTC = ProtoField.absolute_time("test.time.absolute.utc", "Time absolute utc", 1001), }, + bytes = + { + BYTES = ProtoField.bytes ("test.bytes.bytes", "Bytes"), + UINT_BYTES = ProtoField.ubytes ("test.bytes.ubytes", "Uint Bytes"), + OID = ProtoField.oid ("test.bytes.oid", "OID"), + REL_OID = ProtoField.rel_oid("test.bytes.rel_oid", "Relative OID"), + -- GUID = ProtoField.guid ("test.bytes.guid", "GUID"), + }, } -- create a flat array table of the above that can be registered @@ -168,9 +183,16 @@ local getfield = { basic = { - STRING = Field.new ("test.basic.string"), - BOOLEAN = Field.new ("test.basic.boolean"), - UINT16 = Field.new ("test.basic.uint16") + STRING = Field.new ("test.basic.string"), + BOOLEAN = Field.new ("test.basic.boolean"), + UINT16 = Field.new ("test.basic.uint16"), + BYTES = Field.new ("test.basic.bytes"), + UINT_BYTES = Field.new ("test.basic.ubytes"), + OID = Field.new ("test.basic.oid"), + REL_OID = Field.new ("test.basic.rel_oid"), + ABSOLUTE_LOCAL = Field.new ("test.basic.absolute.local"), + ABSOLUTE_UTC = Field.new ("test.basic.absolute.utc"), + -- GUID = Field.new ("test.basic.guid"), }, time = @@ -179,8 +201,18 @@ local getfield = ABSOLUTE_UTC = Field.new ("test.time.absolute.utc"), }, + bytes = + { + BYTES = Field.new ("test.bytes.bytes"), + UINT_BYTES = Field.new ("test.bytes.ubytes"), + OID = Field.new ("test.bytes.oid"), + REL_OID = Field.new ("test.bytes.rel_oid"), + -- GUID = Field.new ("test.bytes.guid"), + }, } +print("test_proto Fields created") + local function addMatchFields(match_fields, ... ) match_fields[#match_fields + 1] = { ... } end @@ -303,14 +335,14 @@ function test_proto.dissector(tvbuf,pktinfo,root) incPktCount(FRAME) incPktCount(OTHER) - testing(OTHER, "Basic") + testing(OTHER, "Basic string") 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 bytearray = ByteArray.new(teststring, true) + local tvb_string = bytearray:tvb("Basic string") local function callTreeAdd(tree,...) tree:add(...) @@ -318,61 +350,242 @@ function test_proto.dissector(tvbuf,pktinfo,root) local string_match_fields = {} - execute ("basic-string", tree:add(testfield.basic.STRING, tvb:range(0,tvb:len())) ~= nil ) + execute ("basic-tvb_get_string", tvb_string:range():string() == teststring ) + + execute ("basic-string", tree:add(testfield.basic.STRING, tvb_string:range(0,tvb_string:len())) ~= nil ) addMatchFields(string_match_fields, teststring) - execute ("basic-string", pcall (callTreeAdd, tree, testfield.basic.STRING, tvb:range() ) ) + execute ("basic-string", pcall (callTreeAdd, tree, testfield.basic.STRING, tvb_string:range() ) ) addMatchFields(string_match_fields, teststring) verifyFields("basic.STRING", string_match_fields) - tvb = ByteArray.new("00FF 0001 8000"):tvb("Basic") +---------------------------------------- + testing(OTHER, "Basic boolean") + + local barray_bytes_hex = "00FF00018000" + local barray_bytes = ByteArray.new(barray_bytes_hex) + local tvb_bytes = barray_bytes:tvb("Basic bytes") local bool_match_fields = {} - execute ("basic-boolean", pcall (callTreeAdd, tree, testfield.basic.BOOLEAN, tvb:range(0,2)) ) + execute ("basic-boolean", pcall (callTreeAdd, tree, testfield.basic.BOOLEAN, tvb_bytes:range(0,2)) ) addMatchFields(bool_match_fields, true) - execute ("basic-boolean", pcall (callTreeAdd, tree, testfield.basic.BOOLEAN, tvb:range(2,2)) ) + execute ("basic-boolean", pcall (callTreeAdd, tree, testfield.basic.BOOLEAN, tvb_bytes:range(2,2)) ) addMatchFields(bool_match_fields, true) - execute ("basic-boolean", pcall (callTreeAdd, tree, testfield.basic.BOOLEAN, tvb:range(4,2)) ) + execute ("basic-boolean", pcall (callTreeAdd, tree, testfield.basic.BOOLEAN, tvb_bytes:range(4,2)) ) addMatchFields(bool_match_fields, false) verifyFields("basic.BOOLEAN", bool_match_fields ) +---------------------------------------- + testing(OTHER, "Basic uint16") + local uint16_match_fields = {} - execute ("basic-uint16", pcall (callTreeAdd, tree, testfield.basic.UINT16, tvb:range(0,2)) ) + execute ("basic-uint16", pcall (callTreeAdd, tree, testfield.basic.UINT16, tvb_bytes:range(0,2)) ) addMatchFields(uint16_match_fields, 255) - execute ("basic-uint16", pcall (callTreeAdd, tree, testfield.basic.UINT16, tvb:range(2,2)) ) + execute ("basic-uint16", pcall (callTreeAdd, tree, testfield.basic.UINT16, tvb_bytes:range(2,2)) ) addMatchFields(uint16_match_fields, 1) - execute ("basic-uint16", pcall (callTreeAdd, tree, testfield.basic.UINT16, tvb:range(4,2)) ) + execute ("basic-uint16", pcall (callTreeAdd, tree, testfield.basic.UINT16, tvb_bytes:range(4,2)) ) addMatchFields(uint16_match_fields, 32768) verifyFields("basic.UINT16", uint16_match_fields) +---------------------------------------- + testing(OTHER, "Basic uint16-le") + local function callTreeAddLE(tree,...) tree:add_le(...) end - execute ("basic-uint16-le", pcall (callTreeAddLE, tree, testfield.basic.UINT16, tvb:range(0,2)) ) + execute ("basic-uint16-le", pcall (callTreeAddLE, tree, testfield.basic.UINT16, tvb_bytes:range(0,2)) ) addMatchFields(uint16_match_fields, 65280) - execute ("basic-uint16-le", pcall (callTreeAddLE, tree, testfield.basic.UINT16, tvb:range(2,2)) ) + execute ("basic-uint16-le", pcall (callTreeAddLE, tree, testfield.basic.UINT16, tvb_bytes:range(2,2)) ) addMatchFields(uint16_match_fields, 256) - execute ("basic-uint16-le", pcall (callTreeAddLE, tree, testfield.basic.UINT16, tvb:range(4,2)) ) + execute ("basic-uint16-le", pcall (callTreeAddLE, tree, testfield.basic.UINT16, tvb_bytes:range(4,2)) ) addMatchFields(uint16_match_fields, 128) verifyFields("basic.UINT16", uint16_match_fields) +---------------------------------------- + testing(OTHER, "Basic bytes") + + local bytes_match_fields = {} + + execute ("basic-tvb_get_string_bytes", + string.lower(tostring(tvb_bytes:range():bytes())) == string.lower(barray_bytes_hex)) + + execute ("basic-bytes", pcall (callTreeAdd, tree, testfield.basic.BYTES, tvb_bytes:range()) ) + addMatchFields(bytes_match_fields, barray_bytes) + + -- TODO: it's silly that tree:add_packet_field() requires an encoding argument + -- need to fix that separately in a bug fix + execute ("add_pfield-bytes", treeAddPField(tree, testfield.basic.BYTES, + tvb_bytes:range(), ENC_BIG_ENDIAN)) + addMatchFields(bytes_match_fields, barray_bytes) + + verifyFields("basic.BYTES", bytes_match_fields) + +---------------------------------------- + testing(OTHER, "Basic uint bytes") + + local len_string = string.format("%02x", barray_bytes:len()) + local barray_uint_bytes = ByteArray.new(len_string) .. barray_bytes + local tvb_uint_bytes = barray_uint_bytes:tvb("Basic UINT_BYTES") + local uint_bytes_match_fields = {} + + execute ("basic-uint-bytes", pcall (callTreeAdd, tree, testfield.basic.UINT_BYTES, + tvb_uint_bytes:range(0,1)) ) + addMatchFields(uint_bytes_match_fields, barray_bytes) + + execute ("add_pfield-uint-bytes", treeAddPField(tree, testfield.basic.UINT_BYTES, + tvb_uint_bytes:range(0,1), ENC_BIG_ENDIAN) ) + addMatchFields(uint_bytes_match_fields, barray_bytes) + + verifyFields("basic.UINT_BYTES", uint_bytes_match_fields) + +---------------------------------------- + testing(OTHER, "Basic OID") + + -- note: the tvb being dissected and compared isn't actually a valid OID. + -- tree:add() and tree:add_packet-field() don't care about its validity right now. + + local oid_match_fields = {} + + execute ("basic-oid", pcall(callTreeAdd, tree, testfield.basic.OID, tvb_bytes:range()) ) + addMatchFields(oid_match_fields, barray_bytes) + + execute ("add_pfield-oid", treeAddPField(tree, testfield.basic.OID, + tvb_bytes:range(), ENC_BIG_ENDIAN) ) + addMatchFields(oid_match_fields, barray_bytes) + + verifyFields("basic.OID", oid_match_fields) + +---------------------------------------- + testing(OTHER, "Basic REL_OID") + + -- note: the tvb being dissected and compared isn't actually a valid OID. + -- tree:add() and tree:add_packet-field() don't care about its validity right now. + + local rel_oid_match_fields = {} + + execute ("basic-rel-oid", pcall(callTreeAdd, tree, testfield.basic.REL_OID, tvb_bytes:range())) + addMatchFields(rel_oid_match_fields, barray_bytes) + + execute ("add_pfield-rel_oid", treeAddPField(tree, testfield.basic.REL_OID, + tvb_bytes:range(), ENC_BIG_ENDIAN) ) + addMatchFields(rel_oid_match_fields, barray_bytes) + + verifyFields("basic.REL_OID", rel_oid_match_fields) + + -- TODO: a FT_GUID is not really a ByteArray, so we can't simply treat it as one + -- local barray_guid = ByteArray.new("00FF0001 80001234 567890AB CDEF00FF") + -- local tvb_guid = barray_guid:tvb("Basic GUID") + -- local guid_match_fields = {} + + -- execute ("basic-guid", pcall(callTreeAdd, tree, testfield.basic.GUID, tvb_guid:range()) ) + -- addMatchFields(guid_match_fields, barray_guid) + + -- execute ("add_pfield-guid", treeAddPField(tree, testfield.basic.GUID, + -- tvb_guid:range(), ENC_BIG_ENDIAN) ) + -- addMatchFields(guid_match_fields, barray_guid) + + -- verifyFields("basic.GUID", guid_match_fields) + + +---------------------------------------- + testing(OTHER, "tree:add_packet_field Bytes") + + resetResults() + bytes_match_fields = {} + local bytes_match_values = {} + + -- something to make this easier to read + local function addMatch(...) + addMatchFieldValues(bytes_match_fields, bytes_match_values, ...) + end + + local bytesstring1 = "deadbeef0123456789DEADBEEFabcdef" + local bytesstring = ByteArray.new(bytesstring1) -- the binary version of above, for comparing + local bytestvb1 = ByteArray.new(bytesstring1, true):tvb("Bytes hex-string 1") + local bytesstring2 = " de:ad:be:ef:01:23:45:67:89:DE:AD:BE:EF:ab:cd:ef" + local bytestvb2 = ByteArray.new(bytesstring2 .. "-f0-00 foobar", true):tvb("Bytes hex-string 2") + + local bytestvb1_decode = bytestvb1:range():bytes(ENC_STR_HEX + ENC_SEP_NONE + ENC_SEP_COLON + ENC_SEP_DASH) + execute ("tvb_get_string_bytes", string.lower(tostring(bytestvb1_decode)) == string.lower(tostring(bytesstring1))) + + execute ("add_pfield-bytes1", treeAddPField(tree, testfield.bytes.BYTES, + bytestvb1:range(), + ENC_STR_HEX + ENC_SEP_NONE + + ENC_SEP_COLON + ENC_SEP_DASH)) + addMatch(bytesstring, string.len(bytesstring1)) + + execute ("add_pfield-bytes2", treeAddPField(tree, testfield.bytes.BYTES, + bytestvb2:range(), + ENC_STR_HEX + ENC_SEP_NONE + + ENC_SEP_COLON + ENC_SEP_DASH)) + addMatch(bytesstring, string.len(bytesstring2)) + + verifyResults("add_pfield-bytes", bytes_match_values) + verifyFields("bytes.BYTES", bytes_match_fields) + + +---------------------------------------- + testing(OTHER, "tree:add_packet_field OID") + + resetResults() + bytes_match_fields = {} + bytes_match_values = {} + + execute ("add_pfield-oid1", treeAddPField(tree, testfield.bytes.OID, + bytestvb1:range(), + ENC_STR_HEX + ENC_SEP_NONE + + ENC_SEP_COLON + ENC_SEP_DASH)) + addMatch(bytesstring, string.len(bytesstring1)) + + execute ("add_pfield-oid2", treeAddPField(tree, testfield.bytes.OID, + bytestvb2:range(), + ENC_STR_HEX + ENC_SEP_NONE + + ENC_SEP_COLON + ENC_SEP_DASH)) + addMatch(bytesstring, string.len(bytesstring2)) + + verifyResults("add_pfield-oid", bytes_match_values) + verifyFields("bytes.OID", bytes_match_fields) + + +---------------------------------------- + testing(OTHER, "tree:add_packet_field REL_OID") + + resetResults() + bytes_match_fields = {} + bytes_match_values = {} + + execute ("add_pfield-rel_oid1", treeAddPField(tree, testfield.bytes.REL_OID, + bytestvb1:range(), + ENC_STR_HEX + ENC_SEP_NONE + + ENC_SEP_COLON + ENC_SEP_DASH)) + addMatch(bytesstring, string.len(bytesstring1)) + + execute ("add_pfield-rel_oid2", treeAddPField(tree, testfield.bytes.REL_OID, + bytestvb2:range(), + ENC_STR_HEX + ENC_SEP_NONE + + ENC_SEP_COLON + ENC_SEP_DASH)) + addMatch(bytesstring, string.len(bytesstring2)) + + verifyResults("add_pfield-rel_oid", bytes_match_values) + verifyFields("bytes.REL_OID", bytes_match_fields) + ---------------------------------------- testing(OTHER, "tree:add Time") - tvb = ByteArray.new("00000000 00000000 0000FF0F 00FF000F"):tvb("Time") + local tvb = ByteArray.new("00000000 00000000 0000FF0F 00FF000F"):tvb("Time") local ALOCAL = testfield.time.ABSOLUTE_LOCAL local alocal_match_fields = {} @@ -414,7 +627,7 @@ function test_proto.dissector(tvbuf,pktinfo,root) local autc_match_values = {} -- something to make this easier to read - local function addMatch(...) + addMatch = function(...) addMatchFieldValues(autc_match_fields, autc_match_values, ...) end diff --git a/test/suite-wslua.sh b/test/suite-wslua.sh index fdeb5f0f2f..04e7a70ca3 100755 --- a/test/suite-wslua.sh +++ b/test/suite-wslua.sh @@ -371,6 +371,14 @@ wslua_step_tvb_test() { fi # Tshark catches lua script failures, so we have to parse the output. + # perform this twice: once with a tree, once without + $TSHARK -r $CAPTURE_DIR/dns_port.pcap -X lua_script:$TESTS_DIR/lua/tvb.lua -V > testout.txt 2>&1 + grep -q "All tests passed!" testout.txt + if [ $? -ne 0 ]; then + cat testout.txt + test_step_failed "lua_args_test test 1 failed" + fi + $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