sharkd: support for export objects, following stream.
authorJakub Zawadzki <darkjames-ws@darkjames.pl>
Sat, 11 Mar 2017 11:27:14 +0000 (12:27 +0100)
committerAlexis La Goutte <alexis.lagoutte@gmail.com>
Sat, 11 Mar 2017 20:35:19 +0000 (20:35 +0000)
Change-Id: I364cb200e537ae07cbe831d8be1f217c5a15ee09
Reviewed-on: https://code.wireshark.org/review/20492
Petri-Dish: Jakub Zawadzki <darkjames-ws@darkjames.pl>
Tested-by: Petri Dish Buildbot <buildbot-no-reply@wireshark.org>
Reviewed-by: Michael Mann <mmann78@netscape.net>
Reviewed-by: Alexis La Goutte <alexis.lagoutte@gmail.com>
sharkd_session.c

index ba06184e3e0234f201946af86c5c48a0db3f6f49..3719f6337f324640fe57d26c3d96a636aea762d6 100644 (file)
@@ -46,6 +46,8 @@
 #include <epan/stats_tree_priv.h>
 #include <epan/stat_tap_ui.h>
 #include <epan/conversation_table.h>
+#include <epan/export_object.h>
+#include <epan/follow.h>
 
 #include <epan/dissectors/packet-h225.h>
 #include <epan/rtp_pt.h>
@@ -60,6 +62,7 @@
 # include <wsutil/pint.h>
 #endif
 
+#include <wsutil/glib-compat.h>
 #include <wsutil/strtoi.h>
 
 #include "sharkd.h"
@@ -243,6 +246,44 @@ sharkd_session_process_info_conv_cb(const void* key, void* value, void* userdata
        return FALSE;
 }
 
+static gboolean
+sharkd_export_object_visit_cb(const void *key _U_, void *value, void *user_data)
+{
+       register_eo_t *eo = (register_eo_t*)value;
+       int *pi = (int *) user_data;
+
+       const int proto_id = get_eo_proto_id(eo);
+       const char *filter = proto_get_protocol_filter_name(proto_id);
+       const char *label  = proto_get_protocol_short_name(find_protocol_by_id(proto_id));
+
+       printf("%s{", (*pi) ? "," : "");
+               printf("\"name\":\"Export Object/%s\"", label);
+               printf(",\"tap\":\"eo:%s\"", filter);
+       printf("}");
+
+       *pi = *pi + 1;
+       return FALSE;
+}
+
+static gboolean
+sharkd_follower_visit_cb(const void *key _U_, void *value, void *user_data)
+{
+       register_follow_t *follower = (register_follow_t*) value;
+       int *pi = (int *) user_data;
+
+       const int proto_id = get_follow_proto_id(follower);
+       const char *label  = proto_get_protocol_short_name(find_protocol_by_id(proto_id));
+       const char *filter = label; /* correct: get_follow_by_name() is registered by short name */
+
+       printf("%s{", (*pi) ? "," : "");
+               printf("\"name\":\"Follow/%s\"", label);
+               printf(",\"tap\":\"follow:%s\"", filter);
+       printf("}");
+
+       *pi = *pi + 1;
+       return FALSE;
+}
+
 /**
  * sharkd_session_process_info()
  *
@@ -261,9 +302,17 @@ sharkd_session_process_info_conv_cb(const void* key, void* value, void* userdata
  *                  'name' - conversation name
  *                  'tap'  - sharkd tap-name for conversation
  *
+ *   (m) eo      - available export object list, array of object with attributes:
+ *                  'name' - export object name
+ *                  'tap'  - sharkd tap-name for conversation
+ *
  *   (m) taps - available taps, array of object with attributes:
  *                  'name' - tap name
  *                  'tap'  - sharkd tap-name
+
+ *   (m) follow - available followers, array of object with attributes:
+ *                  'name' - tap name
+ *                  'tap'  - sharkd tap-name
  *
  *   (m) ftypes   - conversation table for FT_ number to string
  */
