checkAPIs.pl: support for new-style dissectors in check_hf_entries
[metze/wireshark/wip.git] / epan / follow.c
index f16cf9ddfc02a656d8da0b2fcd44dc91588705cd..cf3d12cb6dd648f50e0066fd2abab1c5f7c07345 100644 (file)
  * By Gerald Combs <gerald@wireshark.org>
  * Copyright 1998 Gerald Combs
  *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * SPDX-License-Identifier: GPL-2.0-or-later
  *
  */
 
 #include "config.h"
 
 #include <stdlib.h>
-#include <stdio.h>
 #include <string.h>
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
 
 #include <glib.h>
 #include <epan/packet.h>
-#include <epan/to_str.h>
-#include <epan/emem.h>
-#include <epan/dissectors/packet-tcp.h>
 #include "follow.h"
-#include <epan/conversation.h>
-
-#define MAX_IPADDR_LEN  16
-
-typedef struct _tcp_frag {
-  guint32             seq;
-  guint32             len;
-  guint32             data_len;
-  gchar              *data;
-  struct _tcp_frag   *next;
-} tcp_frag;
-
-WS_DLL_PUBLIC_DEF
-FILE* data_out_file = NULL;
-
-gboolean empty_tcp_stream;
-gboolean incomplete_tcp_stream;
-
-static guint32 tcp_stream_to_follow = 0;
-static guint8  ip_address[2][MAX_IPADDR_LEN];
-static guint   port[2];
-static guint   bytes_written[2];
-static gboolean is_ipv6 = FALSE;
-
-static int check_fragments( int, tcp_stream_chunk *, guint32 );
-static void write_packet_data( int, tcp_stream_chunk *, const char * );
-
-void
-follow_stats(follow_stats_t* stats)
+#include <epan/tap.h>
+
+struct register_follow {
+    int proto_id;              /* protocol id (0-indexed) */
+    const char* tap_listen_str;      /* string used in register_tap_listener */
+    follow_conv_filter_func conv_filter;  /* generate "conversation" filter to follow */
+    follow_index_filter_func index_filter; /* generate stream/index filter to follow */
+    follow_address_filter_func address_filter; /* generate address filter to follow */
+    follow_port_to_display_func port_to_display; /* port to name resolution for follow type */
+    follow_tap_func tap_handler; /* tap listener handler */
+};
+
+static wmem_tree_t *registered_followers = NULL;
+
+void register_follow_stream(const int proto_id, const char* tap_listener,
+                            follow_conv_filter_func conv_filter, follow_index_filter_func index_filter, follow_address_filter_func address_filter,
+                            follow_port_to_display_func port_to_display, follow_tap_func tap_handler)
 {
-  int i;
-
-  for (i = 0; i < 2 ; i++) {
-    memcpy(stats->ip_address[i], ip_address[i], MAX_IPADDR_LEN);
-    stats->port[i] = port[i];
-    stats->bytes_written[i] = bytes_written[i];
-    stats->is_ipv6 = is_ipv6;
-  }
+  register_follow_t *follower;
+  DISSECTOR_ASSERT(tap_listener);
+  DISSECTOR_ASSERT(conv_filter);
+  DISSECTOR_ASSERT(index_filter);
+  DISSECTOR_ASSERT(address_filter);
+  DISSECTOR_ASSERT(port_to_display);
+  DISSECTOR_ASSERT(tap_handler);
+
+  follower = wmem_new(wmem_epan_scope(), register_follow_t);
+
+  follower->proto_id       = proto_id;
+  follower->tap_listen_str = tap_listener;
+  follower->conv_filter    = conv_filter;
+  follower->index_filter   = index_filter;
+  follower->address_filter = address_filter;
+  follower->port_to_display = port_to_display;
+  follower->tap_handler    = tap_handler;
+
+  if (registered_followers == NULL)
+    registered_followers = wmem_tree_new(wmem_epan_scope());
+
+  wmem_tree_insert_string(registered_followers, proto_get_protocol_short_name(find_protocol_by_id(proto_id)), follower, 0);
 }
 
