checkhf: handle C++-style comments
[metze/wireshark/wip.git] / sharkd_session.c
index 0b6bb96d4ce436a7386833649eaea1eb4083e731..dea261991a2051bce5a89f1bf50d97c52be9576e 100644 (file)
@@ -31,6 +31,7 @@
 #include <glib.h>
 
 #include <wsutil/wsjsmn.h>
+#include <wsutil/ws_printf.h>
 
 #include <file.h>
 #include <epan/exceptions.h>
 
 #include <epan/column.h>
 
+#include <ui/ssl_key_export.h>
+
 #include <epan/stats_tree_priv.h>
 #include <epan/stat_tap_ui.h>
 #include <epan/conversation_table.h>
+#include <epan/expert.h>
+#include <epan/export_object.h>
+#include <epan/follow.h>
+#include <epan/rtd_table.h>
+#include <epan/srt_table.h>
 
 #include <epan/dissectors/packet-h225.h>
 #include <epan/rtp_pt.h>
 #include <ui/tap-rtp-common.h>
 #include <epan/to_str.h>
 
+#include <epan/addr_resolv.h>
+#include <epan/dissectors/packet-rtp.h>
+
 #ifdef HAVE_GEOIP
 # include <GeoIP.h>
 # include <epan/geoip_db.h>
 # include <wsutil/pint.h>
 #endif
 
+#include <wsutil/glib-compat.h>
 #include <wsutil/strtoi.h>
 
 #include "sharkd.h"
 
-static struct register_ct *
-_get_conversation_table_by_name(const char *name)
-{
-       guint count = conversation_table_get_num();
-       guint i;
-
-       /* XXX, wow O(n^2), move to libwireshark */
-       for (i = 0; i < count; i++)
-       {
-               struct register_ct *table = get_conversation_table_by_num(i);
-               const char *label = proto_get_protocol_short_name(find_protocol_by_id(get_conversation_proto_id(table)));
-
-               if (!strcmp(label, name))
-                       return table;
-       }
-
-       return NULL;
-}
-
 static void
 json_unescape_str(char *input)
 {
@@ -155,13 +148,13 @@ json_puts_string(const char *str)
 }
 
 static void
-json_print_base64(const guint8 *data, int len)
+json_print_base64(const guint8 *data, size_t len)
 {
-       int i;
+       size_t i;
        int base64_state1 = 0;
        int base64_state2 = 0;
        gsize wrote;
-       gchar buf[(1 / 3 + 1) * 4 + 4];
+       gchar buf[(1 / 3 + 1) * 4 + 4 + 1];
 
        putchar('"');
 
@@ -169,12 +162,18 @@ json_print_base64(const guint8 *data, int len)
        {
                wrote = g_base64_encode_step(&data[i], 1, FALSE, buf, &base64_state1, &base64_state2);
                if (wrote > 0)
-                       fwrite(buf, 1, wrote, stdout);
+               {
+                       buf[wrote] = '\0';
+                       printf("%s", buf);
+               }
        }
 
        wrote = g_base64_encode_close(FALSE, buf, &base64_state1, &base64_state2);
        if (wrote > 0)
-               fwrite(buf, 1, wrote, stdout);
+       {
+               buf[wrote] = '\0';
+               printf("%s", buf);
+       }
 
        putchar('"');
 }
@@ -219,13 +218,73 @@ sharkd_session_filter_data(const char *filter)
        }
 }
 
