Signed-off-by: Frediano Ziglio <fziglio@xxxxxxxxxx> --- tests/dissector_test.c | 424 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 423 insertions(+), 1 deletion(-) diff --git a/tests/dissector_test.c b/tests/dissector_test.c index 25a33b5..5a49f40 100644 --- a/tests/dissector_test.c +++ b/tests/dissector_test.c @@ -21,6 +21,176 @@ static int last_ei_registered = first_ei_registered - 1; static int last_tree_registered = first_tree_registered - 1; static bool got_error = false; +static GPtrArray *hfs; + +struct tvbuff { + guint8 *data; + size_t len; +}; + +static int check(const char *chk_str, int line, int chk, const char *fmt, ...) +{ + if (!chk) { + va_list ap; + va_start(ap, fmt); + fprintf(stderr, "Check failed at line %d\n", line); + fprintf(stderr, "Check: %s\n", chk_str); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + va_end(ap); + abort(); + } + return 1; +} +#define check(chk, ...) check(#chk, __LINE__, chk, __VA_ARGS__) + +static int check_tree(proto_node *node) +{ + assert(node->tree_data == (void*) node); + assert(node->next == NULL); + assert(node->finfo == NULL); + if (!node->first_child) { + assert(node->last_child == NULL); + } else { + assert(node->last_child->next == NULL); + } + return 1; +} + +static int check_item(proto_node *node) +{ + assert(node->tree_data == NULL); + assert(node->finfo != NULL); + assert(node->finfo->rep != NULL); + assert(node->first_child == node->last_child); + assert(node->first_child == NULL || check_tree(node->first_child)); + assert(node->parent); + assert(node->finfo->start >= 0); + assert(node->finfo->length >= 0); + return 1; +} + +guint8 *tvb_bytes(tvbuff_t *tvb, const gint offset, unsigned len) +{ + if (!tvb) + return NULL; + + assert(offset >= 0); + assert(offset + len <= tvb->len); + return tvb->data + offset; +} + +static guint64 read_ule(const guint8 *p, unsigned len) +{ + guint64 low, high; + + switch (len) { + case 1: + return p[0]; + case 2: + return p[0] + 0x100u * p[1]; + case 4: + return p[0] + 0x100u * p[1] + 0x10000u * p[2] + 0x1000000u * p[3]; + case 8: + low = p[0] + 0x100u * p[1] + 0x10000u * p[2] + 0x1000000u * p[3]; + p += 4; + high = p[0] + 0x100u * p[1] + 0x10000u * p[2] + 0x1000000u * p[3]; + return high << 32 | low; + } + assert(0); + return 0; +} + +static gint64 read_sle(const guint8 *p, unsigned len) +{ + guint64 sign_bit = (((guint64) 0x80) << ((len-1) * 8)); + guint64 val = read_ule(p, len); + + if ((val & sign_bit) != 0) + return (gint64) ((val ^ sign_bit) - sign_bit); + return (gint64) val; +} + +static guint64 tvb_get_ule(tvbuff_t *tvb, const gint offset, unsigned len) +{ + guint8 *p = tvb_bytes(tvb, offset, len); + if (!p) + return 0; + return read_ule(p, len); +} + +static gint64 tvb_get_sle(tvbuff_t *tvb, const gint offset, unsigned len) +{ + guint8 *p = tvb_bytes(tvb, offset, len); + if (!p) + return 0; + return read_sle(p, len); +} + +static const char *describe_fttype(enum ftenum type) +{ + switch (type) { +#define FT(name) case FT_ ## name: return "FT_" #name; + FT(NONE) /* used for text labels with no value */ + FT(PROTOCOL) + FT(BOOLEAN) /* TRUE and FALSE come from <glib.h> */ + FT(UINT8) + FT(UINT16) + FT(UINT24) /* really a UINT32, but displayed as 3 hex-digits if FD_HEX*/ + FT(UINT32) + FT(UINT64) + FT(INT8) + FT(INT16) + FT(INT24) /* same as for UINT24 */ + FT(INT32) + FT(INT64) + FT(FLOAT) + FT(DOUBLE) + FT(ABSOLUTE_TIME) + FT(RELATIVE_TIME) + FT(STRING) + FT(STRINGZ) /* for use with proto_tree_add_item() */ + FT(UINT_STRING) /* for use with proto_tree_add_item() */ + FT(ETHER) + FT(BYTES) + FT(UINT_BYTES) + FT(IPv4) + FT(IPv6) + FT(IPXNET) + FT(FRAMENUM) /* a UINT32, but if selected lets you go to frame with that number */ + FT(PCRE) /* a compiled Perl-Compatible Regular Expression object */ + FT(GUID) /* GUID, UUID */ + FT(OID) /* OBJECT IDENTIFIER */ + FT(EUI64) + FT(AX25) + FT(VINES) + FT(REL_OID) /* RELATIVE-OID */ + FT(SYSTEM_ID) + FT(STRINGZPAD) /* for use with proto_tree_add_item() */ + default: + check(false, "Unknown fttype %d", type); + break; + } + return NULL; +} + +static const char *describe_base(int base) +{ + switch (base) { +#define BASE(base) case base: return #base; + BASE(BASE_NONE) + BASE(BASE_DEC) + BASE(BASE_HEX) + BASE(BASE_OCT) + BASE(BASE_DEC_HEX) + BASE(BASE_HEX_DEC) + BASE(BASE_CUSTOM) + BASE(STR_UNICODE) + } + check(false, "Unknown base %d", base); + return NULL; +} + WS_DLL_PUBLIC void proto_register_field_array(const int parent, hf_register_info *hf, const int num_records) { @@ -31,6 +201,7 @@ proto_register_field_array(const int parent, hf_register_info *hf, const int num assert(hf[i].p_id); assert(*hf[i].p_id == -1); *hf[i].p_id = ++last_hf_registered; + g_ptr_array_add(hfs, &hf[i].hfinfo); } } @@ -66,12 +237,245 @@ expert_add_info_format(packet_info *pinfo, proto_item *pi, expert_field *eiindex return; } +struct all_ti +{ + proto_item ti; + field_info info; + item_label_t label; +}; + +WS_DLL_PUBLIC proto_item * +proto_tree_add_text(proto_tree *tree, tvbuff_t *tvb, gint start, gint length, const char *format, + ...) +{ + struct all_ti *all; + proto_item *ti; + va_list ap; + + assert(tvb); + assert(start >= 0); + assert(start <= tvb->len); + assert(length >= 0); + check(start + length <= tvb->len, "start %d len %d tvb_len %d", start, length, tvb->len); + if (!tree) + return NULL; + + check_tree(tree); + all = calloc(1, sizeof(*all)); + assert(all); + ti = &all->ti; + ti->finfo = &all->info; + ti->finfo->rep = &all->label; + ti->parent = tree; + if (tree->first_child) { + assert(tree->last_child->next == NULL); + tree->last_child->next = ti; + tree->last_child = ti; + } else { + tree->first_child = tree->last_child = ti; + } + va_start(ap, format); + vsnprintf(ti->finfo->rep->representation, sizeof(ti->finfo->rep->representation), + format, ap); + va_end(ap); + check_item(ti); + check_tree(tree); + return ti; +} + +WS_DLL_PUBLIC proto_item * +proto_tree_add_item(proto_tree *tree, int hfindex, tvbuff_t *tvb, + const gint start, gint length, const guint encoding) +{ + proto_item *ti; + header_field_info *hfinfo = NULL; + enum ftenum type = FT_NONE; + char *label; + guint64 uval; + gint64 sval; + unsigned size; + const struct true_false_string *tfs; + + assert(hfindex >= first_hf_registered); + assert(hfindex <= last_hf_registered); + + hfindex -= first_hf_registered; + assert(hfs && hfindex < hfs->len); + hfinfo = g_ptr_array_index(hfs, hfindex); + + ti = proto_tree_add_text(tree, tvb, start, length, ""); + if (!ti) + return NULL; + + /* encoding is just used to read data and put in ti->finfo->rep */ + if (hfinfo) + type = hfinfo->type; + label = ti->finfo->rep->representation; + switch (type) { + case FT_NONE: + break; + case FT_UINT8: + size = 1; + goto str_uint; + case FT_UINT16: + size = 2; + goto str_uint; + case FT_UINT32: + size = 4; + goto str_uint; + case FT_UINT64: + size = 8; + str_uint: + uval = tvb_get_ule(tvb, start, size); + sprintf(label, "%" G_GINT64_MODIFIER "u", uval); + break; + case FT_INT8: + size = 1; + goto str_int; + case FT_INT16: + size = 2; + goto str_int; + case FT_INT32: + size = 4; + goto str_int; + case FT_INT64: + size = 8; + str_int: + sval = tvb_get_sle(tvb, start, size); + sprintf(label, "%" G_GINT64_MODIFIER "d", sval); + break; + case FT_BOOLEAN: + uval = tvb_get_ule(tvb, start, length); + uval &= hfinfo->bitmask; + assert(hfinfo->strings); + tfs = (const struct true_false_string *) hfinfo->strings; + strcpy(label, uval ? tfs->true_string : tfs->false_string); + break; + case FT_STRING: + /* TODO read value */ + assert(0); + break; + case FT_STRINGZ: + /* TODO read value */ + assert(0); + break; + case FT_GUID: + /* TODO read value */ + assert(0); + break; + case FT_BYTES: + /* TODO read value */ + assert(0); + break; + default: + assert(0); + } + ti->finfo->hfinfo = hfinfo; + check_item(ti); + check_tree(tree); + return ti; +} + +WS_DLL_PUBLIC guint8 tvb_get_guint8(tvbuff_t *tvb, const gint offset) +{ + return (guint8) tvb_get_ule(tvb, offset, 1); +} + +WS_DLL_PUBLIC guint16 tvb_get_letohs(tvbuff_t *tvb, const gint offset) +{ + return (guint16) tvb_get_ule(tvb, offset, 2); +} + +WS_DLL_PUBLIC guint32 tvb_get_letohl(tvbuff_t *tvb, const gint offset) +{ + return (guint32) tvb_get_ule(tvb, offset, 4); +} + +WS_DLL_PUBLIC guint64 tvb_get_letoh64(tvbuff_t *tvb, const gint offset) +{ + return tvb_get_ule(tvb, offset, 8); +} + +static int indentation = -1; +static enum { PLAIN, XML } format = PLAIN; +static FILE *output_file; +#define oprintf(...) fprintf(output_file, __VA_ARGS__) +#define INDENTED(s) "%*s" s, indentation*4, "" + +static void format_start(const char *name) +{ + ++indentation; + if (format == XML) + oprintf("<%s>\n", name); + else + oprintf(INDENTED("--- %s\n"), name); +} + +static void format_end(const char *name) +{ + if (format == XML) + oprintf("</%s>\n", name); + --indentation; +} + +static void format_item(const char *name, const char *value) +{ + if (format == XML) + /* TODO quote value for XML */ + oprintf("<%s>%s</%s>\n", name, value, name); + else + oprintf(INDENTED("%s: %s\n"), name, value); +} + +static void dump_tree(proto_tree *tree); + +static void dump_item(proto_item *ti) +{ + header_field_info *info = NULL; + check_item(ti); + + format_start("item"); + format_item("Text", ti->finfo->rep->representation); + info = ti->finfo->hfinfo; + if (info) { + format_item("Name", info->name); + format_item("Abbrev", info->abbrev); + if (info->type != FT_NONE) + format_item("Type", describe_fttype(info->type)); + if (info->type == FT_BOOLEAN) { + char buf[32]; + sprintf(buf, "%d", info->display); + format_item("Base", buf); + } else { + if (info->display != BASE_NONE) + format_item("Base", describe_base(info->display)); + } + } + dump_tree(ti->first_child); + format_end("item"); +} + +static void dump_tree(proto_tree *tree) +{ + proto_item *ti; + if (!tree) + return; + + check_tree(tree); + format_start("tree"); + for (ti = tree->first_child; ti; ti = ti->next) + dump_item(ti); + format_end("tree"); +} + static const struct option long_options[] = { { "help", 0, NULL, 'h' }, { "server", 0, NULL, 's' }, { "client", 0, NULL, 'c' }, + { "output-file", required_argument, NULL, 'o' }, + { "xml", 0, NULL, 'x' }, }; -static const char options[] = "hsc"; +static const char options[] = "hscxo:"; static void syntax(FILE *f, int exit_value) { @@ -82,6 +486,8 @@ static void syntax(FILE *f, int exit_value) " -h, --help Show this help\n" " -s, --server Process server messages (default)\n" " -c, --client Process client messages\n" + " -x, --xml Output in XML format\n" + " -o, --output-file=FILE Output to specified file\n" ); exit(exit_value); } @@ -94,6 +500,7 @@ int main(int argc, char **argv) spice_dissect_func_t (*msg_func)(guint8 channel); spice_dissect_func_t channel_func = NULL; + output_file = stdout; msg_func = spice_server_channel_get_dissect; while (1) { @@ -112,6 +519,13 @@ int main(int argc, char **argv) case 'c': msg_func = spice_client_channel_get_dissect; break; + case 'x': + format = XML; + break; + case 'o': + output_file = fopen(optarg, "w"); + check(output_file != NULL, "Error opening output file"); + break; default: syntax(stderr, EXIT_FAILURE); break; @@ -123,6 +537,7 @@ int main(int argc, char **argv) channel = strtol(argv[optind++], NULL, 0); message_type = strtol(argv[optind++], NULL, 0); + hfs = g_ptr_array_new_with_free_func(free); spice_register_fields(1, NULL); memset(&glb, 0, sizeof(glb)); @@ -134,9 +549,16 @@ int main(int argc, char **argv) assert(channel_func); memset(&tree, 0, sizeof(tree)); + tree.tree_data = (void *) &tree; + check_tree(&tree); /* TODO check offset ?? */ channel_func(message_type, &glb, &tree, 0); + dump_tree(&tree); + + if (output_file != stdout) + fclose(output_file); + return got_error ? EXIT_FAILURE : EXIT_SUCCESS; } -- 2.4.3 _______________________________________________ Spice-devel mailing list Spice-devel@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/spice-devel