-/* This will build a display filter text that will only
-   pass the packets related to the stream. There is a
-   chance that two streams could intersect, but not a
-   very good one */
-gchar*
-build_follow_conv_filter( packet_info *pi ) {
-  char* buf;
-  int len;
-  conversation_t *conv=NULL;
-  struct tcp_analysis *tcpd;
-  wmem_list_frame_t* protos;
-  int proto_id;
-  const char* proto_name;
-  gboolean is_tcp = FALSE, is_udp = FALSE;
-
-  protos = wmem_list_head(pi->layers);
-
-  /* walk the list of a available protocols in the packet to
-      figure out if any of them affect context sensitivity */
-  while (protos != NULL)
-  {
-    proto_id = GPOINTER_TO_INT(wmem_list_frame_data(protos));
-    proto_name = proto_get_protocol_filter_name(proto_id);
-
-    if (!strcmp(proto_name, "tcp")) {
-        is_tcp = TRUE;
-    } else if (!strcmp(proto_name, "udp")) {
-        is_udp = TRUE;
-    }
+int get_follow_proto_id(register_follow_t* follower)
+{
+  if (follower == NULL)
+    return -1;
 
-    protos = wmem_list_frame_next(protos);
-  }
-
-  if( ((pi->net_src.type == AT_IPv4 && pi->net_dst.type == AT_IPv4) ||
-       (pi->net_src.type == AT_IPv6 && pi->net_dst.type == AT_IPv6))
-       && is_tcp && (conv=find_conversation(pi->fd->num, &pi->src, &pi->dst, pi->ptype,
-              pi->srcport, pi->destport, 0)) != NULL ) {
-    /* TCP over IPv4 */
-    tcpd=get_tcp_conversation_data(conv, pi);
-    if (tcpd) {
-      buf = g_strdup_printf("tcp.stream eq %d", tcpd->stream);
-      tcp_stream_to_follow = tcpd->stream;
-      if (pi->net_src.type == AT_IPv4) {
-        len = 4;
-        is_ipv6 = FALSE;
-      } else {
-        len = 16;
-        is_ipv6 = TRUE;
-      }
-    } else {
-      return NULL;
-    }
-  }
-  else if( pi->net_src.type == AT_IPv4 && pi->net_dst.type == AT_IPv4
-          && is_udp ) {
-  /* UDP over IPv4 */
-    buf = g_strdup_printf(
-            "(ip.addr eq %s and ip.addr eq %s) and (udp.port eq %d and udp.port eq %d)",
-            ip_to_str((const guint8 *)pi->net_src.data),
-            ip_to_str((const guint8 *)pi->net_dst.data),
-            pi->srcport, pi->destport );
-    len = 4;
-    is_ipv6 = FALSE;
-  }
-  else if( pi->net_src.type == AT_IPv6 && pi->net_dst.type == AT_IPv6
-       && is_udp ) {
-    /* UDP over IPv6 */
-    buf = g_strdup_printf(
-            "(ipv6.addr eq %s and ipv6.addr eq %s) and (udp.port eq %d and udp.port eq %d)",
-            ip6_to_str((const struct e_in6_addr *)pi->net_src.data),
-            ip6_to_str((const struct e_in6_addr *)pi->net_dst.data),
-            pi->srcport, pi->destport );
-    len = 16;
-    is_ipv6 = TRUE;
-  }
-  else {
-    return NULL;
-  }
-  memcpy(ip_address[0], pi->net_src.data, len);
-  memcpy(ip_address[1], pi->net_dst.data, len);
-  port[0] = pi->srcport;
-  port[1] = pi->destport;
-  return buf;
+  return follower->proto_id;
 }
 
-static gboolean         find_tcp_addr;
-static address          tcp_addr[2];
-static gboolean         find_tcp_index;
-
-gchar*
-build_follow_index_filter(void) {
-  gchar *buf;
+const char* get_follow_tap_string(register_follow_t* follower)
+{
+  if (follower == NULL)
+    return "";
 
-  find_tcp_addr = TRUE;
-  buf = g_strdup_printf("tcp.stream eq %d", tcp_stream_to_follow);
-  return buf;
+  return follower->tap_listen_str;
 }
 
-/* select a tcp stream to follow via it's address/port pairs */
-gboolean
-follow_tcp_addr(const address *addr0, guint port0,
-                const address *addr1, guint port1)
+follow_conv_filter_func get_follow_conv_func(register_follow_t* follower)
 {
-  if (addr0 == NULL || addr1 == NULL || addr0->type != addr1->type ||
-      port0 > G_MAXUINT16 || port1 > G_MAXUINT16 )  {
-    return FALSE;
-  }
-
-  if (find_tcp_index || find_tcp_addr) {
-    return FALSE;
-  }
-
-  switch (addr0->type) {
-  default:
-    return FALSE;
-  case AT_IPv4:
-  case AT_IPv6:
-    is_ipv6 = addr0->type == AT_IPv6;
-    break;
-  }
-
-  find_tcp_index = TRUE;
+  return follower->conv_filter;
+}
 
-  memcpy(ip_address[0], addr0->data, addr0->len);
-  SET_ADDRESS(&tcp_addr[0], addr0->type, addr0->len, ip_address[0]);
-  port[0] = port0;
+follow_index_filter_func get_follow_index_func(register_follow_t* follower)
+{
+  return follower->index_filter;
+}
 
-  memcpy(ip_address[1], addr1->data, addr1->len);
-  SET_ADDRESS(&tcp_addr[1], addr1->type, addr1->len, ip_address[1]);
-  port[1] = port1;
+follow_address_filter_func get_follow_address_func(register_follow_t* follower)
+{
+  return follower->address_filter;
+}
 
-  return TRUE;
+follow_port_to_display_func get_follow_port_to_display(register_follow_t* follower)
+{
+  return follower->port_to_display;
 }
 
-/* select a tcp stream to follow via its index */
-gboolean
-follow_tcp_index(guint32 indx)
+follow_tap_func get_follow_tap_handler(register_follow_t* follower)
 {
-  if (find_tcp_index || find_tcp_addr) {
-    return FALSE;
-  }
+  return follower->tap_handler;
+}
 
-  if (indx > get_tcp_stream_count()) {
-    return FALSE;
-  }
 
-  find_tcp_addr = TRUE;
-  tcp_stream_to_follow = indx;
-  memset(ip_address, 0, sizeof ip_address);
-  port[0] = port[1] = 0;
+register_follow_t* get_follow_by_name(const char* proto_short_name)
+{
+  return (register_follow_t*)wmem_tree_lookup_string(registered_followers, proto_short_name, 0);
+}
 
-  return TRUE;
+void follow_iterate_followers(wmem_foreach_func func, gpointer user_data)
+{
+    wmem_tree_foreach(registered_followers, func, user_data);
 }
 
-guint32
-get_follow_tcp_index(void) {
-  return tcp_stream_to_follow;
+gchar* follow_get_stat_tap_string(register_follow_t* follower)
+{
+    GString *cmd_str = g_string_new("follow,");
+    g_string_append(cmd_str, proto_get_protocol_filter_name(follower->proto_id));
+    return g_string_free(cmd_str, FALSE);
 }
 
 /* here we are going to try and reconstruct the data portion of a TCP
    session. We will try and handle duplicates, TCP fragments, and out
    of order packets in a smart way. */
-
-static tcp_frag *frags[2] = { 0, 0 };
-static guint32 seq[2];
-static guint8 src_addr[2][MAX_IPADDR_LEN];
-static guint src_port[2] = { 0, 0 };
+void
+follow_reset_stream(follow_info_t* info)
+{
+    info->bytes_written[0] = info->bytes_written[1] = 0;
+    info->client_port = 0;
+    info->server_port = 0;
+    info->client_ip.type = FT_NONE;
+    info->client_ip.len = 0;
+    info->server_ip.type = FT_NONE;
+    info->server_ip.len = 0;
+    info->fragments[0] = info->fragments[1] = NULL;
+    info->seq[0] = info->seq[1] = 0;
+}
 
 void
-reassemble_tcp( guint32 tcp_stream, guint32 sequence, guint32 acknowledgement,
-                guint32 length, const char* data, guint32 data_length,
-                int synflag, address *net_src, address *net_dst,
-                guint srcport, guint dstport, guint32 packet_num) {
-  guint8 srcx[MAX_IPADDR_LEN], dstx[MAX_IPADDR_LEN];
-  int src_index, j, first = 0, len;
-  guint32 newseq;
-  tcp_frag *tmp_frag;
-  tcp_stream_chunk sc;
-
-  src_index = -1;
-
-  /* First, check if this packet should be processed. */
-  if (find_tcp_index) {
-    if ((port[0] == srcport && port[1] == dstport &&
-         ADDRESSES_EQUAL(&tcp_addr[0], net_src) &&
-         ADDRESSES_EQUAL(&tcp_addr[1], net_dst))
-        ||
-        (port[1] == srcport && port[0] == dstport &&
-         ADDRESSES_EQUAL(&tcp_addr[1], net_src) &&
-         ADDRESSES_EQUAL(&tcp_addr[0], net_dst))) {
-      find_tcp_index = FALSE;
-      tcp_stream_to_follow = tcp_stream;
-    }
-    else {
-      return;
-    }
-  }
-  else if ( tcp_stream != tcp_stream_to_follow )
-    return;
-
-  if ((net_src->type != AT_IPv4 && net_src->type != AT_IPv6) ||
-      (net_dst->type != AT_IPv4 && net_dst->type != AT_IPv6))
-    return;
-
-  if (net_src->type == AT_IPv4)
-    len = 4;
-  else
-    len = 16;
-
-  memcpy(srcx, net_src->data, len);
-  memcpy(dstx, net_dst->data, len);
-
-  /* follow_tcp_index() needs to learn address/port pairs */
-  if (find_tcp_addr) {
-    find_tcp_addr = FALSE;
-    memcpy(ip_address[0], net_src->data, net_src->len);
-    port[0] = srcport;
-    memcpy(ip_address[1], net_dst->data, net_dst->len);
-    port[1] = dstport;
-    if (net_src->type == AT_IPv6 && net_dst->type == AT_IPv6) {
-      is_ipv6 = TRUE;
-    } else {
-      is_ipv6 = FALSE;
-    }
-  }
-
-  /* Check to see if we have seen this source IP and port before.
-     (Yes, we have to check both source IP and port; the connection
-     might be between two different ports on the same machine.) */
-  for( j=0; j<2; j++ ) {
-    if (memcmp(src_addr[j], srcx, len) == 0 && src_port[j] == srcport ) {
-      src_index = j;
-    }
-  }
-  /* we didn't find it if src_index == -1 */
-  if( src_index < 0 ) {
-    /* assign it to a src_index and get going */
-    for( j=0; j<2; j++ ) {
-      if( src_port[j] == 0 ) {
-        memcpy(src_addr[j], srcx, len);
-        src_port[j] = srcport;
-        src_index = j;
-        first = 1;
-        break;
-      }
-    }
-  }
-  if( src_index < 0 ) {
-    fprintf( stderr, "ERROR in reassemble_tcp: Too many addresses!\n");
-    return;
-  }
-
-  if( data_length < length ) {
-    incomplete_tcp_stream = TRUE;
-  }
-
-  /* Before adding data for this flow to the data_out_file, check whether
-   * this frame acks fragments that were already seen. This happens when
-   * frames are not in the capture file, but were actually seen by the
-   * receiving host (Fixes bug 592).
-   */
-  if( frags[1-src_index] ) {
-    memcpy(sc.src_addr, dstx, len);
-    sc.src_port = dstport;
-    sc.dlen     = 0;        /* Will be filled in in check_fragments */
-    while ( check_fragments( 1-src_index, &sc, acknowledgement ) )
-      ;
-  }
-
-  /* Initialize our stream chunk.  This data gets written to disk. */
-  memcpy(sc.src_addr, srcx, len);
-  sc.src_port   = srcport;
-  sc.dlen       = data_length;
-  sc.packet_num = packet_num;
-
-  /* now that we have filed away the srcs, lets get the sequence number stuff
-     figured out */
-  if( first ) {
-    /* this is the first time we have seen this src's sequence number */
-    seq[src_index] = sequence + length;
-    if( synflag ) {
-      seq[src_index]++;
-    }
-    /* write out the packet data */
-    write_packet_data( src_index, &sc, data );
-    return;
-  }
-  /* if we are here, we have already seen this src, let's
-     try and figure out if this packet is in the right place */
-  if( LT_SEQ(sequence, seq[src_index]) ) {
-    /* this sequence number seems dated, but
-       check the end to make sure it has no more
-       info than we have already seen */
-    newseq = sequence + length;
-    if( GT_SEQ(newseq, seq[src_index]) ) {
-      guint32 new_len;
-
-      /* this one has more than we have seen. let's get the
-         payload that we have not seen. */
-
-      new_len = seq[src_index] - sequence;
-
-      if ( data_length <= new_len ) {
-        data = NULL;
-        data_length = 0;
-        incomplete_tcp_stream = TRUE;
-      } else {
-        data += new_len;
-        data_length -= new_len;
-      }
-      sc.dlen = data_length;
-      sequence = seq[src_index];
-      length = newseq - seq[src_index];
-
-      /* this will now appear to be right on time :) */
-    }
-  }
-  if ( EQ_SEQ(sequence, seq[src_index]) ) {
-    /* right on time */
-    seq[src_index] += length;
-    if( synflag ) seq[src_index]++;
-    if( data ) {
-      write_packet_data( src_index, &sc, data );
-    }
-    /* done with the packet, see if it caused a fragment to fit */
-    while( check_fragments( src_index, &sc, 0 ) )
-      ;
-  }
-  else {
-    /* out of order packet */
-    if(data_length > 0 && GT_SEQ(sequence, seq[src_index]) ) {
-      tmp_frag = (tcp_frag *)g_malloc( sizeof( tcp_frag ) );
-      tmp_frag->data = (gchar *)g_malloc( data_length );
-      tmp_frag->seq = sequence;
-      tmp_frag->len = length;
-      tmp_frag->data_len = data_length;
-      memcpy( tmp_frag->data, data, data_length );
-      if( frags[src_index] ) {
-        tmp_frag->next = frags[src_index];
-      } else {
-        tmp_frag->next = NULL;
-      }
-      frags[src_index] = tmp_frag;
-    }
-  }
-} /* end reassemble_tcp */
-
-/* here we search through all the frag we have collected to see if
-   one fits */
-static int
-check_fragments( int idx, tcp_stream_chunk *sc, guint32 acknowledged ) {
-  tcp_frag *prev = NULL;
-  tcp_frag *current;
-  guint32 lowest_seq;
-  gchar *dummy_str;
-
-  current = frags[idx];
-  if( current ) {
-    lowest_seq = current->seq;
-    while( current ) {
-      if( GT_SEQ(lowest_seq, current->seq) ) {
-        lowest_seq = current->seq;
-      }
-
-      if( LT_SEQ(current->seq, seq[idx]) ) {
-        guint32 newseq;
-        /* this sequence number seems dated, but
-           check the end to make sure it has no more
-           info than we have already seen */
-        newseq = current->seq + current->len;
-        if( GT_SEQ(newseq, seq[idx]) ) {
-          guint32 new_pos;
-
-          /* this one has more than we have seen. let's get the
-             payload that we have not seen. This happens when
-             part of this frame has been retransmitted */
-
-          new_pos = seq[idx] - current->seq;
-
-          if ( current->data_len > new_pos ) {
-            sc->dlen = current->data_len - new_pos;
-            write_packet_data( idx, sc, current->data + new_pos );
-          }
-
-          seq[idx] += (current->len - new_pos);
-        }
+follow_info_free(follow_info_t* follow_info)
+{
+    GList *cur;
+    follow_record_t *follow_record;
 
-        /* Remove the fragment from the list as the "new" part of it
-         * has been processed or its data has been seen already in
-         * another packet. */
-        if( prev ) {
-          prev->next = current->next;
-        } else {
-          frags[idx] = current->next;
-        }
-        g_free( current->data );
-        g_free( current );
-        return 1;
-      }
-
-      if( EQ_SEQ(current->seq, seq[idx]) ) {
-        /* this fragment fits the stream */
-        if( current->data ) {
-          sc->dlen = current->data_len;
-          write_packet_data( idx, sc, current->data );
+    for (cur = follow_info->payload; cur; cur = g_list_next(cur)) {
+        if(cur->data) {
+            follow_record = (follow_record_t *)cur->data;
+            if(follow_record->data)
+                g_byte_array_free(follow_record->data, TRUE);
+
+            g_free(follow_record);
         }
-        seq[idx] += current->len;
-        if( prev ) {
-          prev->next = current->next;
-        } else {
-          frags[idx] = current->next;
+    }
+    g_list_free(follow_info->payload);
+
+    //Only TCP stream uses fragments
+    for (cur = follow_info->fragments[0]; cur; cur = g_list_next(cur)) {
+        follow_record = (follow_record_t *)cur->data;
+        if(follow_record->data) {
+            g_byte_array_free(follow_record->data, TRUE);
         }
-        g_free( current->data );
-        g_free( current );
-        return 1;
-      }
-      prev = current;
-      current = current->next;
+        g_free(follow_record);
     }
-    if( GT_SEQ(acknowledged, lowest_seq) ) {
-      /* There are frames missing in the capture file that were seen
-       * by the receiving host. Add dummy stream chunk with the data
-       * "[xxx bytes missing in capture file]".
-       */
-      dummy_str = g_strdup_printf("[%d bytes missing in capture file]",
-                        (int)(lowest_seq - seq[idx]) );
-      sc->dlen = (guint32) strlen(dummy_str);
-      write_packet_data( idx, sc, dummy_str );
-      g_free(dummy_str);
-      seq[idx] = lowest_seq;
-      return 1;
+    for (cur = follow_info->fragments[1]; cur; cur = g_list_next(cur)) {
+        follow_record = (follow_record_t *)cur->data;
+        if(follow_record->data) {
+            g_byte_array_free(follow_record->data, TRUE);
+        }
+        g_free(follow_record);
     }
-  }
-  return 0;
-}
 
-/* this should always be called before we start to reassemble a stream */
-void
-reset_tcp_reassembly(void)
-{
-  tcp_frag *current, *next;
-  int i;
-
-  empty_tcp_stream = TRUE;
-  incomplete_tcp_stream = FALSE;
-  find_tcp_addr = FALSE;
-  find_tcp_index = FALSE;
-  for( i=0; i<2; i++ ) {
-    seq[i] = 0;
-    memset(src_addr[i], '\0', MAX_IPADDR_LEN);
-    src_port[i] = 0;
-    memset(ip_address[i], '\0', MAX_IPADDR_LEN);
-    port[i] = 0;
-    bytes_written[i] = 0;
-    current = frags[i];
-    while( current ) {
-      next = current->next;
-      g_free( current->data );
-      g_free( current );
-      current = next;
-    }
-    frags[i] = NULL;
-  }
+    free_address(&follow_info->client_ip);
+    free_address(&follow_info->server_ip);
+    g_free(follow_info->filter_out_filter);
+    g_free(follow_info);
 }
 
-static void
-write_packet_data( int idx, tcp_stream_chunk *sc, const char *data )
+gboolean
+follow_tvb_tap_listener(void *tapdata, packet_info *pinfo,
+                      epan_dissect_t *edt _U_, const void *data)
 {
-  size_t ret;
+    follow_record_t *follow_record;
+    follow_info_t *follow_info = (follow_info_t *)tapdata;
+    tvbuff_t *next_tvb = (tvbuff_t *)data;
+
+    follow_record = g_new(follow_record_t,1);
+
+    follow_record->data = g_byte_array_sized_new(tvb_captured_length(next_tvb));
+    follow_record->data = g_byte_array_append(follow_record->data,
+                                              tvb_get_ptr(next_tvb, 0, -1),
+                                              tvb_captured_length(next_tvb));
+    follow_record->packet_num = pinfo->fd->num;
+
+    if (follow_info->client_port == 0) {
+        follow_info->client_port = pinfo->srcport;
+        copy_address(&follow_info->client_ip, &pinfo->src);
+        follow_info->server_port = pinfo->destport;
+        copy_address(&follow_info->server_ip, &pinfo->dst);
+    }
 
-  ret = fwrite( sc, 1, sizeof(tcp_stream_chunk), data_out_file );
-  DISSECTOR_ASSERT(sizeof(tcp_stream_chunk) == ret);
+    if (addresses_equal(&follow_info->client_ip, &pinfo->src) && follow_info->client_port == pinfo->srcport)
+        follow_record->is_server = FALSE;
+    else
+        follow_record->is_server = TRUE;
 
-  ret = fwrite( data, 1, sc->dlen, data_out_file );
-  DISSECTOR_ASSERT(sc->dlen == ret);
+    /* update stream counter */
+    follow_info->bytes_written[follow_record->is_server] += follow_record->data->len;
 
-  bytes_written[idx] += sc->dlen;
-  empty_tcp_stream = FALSE;
+    follow_info->payload = g_list_prepend(follow_info->payload, follow_record);
+    return FALSE;
 }
+
+/*
+ * Editor modelines  -  http://www.wireshark.org/tools/modelines.html
+ *
+ * Local Variables:
+ * c-basic-offset: 2
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * ex: set shiftwidth=2 tabstop=8 expandtab:
+ * :indentSize=2:tabSize=8:noTabs=true:
+ */