-static void
-sharkd_session_process_info_conv_cb(gpointer data, gpointer user_data)
+struct sharkd_rtp_match
 {
-       struct register_ct *table = (struct register_ct *) data;
-       int *pi = (int *) user_data;
+       guint32 addr_src, addr_dst;
+       address src_addr;
+       address dst_addr;
+       guint16 src_port;
+       guint16 dst_port;
+       guint32 ssrc;
+};
+
+static gboolean
+sharkd_rtp_match_init(struct sharkd_rtp_match *req, const char *init_str)
+{
+       gboolean ret = FALSE;
+       char **arr;
+
+       arr = g_strsplit(init_str, "_", 7); /* pass larger value, so we'll catch incorrect input :) */
+       if (g_strv_length(arr) != 5)
+               goto fail;
+
+       /* TODO, for now only IPv4 */
+       if (!get_host_ipaddr(arr[0], &req->addr_src))
+               goto fail;
+
+       if (!ws_strtou16(arr[1], NULL, &req->src_port))
+               goto fail;
 
-       const char *label = proto_get_protocol_short_name(find_protocol_by_id(get_conversation_proto_id(table)));
+       if (!get_host_ipaddr(arr[2], &req->addr_dst))
+               goto fail;
+
+       if (!ws_strtou16(arr[3], NULL, &req->dst_port))
+               goto fail;
+
+       if (!ws_hexstrtou32(arr[4], NULL, &req->ssrc))
+               goto fail;
+
+       set_address(&req->src_addr, AT_IPv4, 4, &req->addr_src);
+       set_address(&req->dst_addr, AT_IPv4, 4, &req->addr_dst);
+       ret = TRUE;
+
+fail:
+       g_strfreev(arr);
+       return ret;
+}
+
+static gboolean
+sharkd_rtp_match_check(const struct sharkd_rtp_match *req, const packet_info *pinfo, const struct _rtp_info *rtp_info)
+{
+       if (rtp_info->info_sync_src == req->ssrc &&
+               pinfo->srcport == req->src_port &&
+               pinfo->destport == req->dst_port &&
+               addresses_equal(&pinfo->src, &req->src_addr) &&
+               addresses_equal(&pinfo->dst, &req->dst_addr))
+       {
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+static gboolean
+sharkd_session_process_info_conv_cb(const void* key, void* value, void* userdata)
+{
+       struct register_ct *table = (struct register_ct *) value;
+       int *pi = (int *) userdata;
+
+       const char *label = (const char*)key;
 
        if (get_conversation_packet_func(table))
        {
@@ -246,6 +305,83 @@ sharkd_session_process_info_conv_cb(gpointer data, gpointer user_data)
 
                *pi = *pi + 1;
        }
+       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_srt_visit_cb(const void *key _U_, void *value, void *user_data)
+{
+       register_srt_t *srt = (register_srt_t *) value;
+       int *pi = (int *) user_data;
+
+       const int proto_id = get_srt_proto_id(srt);
+       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\":\"Service Response Time/%s\"", label);
+               printf(",\"tap\":\"srt:%s\"", filter);
+       printf("}");
+
+       *pi = *pi + 1;
+       return FALSE;
+}
+
+static gboolean
+sharkd_rtd_visit_cb(const void *key _U_, void *value, void *user_data)
+{
+       register_rtd_t *rtd = (register_rtd_t *) value;
+       int *pi = (int *) user_data;
+
+       const int proto_id = get_rtd_proto_id(rtd);
+       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\":\"Response Time Delay/%s\"", label);
+               printf(",\"tap\":\"rtd:%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;
 }
 
 /**
@@ -266,10 +402,26 @@ sharkd_session_process_info_conv_cb(gpointer data, gpointer user_data)
  *                  '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 eo
+ *
+ *   (m) srt     - available service response time list, array of object with attributes:
+ *                  'name' - service response time name
+ *                  'tap'  - sharkd tap-name for srt
+ *
+ *   (m) rtd     - available response time delay list, array of object with attributes:
+ *                  'name' - response time delay name
+ *                  'tap'  - sharkd tap-name for rtd
+ *
  *   (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
  */
 static void
@@ -329,6 +481,30 @@ sharkd_session_process_info(void)
        printf("]");
 
        printf(",\"taps\":[");
+       {
+               printf("{\"name\":\"%s\",\"tap\":\"%s\"}", "RTP streams", "rtp-streams");
+               printf(",{\"name\":\"%s\",\"tap\":\"%s\"}", "Expert Information", "expert");
+       }
+       printf("]");
+
+       printf(",\"eo\":[");
+       i = 0;
+       eo_iterate_tables(sharkd_export_object_visit_cb, &i);
+       printf("]");
+
+       printf(",\"srt\":[");
+       i = 0;
+       srt_table_iterate_tables(sharkd_srt_visit_cb, &i);
+       printf("]");
+
+       printf(",\"rtd\":[");
+       i = 0;
+       rtd_table_iterate_tables(sharkd_rtd_visit_cb, &i);
+       printf("]");
+
+       printf(",\"follow\":[");
+       i = 0;
+       follow_iterate_followers(sharkd_follower_visit_cb, &i);
        printf("]");
 
        printf("}\n");
@@ -387,7 +563,7 @@ sharkd_session_process_load(const char *buf, const jsmntok_t *tokens, int count)
 static void
 sharkd_session_process_status(void)
 {
-       printf("{\"frames\":%d", cfile.count);
+       printf("{\"frames\":%u", cfile.count);
 
        printf("}\n");
 }
@@ -417,7 +593,7 @@ sharkd_session_process_analyse_cb(packet_info *pi, proto_tree *tree, struct epan
 
        if (pi->layers)
        {
-               wmem_list_frame_t *frame = wmem_list_head(pi->layers);
+               wmem_list_frame_t *frame;
 
                for (frame = wmem_list_head(pi->layers); frame; frame = wmem_list_frame_next(frame))
                {
@@ -457,7 +633,7 @@ sharkd_session_process_analyse(void)
        analyser.last_time  = NULL;
        analyser.protocols_set = g_hash_table_new(NULL /* g_direct_hash() */, NULL /* g_direct_equal */);
 
-       printf("{\"frames\":%d", cfile.count);
+       printf("{\"frames\":%u", cfile.count);
 
        printf(",\"protocols\":[");
        for (framenum = 1; framenum <= cfile.count; framenum++)
@@ -566,12 +742,12 @@ sharkd_session_process_tap_stats_node_cb(const stat_node *n)
        {
                /* code based on stats_tree_get_values_from_node() */
                printf("%s{\"name\":\"%s\"", sepa, node->name);
-               printf(",\"count\":%u", node->counter);
+               printf(",\"count\":%d", node->counter);
                if (node->counter && ((node->st_flags & ST_FLG_AVERAGE) || node->rng))
                {
                        printf(",\"avg\":%.2f", ((float)node->total) / node->counter);
-                       printf(",\"min\":%u", node->minvalue);
-                       printf(",\"max\":%u", node->maxvalue);
+                       printf(",\"min\":%d", node->minvalue);
+                       printf(",\"max\":%d", node->maxvalue);
                }
 
                if (node->st->elapsed)
@@ -627,7 +803,7 @@ sharkd_session_process_tap_stats_node_cb(const stat_node *n)
 static void
 sharkd_session_process_tap_stats_cb(void *psp)
 {
-       stats_tree *st = (stats_tree *)psp;
+       stats_tree *st = (stats_tree *) psp;
 
        printf("{\"tap\":\"stats:%s\",\"type\":\"stats\"", st->cfg->abbr);
 
@@ -636,6 +812,104 @@ sharkd_session_process_tap_stats_cb(void *psp)
        printf("},");
 }
 
+static void
+sharkd_session_free_tap_stats_cb(void *psp)
+{
+       stats_tree *st = (stats_tree *) psp;
+
+       stats_tree_free(st);
+}
+
+struct sharkd_expert_tap
+{
+       GSList *details;
+       GStringChunk *text;
+};
+
+/**
+ * sharkd_session_process_tap_expert_cb()
+ *
+ * Output expert tap:
+ *
+ *   (m) tap         - tap name
+ *   (m) type:expert - tap output type
+ *   (m) details     - array of object with attributes:
+ *                  (m) f - frame number, which generated expert information
+ *                  (o) s - severity
+ *                  (o) g - group
+ *                  (m) m - expert message
+ *                  (o) p - protocol
+ */
+static void
+sharkd_session_process_tap_expert_cb(void *tapdata)
+{
+       struct sharkd_expert_tap *etd = (struct sharkd_expert_tap *) tapdata;
+       GSList *list;
+       const char *sepa = "";
+
+       printf("{\"tap\":\"%s\",\"type\":\"%s\"", "expert", "expert");
+
+       printf(",\"details\":[");
+       for (list = etd->details; list; list = list->next)
+       {
+               expert_info_t *ei = (expert_info_t *) list->data;
+               const char *tmp;
+
+               printf("%s{", sepa);
+
+               printf("\"f\":%u,", ei->packet_num);
+
+               tmp = try_val_to_str(ei->severity, expert_severity_vals);
+               if (tmp)
+                       printf("\"s\":\"%s\",", tmp);
+
+               tmp = try_val_to_str(ei->group, expert_group_vals);
+               if (tmp)
+                       printf("\"g\":\"%s\",", tmp);
+
+               printf("\"m\":");
+               json_puts_string(ei->summary);
+               printf(",");
+
+               if (ei->protocol)
+               {
+                       printf("\"p\":");
+                       json_puts_string(ei->protocol);
+               }
+
+               printf("}");
+               sepa = ",";
+       }
+       printf("]");
+
+       printf("},");
+}
+
+static gboolean
+sharkd_session_packet_tap_expert_cb(void *tapdata, packet_info *pinfo _U_, epan_dissect_t *edt _U_, const void *pointer)
+{
+       struct sharkd_expert_tap *etd = (struct sharkd_expert_tap *) tapdata;
+       expert_info_t *ei             = (expert_info_t *) pointer;
+
+       ei = (expert_info_t *) g_memdup(ei, sizeof(*ei));
+       ei->protocol = g_string_chunk_insert_const(etd->text, ei->protocol);
+       ei->summary  = g_string_chunk_insert_const(etd->text, ei->summary);
+
+       etd->details = g_slist_prepend(etd->details, ei);
+
+       return TRUE;
+}
+
+static void
+sharkd_session_free_tap_expert_cb(void *tapdata)
+{
+       struct sharkd_expert_tap *etd = (struct sharkd_expert_tap *) tapdata;
+
+       g_slist_free_full(etd->details, g_free);
+       g_string_chunk_free(etd->text);
+       g_free(etd);
+}
+
 struct sharkd_conv_tap_data
 {
        const char *type;
@@ -655,7 +929,7 @@ sharkd_session_geoip_addr(address *addr, const char *suffix)
 #ifdef HAVE_GEOIP
        if (addr->type == AT_IPv4)
        {
-               uint32_t ip = pntoh32(addr->data);
+               guint32 ip = pntoh32(addr->data);
 
                guint num_dbs = geoip_db_num_dbs();
                guint dbnum;
@@ -707,7 +981,6 @@ sharkd_session_geoip_addr(address *addr, const char *suffix)
                        }
                }
        }
-#endif
 #ifdef HAVE_GEOIP_V6
        if (addr->type == AT_IPv6)
        {
@@ -763,11 +1036,194 @@ sharkd_session_geoip_addr(address *addr, const char *suffix)
                        }
                }
        }
-#endif
+#endif /* HAVE_GEOIP_V6 */
+#endif /* HAVE_GEOIP */
 
        return with_geoip;
 }
 
+struct sharkd_analyse_rtp_items
+{
+       guint32 frame_num;
+       guint32 sequence_num;
+
+       double delta;
+       double jitter;
+       double skew;
+       double bandwidth;
+       gboolean marker;
+
+       double arrive_offset;
+
+       /* from tap_rtp_stat_t */
+       guint32 flags;
+       guint16 pt;
+};
+
+struct sharkd_analyse_rtp
+{
+       const char *tap_name;
+       struct sharkd_rtp_match rtp;
+
+       GSList *packets;
+       double start_time;
+       tap_rtp_stat_t statinfo;
+};
+
+static void
+sharkd_session_process_tap_rtp_free_cb(void *tapdata)
+{
+       struct sharkd_analyse_rtp *rtp_req = (struct sharkd_analyse_rtp *) tapdata;
+
+       g_slist_free_full(rtp_req->packets, g_free);
+       g_free(rtp_req);
+}
+
+static gboolean
+sharkd_session_packet_tap_rtp_analyse_cb(void *tapdata, packet_info *pinfo, epan_dissect_t *edt _U_, const void *pointer)
+{
+       struct sharkd_analyse_rtp *rtp_req = (struct sharkd_analyse_rtp *) tapdata;
+       const struct _rtp_info *rtpinfo = (const struct _rtp_info *) pointer;
+
+       if (sharkd_rtp_match_check(&rtp_req->rtp, pinfo, rtpinfo))
+       {
+               tap_rtp_stat_t *statinfo = &(rtp_req->statinfo);
+               struct sharkd_analyse_rtp_items *item;
+
+               rtp_packet_analyse(statinfo, pinfo, rtpinfo);
+
+               item = (struct sharkd_analyse_rtp_items *) g_malloc(sizeof(struct sharkd_analyse_rtp_items));
+
+               if (!rtp_req->packets)
+                       rtp_req->start_time = nstime_to_sec(&pinfo->abs_ts);
+
+               item->frame_num    = pinfo->num;
+               item->sequence_num = rtpinfo->info_seq_num;
+               item->delta        = (statinfo->flags & STAT_FLAG_FIRST) ? 0.0 : statinfo->delta;
+               item->jitter       = (statinfo->flags & STAT_FLAG_FIRST) ? 0.0 : statinfo->jitter;
+               item->skew         = (statinfo->flags & STAT_FLAG_FIRST) ? 0.0 : statinfo->skew;
+               item->bandwidth    = statinfo->bandwidth;
+               item->marker       = rtpinfo->info_marker_set ? TRUE : FALSE;
+               item->arrive_offset= nstime_to_sec(&pinfo->abs_ts) - rtp_req->start_time;
+
+               item->flags = statinfo->flags;
+               item->pt    = statinfo->pt;
+
+               /* XXX, O(n) optimize */
+               rtp_req->packets = g_slist_append(rtp_req->packets, item);
+       }
+
+       return TRUE;
+}
+
+/**
+ * sharkd_session_process_tap_rtp_analyse_cb()
+ *
+ * Output rtp analyse tap:
+ *   (m) tap   - tap name
+ *   (m) type  - tap output type
+ *   (m) ssrc         - RTP SSRC
+ *   (m) max_delta    - Max delta (ms)
+ *   (m) max_delta_nr - Max delta packet #
+ *   (m) max_jitter   - Max jitter (ms)
+ *   (m) mean_jitter  - Mean jitter (ms)
+ *   (m) max_skew     - Max skew (ms)
+ *   (m) total_nr     - Total number of RTP packets
+ *   (m) seq_err      - Number of sequence errors
+ *   (m) duration     - Duration (ms)
+ *   (m) items      - array of object with attributes:
+ *                  (m) f    - frame number
+ *                  (m) o    - arrive offset
+ *                  (m) sn   - sequence number
+ *                  (m) d    - delta
+ *                  (m) j    - jitter
+ *                  (m) sk   - skew
+ *                  (m) bw   - bandwidth
+ *                  (o) s    - status string
+ *                  (o) t    - status type
+ *                  (o) mark - rtp mark
+ */
+static void
+sharkd_session_process_tap_rtp_analyse_cb(void *tapdata)
+{
+       const int RTP_TYPE_CN       = 1;
+       const int RTP_TYPE_ERROR    = 2;
+       const int RTP_TYPE_WARN     = 3;
+       const int RTP_TYPE_PT_EVENT = 4;
+
+       const struct sharkd_analyse_rtp *rtp_req = (struct sharkd_analyse_rtp *) tapdata;
+       const tap_rtp_stat_t *statinfo = &rtp_req->statinfo;
+
+       const char *sepa = "";
+       GSList *l;
+
+       printf("{\"tap\":\"%s\",\"type\":\"rtp-analyse\"", rtp_req->tap_name);
+
+       printf(",\"ssrc\":%u", rtp_req->rtp.ssrc);
+
+       printf(",\"max_delta\":%f", statinfo->max_delta);
+       printf(",\"max_delta_nr\":%u", statinfo->max_nr);
+       printf(",\"max_jitter\":%f", statinfo->max_jitter);
+       printf(",\"mean_jitter\":%f", statinfo->mean_jitter);
+       printf(",\"max_skew\":%f", statinfo->max_skew);
+       printf(",\"total_nr\":%u", statinfo->total_nr);
+       printf(",\"seq_err\":%u", statinfo->sequence);
+       printf(",\"duration\":%f", statinfo->time - statinfo->start_time);
+
+       printf(",\"items\":[");
+       for (l = rtp_req->packets; l; l = l->next)
+       {
+               struct sharkd_analyse_rtp_items *item = (struct sharkd_analyse_rtp_items *) l->data;
+
+               printf("%s{", sepa);
+
+               printf("\"f\":%u", item->frame_num);
+               printf(",\"o\":%.9f", item->arrive_offset);
+               printf(",\"sn\":%u", item->sequence_num);
+               printf(",\"d\":%.2f", item->delta);
+               printf(",\"j\":%.2f", item->jitter);
+               printf(",\"sk\":%.2f", item->skew);
+               printf(",\"bw\":%.2f", item->bandwidth);
+
+               if (item->pt == PT_CN)
+                       printf(",\"s\":\"%s\",\"t\":%d", "Comfort noise (PT=13, RFC 3389)", RTP_TYPE_CN);
+               else if (item->pt == PT_CN_OLD)
+                       printf(",\"s\":\"%s\",\"t\":%d", "Comfort noise (PT=19, reserved)", RTP_TYPE_CN);
+               else if (item->flags & STAT_FLAG_WRONG_SEQ)
+                       printf(",\"s\":\"%s\",\"t\":%d", "Wrong sequence number", RTP_TYPE_ERROR);
+               else if (item->flags & STAT_FLAG_DUP_PKT)
+                       printf(",\"s\":\"%s\",\"t\":%d", "Suspected duplicate (MAC address) only delta time calculated", RTP_TYPE_WARN);
+               else if (item->flags & STAT_FLAG_REG_PT_CHANGE)
+                       printf(",\"s\":\"Payload changed to PT=%u%s\",\"t\":%d",
+                               item->pt,
+                               (item->flags & STAT_FLAG_PT_T_EVENT) ? " telephone/event" : "",
+                               RTP_TYPE_WARN);
+               else if (item->flags & STAT_FLAG_WRONG_TIMESTAMP)
+                       printf(",\"s\":\"%s\",\"t\":%d", "Incorrect timestamp", RTP_TYPE_WARN);
+               else if ((item->flags & STAT_FLAG_PT_CHANGE)
+                       &&  !(item->flags & STAT_FLAG_FIRST)
+                       &&  !(item->flags & STAT_FLAG_PT_CN)
+                       &&  (item->flags & STAT_FLAG_FOLLOW_PT_CN)
+                       &&  !(item->flags & STAT_FLAG_MARKER))
+               {
+                       printf(",\"s\":\"%s\",\"t\":%d", "Marker missing?", RTP_TYPE_WARN);
+               }
+               else if (item->flags & STAT_FLAG_PT_T_EVENT)
+                       printf(",\"s\":\"PT=%u telephone/event\",\"t\":%d", item->pt, RTP_TYPE_PT_EVENT);
+               else if (item->flags & STAT_FLAG_MARKER)
+                       printf(",\"t\":%d", RTP_TYPE_WARN);
+
+               if (item->marker)
+                       printf(",\"mark\":1");
+
+               printf("}");
+               sepa = ",";
+       }
+       printf("]");
+
+       printf("},");
+}
+
 /**
  * sharkd_session_process_tap_conv_cb()
  *
@@ -886,39 +1342,445 @@ sharkd_session_process_tap_conv_cb(void *arg)
                        char *host_str, *port_str;
                        char *filter_str;
 
-                       printf("%s{", i ? "," : "");
+                       printf("%s{", i ? "," : "");
+
+                       printf("\"host\":\"%s\"", (host_str = get_conversation_address(NULL, &host->myaddress, iu->resolve_name)));
+
+                       if (proto_with_port)
+                       {
+                               printf(",\"port\":\"%s\"", (port_str = get_conversation_port(NULL, host->port, host->ptype, iu->resolve_port)));
+
+                               wmem_free(NULL, port_str);
+                       }
+
+                       printf(",\"rxf\":%" G_GUINT64_FORMAT, host->rx_frames);
+                       printf(",\"rxb\":%" G_GUINT64_FORMAT, host->rx_bytes);
+
+                       printf(",\"txf\":%" G_GUINT64_FORMAT, host->tx_frames);
+                       printf(",\"txb\":%" G_GUINT64_FORMAT, host->tx_bytes);
+
+                       filter_str = get_hostlist_filter(host);
+                       if (filter_str)
+                       {
+                               printf(",\"filter\":\"%s\"", filter_str);
+                               g_free(filter_str);
+                       }
+
+                       wmem_free(NULL, host_str);
+
+                       if (sharkd_session_geoip_addr(&(host->myaddress), ""))
+                               with_geoip = 1;
+                       printf("}");
+               }
+       }
+
+       printf("],\"proto\":\"%s\",\"geoip\":%s},", proto, with_geoip ? "true" : "false");
+}
+
+static void
+sharkd_session_free_tap_conv_cb(void *arg)
+{
+       conv_hash_t *hash = (conv_hash_t *) arg;
+       struct sharkd_conv_tap_data *iu = (struct sharkd_conv_tap_data *) hash->user_data;
+
+       if (!strncmp(iu->type, "conv:", 5))
+       {
+               reset_conversation_table_data(hash);
+       }
+       else if (!strncmp(iu->type, "endpt:", 6))
+       {
+               reset_hostlist_table_data(hash);
+       }
+
+       g_free(iu);
+}
+
+/**
+ * sharkd_session_process_tap_rtd_cb()
+ *
+ * Output rtd tap:
+ *   (m) tap        - tap name
+ *   (m) type       - tap output type
+ *   (m) stats - statistics rows - array object with attributes:
+ *                  (m) type - statistic name
+ *                  (m) num - number of messages
+ *                  (m) min - minimum SRT time
+ *                  (m) max - maximum SRT time
+ *                  (m) tot - total SRT time
+ *                  (m) min_frame - minimal SRT
+ *                  (m) max_frame - maximum SRT
+ *                  (o) open_req - Open Requests
+ *                  (o) disc_rsp - Discarded Responses
+ *                  (o) req_dup  - Duplicated Requests
+ *                  (o) rsp_dup  - Duplicated Responses
+ *   (o) open_req   - Open Requests
+ *   (o) disc_rsp   - Discarded Responses
+ *   (o) req_dup    - Duplicated Requests
+ *   (o) rsp_dup    - Duplicated Responses
+ */
+static void
+sharkd_session_process_tap_rtd_cb(void *arg)
+{
+       rtd_data_t *rtd_data = (rtd_data_t *) arg;
+       register_rtd_t *rtd  = (register_rtd_t *) rtd_data->user_data;
+
+       guint i, j;
+
+       const char *filter = proto_get_protocol_filter_name(get_rtd_proto_id(rtd));
+
+       /* XXX, some dissectors are having single table and multiple timestats (mgcp, megaco),
+        *      some multiple table and single timestat (radius, h225)
+        *      and it seems that value_string is used one for timestamp-ID, other one for table-ID
+        *      I wonder how it will gonna work with multiple timestats and multiple timestat...
+        * (for usage grep for: register_rtd_table)
+        */
+       const value_string *vs = get_rtd_value_string(rtd);
+       const char *sepa = "";
+
+       printf("{\"tap\":\"rtd:%s\",\"type\":\"rtd\"", filter);
+
+       if (rtd_data->stat_table.num_rtds == 1)
+       {
+               const rtd_timestat *ms = &rtd_data->stat_table.time_stats[0];
+
+               printf(",\"open_req\":%u", ms->open_req_num);
+               printf(",\"disc_rsp\":%u", ms->disc_rsp_num);
+               printf(",\"req_dup\":%u", ms->req_dup_num);
+               printf(",\"rsp_dup\":%u", ms->rsp_dup_num);
+       }
+
+       printf(",\"stats\":[");
+       for (i = 0; i < rtd_data->stat_table.num_rtds; i++)
+       {
+               const rtd_timestat *ms = &rtd_data->stat_table.time_stats[i];
+
+               for (j = 0; j < ms->num_timestat; j++)
+               {
+                       const char *type_str;
+
+                       if (ms->rtd[j].num == 0)
+                               continue;
+
+                       printf("%s{", sepa);
+
+                       if (rtd_data->stat_table.num_rtds == 1)
+                               type_str = val_to_str_const(j, vs, "Other"); /* 1 table - description per row */
+                       else
+                               type_str = val_to_str_const(i, vs, "Other"); /* multiple table - description per table */
+                       printf("\"type\":");
+                       json_puts_string(type_str);
+
+                       printf(",\"num\":%u", ms->rtd[j].num);
+                       printf(",\"min\":%.9f", nstime_to_sec(&(ms->rtd[j].min)));
+                       printf(",\"max\":%.9f", nstime_to_sec(&(ms->rtd[j].max)));
+                       printf(",\"tot\":%.9f", nstime_to_sec(&(ms->rtd[j].tot)));
+                       printf(",\"min_frame\":%u", ms->rtd[j].min_num);
+                       printf(",\"max_frame\":%u", ms->rtd[j].max_num);
+
+                       if (rtd_data->stat_table.num_rtds != 1)
+                       {
+                               /* like in tshark, display it on every row */
+                               printf(",\"open_req\":%u", ms->open_req_num);
+                               printf(",\"disc_rsp\":%u", ms->disc_rsp_num);
+                               printf(",\"req_dup\":%u", ms->req_dup_num);
+                               printf(",\"rsp_dup\":%u", ms->rsp_dup_num);
+                       }
+
+                       printf("}");
+                       sepa = ",";
+               }
+       }
+       printf("]},");
+}
+
+static void
+sharkd_session_free_tap_rtd_cb(void *arg)
+{
+       rtd_data_t *rtd_data = (rtd_data_t *) arg;
+
+       free_rtd_table(&rtd_data->stat_table, NULL, NULL);
+       g_free(rtd_data);
+}
+
+/**
+ * sharkd_session_process_tap_srt_cb()
+ *
+ * Output srt tap:
+ *   (m) tap        - tap name
+ *   (m) type       - tap output type
+ *
+ *   (m) tables - array of object with attributes:
+ *                  (m) n - table name
+ *                  (m) f - table filter
+ *                  (o) c - table column name
+ *                  (m) r - table rows - array object with attributes:
+ *                            (m) n   - row name
+ *                            (m) idx - procedure index
+ *                            (m) num - number of events
+ *                            (m) min - minimum SRT time
+ *                            (m) max - maximum SRT time
+ *                            (m) tot - total SRT time
+ */
+static void
+sharkd_session_process_tap_srt_cb(void *arg)
+{
+       srt_data_t *srt_data = (srt_data_t *) arg;
+       register_srt_t *srt = (register_srt_t *) srt_data->user_data;
+
+       const char *filter = proto_get_protocol_filter_name(get_srt_proto_id(srt));
+
+       guint i;
+
+       printf("{\"tap\":\"srt:%s\",\"type\":\"srt\"", filter);
+
+       printf(",\"tables\":[");
+       for (i = 0; i < srt_data->srt_array->len; i++)
+       {
+               /* SRT table */
+               srt_stat_table *rst = g_array_index(srt_data->srt_array, srt_stat_table *, i);
+               const char *sepa = "";
+
+               int j;
+
+               if (i)
+                       printf(",");
+               printf("{");
+
+               printf("\"n\":");
+               if (rst->name)
+                       json_puts_string(rst->name);
+               else if (rst->short_name)
+                       json_puts_string(rst->short_name);
+               else
+                       printf("\"table%u\"", i);
+
+               if (rst->filter_string)
+               {
+                       printf(",\"f\":");
+                       json_puts_string(rst->filter_string);
+               }
+
+               if (rst->proc_column_name)
+               {
+                       printf(",\"c\":");
+                       json_puts_string(rst->proc_column_name);
+               }
+
+               printf(",\"r\":[");
+               for (j = 0; j < rst->num_procs; j++)
+               {
+                       /* SRT row */
+                       srt_procedure_t *proc = &rst->procedures[j];
+
+                       if (proc->stats.num == 0)
+                               continue;
+
+                       printf("%s{", sepa);
+
+                       printf("\"n\":");
+                       json_puts_string(proc->procedure);
+
+                       if (rst->filter_string)
+                               printf(",\"idx\":%d", proc->proc_index);
+
+                       printf(",\"num\":%u", proc->stats.num);
+
+                       printf(",\"min\":%.9f", nstime_to_sec(&proc->stats.min));
+                       printf(",\"max\":%.9f", nstime_to_sec(&proc->stats.max));
+                       printf(",\"tot\":%.9f", nstime_to_sec(&proc->stats.tot));
+
+                       printf("}");
+                       sepa = ",";
+               }
+               printf("]}");
+       }
+
+       printf("]},");
+}
+
+static void
+sharkd_session_free_tap_srt_cb(void *arg)
+{
+       srt_data_t *srt_data = (srt_data_t *) arg;
+       register_srt_t *srt = (register_srt_t *) srt_data->user_data;
+
+       free_srt_table(srt, srt_data->srt_array, NULL, NULL);
+       g_array_free(srt_data->srt_array, TRUE);
+       g_free(srt_data);
+}
+
+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_GINT64_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()
+ *
+ * Output RTP streams tap:
+ *   (m) tap        - tap name
+ *   (m) type       - tap output type
+ *   (m) streams    - array of object with attributes:
+ *                  (m) ssrc        - RTP synchronization source identifier
+ *                  (m) payload     - stream payload
+ *                  (m) saddr       - source address
+ *                  (m) sport       - source port
+ *                  (m) daddr       - destination address
+ *                  (m) dport       - destination port
+ *                  (m) pkts        - packets count
+ *                  (m) max_delta   - max delta (ms)
+ *                  (m) max_jitter  - max jitter (ms)
+ *                  (m) mean_jitter - mean jitter (ms)
+ *                  (m) expectednr  -
+ *                  (m) totalnr     -
+ *                  (m) problem     - if analyser found the problem
+ *                  (m) ipver       - address IP version (4 or 6)
+ */
+static void
+sharkd_session_process_tap_rtp_cb(void *arg)
+{
+       rtpstream_tapinfo_t *rtp_tapinfo = (rtpstream_tapinfo_t *) arg;
+
+       GList *listx;
+       const char *sepa = "";
+
+       printf("{\"tap\":\"%s\",\"type\":\"%s\"", "rtp-streams", "rtp-streams");
+
+       printf(",\"streams\":[");
+       for (listx = g_list_first(rtp_tapinfo->strinfo_list); listx; listx = listx->next)
+       {
+               rtp_stream_info_t *streaminfo = (rtp_stream_info_t *) listx->data;
+
+               char *src_addr, *dst_addr;
+               char *payload;
+               guint32 expected;
 
-                       printf("\"host\":\"%s\"", (host_str = get_conversation_address(NULL, &host->myaddress, iu->resolve_name)));
+               src_addr = address_to_display(NULL, &(streaminfo->src_addr));
+               dst_addr = address_to_display(NULL, &(streaminfo->dest_addr));
 
-                       if (proto_with_port)
-                       {
-                               printf(",\"port\":\"%s\"", (port_str = get_conversation_port(NULL, host->port, host->ptype, iu->resolve_port)));
+               if (streaminfo->payload_type_name != NULL)
+                       payload = wmem_strdup(NULL, streaminfo->payload_type_name);
+               else
+                       payload = val_to_str_ext_wmem(NULL, streaminfo->payload_type, &rtp_payload_type_short_vals_ext, "Unknown (%u)");
 
-                               wmem_free(NULL, port_str);
-                       }
+               printf("%s{\"ssrc\":%u", sepa, streaminfo->ssrc);
+               printf(",\"payload\":\"%s\"", payload);
 
-                       printf(",\"rxf\":%" G_GUINT64_FORMAT, host->rx_frames);
-                       printf(",\"rxb\":%" G_GUINT64_FORMAT, host->rx_bytes);
+               printf(",\"saddr\":\"%s\"", src_addr);
+               printf(",\"sport\":%u", streaminfo->src_port);
 
-                       printf(",\"txf\":%" G_GUINT64_FORMAT, host->tx_frames);
-                       printf(",\"txb\":%" G_GUINT64_FORMAT, host->tx_bytes);
+               printf(",\"daddr\":\"%s\"", dst_addr);
+               printf(",\"dport\":%u", streaminfo->dest_port);
 
-                       filter_str = get_hostlist_filter(host);
-                       if (filter_str)
-                       {
-                               printf(",\"filter\":\"%s\"", filter_str);
-                               g_free(filter_str);
-                       }
+               printf(",\"pkts\":%u", streaminfo->packet_count);
 
-                       wmem_free(NULL, host_str);
+               printf(",\"max_delta\":%f", streaminfo->rtp_stats.max_delta);
+               printf(",\"max_jitter\":%f", streaminfo->rtp_stats.max_jitter);
+               printf(",\"mean_jitter\":%f", streaminfo->rtp_stats.mean_jitter);
 
-                       if (sharkd_session_geoip_addr(&(host->myaddress), ""))
-                               with_geoip = 1;
-                       printf("}");
-               }
-       }
+               expected = (streaminfo->rtp_stats.stop_seq_nr + streaminfo->rtp_stats.cycles * 65536) - streaminfo->rtp_stats.start_seq_nr + 1;
+               printf(",\"expectednr\":%u", expected);
+               printf(",\"totalnr\":%u", streaminfo->rtp_stats.total_nr);
 
-       printf("],\"proto\":\"%s\",\"geoip\":%s},", proto, with_geoip ? "true" : "false");
+               printf(",\"problem\":%s", streaminfo->problem ? "true" : "false");
+
+               /* for filter */
+               printf(",\"ipver\":%d", (streaminfo->src_addr.type == AT_IPv6) ? 6 : 4);
+
+               wmem_free(NULL, src_addr);
+               wmem_free(NULL, dst_addr);
+               wmem_free(NULL, payload);
+
+               printf("}");
+               sepa = ",";
+       }
+       printf("]},");
 }
 
 /**
@@ -938,6 +1800,12 @@ sharkd_session_process_tap_conv_cb(void *arg)
  *                  for type:stats see sharkd_session_process_tap_stats_cb()
  *                  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:rtp-analyse see sharkd_session_process_tap_rtp_analyse_cb()
+ *                  for type:eo see sharkd_session_process_tap_eo_cb()
+ *                  for type:expert see sharkd_session_process_tap_expert_cb()
+ *                  for type:rtd see sharkd_session_process_tap_rtd_cb()
+ *                  for type:srt see sharkd_session_process_tap_srt_cb()
  *
  *   (m) err   - error code
  */
