3 * Copyright (c) 2010, Holger Grandy, BMW Car IT GmbH (holger.grandy@bmw-carit.de)
7 * Apache Etch Protocol dissector
8 * http://incubator.apache.org/etch/
10 * This dissector reads configuration files (generated by Etch IDL compiler).
11 * Configuration file directory path is given in dissector options.
13 * Wireshark - Network traffic analyzer
14 * By Gerald Combs <gerald@wireshark.org>
15 * Copyright 1998 Gerald Combs
18 * This program is free software; you can redistribute it and/or
19 * modify it under the terms of the GNU General Public License
20 * as published by the Free Software Foundation; either version 2
21 * of the License, or (at your option) any later version.
23 * This program is distributed in the hope that it will be useful,
24 * but WITHOUT ANY WARRANTY; without even the implied warranty of
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 * GNU General Public License for more details.
28 * You should have received a copy of the GNU General Public License
29 * along with this program; if not, write to the Free Software
30 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
45 #include <wsutil/file_util.h>
46 #include <epan/packet.h>
47 #include <epan/prefs.h>
48 #include <epan/report_err.h>
49 #include <epan/dissectors/packet-tcp.h>
52 * maximum numbers for symbols from config files
54 #define ETCH_MAX_SYMBOL_LENGTH "256"
57 * Magic Number for Etch
59 static const guint8 etch_magic[] = { 0xde, 0xad, 0xbe, 0xef };
62 * Typecodes in the Etch protocol, representing the field types
64 #define ETCH_TC_NULL 0x80
65 #define ETCH_TC_NONE 0x81
66 #define ETCH_TC_BOOLEAN_FALSE 0x82
67 #define ETCH_TC_BOOLEAN_TRUE 0x83
68 #define ETCH_TC_BYTE 0x84
69 #define ETCH_TC_SHORT 0x85
70 #define ETCH_TC_INT 0x86
71 #define ETCH_TC_LONG 0x87
72 #define ETCH_TC_FLOAT 0x88
73 #define ETCH_TC_DOUBLE 0x89
74 #define ETCH_TC_BYTES 0x8B
75 #define ETCH_TC_ARRAY 0x91
76 #define ETCH_TC_EMPTY_STRING 0x92
77 #define ETCH_TC_STRING 0x93
78 #define ETCH_TC_STRUCT 0x94
79 #define ETCH_TC_CUSTOM 0x95
80 #define ETCH_TC_ANY 0x96
81 #define ETCH_TC_MIN_TINY_INT 0xC0
82 #define ETCH_TC_MAX_TINY_INT 0x7F
84 /***************************************************************************/
88 * String representation of all Type Codes
90 static const value_string tc_lookup_table[] = {
91 { ETCH_TC_NULL, "Etch TypeCode: NULL"},
92 { ETCH_TC_NONE, "Etch TypeCode: NONE"},
93 { ETCH_TC_BOOLEAN_FALSE, "Etch TypeCode: BOOLEAN_FALSE" },
94 { ETCH_TC_BOOLEAN_TRUE, "Etch TypeCode: BOOLEAN_TRUE"},
95 { ETCH_TC_BYTE, "Etch TypeCode: BYTE"},
96 { ETCH_TC_SHORT, "Etch TypeCode: SHORT"},
97 { ETCH_TC_INT, "Etch TypeCode: INT"},
98 { ETCH_TC_LONG, "Etch TypeCode: LONG"},
99 { ETCH_TC_FLOAT, "Etch TypeCode: FLOAT"},
100 { ETCH_TC_DOUBLE, "Etch TypeCode: DOUBLE"},
101 { ETCH_TC_BYTES, "Etch TypeCode: BYTES"},
102 { ETCH_TC_ARRAY, "Etch TypeCode: ARRAY"},
103 { ETCH_TC_EMPTY_STRING, "Etch TypeCode: EMPTY_STRING"},
104 { ETCH_TC_STRING, "Etch TypeCode: STRING"},
105 { ETCH_TC_STRUCT, "Etch TypeCode: STRUCT"},
106 { ETCH_TC_CUSTOM, "Etch TypeCode: CUSTOM"},
107 { ETCH_TC_ANY, "Etch TypeCode: ANY"},
112 * Wireshark internal fields
114 static int proto_etch = -1;
115 static gint ett_etch = -1;
116 static gint ett_etch_struct = -1;
117 static gint ett_etch_keyvalue = -1;
118 static gint ett_etch_key = -1;
119 static gint ett_etch_value = -1;
120 static int hf_etch_sig = 0;
121 static int hf_etch_length = 0;
122 static int hf_etch_version = 0;
123 static int hf_etch_typecode = 0;
124 static int hf_etch_value = 0;
125 static int hf_etch_bytes = 0;
126 static int hf_etch_byte = 0;
127 static int hf_etch_short = 0;
128 static int hf_etch_int = 0;
129 static int hf_etch_long = 0;
130 static int hf_etch_float = 0;
131 static int hf_etch_double = 0;
132 static int hf_etch_key = 0;
133 static int hf_etch_valuename = 0;
134 static int hf_etch_keyname = 0;
135 static int hf_etch_string = 0;
136 static int hf_etch_keyvalue = 0;
137 static int hf_etch_struct = 0;
138 static int hf_etch_dim = 0;
139 static int hf_etch_symbol = 0;
142 * internal fields/defines for dissector
145 static const char *gbl_keytab_folder = "";
146 static guint gbl_etch_port = 0;
147 static char *gbl_current_keytab_folder = NULL;
149 static int gbl_pdu_counter;
150 static guint32 gbl_old_frame_num;
152 static emem_strbuf_t *gbl_symbol_buffer = NULL;
153 static gboolean gbl_have_symbol = FALSE;
155 /***************************************************************************/
159 * forward declared dissector methods
161 static void read_key_value(unsigned int *offset, tvbuff_t *tvb,
162 proto_tree *etch_tree);
163 static void read_struct(unsigned int *offset, tvbuff_t *tvb,
164 proto_tree *etch_tree, int add_type_field);
165 static int read_value(unsigned int *offset, tvbuff_t *tvb, proto_tree *etch_tree,
167 void proto_reg_handoff_etch(void);
169 /************************************************************************
170 * Symbol value-string functions
171 * Essentially: Build a value_string_ext at runtime:
172 * a. Upon startup & whenever symbol folder changed: Read from file(s)
173 * and add all hash/symbol pairs to a GArray;
174 * b. When file reads complete, sort the GArray and then create a
175 * value_string_ext from the array for use by match_strval_ext & friends.
176 * (Code based upon code in packet-diameter.c)
178 static GArray *gbl_symbols_array = NULL;
179 static value_string_ext *gbl_symbols_vs_ext = NULL;
182 gbl_symbols_new(void) {
183 g_assert(gbl_symbols_array == NULL);
184 gbl_symbols_array = g_array_new(TRUE, TRUE, sizeof(value_string));
188 gbl_symbols_free(void) {
189 g_free(gbl_symbols_vs_ext);
190 gbl_symbols_vs_ext = NULL;
192 if (gbl_symbols_array != NULL) {
195 vs_p = (value_string *)gbl_symbols_array->data;
196 for (i=0; i<gbl_symbols_array->len; i++) {
197 g_free((gchar *)vs_p[i].strptr);
199 g_array_free(gbl_symbols_array, TRUE);
200 gbl_symbols_array = NULL;
205 gbl_symbols_array_append(int hash, gchar *symbol) {
206 value_string vs = {hash, symbol};
207 g_assert(gbl_symbols_array != NULL);
208 g_array_append_val(gbl_symbols_array, vs);
212 gbl_symbols_compare_vs(gconstpointer a, gconstpointer b)
214 value_string *vsa = (value_string *)a;
215 value_string *vsb = (value_string *)b;
217 if(vsa->value > vsb->value)
219 if(vsa->value < vsb->value)
226 gbl_symbols_vs_ext_new(void) {
227 g_assert(gbl_symbols_vs_ext == NULL);
228 g_assert(gbl_symbols_array != NULL);
229 g_array_sort(gbl_symbols_array, gbl_symbols_compare_vs);
230 gbl_symbols_vs_ext = value_string_ext_new((value_string *)gbl_symbols_array->data,
231 gbl_symbols_array->len+1,
232 "etch-global-symbols" );
235 /*********************************************************************************/
239 * get the length of a given typecode in bytes, -1 if to be derived from message
242 get_byte_length(guint8 typecode)
247 case ETCH_TC_BOOLEAN_FALSE:
248 case ETCH_TC_BOOLEAN_TRUE:
249 case ETCH_TC_EMPTY_STRING:
250 case ETCH_TC_MIN_TINY_INT:
251 case ETCH_TC_MAX_TINY_INT:
283 * add all etch symbols from file to our symbol cache
286 add_symbols_of_file(const char *filename)
289 pFile = ws_fopen(filename, "r");
293 while (fgets(line, sizeof line, pFile) != NULL) {
297 length = strlen(line);
299 /* Must at least have a hash, else skip line */
304 while (pos > 0 && (line[pos] == 0xD || line[pos] == 0xA)) {
307 line[pos + 1] = '\0';
310 if (sscanf(&line[0], "%x", &hash) != 1)
311 continue; /* didn't find a valid hex value at the beginning of the line */
313 /* And read the symbol */
314 pos = strcspn(line, ",");
315 if ((line[pos] != '\0') && (line[pos+1] !='\0')) /* require at least 1 char in symbol */
316 gbl_symbols_array_append(hash,
317 g_strdup_printf("%." ETCH_MAX_SYMBOL_LENGTH "s", &line[pos+1]));
324 * add all etch symbol from directory to our symbol cache
327 read_hashed_symbols_from_dir(const char *dirname)
333 GError *err_p = NULL;
335 if(gbl_current_keytab_folder != NULL) {
336 g_free(gbl_current_keytab_folder);
337 gbl_current_keytab_folder = NULL;
342 if ((dirname == NULL) || (dirname[0] == '\0'))
345 if ((dir = ws_dir_open(dirname, 0, &err_p)) != NULL) {
348 gbl_current_keytab_folder = g_strdup(dirname);
349 while ((file = ws_dir_read_name(dir)) != NULL) {
350 name = ws_dir_get_name(file);
352 if (g_str_has_suffix(file, ".ewh")) {
354 g_strdup_printf("%s" G_DIR_SEPARATOR_S "%s", dirname,
356 add_symbols_of_file(filename);
361 gbl_symbols_vs_ext_new();
363 report_failure("%s", err_p->message);
368 /***********************************************************************************/
369 /* Etch Protocol Functions */
372 * read a type flag from tvb and add it to tree
375 read_type(unsigned int *offset, tvbuff_t *tvb, proto_tree *etch_tree)
379 const gchar *type_as_string;
381 type_code = tvb_get_guint8(tvb, *offset);
382 type_as_string = val_to_str(type_code, tc_lookup_table, "Etch TypeCode: 0x%02x");
383 proto_tree_add_text(etch_tree, tvb, *offset, 1, "%s", type_as_string);
389 * read a array type flag and add it to tree
392 read_array_type(unsigned int *offset, tvbuff_t *tvb, proto_tree *etch_tree)
396 type_code = tvb_get_guint8(tvb, *offset);
398 read_type(offset, tvb, etch_tree);
399 if (type_code == ETCH_TC_CUSTOM) {
400 type_code = read_type(offset, tvb, etch_tree);
401 proto_tree_add_item(etch_tree, hf_etch_value, tvb, *offset, 4,
409 * read the length of an array and add it to tree
412 read_length(unsigned int *offset, tvbuff_t *tvb, proto_tree *etch_tree)
415 int length_of_array_length_type;
418 tiny = tvb_get_guint8(tvb, *offset);
420 /* Is this the value already? */
421 if ( tiny <= ETCH_TC_MAX_TINY_INT
422 || tiny >= ETCH_TC_MIN_TINY_INT) {
424 length_of_array_length_type = 1;
427 type_code = read_type(offset, tvb, etch_tree);
428 length_of_array_length_type = get_byte_length(type_code);
430 switch (length_of_array_length_type) {
432 length = tvb_get_guint8(tvb, *offset);
435 length = tvb_get_ntohs(tvb, *offset);
438 length = tvb_get_ntohl(tvb, *offset);
441 return 0; /* error! */
444 proto_tree_add_item(etch_tree, hf_etch_length, tvb, *offset,
445 length_of_array_length_type, ENC_BIG_ENDIAN);
446 (*offset) += length_of_array_length_type;
452 * read an array from tvb and add it to tree
455 read_array(unsigned int *offset, tvbuff_t *tvb, proto_tree *etch_tree)
460 read_type(offset, tvb, etch_tree);
463 read_array_type(offset, tvb, etch_tree);
466 proto_tree_add_item(etch_tree, hf_etch_dim, tvb, *offset, 1, ENC_NA);
470 length = read_length(offset, tvb, etch_tree);
472 for (; length > 0; length--) {
473 read_value(offset, tvb, etch_tree, hf_etch_value);
476 read_type(offset, tvb, etch_tree);
481 * read a sequence of bytes and add them to tree
484 read_bytes(unsigned int *offset, tvbuff_t *tvb, proto_tree *etch_tree)
487 read_type(offset, tvb, etch_tree);
488 length = read_length(offset, tvb, etch_tree);
489 proto_tree_add_item(etch_tree, hf_etch_bytes, tvb, *offset, length,
495 * read a string and add it to tree
498 read_string(unsigned int *offset, tvbuff_t *tvb, proto_tree *etch_tree)
501 read_type(offset, tvb, etch_tree);
503 byteLength = read_length(offset, tvb, etch_tree);
505 proto_tree_add_item(etch_tree, hf_etch_string, tvb, *offset,
507 (*offset) += byteLength;
511 * read a number and add it to tree
514 read_number(unsigned int *offset, tvbuff_t *tvb, proto_tree *etch_tree,
515 int asWhat, guint8 type_code)
519 read_type(offset, tvb, etch_tree);
520 byteLength = get_byte_length(type_code);
521 if (byteLength > 0) {
523 const gchar *symbol = NULL;
526 gbl_symbol_buffer = ep_strbuf_new_label(""); /* no symbol found yet */
527 if (byteLength == 4) {
528 hash = tvb_get_ntohl(tvb, *offset);
529 symbol = match_strval_ext(hash, gbl_symbols_vs_ext);
531 asWhat = hf_etch_symbol;
532 gbl_have_symbol = TRUE;
533 ep_strbuf_append_printf(gbl_symbol_buffer,"%s",symbol);
536 ti = proto_tree_add_item(etch_tree, asWhat, tvb, *offset,
537 byteLength, ENC_BIG_ENDIAN);
538 *offset += byteLength;
539 if (symbol != NULL) {
540 proto_item_append_text(ti, " (0x%08x) %s", hash, symbol);
546 * read a value and add it to tree
549 read_value(unsigned int *offset, tvbuff_t *tvb, proto_tree *etch_tree,
554 type_code = tvb_get_guint8(tvb, *offset);
555 if (type_code <= ETCH_TC_MAX_TINY_INT ||
556 type_code >= ETCH_TC_MIN_TINY_INT) {
557 /* this is the value already */
558 proto_tree_add_item(etch_tree, asWhat, tvb, *offset, 1, ENC_BIG_ENDIAN);
565 read_struct(offset, tvb, etch_tree, 1);
568 read_array(offset, tvb, etch_tree);
571 read_string(offset, tvb, etch_tree);
574 read_number(offset, tvb, etch_tree, hf_etch_float, type_code);
577 read_number(offset, tvb, etch_tree, hf_etch_double, type_code);
580 read_number(offset, tvb, etch_tree, hf_etch_short, type_code);
583 read_number(offset, tvb, etch_tree, hf_etch_int, type_code);
586 read_number(offset, tvb, etch_tree, hf_etch_long, type_code);
589 read_number(offset, tvb, etch_tree, hf_etch_byte, type_code);
592 read_bytes(offset, tvb, etch_tree);
595 read_number(offset, tvb, etch_tree, asWhat, type_code);
601 * read a struct and add it to tree
604 read_struct(unsigned int *offset, tvbuff_t *tvb, proto_tree *etch_tree,
608 proto_tree *new_tree;
612 ti = proto_tree_add_item(etch_tree, hf_etch_struct, tvb, *offset,
613 tvb->length - *offset, ENC_NA);
614 new_tree = proto_item_add_subtree(ti, ett_etch_struct);
616 if (add_type_field) {
617 read_type(offset, tvb, new_tree);
619 /* struct type as hash */
620 read_value(offset, tvb, new_tree, hf_etch_value);
623 length = read_value(offset, tvb, new_tree, hf_etch_length);
625 for (i = 0; i < length; i++) {
626 read_key_value(offset, tvb, new_tree);
630 read_type(offset, tvb, new_tree);
634 * read a key value pair and add it to tree
637 read_key_value(unsigned int *offset, tvbuff_t *tvb, proto_tree *etch_tree)
639 proto_tree *new_tree;
640 proto_tree *new_tree_bck;
641 proto_item *ti, *parent_ti;
643 gbl_have_symbol = FALSE;
646 proto_tree_add_item(etch_tree, hf_etch_keyvalue, tvb, *offset, 1,
648 new_tree_bck = new_tree =
649 proto_item_add_subtree(parent_ti, ett_etch_keyvalue);
651 ti = proto_tree_add_item(new_tree, hf_etch_keyname, tvb, *offset, 0,
653 new_tree = proto_item_add_subtree(ti, ett_etch_key);
654 read_value(offset, tvb, new_tree, hf_etch_value);
656 /* append the symbol of the key */
657 if(gbl_have_symbol == TRUE){
658 proto_item_append_text(parent_ti, " (");
659 proto_item_append_text(parent_ti, "%s", gbl_symbol_buffer->str);
660 proto_item_append_text(parent_ti, ")");
663 ti = proto_tree_add_item(new_tree_bck, hf_etch_valuename, tvb, *offset,
665 new_tree = proto_item_add_subtree(ti, ett_etch_value);
666 read_value(offset, tvb, new_tree, hf_etch_value);
669 /*************************************************************************/
671 * Preparse the message for the info column
673 static emem_strbuf_t*
674 get_column_info(tvbuff_t *tvb)
678 emem_strbuf_t *result_buf;
681 /* We've a full PDU: 8 bytes + pdu_packetlen bytes */
682 result_buf = ep_strbuf_new_label("");
684 my_offset += (4 + 4 + 1); /* skip Magic, Length, Version */
686 type_code = tvb_get_guint8(tvb, my_offset);
687 byte_length = get_byte_length(type_code);
690 if (byte_length == 4) {
693 hash = tvb_get_ntohl(tvb, my_offset);
694 symbol = match_strval_ext(hash, gbl_symbols_vs_ext);
695 if (symbol != NULL) {
696 ep_strbuf_append_printf(result_buf, "%s()", symbol);
704 /****************************************************************************************************/
706 * main dissector function for an etch message
709 dissect_etch_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
711 /* We've a full PDU: 8 bytes + pdu_packetlen bytes */
712 emem_strbuf_t *colInfo = NULL;
714 if (pinfo->cinfo || tree) {
715 colInfo = get_column_info(tvb); /* get current symbol */
719 col_set_str(pinfo->cinfo, COL_PROTOCOL, "ETCH");
722 /* Switch to another frame? => Clear column */
723 if (pinfo->fd->num != gbl_old_frame_num) {
724 col_clear(pinfo->cinfo, COL_INFO);
727 gbl_old_frame_num = pinfo->fd->num;
729 col_set_writable(pinfo->cinfo, 1);
730 col_append_fstr(pinfo->cinfo, COL_INFO, "%s ", colInfo->str);
734 /* we are being asked for details */
737 proto_tree *etch_tree;
739 ti = proto_tree_add_protocol_format(tree, proto_etch, tvb, 0, -1,
740 "ETCH Protocol: %s", colInfo->str);
743 etch_tree = proto_item_add_subtree(ti, ett_etch);
744 proto_tree_add_item(etch_tree, hf_etch_sig, tvb, 0, 4, ENC_BIG_ENDIAN);
745 proto_tree_add_item(etch_tree, hf_etch_length, tvb, 4, 4, ENC_BIG_ENDIAN);
746 proto_tree_add_item(etch_tree, hf_etch_version, tvb, 8, 1, ENC_NA);
747 read_struct(&offset, tvb, etch_tree, 0);
753 * determine PDU length of protocol etch
756 get_etch_message_len(packet_info *pinfo _U_, tvbuff_t *tvb, int offset)
758 /* length is at offset 4. we add magic bytes length + length size */
759 return tvb_get_ntohl(tvb, offset + 4) + 8;
764 * main dissector function for the etch protocol
767 dissect_etch(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
769 if (tvb_length(tvb) < 4) {
770 /* Too small for an etch packet. */
774 if (tvb_memeql(tvb, 0, etch_magic, 4) == -1) {
775 /* Not an etch packet. */
779 tcp_dissect_pdus(tvb, pinfo, tree, TRUE, 8, get_etch_message_len,
780 dissect_etch_message);
782 if (gbl_pdu_counter > 0) {
783 col_set_writable(pinfo->cinfo, 1);
784 col_prepend_fstr(pinfo->cinfo, COL_INFO, "[%d] ", gbl_pdu_counter + 1);
791 etch_dissector_init(void)
794 gbl_old_frame_num = 0xFFFFFFFF;
797 void proto_register_etch(void)
799 module_t *etch_module;
801 static hf_register_info hf[] = {
803 {"Etch Signature", "etch.signature",
809 {"Etch Length", "etch.msglength",
815 {"Etch Dim", "etch.dim",
821 {"Etch Version", "etch.version",
827 {"Etch TypeCode", "etch.typecode",
828 FT_STRING, BASE_NONE, /* FT_INT8 */
833 {"Etch Value", "etch.value",
839 {"Etch Bytes", "etch.bytes",
845 {"Etch Byte", "etch.byte",
851 {"Etch Short", "etch.short",
857 {"Etch Int", "etch.int",
863 {"Etch Long", "etch.long",
869 {"Etch Float", "etch.float",
875 {"Etch Double", "etch.double",
876 FT_DOUBLE, BASE_NONE,
881 {"Etch keyValue", "etch.keyvalue",
887 {"Etch key", "etch.key",
893 {"Etch symbol", "etch.symbol",
899 {"Etch Struct", "etch.struct",
905 {"Etch String", "etch.string",
906 FT_STRING, BASE_NONE,
911 {"Etch key", "etch.keyname",
917 {"Etch value", "etch.valuename",
924 /* Setup protocol subtree array */
925 static gint *ett[] = {
933 proto_etch = proto_register_protocol("Apache Etch Protocol", /* name */
934 "ETCH", /* short name */
938 proto_register_field_array(proto_etch, hf, array_length(hf));
939 proto_register_subtree_array(ett, array_length(ett));
940 new_register_dissector("etch", dissect_etch, proto_etch);
942 register_init_routine(&etch_dissector_init);
944 etch_module = prefs_register_protocol(proto_etch,
945 proto_reg_handoff_etch);
947 prefs_register_string_preference(etch_module, "file",
948 "Apache Etch symbol folder",
949 "Place the hash/symbol files "
950 "(generated by the Apache Etch compiler) "
951 "ending with .ewh here",
953 prefs_register_uint_preference(etch_module, "tcp.port",
960 void proto_reg_handoff_etch(void)
962 static gboolean etch_prefs_initialized = FALSE;
963 static dissector_handle_t etch_handle;
964 static guint old_etch_port = 0;
966 /* create dissector handle only once */
967 if(!etch_prefs_initialized) {
968 etch_handle = new_create_dissector_handle(dissect_etch, proto_etch);
969 /* add heuristic dissector for tcp */
970 heur_dissector_add("tcp", dissect_etch, proto_etch);
971 etch_prefs_initialized = TRUE;
974 if(old_etch_port != 0 && old_etch_port != gbl_etch_port){
975 dissector_delete("tcp.port", old_etch_port, etch_handle);
978 if(gbl_etch_port != 0 && old_etch_port != gbl_etch_port) {
979 dissector_add("tcp.port", gbl_etch_port, etch_handle);
982 old_etch_port = gbl_etch_port;
984 /* read config folder files, if filename has changed
985 * (while protecting strcmp() from NULLs)
987 if(gbl_keytab_folder == NULL || gbl_current_keytab_folder == NULL ||
988 strcmp(gbl_keytab_folder, gbl_current_keytab_folder) != 0) {
989 read_hashed_symbols_from_dir(gbl_keytab_folder);