Use dynamically created value_string_ext to store hash/symbol value_strings;
[obnox/wireshark/wip.git] / epan / dissectors / packet-etch.c
1 /* packet-etch.c
2  *
3  * Copyright (c) 2010, Holger Grandy, BMW Car IT GmbH (holger.grandy@bmw-carit.de)
4  *
5  * $Id$
6  *
7  * Apache Etch Protocol dissector
8  * http://incubator.apache.org/etch/
9  *
10  * This dissector reads configuration files (generated by Etch IDL compiler).
11  * Configuration file directory path is given in dissector options.
12  *
13  * Wireshark - Network traffic analyzer
14  * By Gerald Combs <gerald@wireshark.org>
15  * Copyright 1998 Gerald Combs
16  *
17  *
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.
22  *
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.
27  *
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.
31  */
32
33
34 /* Include files */
35
36 #ifdef HAVE_CONFIG_H
37 #include "config.h"
38 #endif
39
40 #include <glib.h>
41
42 #include <stdio.h>
43 #include <string.h>
44
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>
50
51 /*
52  * maximum numbers for symbols from config files
53  */
54 #define ETCH_MAX_SYMBOL_LENGTH "256"
55
56 /*
57  * Magic Number for Etch
58  */
59 static const guint8 etch_magic[] = { 0xde, 0xad, 0xbe, 0xef };
60
61 /*
62  * Typecodes in the Etch protocol, representing the field types
63  */
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
83
84 /***************************************************************************/
85 /* Variables */
86
87 /*
88  * String representation of all Type Codes
89  */
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"},
108   { 0,                     NULL}
109 };
110
111 /*
112  * Wireshark internal fields
113  */
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;
140
141 /*
142  * internal fields/defines for dissector
143  */
144
145 static const char       *gbl_keytab_folder = "";
146 static guint             gbl_etch_port     = 0;
147 static char             *gbl_current_keytab_folder = NULL;
148
149 static int               gbl_pdu_counter;
150 static guint32           gbl_old_frame_num;
151
152 static emem_strbuf_t    *gbl_symbol_buffer = NULL;
153 static gboolean          gbl_have_symbol   = FALSE;
154
155 /***************************************************************************/
156 /* Methods */
157
158 /*
159  * forward declared dissector methods
160  */
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,
166                       int asWhat);
167 void proto_reg_handoff_etch(void);
168
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)
177  */
178 static GArray           *gbl_symbols_array  = NULL;
179 static value_string_ext *gbl_symbols_vs_ext = NULL;
180
181 static void
182 gbl_symbols_new(void) {
183   g_assert(gbl_symbols_array == NULL);
184   gbl_symbols_array = g_array_new(TRUE, TRUE, sizeof(value_string));
185 }
186
187 static void
188 gbl_symbols_free(void) {
189   g_free(gbl_symbols_vs_ext);
190   gbl_symbols_vs_ext = NULL;
191
192   if (gbl_symbols_array != NULL) {
193     value_string *vs_p;
194     guint i;
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);
198     }
199     g_array_free(gbl_symbols_array, TRUE);
200     gbl_symbols_array = NULL;
201   }
202 }
203
204 static void
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);
209 }
210
211 static gint
212 gbl_symbols_compare_vs(gconstpointer  a, gconstpointer  b)
213 {
214   value_string *vsa = (value_string *)a;
215   value_string *vsb = (value_string *)b;
216
217   if(vsa->value > vsb->value)
218     return 1;
219   if(vsa->value < vsb->value)
220     return -1;
221
222   return 0;
223 }
224
225 static void
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" );
233 }
234
235 /*********************************************************************************/
236 /* Aux Functions */
237
238 /*
239  * get the length of a given typecode in bytes, -1 if to be derived from message
240  */
241 static gint32
242 get_byte_length(guint8 typecode)
243 {
244   switch (typecode) {
245   case ETCH_TC_NULL:
246   case ETCH_TC_NONE:
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:
252     return  0;
253     break;
254   case ETCH_TC_BYTE:
255     return  1;
256     break;
257   case ETCH_TC_SHORT:
258     return  2;
259     break;
260   case ETCH_TC_INT:
261   case ETCH_TC_FLOAT:
262     return  4;
263     break;
264   case ETCH_TC_LONG:
265   case ETCH_TC_DOUBLE:
266     return  8;
267     break;
268   case ETCH_TC_BYTES:
269   case ETCH_TC_ARRAY:
270   case ETCH_TC_STRING:
271   case ETCH_TC_STRUCT:
272   case ETCH_TC_CUSTOM:
273   case ETCH_TC_ANY:
274     return  -1;
275     break;
276   default:
277     return 0;
278     break;
279   }
280 }
281
282 /*
283  * add all etch symbols from file to our symbol cache
284  */
285 static void
286 add_symbols_of_file(const char *filename)
287 {
288   FILE *pFile;
289   pFile = ws_fopen(filename, "r");
290
291   if (pFile != NULL) {
292     char line[256];
293     while (fgets(line, sizeof line, pFile) != NULL) {
294       int    hash;
295       size_t length, pos;
296
297       length = strlen(line);
298
299       /* Must at least have a hash, else skip line */
300       if (length < 10)
301         continue;
302
303       pos = length - 1;
304       while (pos > 0 && (line[pos] == 0xD || line[pos] == 0xA)) {
305         pos--;
306       }
307       line[pos + 1] = '\0';
308
309      /* Parse the Hash */
310       if (sscanf(&line[0], "%x", &hash) != 1)
311         continue;  /* didn't find a valid hex value at the beginning of the line */
312
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]));
318       }
319     fclose(pFile);
320   }
321 }
322
323 /*
324  * add all etch symbol from directory to our symbol cache
325  */
326 static void
327 read_hashed_symbols_from_dir(const char *dirname)
328 {
329   WS_DIR       *dir;
330   WS_DIRENT    *file;
331   const char   *name;
332   char         *filename;
333   GError       *err_p = NULL;
334
335   if(gbl_current_keytab_folder != NULL) {
336     g_free(gbl_current_keytab_folder);
337     gbl_current_keytab_folder = NULL;
338   }
339
340   gbl_symbols_free();
341
342   if ((dirname == NULL) || (dirname[0] == '\0'))
343     return;
344
345   if ((dir = ws_dir_open(dirname, 0, &err_p)) != NULL) {
346     gbl_symbols_new();
347
348     gbl_current_keytab_folder = g_strdup(dirname);
349     while ((file = ws_dir_read_name(dir)) != NULL) {
350       name = ws_dir_get_name(file);
351
352       if (g_str_has_suffix(file, ".ewh")) {
353         filename =
354           g_strdup_printf("%s" G_DIR_SEPARATOR_S "%s", dirname,
355                           name);
356         add_symbols_of_file(filename);
357         g_free(filename);
358       }
359     }
360     ws_dir_close(dir);
361     gbl_symbols_vs_ext_new();
362   }else{
363     report_failure("%s", err_p->message);
364     g_error_free(err_p);
365   }
366 }
367
368 /***********************************************************************************/
369 /* Etch Protocol Functions */
370
371 /*
372  * read a type flag from tvb and add it to tree
373  */
374 static guint8
375 read_type(unsigned int *offset, tvbuff_t *tvb, proto_tree *etch_tree)
376 {
377
378   guint32      type_code;
379   const gchar *type_as_string;
380
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);
384   (*offset)++;
385   return type_code;
386 }
387
388 /*
389  * read a array type flag and add it to tree
390  */
391 static void
392 read_array_type(unsigned int *offset, tvbuff_t *tvb, proto_tree *etch_tree)
393 {
394   guint32 type_code;
395
396   type_code = tvb_get_guint8(tvb, *offset);
397
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,
402                         ENC_BIG_ENDIAN);
403     (*offset) += 4;
404   }
405 }
406
407
408 /*
409  * read the length of an array and add it to tree
410  */
411 static guint32
412 read_length(unsigned int *offset, tvbuff_t *tvb, proto_tree *etch_tree)
413 {
414   guint32 length;
415   int length_of_array_length_type;
416   guint8 tiny;
417
418   tiny = tvb_get_guint8(tvb, *offset);
419
420   /*  Is this the value already? */
421   if (  tiny <= ETCH_TC_MAX_TINY_INT
422         || tiny >= ETCH_TC_MIN_TINY_INT) {
423     length = tiny;
424     length_of_array_length_type = 1;
425   } else {
426     guint8 type_code;
427     type_code = read_type(offset, tvb, etch_tree);
428     length_of_array_length_type = get_byte_length(type_code);
429
430     switch (length_of_array_length_type) {
431     case 1:
432       length = tvb_get_guint8(tvb, *offset);
433       break;
434     case 2:
435       length = tvb_get_ntohs(tvb, *offset);
436       break;
437     case 4:
438       length = tvb_get_ntohl(tvb, *offset);
439       break;
440     default:
441       return 0;             /* error! */
442     }
443   }
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;
447   return length;
448 }
449
450
451 /*
452  * read an array from tvb and add it to tree
453  */
454 static void
455 read_array(unsigned int *offset, tvbuff_t *tvb, proto_tree *etch_tree)
456 {
457   int length;
458
459   /*  array type */
460   read_type(offset, tvb, etch_tree);
461
462   /*  Array of type: */
463   read_array_type(offset, tvb, etch_tree);
464
465   /*  Array dim */
466   proto_tree_add_item(etch_tree, hf_etch_dim, tvb, *offset, 1, ENC_NA);
467   (*offset)++;
468
469   /*  Array length */
470   length = read_length(offset, tvb, etch_tree);
471
472   for (; length > 0; length--) {
473     read_value(offset, tvb, etch_tree, hf_etch_value);
474   }
475   /*  terminaton */
476   read_type(offset, tvb, etch_tree);
477 }
478
479
480 /*
481  * read a sequence of bytes and add them to tree
482  */
483 static void
484 read_bytes(unsigned int *offset, tvbuff_t *tvb, proto_tree *etch_tree)
485 {
486   int length;
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,
490                       ENC_NA);
491   (*offset) += length;
492 }
493
494 /*
495  * read a string and add it to tree
496  */
497 static void
498 read_string(unsigned int *offset, tvbuff_t *tvb, proto_tree *etch_tree)
499 {
500   int byteLength;
501   read_type(offset, tvb, etch_tree);
502
503   byteLength = read_length(offset, tvb, etch_tree);
504
505   proto_tree_add_item(etch_tree, hf_etch_string, tvb, *offset,
506                       byteLength, ENC_NA);
507   (*offset) += byteLength;
508 }
509
510 /*
511  * read a number and add it to tree
512  */
513 static void
514 read_number(unsigned int *offset, tvbuff_t *tvb, proto_tree *etch_tree,
515             int asWhat, guint8 type_code)
516 {
517   int byteLength;
518
519   read_type(offset, tvb, etch_tree);
520   byteLength = get_byte_length(type_code);
521   if (byteLength > 0) {
522     proto_item  *ti;
523     const gchar *symbol = NULL;
524     guint32      hash   = 0;
525
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);
530       if(symbol != NULL) {
531         asWhat = hf_etch_symbol;
532         gbl_have_symbol = TRUE;
533         ep_strbuf_append_printf(gbl_symbol_buffer,"%s",symbol);
534       }
535     }
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);
541     }
542   }
543 }
544
545 /*
546  * read a value and add it to tree
547  */
548 static int
549 read_value(unsigned int *offset, tvbuff_t *tvb, proto_tree *etch_tree,
550            int asWhat)
551 {
552   guint8 type_code;
553
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);
559     (*offset)++;
560     return type_code;
561   }
562
563   switch(type_code) {
564   case ETCH_TC_CUSTOM:
565     read_struct(offset, tvb, etch_tree, 1);
566     break;
567   case ETCH_TC_ARRAY:
568     read_array(offset, tvb, etch_tree);
569     break;
570   case ETCH_TC_STRING:
571     read_string(offset, tvb, etch_tree);
572     break;
573   case ETCH_TC_FLOAT:
574     read_number(offset, tvb, etch_tree, hf_etch_float, type_code);
575     break;
576   case ETCH_TC_DOUBLE:
577     read_number(offset, tvb, etch_tree, hf_etch_double, type_code);
578     break;
579   case ETCH_TC_SHORT:
580     read_number(offset, tvb, etch_tree, hf_etch_short, type_code);
581     break;
582   case ETCH_TC_INT:
583     read_number(offset, tvb, etch_tree, hf_etch_int, type_code);
584     break;
585   case ETCH_TC_LONG:
586     read_number(offset, tvb, etch_tree, hf_etch_long, type_code);
587     break;
588   case ETCH_TC_BYTE:
589     read_number(offset, tvb, etch_tree, hf_etch_byte, type_code);
590     break;
591   case ETCH_TC_BYTES:
592     read_bytes(offset, tvb, etch_tree);
593     break;
594   default:
595     read_number(offset, tvb, etch_tree, asWhat, type_code);
596   }
597   return 0;
598 }
599
600 /*
601  * read a struct and add it to tree
602  */
603 static void
604 read_struct(unsigned int *offset, tvbuff_t *tvb, proto_tree *etch_tree,
605             int add_type_field)
606 {
607   proto_item *ti;
608   proto_tree *new_tree;
609   int length;
610   int i;
611
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);
615
616   if (add_type_field) {
617     read_type(offset, tvb, new_tree);
618   }
619   /* struct type as hash */
620   read_value(offset, tvb, new_tree, hf_etch_value);
621
622   /* struct length */
623   length = read_value(offset, tvb, new_tree, hf_etch_length);
624
625   for (i = 0; i < length; i++) {
626     read_key_value(offset, tvb, new_tree);
627   }
628
629   /* termination */
630   read_type(offset, tvb, new_tree);
631 }
632
633 /*
634  * read a key value pair and add it to tree
635  */
636 static void
637 read_key_value(unsigned int *offset, tvbuff_t *tvb, proto_tree *etch_tree)
638 {
639   proto_tree *new_tree;
640   proto_tree *new_tree_bck;
641   proto_item *ti, *parent_ti;
642
643   gbl_have_symbol = FALSE;
644
645   parent_ti =
646     proto_tree_add_item(etch_tree, hf_etch_keyvalue, tvb, *offset, 1,
647                         ENC_NA);
648   new_tree_bck = new_tree =
649     proto_item_add_subtree(parent_ti, ett_etch_keyvalue);
650
651   ti = proto_tree_add_item(new_tree, hf_etch_keyname, tvb, *offset, 0,
652                            ENC_NA);
653   new_tree = proto_item_add_subtree(ti, ett_etch_key);
654   read_value(offset, tvb, new_tree, hf_etch_value);
655
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, ")");
661   }
662
663   ti = proto_tree_add_item(new_tree_bck, hf_etch_valuename, tvb, *offset,
664                            0, ENC_NA);
665   new_tree = proto_item_add_subtree(ti, ett_etch_value);
666   read_value(offset, tvb, new_tree, hf_etch_value);
667 }
668
669 /*************************************************************************/
670 /*
671  * Preparse the message for the info column
672  */
673 static emem_strbuf_t*
674 get_column_info(tvbuff_t *tvb)
675 {
676   int byte_length;
677   guint8         type_code;
678   emem_strbuf_t *result_buf;
679   int            my_offset = 0;
680
681   /* We've a full PDU: 8 bytes + pdu_packetlen bytes  */
682   result_buf = ep_strbuf_new_label("");
683
684   my_offset += (4 + 4 + 1); /* skip Magic, Length, Version */
685
686   type_code = tvb_get_guint8(tvb, my_offset);
687   byte_length = get_byte_length(type_code);
688   my_offset++;
689
690   if (byte_length == 4) {
691     const gchar *symbol;
692     guint32      hash;
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);
697     }
698   }
699
700   return result_buf;
701 }
702
703
704 /****************************************************************************************************/
705 /*
706  * main dissector function for an etch message
707  */
708 static void
709 dissect_etch_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
710 {
711   /* We've a full PDU: 8 bytes + pdu_packetlen bytes  */
712   emem_strbuf_t *colInfo = NULL;
713
714   if (pinfo->cinfo || tree) {
715     colInfo = get_column_info(tvb);    /* get current symbol */
716   }
717
718   if (pinfo->cinfo) {
719     col_set_str(pinfo->cinfo, COL_PROTOCOL, "ETCH");
720     gbl_pdu_counter++;
721
722     /* Switch to another frame? => Clear column */
723     if (pinfo->fd->num != gbl_old_frame_num) {
724       col_clear(pinfo->cinfo, COL_INFO);
725       gbl_pdu_counter = 0;
726     }
727     gbl_old_frame_num = pinfo->fd->num;
728
729     col_set_writable(pinfo->cinfo, 1);
730     col_append_fstr(pinfo->cinfo, COL_INFO, "%s ", colInfo->str);
731   }
732
733   if (tree) {
734     /* we are being asked for details */
735     unsigned int offset;
736     proto_item *ti;
737     proto_tree *etch_tree;
738
739     ti = proto_tree_add_protocol_format(tree, proto_etch, tvb, 0, -1,
740                                         "ETCH Protocol: %s", colInfo->str);
741
742     offset = 9;
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);
748   }
749
750 }
751
752 /*
753  * determine PDU length of protocol etch
754  */
755 static guint
756 get_etch_message_len(packet_info *pinfo _U_, tvbuff_t *tvb, int offset)
757 {
758   /* length is at offset 4. we add magic bytes length + length size */
759   return tvb_get_ntohl(tvb, offset + 4) + 8;
760 }
761
762
763 /*
764  * main dissector function for the etch protocol
765  */
766 static int
767 dissect_etch(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
768 {
769   if (tvb_length(tvb) < 4) {
770     /* Too small for an etch packet. */
771     return 0;
772   }
773
774   if (tvb_memeql(tvb, 0, etch_magic, 4) == -1) {
775     /* Not an etch packet. */
776     return 0;
777   }
778
779   tcp_dissect_pdus(tvb, pinfo, tree, TRUE, 8, get_etch_message_len,
780                    dissect_etch_message);
781
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);
785   }
786
787   return 1;
788 }
789
790 static void
791 etch_dissector_init(void)
792 {
793   gbl_pdu_counter   = 0;
794   gbl_old_frame_num = 0xFFFFFFFF;
795 }
796
797 void proto_register_etch(void)
798 {
799   module_t *etch_module;
800
801   static hf_register_info hf[] = {
802     {&hf_etch_sig,
803      {"Etch Signature", "etch.signature",
804       FT_UINT32, BASE_HEX,
805       NULL, 0x0,
806       NULL, HFILL}
807     },
808     {&hf_etch_length,
809      {"Etch Length", "etch.msglength",
810       FT_UINT64, BASE_DEC,
811       NULL, 0x0,
812       NULL, HFILL}
813     },
814     {&hf_etch_dim,
815      {"Etch Dim", "etch.dim",
816       FT_UINT8, BASE_DEC,
817       NULL, 0x0,
818       NULL, HFILL}
819     },
820     {&hf_etch_version,
821      {"Etch Version", "etch.version",
822       FT_UINT8, BASE_DEC,
823       NULL, 0x0,
824       NULL, HFILL}
825     },
826     {&hf_etch_typecode,
827      {"Etch TypeCode", "etch.typecode",
828       FT_STRING, BASE_NONE,    /* FT_INT8 */
829       NULL, 0x0,
830       NULL, HFILL}
831     },
832     {&hf_etch_value,
833      {"Etch Value", "etch.value",
834       FT_UINT64, BASE_DEC,
835       NULL, 0x0,
836       NULL, HFILL}
837     },
838     {&hf_etch_bytes,
839      {"Etch Bytes", "etch.bytes",
840       FT_BYTES, BASE_NONE,
841       NULL, 0x0,
842       NULL, HFILL}
843     },
844     {&hf_etch_byte,
845      {"Etch Byte", "etch.byte",
846       FT_INT8, BASE_DEC,
847       NULL, 0x0,
848       NULL, HFILL}
849     },
850     {&hf_etch_short,
851      {"Etch Short", "etch.short",
852       FT_INT16, BASE_DEC,
853       NULL, 0x0,
854       NULL, HFILL}
855     },
856     {&hf_etch_int,
857      {"Etch Int", "etch.int",
858       FT_INT32, BASE_DEC,
859       NULL, 0x0,
860       NULL, HFILL}
861     },
862     {&hf_etch_long,
863      {"Etch Long", "etch.long",
864       FT_INT64, BASE_DEC,
865       NULL, 0x0,
866       NULL, HFILL}
867     },
868     {&hf_etch_float,
869      {"Etch Float", "etch.float",
870       FT_FLOAT, BASE_NONE,
871       NULL, 0x0,
872       NULL, HFILL}
873     },
874     {&hf_etch_double,
875      {"Etch Double", "etch.double",
876       FT_DOUBLE, BASE_NONE,
877       NULL, 0x0,
878       NULL, HFILL}
879     },
880     {&hf_etch_keyvalue,
881      {"Etch keyValue", "etch.keyvalue",
882       FT_NONE, BASE_NONE,
883       NULL, 0x0,
884       NULL, HFILL}
885     },
886     {&hf_etch_key,
887      {"Etch key", "etch.key",
888       FT_BYTES, BASE_NONE,
889       NULL, 0x0,
890       NULL, HFILL}
891     },
892     {&hf_etch_symbol,
893      {"Etch symbol", "etch.symbol",
894       FT_UINT32, BASE_HEX,
895       NULL, 0x0,
896       NULL, HFILL}
897     },
898     {&hf_etch_struct,
899      {"Etch Struct", "etch.struct",
900       FT_BYTES, BASE_NONE,
901       NULL, 0x0,
902       NULL, HFILL}
903     },
904     {&hf_etch_string,
905      {"Etch String", "etch.string",
906       FT_STRING, BASE_NONE,
907       NULL, 0x0,
908       NULL, HFILL}
909     },
910     {&hf_etch_keyname,
911      {"Etch key", "etch.keyname",
912       FT_NONE, BASE_NONE,
913       NULL, 0x0,
914       NULL, HFILL}
915     },
916     {&hf_etch_valuename,
917      {"Etch value", "etch.valuename",
918       FT_NONE, BASE_NONE,
919       NULL, 0x0,
920       NULL, HFILL}
921     },
922   };
923
924   /* Setup protocol subtree array */
925   static gint *ett[] = {
926     &ett_etch,
927     &ett_etch_struct,
928     &ett_etch_keyvalue,
929     &ett_etch_key,
930     &ett_etch_value,
931   };
932
933   proto_etch = proto_register_protocol("Apache Etch Protocol", /* name       */
934                                        "ETCH",                 /* short name */
935                                        "etch"                  /* abbrev     */
936                                        );
937
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);
941
942   register_init_routine(&etch_dissector_init);
943
944   etch_module = prefs_register_protocol(proto_etch,
945                                         proto_reg_handoff_etch);
946
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",
952                                    &gbl_keytab_folder);
953   prefs_register_uint_preference(etch_module, "tcp.port",
954                                  "etch TCP Port",
955                                  "Etch TCP port",
956                                  10,
957                                  &gbl_etch_port);
958 }
959
960 void proto_reg_handoff_etch(void)
961 {
962   static gboolean etch_prefs_initialized = FALSE;
963   static dissector_handle_t etch_handle;
964   static guint old_etch_port = 0;
965
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;
972   }
973
974   if(old_etch_port != 0 && old_etch_port != gbl_etch_port){
975     dissector_delete("tcp.port", old_etch_port, etch_handle);
976   }
977
978   if(gbl_etch_port != 0 && old_etch_port != gbl_etch_port) {
979     dissector_add("tcp.port", gbl_etch_port, etch_handle);
980   }
981
982   old_etch_port = gbl_etch_port;
983
984   /* read config folder files, if filename has changed
985    * (while protecting strcmp() from NULLs)
986    */
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);
990   }
991 }