@@ -945,22 +1813,24 @@ static void
 sharkd_session_process_tap(char *buf, const jsmntok_t *tokens, int count)
 {
        void *taps_data[16];
+       GFreeFunc taps_free[16];
        int taps_count = 0;
        int i;
 
+       rtpstream_tapinfo_t rtp_tapinfo =
+               {NULL, NULL, NULL, NULL, 0, NULL, 0, TAP_ANALYSE, NULL, NULL, NULL, FALSE};
+
        for (i = 0; i < 16; i++)
        {
                char tapbuf[32];
                const char *tok_tap;
 
-               tap_packet_cb tap_func = NULL;
                void *tap_data = NULL;
+               GFreeFunc tap_free = NULL;
                const char *tap_filter = "";
                GString *tap_error = NULL;
 
-               taps_data[i] = NULL;
-
-               snprintf(tapbuf, sizeof(tapbuf), "tap%d", i);
+               ws_snprintf(tapbuf, sizeof(tapbuf), "tap%d", i);
                tok_tap = json_find_attr(buf, tokens, count, tapbuf);
                if (!tok_tap)
                        break;
@@ -980,20 +1850,34 @@ sharkd_session_process_tap(char *buf, const jsmntok_t *tokens, int count)
 
                        tap_error = register_tap_listener(st->cfg->tapname, st, st->filter, st->cfg->flags, stats_tree_reset, stats_tree_packet, sharkd_session_process_tap_stats_cb);
 
-                       tap_data = st;
-
                        if (!tap_error && cfg->init)
                                cfg->init(st);
+
+                       tap_data = st;
+                       tap_free = sharkd_session_free_tap_stats_cb;
+               }
+               else if (!strcmp(tok_tap, "expert"))
+               {
+                       struct sharkd_expert_tap *expert_tap;
+
+                       expert_tap = g_new0(struct sharkd_expert_tap, 1);
+                       expert_tap->text = g_string_chunk_new(100);
+
+                       tap_error = register_tap_listener("expert", expert_tap, NULL, 0, NULL, sharkd_session_packet_tap_expert_cb, sharkd_session_process_tap_expert_cb);
+
+                       tap_data = expert_tap;
+                       tap_free = sharkd_session_free_tap_expert_cb;
                }
                else if (!strncmp(tok_tap, "conv:", 5) || !strncmp(tok_tap, "endpt:", 6))
                {
                        struct register_ct *ct = NULL;
                        const char *ct_tapname;
                        struct sharkd_conv_tap_data *ct_data;
+                       tap_packet_cb tap_func = NULL;
 
                        if (!strncmp(tok_tap, "conv:", 5))
                        {
-                               ct = _get_conversation_table_by_name(tok_tap + 5);
+                               ct = get_conversation_by_proto_id(proto_get_id_by_short_name(tok_tap + 5));
 
                                if (!ct || !(tap_func = get_conversation_packet_func(ct)))
                                {
@@ -1003,11 +1887,11 @@ sharkd_session_process_tap(char *buf, const jsmntok_t *tokens, int count)
                        }
                        else if (!strncmp(tok_tap, "endpt:", 6))
                        {
-                               ct = _get_conversation_table_by_name(tok_tap + 6);
+                               ct = get_conversation_by_proto_id(proto_get_id_by_short_name(tok_tap + 6));
 
                                if (!ct || !(tap_func = get_hostlist_packet_func(ct)))
                                {
-                                       fprintf(stderr, "sharkd_session_process_tap() endpt %s not found\n", tok_tap + 5);
+                                       fprintf(stderr, "sharkd_session_process_tap() endpt %s not found\n", tok_tap + 6);
                                        continue;
                                }
                        }
@@ -1030,6 +1914,135 @@ sharkd_session_process_tap(char *buf, const jsmntok_t *tokens, int count)
                        tap_error = register_tap_listener(ct_tapname, &ct_data->hash, tap_filter, 0, NULL, tap_func, sharkd_session_process_tap_conv_cb);
 
                        tap_data = &ct_data->hash;
+                       tap_free = sharkd_session_free_tap_conv_cb;
+               }
+               else if (!strncmp(tok_tap, "rtd:", 4))
+               {
+                       register_rtd_t *rtd = get_rtd_table_by_name(tok_tap + 4);
+                       rtd_data_t *rtd_data;
+                       char *err;
+
+                       if (!rtd)
+                       {
+                               fprintf(stderr, "sharkd_session_process_tap() rtd=%s not found\n", tok_tap + 4);
+                               continue;
+                       }
+
+                       rtd_table_get_filter(rtd, "", &tap_filter, &err);
+                       if (err != NULL)
+                       {
+                               fprintf(stderr, "sharkd_session_process_tap() rtd=%s err=%s\n", tok_tap + 4, err);
+                               g_free(err);
+                               continue;
+                       }
+
+                       rtd_data = g_new0(rtd_data_t, 1);
+                       rtd_data->user_data = rtd;
+                       rtd_table_dissector_init(rtd, &rtd_data->stat_table, NULL, NULL);
+
+                       tap_error = register_tap_listener(get_rtd_tap_listener_name(rtd), rtd_data, tap_filter, 0, NULL, get_rtd_packet_func(rtd), sharkd_session_process_tap_rtd_cb);
+
+                       tap_data = rtd_data;
+                       tap_free = sharkd_session_free_tap_rtd_cb;
+               }
+               else if (!strncmp(tok_tap, "srt:", 4))
+               {
+                       register_srt_t *srt = get_srt_table_by_name(tok_tap + 4);
+                       srt_data_t *srt_data;
+                       char *err;
+
+                       if (!srt)
+                       {
+                               fprintf(stderr, "sharkd_session_process_tap() srt=%s not found\n", tok_tap + 4);
+                               continue;
+                       }
+
+                       srt_table_get_filter(srt, "", &tap_filter, &err);
+                       if (err != NULL)
+                       {
+                               fprintf(stderr, "sharkd_session_process_tap() srt=%s err=%s\n", tok_tap + 4, err);
+                               g_free(err);
+                               continue;
+                       }
+
+                       srt_data = g_new0(srt_data_t, 1);
+                       srt_data->srt_array = g_array_new(FALSE, TRUE, sizeof(srt_stat_table *));
+                       srt_data->user_data = srt;
+                       srt_table_dissector_init(srt, srt_data->srt_array, NULL, NULL);
+
+                       tap_error = register_tap_listener(get_srt_tap_listener_name(srt), srt_data, tap_filter, 0, NULL, get_srt_packet_func(srt), sharkd_session_process_tap_srt_cb);
+
+                       tap_data = srt_data;
+                       tap_free = sharkd_session_free_tap_srt_cb;
+               }
+               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;
+                       tap_free = g_free; /* need to free only eo_object, object_list need to be kept for potential download */
+               }
+               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);
+
+                       tap_data = &rtp_tapinfo;
+                       tap_free = rtpstream_reset_cb;
+               }
+               else if (!strncmp(tok_tap, "rtp-analyse:", 12))
+               {
+                       struct sharkd_analyse_rtp *rtp_req;
+
+                       rtp_req = (struct sharkd_analyse_rtp *) g_malloc0(sizeof(*rtp_req));
+                       if (!sharkd_rtp_match_init(&rtp_req->rtp, tok_tap + 12))
+                       {
+                               g_free(rtp_req);
+                               continue;
+                       }
+
+                       rtp_req->tap_name = tok_tap;
+                       rtp_req->statinfo.first_packet = TRUE;
+                       rtp_req->statinfo.reg_pt = PT_UNDEFINED;
+
+                       tap_error = register_tap_listener("rtp", rtp_req, tap_filter, 0, NULL, sharkd_session_packet_tap_rtp_analyse_cb, sharkd_session_process_tap_rtp_analyse_cb);
+
+                       tap_data = rtp_req;
+                       tap_free = sharkd_session_process_tap_rtp_free_cb;
                }
                else
                {
@@ -1039,13 +2052,15 @@ sharkd_session_process_tap(char *buf, const jsmntok_t *tokens, int count)
 
                if (tap_error)
                {
-                       /* XXX, tap data memleaks */
                        fprintf(stderr, "sharkd_session_process_tap() name=%s error=%s", tok_tap, tap_error->str);
                        g_string_free(tap_error, TRUE);
+                       if (tap_free)
+                               tap_free(tap_data);
                        continue;
                }
 
-               taps_data[i] = tap_data;
+               taps_data[taps_count] = tap_data;
+               taps_free[taps_count] = tap_free;
                taps_count++;
        }
 