@@ -329,6 +378,16 @@ sharkd_session_process_info(void)
        }
        printf("]");
 
+       printf(",\"eo\":[");
+       i = 0;
+       eo_iterate_tables(sharkd_export_object_visit_cb, &i);
+       printf("]");
+
+       printf(",\"follow\":[");
+       i = 0;
+       follow_iterate_followers(sharkd_follower_visit_cb, &i);
+       printf("]");
+
        printf("}\n");
 }
 
@@ -918,6 +977,97 @@ sharkd_session_process_tap_conv_cb(void *arg)
        printf("],\"proto\":\"%s\",\"geoip\":%s},", proto, with_geoip ? "true" : "false");
 }
 
+struct sharkd_export_object_list
+{
+       struct sharkd_export_object_list *next;
+
+       char *type;
+       const char *proto;
+       GSList *entries;
+};
+
+static struct sharkd_export_object_list *sharkd_eo_list;
+
+/**
+ * sharkd_session_process_tap_eo_cb()
+ *
+ * Output eo tap:
+ *   (m) tap        - tap name
+ *   (m) type       - tap output type
+ *   (m) proto      - protocol short name
+ *   (m) objects    - array of object with attributes:
+ *                  (m) pkt - packet number
+ *                  (o) hostname - hostname
+ *                  (o) type - content type
+ *                  (o) filename - filename
+ *                  (m) len - object length
+ */
+static void
+sharkd_session_process_tap_eo_cb(void *tapdata)
+{
+       export_object_list_t *tap_object = (export_object_list_t *) tapdata;
+       struct sharkd_export_object_list *object_list = (struct sharkd_export_object_list*) tap_object->gui_data;
+       GSList *slist;
+       int i = 0;
+
+       printf("{\"tap\":\"%s\",\"type\":\"eo\"", object_list->type);
+       printf(",\"proto\":\"%s\"", object_list->proto);
+       printf(",\"objects\":[");
+
+       for (slist = object_list->entries; slist; slist = slist->next)
+       {
+               const export_object_entry_t *eo_entry = (export_object_entry_t *) slist->data;
+
+               printf("%s{", i ? "," : "");
+
+               printf("\"pkt\":%u", eo_entry->pkt_num);
+
+               if (eo_entry->hostname)
+               {
+                       printf(",\"hostname\":");
+                       json_puts_string(eo_entry->hostname);
+               }
+
+               if (eo_entry->content_type)
+               {
+                       printf(",\"type\":");
+                       json_puts_string(eo_entry->content_type);
+               }
+
+               if (eo_entry->filename)
+               {
+                       printf(",\"filename\":");
+                       json_puts_string(eo_entry->filename);
+               }
+
+               printf(",\"_download\":\"%s_%d\"", object_list->type, i);
+
+               printf(",\"len\":%" G_GUINT64_FORMAT, eo_entry->payload_len);
+
+               printf("}");
+
+               i++;
+       }
+
+       printf("]},");
+}
+
+static void
+sharkd_eo_object_list_add_entry(void *gui_data, export_object_entry_t *entry)
+{
+       struct sharkd_export_object_list *object_list = (struct sharkd_export_object_list *) gui_data;
+
+       object_list->entries = g_slist_append(object_list->entries, entry);
+}
+
+static export_object_entry_t *
+sharkd_eo_object_list_get_entry(void *gui_data, int row)
+{
+       struct sharkd_export_object_list *object_list = (struct sharkd_export_object_list *) gui_data;
+
+       return (export_object_entry_t *) g_slist_nth_data(object_list->entries, row);
+}
+
 /**
  * sharkd_session_process_tap_rtp_cb()
  *
@@ -1019,6 +1169,7 @@ sharkd_session_process_tap_rtp_cb(void *arg)
  *                  for type:conv see sharkd_session_process_tap_conv_cb()
  *                  for type:host see sharkd_session_process_tap_conv_cb()
  *                  for type:rtp-streams see sharkd_session_process_tap_rtp_cb()
+ *                  for type:eo see sharkd_session_process_tap_eo_cb()
  *
  *   (m) err   - error code
  */