@@ -1057,13 +2072,138 @@ sharkd_session_process_tap(char *buf, const jsmntok_t *tokens, int count)
        sharkd_retap();
        printf("null],\"err\":0}\n");
 
-       for (i = 0; i < 16; i++)
+       for (i = 0; i < taps_count; i++)
        {
                if (taps_data[i])
                        remove_tap_listener(taps_data[i]);
 
-               /* XXX, taps data memleaks */
+               if (taps_free[i])
+                       taps_free[i](taps_data[i]);
+       }
+}
+
+/**
+ * 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
@@ -1115,40 +2255,36 @@ sharkd_session_process_frame_cb_tree(proto_tree *tree, tvbuff_t **tvbs)
                }
 
                if (finfo->start >= 0 && finfo->length > 0)
-                       printf(",\"h\":[%u,%u]", finfo->start, finfo->length);
+                       printf(",\"h\":[%d,%d]", finfo->start, finfo->length);
 
                if (finfo->appendix_start >= 0 && finfo->appendix_length > 0)
-                       printf(",\"i\":[%u,%u]", finfo->appendix_start, finfo->appendix_length);
+                       printf(",\"i\":[%d,%d]", finfo->appendix_start, finfo->appendix_length);
 
-               if (finfo->hfinfo && finfo->hfinfo->type == FT_PROTOCOL)
-                       printf(",\"t\":\"proto\"");
 
-               if (FI_GET_FLAG(finfo, PI_SEVERITY_MASK))
+               if (finfo->hfinfo)
                {
-                       const char *severity = NULL;
-
-                       switch (FI_GET_FLAG(finfo, PI_SEVERITY_MASK))
+                       if (finfo->hfinfo->type == FT_PROTOCOL)
                        {
-                               case PI_COMMENT:
-                                       severity = "comment";
-                                       break;
-
-                               case PI_CHAT:
-                                       severity = "chat";
-                                       break;
+                               printf(",\"t\":\"proto\"");
+                       }
+                       else if (finfo->hfinfo->type == FT_FRAMENUM)
+                       {
+                               printf(",\"t\":\"framenum\",\"fnum\":%u", finfo->value.value.uinteger);
+                       }
+                       else if (FI_GET_FLAG(finfo, FI_URL) && IS_FT_STRING(finfo->hfinfo->type))
+                       {
+                               char *url = fvalue_to_string_repr(NULL, &finfo->value, FTREPR_DISPLAY, finfo->hfinfo->display);
 
-                               case PI_NOTE:
-                                       severity = "note";
-                                       break;
+                               printf(",\"t\":\"url\",\"url\":");
+                               json_puts_string(url);
+                               wmem_free(NULL, url);
+                       }
+               }
 
-                               case PI_WARN:
-                                       severity = "warn";
-                                       break;
+               if (FI_GET_FLAG(finfo, PI_SEVERITY_MASK))
+               {
+                       const char *severity = try_val_to_str(FI_GET_FLAG(finfo, PI_SEVERITY_MASK), expert_severity_vals);
 
-                               case PI_ERROR:
-                                       severity = "error";
-                                       break;
-                       }
                        g_assert(severity != NULL);
 
                        printf(",\"s\":\"%s\"", severity);
@@ -1167,6 +2303,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)
 {
@@ -1290,6 +2453,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");
 }
 
@@ -1326,7 +2493,7 @@ sharkd_session_process_intervals(char *buf, const jsmntok_t *tokens, int count)
        {
                unsigned int frames;
                guint64 bytes;
-       } stat, stat_total;
+       } st, st_total;
 
        nstime_t *start_ts = NULL;
 
@@ -1334,11 +2501,15 @@ sharkd_session_process_intervals(char *buf, const jsmntok_t *tokens, int count)
 
        const char *sepa = "";
        unsigned int framenum;
-       int idx;
-       int max_idx = 0;
+       gint64 idx;
+       gint64 max_idx = 0;
 
-       if (tok_interval)
-               (void) ws_strtou32(tok_interval, NULL, &interval_ms);
+       if (tok_interval) {
+               if (!ws_strtou32(tok_interval, NULL, &interval_ms) || interval_ms == 0) {
+                       fprintf(stderr, "Invalid interval parameter: %s.\n", tok_interval);
+                       return;
+               }
+       }
 
        if (tok_filter)
        {
@@ -1347,11 +2518,11 @@ sharkd_session_process_intervals(char *buf, const jsmntok_t *tokens, int count)
                        return;
        }
 
-       stat_total.frames = 0;
-       stat_total.bytes  = 0;
+       st_total.frames = 0;
+       st_total.bytes  = 0;
 
-       stat.frames = 0;
-       stat.bytes  = 0;
+       st.frames = 0;
+       st.bytes  = 0;
 
        idx = 0;
 
@@ -1360,8 +2531,8 @@ sharkd_session_process_intervals(char *buf, const jsmntok_t *tokens, int count)
        for (framenum = 1; framenum <= cfile.count; framenum++)
        {
                frame_data *fdata = frame_data_sequence_find(cfile.frames, framenum);
-               int msec_rel;
-               int new_idx;
+               gint64 msec_rel;
+               gint64 new_idx;
 
                if (start_ts == NULL)
                        start_ts = &fdata->abs_ts;
@@ -1369,15 +2540,14 @@ sharkd_session_process_intervals(char *buf, const jsmntok_t *tokens, int count)
                if (filter_data && !(filter_data[framenum / 8] & (1 << (framenum % 8))))
                        continue;
 
-               /* TODO, make it 64-bit, to avoid msec overflow after 24days */
-               msec_rel = ((fdata->abs_ts.secs - start_ts->secs) * 1000 + (fdata->abs_ts.nsecs - start_ts->nsecs) / 1000000);
+               msec_rel = (fdata->abs_ts.secs - start_ts->secs) * (gint64) 1000 + (fdata->abs_ts.nsecs - start_ts->nsecs) / 1000000;
                new_idx  = msec_rel / interval_ms;
 
                if (idx != new_idx)
                {
-                       if (stat.frames != 0)
+                       if (st.frames != 0)
                        {
-                               printf("%s[%d,%u,%" G_GUINT64_FORMAT "]", sepa, idx, stat.frames, stat.bytes);
+                               printf("%s[%" G_GINT64_FORMAT ",%u,%" G_GUINT64_FORMAT "]", sepa, idx, st.frames, st.bytes);
                                sepa = ",";
                        }
 
@@ -1385,24 +2555,24 @@ sharkd_session_process_intervals(char *buf, const jsmntok_t *tokens, int count)
                        if (idx > max_idx)
                                max_idx = idx;
 
-                       stat.frames = 0;
-                       stat.bytes  = 0;
+                       st.frames = 0;
+                       st.bytes  = 0;
                }
 
-               stat.frames += 1;
-               stat.bytes  += fdata->pkt_len;
+               st.frames += 1;
+               st.bytes  += fdata->pkt_len;
 
-               stat_total.frames += 1;
-               stat_total.bytes  += fdata->pkt_len;
+               st_total.frames += 1;
+               st_total.bytes  += fdata->pkt_len;
        }
 
-       if (stat.frames != 0)
+       if (st.frames != 0)
        {
-               printf("%s[%d,%u,%" G_GUINT64_FORMAT "]", sepa, idx, stat.frames, stat.bytes);
+               printf("%s[%" G_GINT64_FORMAT ",%u,%" G_GUINT64_FORMAT "]", sepa, idx, st.frames, st.bytes);
                /* sepa = ","; */
        }
 
-       printf("],\"last\":%d,\"frames\":%u,\"bytes\":%" G_GUINT64_FORMAT "}\n", max_idx, stat_total.frames, stat_total.bytes);
+       printf("],\"last\":%" G_GINT64_FORMAT ",\"frames\":%u,\"bytes\":%" G_GUINT64_FORMAT "}\n", max_idx, st_total.frames, st_total.bytes);
 }
 
 /**
@@ -1420,7 +2590,7 @@ sharkd_session_process_intervals(char *buf, const jsmntok_t *tokens, int count)
  *   (m) err   - 0 if succeed
  *   (o) tree  - array of frame nodes with attributes:
  *                  l - label
- *                  t: 'proto'
+ *                  t: 'proto', 'framenum', 'url' - type of node
  *                  s - severity
  *                  e - subtree ett index
  *                  n - array of subtree nodes
@@ -1428,10 +2598,15 @@ sharkd_session_process_intervals(char *buf, const jsmntok_t *tokens, int count)
  *                  i - two item array: (appendix start, appendix length)
  *                  p - [RESERVED] two item array: (protocol start, protocol length)
  *                  ds- data src index
+ *                  url  - only for t:'url', url
+ *                  fnum - only for t:'framenum', frame number
  *
  *   (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)
@@ -1689,7 +2864,7 @@ sharkd_session_process_setconf(char *buf, const jsmntok_t *tokens, int count)
        if (!tok_name || tok_name[0] == '\0' || !tok_value)
                return;
 
-       snprintf(pref, sizeof(pref), "%s:%s", tok_name, tok_value);
+       ws_snprintf(pref, sizeof(pref), "%s:%s", tok_name, tok_value);
 
        ret = prefs_set_pref(pref);
        printf("{\"err\":%d}\n", ret);
@@ -1707,8 +2882,73 @@ sharkd_session_process_dumpconf_cb(pref_t *pref, gpointer d)
        struct sharkd_session_process_dumpconf_data *data = (struct sharkd_session_process_dumpconf_data *) d;
        const char *pref_name = prefs_get_name(pref);
 
-       printf("%s\"%s.%s\":{}", data->sepa, data->module->name, pref_name);
+       printf("%s\"%s.%s\":{", data->sepa, data->module->name, pref_name);
+
+       switch (prefs_get_type(pref))
+       {
+               case PREF_UINT:
+               case PREF_DECODE_AS_UINT:
+                       printf("\"u\":%u", prefs_get_uint_value_real(pref, pref_current));
+                       if (prefs_get_uint_base(pref) != 10)
+                               printf(",\"ub\":%u", prefs_get_uint_base(pref));
+                       break;
+
+               case PREF_BOOL:
+                       printf("\"b\":%s", prefs_get_bool_value(pref, pref_current) ? "1" : "0");
+                       break;
+
+               case PREF_STRING:
+                       printf("\"s\":");
+                       json_puts_string(prefs_get_string_value(pref, pref_current));
+                       break;
+
+               case PREF_ENUM:
+               {
+                       const enum_val_t *enums;
+                       const char *enum_sepa = "";
+
+                       printf("\"e\":[");
+                       for (enums = prefs_get_enumvals(pref); enums->name; enums++)
+                       {
+                               printf("%s{\"v\":%d", enum_sepa, enums->value);
+
+                               if (enums->value == prefs_get_enum_value(pref, pref_current))
+                                       printf(",\"s\":1");
+
+                               printf(",\"d\":");
+                               json_puts_string(enums->description);
+
+                               printf("}");
+                               enum_sepa = ",";
+                       }
+                       printf("]");
+                       break;
+               }
+
+               case PREF_RANGE:
+               case PREF_DECODE_AS_RANGE:
+               {
+                       char *range_str = range_convert_range(NULL, prefs_get_range_value_real(pref, pref_current));
+                       printf("\"r\":\"%s\"", range_str);
+                       wmem_free(NULL, range_str);
+                       break;
+               }
+
+               case PREF_UAT:
+               case PREF_COLOR:
+               case PREF_CUSTOM:
+               case PREF_STATIC_TEXT:
+               case PREF_OBSOLETE:
+                       /* TODO */
+                       break;
+       }
+
+#if 0
+       printf(",\"t\":");
+       json_puts_string(prefs_get_title(pref));
+#endif
 