@@ -1115,6 +1266,47 @@ sharkd_session_process_tap(char *buf, const jsmntok_t *tokens, int count)
 
                        tap_data = &ct_data->hash;
                }
+               else if (!strncmp(tok_tap, "eo:", 3))
+               {
+                       register_eo_t *eo = get_eo_by_name(tok_tap + 3);
+                       export_object_list_t *eo_object;
+                       struct sharkd_export_object_list *object_list;
+
+                       if (!eo)
+                       {
+                               fprintf(stderr, "sharkd_session_process_tap() eo=%s not found\n", tok_tap + 3);
+                               continue;
+                       }
+
+                       for (object_list = sharkd_eo_list; object_list; object_list = object_list->next)
+                       {
+                               if (!strcmp(object_list->type, tok_tap))
+                               {
+                                       g_slist_free_full(object_list->entries, (GDestroyNotify) eo_free_entry);
+                                       object_list->entries = NULL;
+                                       break;
+                               }
+                       }
+
+                       if (!object_list)
+                       {
+                               object_list = g_new(struct sharkd_export_object_list, 1);
+                               object_list->type = g_strdup(tok_tap);
+                               object_list->proto = proto_get_protocol_short_name(find_protocol_by_id(get_eo_proto_id(eo)));
+                               object_list->entries = NULL;
+                               object_list->next = sharkd_eo_list;
+                               sharkd_eo_list = object_list;
+                       }
+
+                       eo_object  = g_new0(export_object_list_t, 1);
+                       eo_object->add_entry = sharkd_eo_object_list_add_entry;
+                       eo_object->get_entry = sharkd_eo_object_list_get_entry;
+                       eo_object->gui_data = (void *) object_list;
+
+                       tap_error = register_tap_listener(get_eo_tap_listener_name(eo), eo_object, NULL, 0, NULL, get_eo_packet_func(eo), sharkd_session_process_tap_eo_cb);
+
+                       tap_data = eo_object;
+               }
                else if (!strcmp(tok_tap, "rtp-streams"))
                {
                        tap_error = register_tap_listener("rtp", &rtp_tapinfo, tap_filter, 0, rtpstream_reset_cb, rtpstream_packet, sharkd_session_process_tap_rtp_cb);
@@ -1156,6 +1348,130 @@ sharkd_session_process_tap(char *buf, const jsmntok_t *tokens, int count)
        }
 }
 