+       printf("}");
        data->sepa = ",";
 
        return 0; /* continue */
@@ -1803,6 +3043,83 @@ 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;
+
+                               if (sscanf(&tok_token[eo_type_len + 1], "%d", &row) != 1)
+                                       break;
+
+                               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");
+               }
+       }
+       else if (!strcmp(tok_token, "ssl-secrets"))
+       {
+               char *str = ssl_export_sessions();
+
+               if (str)
+               {
+                       const char *mime     = "text/plain";
+                       const char *filename = "keylog.txt";
+
+                       printf("{\"file\":");
+                       json_puts_string(filename);
+                       printf(",\"mime\":");
+                       json_puts_string(mime);
+                       printf(",\"data\":");
+                       json_print_base64(str, strlen(str));
+                       printf("}\n");
+               }
+               g_free(str);
+       }
+}
+
 static void
 sharkd_session_process(char *buf, const jsmntok_t *tokens, int count)
 {
@@ -1845,7 +3162,7 @@ sharkd_session_process(char *buf, const jsmntok_t *tokens, int count)
 
                if (!tok_req)
                {
-                       fprintf(stderr, "sanity check(4): no \"req\"!\n");
+                       fprintf(stderr, "sanity check(4): no \"req\".\n");
                        return;
                }
 
@@ -1865,6 +3182,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"))
@@ -1873,12 +3192,31 @@ 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);
+                       exit(0);
                else
                        fprintf(stderr, "::: req = %s\n", tok_req);
 
+               /* reply for every command are 0+ lines of JSON reply (outputed above), finished by empty new line */
                printf("\n");
+
+               /*
+                * We do an explicit fflush after every line, because
+                * we want output to be written to the socket as soon
+                * as the line is complete.
+                *
+                * The stream is fully-buffered by default, so it's
+                * only flushed when the buffer fills or the FILE *
+                * is closed.  On UN*X, we could set it to be line
+                * buffered, but the MSVC standard I/O routines don't
+                * support line buffering - they only support *byte*
+                * buffering, doing a write for every byte written,
+                * which is too inefficient, and full buffering,
+                * which is what you get if you request line buffering.
+                */
+               fflush(stdout);
        }
 }
 
@@ -1889,8 +3227,7 @@ sharkd_session_main(void)
        jsmntok_t *tokens = NULL;
        int tokens_max = -1;
 
-       fprintf(stderr, "Hello in child!\n");
-       setlinebuf(stdout);
+       fprintf(stderr, "Hello in child.\n");
 
        while (fgets(buf, sizeof(buf), stdin))
        {