+/**
+ * sharkd_session_process_follow()
+ *
+ * Process follow request
+ *
+ * Input:
+ *   (m) follow  - follow protocol request (e.g. HTTP)
+ *   (m) filter  - filter request (e.g. tcp.stream == 1)
+ *
+ * Output object with attributes:
+ *
+ *   (m) err    - error code
+ *   (m) shost  - server host
+ *   (m) sport  - server port
+ *   (m) sbytes - server send bytes count
+ *   (m) chost  - client host
+ *   (m) cport  - client port
+ *   (m) cbytes - client send bytes count
+ *   (o) payloads - array of object with attributes:
+ *                  (o) s - set if server sent, else client
+ *                  (m) n - packet number
+ *                  (m) d - data base64 encoded
+ */
+static void
+sharkd_session_process_follow(char *buf, const jsmntok_t *tokens, int count)
+{
+       const char *tok_follow = json_find_attr(buf, tokens, count, "follow");
+       const char *tok_filter = json_find_attr(buf, tokens, count, "filter");
+
+       register_follow_t *follower;
+       GString *tap_error;
+
+       follow_info_t *follow_info;
+       const char *host;
+       char *port;
+
+       if (!tok_follow || !tok_filter)
+               return;
+
+       follower = get_follow_by_name(tok_follow);
+       if (!follower)
+       {
+               fprintf(stderr, "sharkd_session_process_follow() follower=%s not found\n", tok_follow);
+               return;
+       }
+
+       /* follow_reset_stream ? */
+       follow_info = g_new0(follow_info_t, 1);
+       /* gui_data, filter_out_filter not set, but not used by dissector */
+
+       tap_error = register_tap_listener(get_follow_tap_string(follower), follow_info, tok_filter, 0, NULL, get_follow_tap_handler(follower), NULL);
+       if (tap_error)
+       {
+               fprintf(stderr, "sharkd_session_process_follow() name=%s error=%s", tok_follow, tap_error->str);
+               g_string_free(tap_error, TRUE);
+               g_free(follow_info);
+               return;
+       }
+
+       sharkd_retap();
+
+       printf("{");
+
+       printf("\"err\":0");
+
+       /* Server information: hostname, port, bytes sent */
+       host = address_to_name(&follow_info->server_ip);
+       printf(",\"shost\":");
+       json_puts_string(host);
+
+       port = get_follow_port_to_display(follower)(NULL, follow_info->server_port);
+       printf(",\"sport\":");
+       json_puts_string(port);
+       wmem_free(NULL, port);
+
+       printf(",\"sbytes\":%u", follow_info->bytes_written[0]);
+
+       /* Client information: hostname, port, bytes sent */
+       host = address_to_name(&follow_info->client_ip);
+       printf(",\"chost\":");
+       json_puts_string(host);
+
+       port = get_follow_port_to_display(follower)(NULL, follow_info->client_port);
+       printf(",\"cport\":");
+       json_puts_string(port);
+       wmem_free(NULL, port);
+
+       printf(",\"cbytes\":%u", follow_info->bytes_written[1]);
+
+       if (follow_info->payload)
+       {
+               follow_record_t *follow_record;
+               GList *cur;
+               const char *sepa = "";
+
+               printf(",\"payloads\":[");
+
+               for (cur = follow_info->payload; cur; cur = g_list_next(cur))
+               {
+                       follow_record = (follow_record_t *) cur->data;
+
+                       printf("%s{", sepa);
+
+                       printf("\"n\":%u", follow_record->packet_num);
+
+                       printf(",\"d\":");
+                       json_print_base64(follow_record->data->data, follow_record->data->len);
+
+                       if (follow_record->is_server)
+                               printf(",\"s\":%d", 1);
+
+                       printf("}");
+                       sepa = ",";
+               }
+
+               printf("]");
+       }
+
+       printf("}\n");
+
+       remove_tap_listener(follow_info);
+       follow_info_free(follow_info);
+}
+
 static void
 sharkd_session_process_frame_cb_tree(proto_tree *tree, tvbuff_t **tvbs)
 {
@@ -1257,6 +1573,33 @@ sharkd_session_process_frame_cb_tree(proto_tree *tree, tvbuff_t **tvbs)
        printf("]");
 }
 
+static gboolean
+sharkd_follower_visit_layers_cb(const void *key _U_, void *value, void *user_data)
+{
+       register_follow_t *follower = (register_follow_t *) value;
+       packet_info *pi = (packet_info *) user_data;
+
+       const int proto_id = get_follow_proto_id(follower);
+
+       guint32 ignore_stream;
+
+       if (proto_is_frame_protocol(pi->layers, proto_get_protocol_filter_name(proto_id)))
+       {
+               const char *layer_proto = proto_get_protocol_short_name(find_protocol_by_id(proto_id));
+               char *follow_filter;
+
+               follow_filter = get_follow_conv_func(follower)(pi, &ignore_stream);
+
+               printf(",[\"%s\",", layer_proto);
+               json_puts_string(follow_filter);
+               printf("]");
+
+               g_free(follow_filter);
+       }
+
+       return FALSE;
+}
+
 static void
 sharkd_session_process_frame_cb(packet_info *pi, proto_tree *tree, struct epan_column_info *cinfo, const GSList *data_src, void *data)
 {
@@ -1380,6 +1723,10 @@ sharkd_session_process_frame_cb(packet_info *pi, proto_tree *tree, struct epan_c
                        printf("]");
        }
 
+       printf(",\"fol\":[0");
+       follow_iterate_followers(sharkd_follower_visit_layers_cb, pi);
+       printf("]");
+
        printf("}\n");
 }
 
@@ -1525,6 +1872,9 @@ sharkd_session_process_intervals(char *buf, const jsmntok_t *tokens, int count)
  *   (o) col   - array of column data
  *   (o) bytes - base64 of frame bytes
  *   (o) ds    - array of other data srcs
+ *   (o) fol   - array of follow filters:
+ *                  [0] - protocol
+ *                  [1] - filter string
  */
 static void
 sharkd_session_process_frame(char *buf, const jsmntok_t *tokens, int count)
@@ -1961,6 +2311,63 @@ sharkd_session_process_dumpconf(char *buf, const jsmntok_t *tokens, int count)
     }
 }
 
+/**
+ * sharkd_session_process_download()
+ *
+ * Process download request
+ *
+ * Input:
+ *   (m) token  - token to download
+ *
+ * Output object with attributes:
+ *   (o) file - suggested name of file
+ *   (o) mime - suggested content type
+ *   (o) data - payload base64 encoded
+ */
+static void
+sharkd_session_process_download(char *buf, const jsmntok_t *tokens, int count)
+{
+       const char *tok_token      = json_find_attr(buf, tokens, count, "token");
+
+       if (!tok_token)
+               return;
+
+       if (!strncmp(tok_token, "eo:", 3))
+       {
+               struct sharkd_export_object_list *object_list;
+               const export_object_entry_t *eo_entry = NULL;
+
+               for (object_list = sharkd_eo_list; object_list; object_list = object_list->next)
+               {
+                       size_t eo_type_len = strlen(object_list->type);
+
+                       if (!strncmp(tok_token, object_list->type, eo_type_len) && tok_token[eo_type_len] == '_')
+                       {
+                               int row;
+
+                               sscanf(&tok_token[eo_type_len + 1], "%d", &row);
+
+                               eo_entry = (export_object_entry_t *) g_slist_nth_data(object_list->entries, row);
+                               break;
+                       }
+               }
+
+               if (eo_entry)
+               {
+                       const char *mime     = (eo_entry->content_type) ? eo_entry->content_type : "application/octet-stream";
+                       const char *filename = (eo_entry->filename) ? eo_entry->filename : tok_token;
+
+                       printf("{\"file\":");
+                       json_puts_string(filename);
+                       printf(",\"mime\":");
+                       json_puts_string(mime);
+                       printf(",\"data\":");
+                       json_print_base64(eo_entry->payload_data, (int) eo_entry->payload_len); /* XXX, export object will be truncated if >= 2^31 */
+                       printf("}\n");
+               }
+       }
+}
+
 static void
 sharkd_session_process(char *buf, const jsmntok_t *tokens, int count)
 {
@@ -2023,6 +2430,8 @@ sharkd_session_process(char *buf, const jsmntok_t *tokens, int count)
                        sharkd_session_process_frames(buf, tokens, count);
                else if (!strcmp(tok_req, "tap"))
                        sharkd_session_process_tap(buf, tokens, count);
+               else if (!strcmp(tok_req, "follow"))
+                       sharkd_session_process_follow(buf, tokens, count);
                else if (!strcmp(tok_req, "intervals"))
                        sharkd_session_process_intervals(buf, tokens, count);
                else if (!strcmp(tok_req, "frame"))
@@ -2031,6 +2440,8 @@ sharkd_session_process(char *buf, const jsmntok_t *tokens, int count)
                        sharkd_session_process_setconf(buf, tokens, count);
                else if (!strcmp(tok_req, "dumpconf"))
                        sharkd_session_process_dumpconf(buf, tokens, count);
+               else if (!strcmp(tok_req, "download"))
+                       sharkd_session_process_download(buf, tokens, count);
                else if (!strcmp(tok_req, "bye"))
                        exit(0);
                else