Ethereal-dev: Re: [Ethereal-dev] external plugin and exported symbols

Note: This archive is from the project's previous web site, ethereal.com. This list is no longer active.

From: Paolo Abeni <00918190@xxxxxxxxx>
Date: Sat, 04 Feb 2006 16:34:19 +0100
On Fri, 2006-02-03 at 23:34 +0100, Ulf Lamping wrote: 
> Paolo Abeni wrote:
> > I have developed an external plugin to enable ssl decryption in
> > ethereal. 
> Do you use a "special" lib for this, or is this an implementation from 
> scratch?

I (partially) rewrote the standard ssl dissector, adding the require
code to decrypt the ssl application data. My plugin links against gnutls
and libgcrypt.

> > It works well under Linux, but I got some problems to compile
> > it under windows: It seams that a few symbols defined in the libethereal
> > API are not published in the libethereal.def export file.
> >
> > The relevant symbols are:
> >
> > p_rem_proto_data
> 
> Please note, that I've recently renamed this function, it's now called 
> p_remove_proto_data.

I'm using the ethereal 0.10.14 version. It contains the old symbol name.
I suppose that the change apply to the some more recente subversion
snapshot. 
  
> Could you send a patch?

I posted the patch previously and there was a bit of discussion about
it's inclusion. Anyway I attach the latest version of the patch

> Why not simply adding it to Ethereal itself instead of a plugin?

I would like to see the decryption functionality in ethereal, but so far
the patch does not get included. The plugin enable users to get
decryption functionality without recompiling ethereal.

Best Regard,

Paolo


Gruppo Telecom Italia - Direzione e coordinamento di Telecom Italia S.p.A.

====================================================================
CONFIDENTIALITY NOTICE
This message and its attachments are addressed solely to the persons
above and may contain confidential information. If you have received
the message in error, be informed that any use of the content hereof
is prohibited. Please return it immediately to the sender and delete
the message. Should you have any questions, please send an e_mail to 
MailAdmin@xxxxxxxxx. Thank you
====================================================================
diff -uNr ethereal-0.10.14/acinclude.m4 ethereal-0.10.14-patch/acinclude.m4
--- ethereal-0.10.14/acinclude.m4	2005-12-27 03:21:20.000000000 +0100
+++ ethereal-0.10.14-patch/acinclude.m4	2006-02-02 20:12:16.000000000 +0100
@@ -1268,3 +1268,161 @@
 	fi
 	AC_SUBST(KRB5_LIBS)
 ])
+
+dnl Autoconf macros for libgnutls
+dnl $id$
+
+# Modified for LIBGNUTLS -- nmav
+# Configure paths for LIBGCRYPT
+# Shamelessly stolen from the one of XDELTA by Owen Taylor
+# Werner Koch   99-12-09
+
+dnl AM_PATH_LIBGNUTLS([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND ]]])
+dnl Test for libgnutls, and define LIBGNUTLS_CFLAGS and LIBGNUTLS_LIBS
+dnl
+AC_DEFUN([AM_PATH_LIBGNUTLS],
+[dnl
+dnl Get the cflags and libraries from the libgnutls-config script
+dnl
+AC_ARG_WITH(libgnutls-prefix,
+          [  --with-libgnutls-prefix=PFX   Prefix where libgnutls is installed (optional)],
+          libgnutls_config_prefix="$withval", libgnutls_config_prefix="")
+
+  if test x$libgnutls_config_prefix != x ; then
+     if test x${LIBGNUTLS_CONFIG+set} != xset ; then
+        LIBGNUTLS_CONFIG=$libgnutls_config_prefix/bin/libgnutls-config
+     fi
+  fi
+
+  AC_PATH_PROG(LIBGNUTLS_CONFIG, libgnutls-config, no)
+  min_libgnutls_version=ifelse([$1], ,0.1.0,$1)
+  AC_MSG_CHECKING(for libgnutls - version >= $min_libgnutls_version)
+  no_libgnutls=""
+  if test "$LIBGNUTLS_CONFIG" = "no" ; then
+    no_libgnutls=yes
+  else
+    LIBGNUTLS_CFLAGS=`$LIBGNUTLS_CONFIG $libgnutls_config_args --cflags`
+    LIBGNUTLS_LIBS=`$LIBGNUTLS_CONFIG $libgnutls_config_args --libs`
+    libgnutls_config_version=`$LIBGNUTLS_CONFIG $libgnutls_config_args --version`
+
+      ac_save_CFLAGS="$CFLAGS"
+      ac_save_LIBS="$LIBS"
+      CFLAGS="$CFLAGS $LIBGNUTLS_CFLAGS"
+      LIBS="$LIBS $LIBGNUTLS_LIBS"
+dnl
+dnl Now check if the installed libgnutls is sufficiently new. Also sanity
+dnl checks the results of libgnutls-config to some extent
+dnl
+      rm -f conf.libgnutlstest
+      AC_TRY_RUN([
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <gnutls/gnutls.h>
+
+int
+main ()
+{
+    system ("touch conf.libgnutlstest");
+
+    if( strcmp( gnutls_check_version(NULL), "$libgnutls_config_version" ) )
+    {
+      printf("\n*** 'libgnutls-config --version' returned %s, but LIBGNUTLS (%s)\n",
+             "$libgnutls_config_version", gnutls_check_version(NULL) );
+      printf("*** was found! If libgnutls-config was correct, then it is best\n");
+      printf("*** to remove the old version of LIBGNUTLS. You may also be able to fix the error\n");
+      printf("*** by modifying your LD_LIBRARY_PATH enviroment variable, or by editing\n");
+      printf("*** /etc/ld.so.conf. Make sure you have run ldconfig if that is\n");
+      printf("*** required on your system.\n");
+      printf("*** If libgnutls-config was wrong, set the environment variable LIBGNUTLS_CONFIG\n");
+      printf("*** to point to the correct copy of libgnutls-config, and remove the file config.cache\n");
+      printf("*** before re-running configure\n");
+    }
+    else if ( strcmp(gnutls_check_version(NULL), LIBGNUTLS_VERSION ) )
+    {
+      printf("\n*** LIBGNUTLS header file (version %s) does not match\n", LIBGNUTLS_VERSION);
+      printf("*** library (version %s)\n", gnutls_check_version(NULL) );
+    }
+    else
+    {
+      if ( gnutls_check_version( "$min_libgnutls_version" ) )
+      {
+        return 0;
+      }
+     else
+      {
+        printf("no\n*** An old version of LIBGNUTLS (%s) was found.\n",
+                gnutls_check_version(NULL) );
+        printf("*** You need a version of LIBGNUTLS newer than %s. The latest version of\n",
+               "$min_libgnutls_version" );
+        printf("*** LIBGNUTLS is always available from ftp://gnutls.hellug.gr/pub/gnutls.\n";);
+        printf("*** \n");
+        printf("*** If you have already installed a sufficiently new version, this error\n");
+        printf("*** probably means that the wrong copy of the libgnutls-config shell script is\n");
+        printf("*** being found. The easiest way to fix this is to remove the old version\n");
+        printf("*** of LIBGNUTLS, but you can also set the LIBGNUTLS_CONFIG environment to point to the\n");
+        printf("*** correct copy of libgnutls-config. (In this case, you will have to\n");
+        printf("*** modify your LD_LIBRARY_PATH enviroment variable, or edit /etc/ld.so.conf\n");
+        printf("*** so that the correct libraries are found at run-time))\n");
+      }
+    }
+  return 1;
+}
+],, no_libgnutls=yes,[echo $ac_n "cross compiling; assumed OK... $ac_c"])
+       CFLAGS="$ac_save_CFLAGS"
+       LIBS="$ac_save_LIBS"
+  fi
+
+  if test "x$no_libgnutls" = x ; then
+     AC_MSG_RESULT(yes)
+     ifelse([$2], , :, [$2])
+  else
+     if test -f conf.libgnutlstest ; then
+        :
+     else
+        AC_MSG_RESULT(no)
+     fi
+     if test "$LIBGNUTLS_CONFIG" = "no" ; then
+       echo "*** The libgnutls-config script installed by LIBGNUTLS could not be found"
+       echo "*** If LIBGNUTLS was installed in PREFIX, make sure PREFIX/bin is in"
+       echo "*** your path, or set the LIBGNUTLS_CONFIG environment variable to the"
+       echo "*** full path to libgnutls-config."
+     else
+       if test -f conf.libgnutlstest ; then
+        :
+       else
+          echo "*** Could not run libgnutls test program, checking why..."
+          CFLAGS="$CFLAGS $LIBGNUTLS_CFLAGS"
+          LIBS="$LIBS $LIBGNUTLS_LIBS"
+          AC_TRY_LINK([
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <gnutls/gnutls.h>
+],      [ return !!gnutls_check_version(NULL); ],
+        [ echo "*** The test program compiled, but did not run. This usually means"
+          echo "*** that the run-time linker is not finding LIBGNUTLS or finding the wrong"
+          echo "*** version of LIBGNUTLS. If it is not finding LIBGNUTLS, you'll need to set your"
+          echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point"
+          echo "*** to the installed location  Also, make sure you have run ldconfig if that"
+          echo "*** is required on your system"
+          echo "***"
+          echo "*** If you have an old version installed, it is best to remove it, although"
+          echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH"
+          echo "***" ],
+        [ echo "*** The test program failed to compile or link. See the file config.log for the"
+          echo "*** exact error that occured. This usually means LIBGNUTLS was incorrectly installed"
+          echo "*** or that you have moved LIBGNUTLS since it was installed. In the latter case, you"
+          echo "*** may want to edit the libgnutls-config script: $LIBGNUTLS_CONFIG" ])
+          CFLAGS="$ac_save_CFLAGS"
+          LIBS="$ac_save_LIBS"
+       fi
+     fi
+     LIBGNUTLS_CFLAGS=""
+     LIBGNUTLS_LIBS=""
+     ifelse([$3], , :, [$3])
+  fi
+  rm -f conf.libgnutlstest
+  AC_SUBST(LIBGNUTLS_CFLAGS)
+  AC_SUBST(LIBGNUTLS_LIBS)
+])
diff -uNr ethereal-0.10.14/config.h.in ethereal-0.10.14-patch/config.h.in
--- ethereal-0.10.14/config.h.in	2005-12-27 18:22:12.000000000 +0100
+++ ethereal-0.10.14-patch/config.h.in	2006-02-02 20:12:16.000000000 +0100
@@ -55,6 +55,9 @@
 /* Define if krb5.h defines KEYTYPE_ARCFOUR_56 */
 #undef HAVE_KEYTYPE_ARCFOUR_56
 
+/* Define to use gnutls library */
+#undef HAVE_LIBGNUTLS
+
 /* Define to use libpcap library */
 #undef HAVE_LIBPCAP
 
diff -uNr ethereal-0.10.14/configure.in ethereal-0.10.14-patch/configure.in
--- ethereal-0.10.14/configure.in	2005-12-27 03:21:20.000000000 +0100
+++ ethereal-0.10.14-patch/configure.in	2006-02-02 20:12:16.000000000 +0100
@@ -63,6 +63,24 @@
 AC_CHECK_PROG(HAVE_DOXYGEN, doxygen, "yes", "no")
 AM_CONDITIONAL(HAVE_DOXYGEN, test x$HAVE_DOXYGEN = xyes)
 
+# gnu tls
+AM_PATH_LIBGNUTLS(1.0.0, 
+        [
+                echo "gnuTLS found, enabling ssl decryption"
+                AC_DEFINE(HAVE_LIBGNUTLS, 1, [Define to use gnutls library])
+                tls_message="yes"
+        ]
+        , [
+                if test x$libgnutls_config_prefix != x ; then
+	                AC_MSG_ERROR([[gnuTLS not found; install gnuTLS-devel package for your system]])
+                else
+                        echo echo "gnuTLS not found, disabling ssl decryption"
+                        tls_message="no"
+                fi
+        ]
+)        
+        
+
 # Check for xsltproc
 AC_PATH_PROG(XSLTPROC, xsltproc)
 AC_CHECK_PROG(HAVE_XSLTPROC, xsltproc, "yes", "no")
@@ -1348,3 +1366,4 @@
 echo "            Use SSL crypto library : $ssl_message"
 echo "          Use IPv6 name resolution : $enable_ipv6"
 echo "     Use UCD SNMP/Net-SNMP library : $snmp_libs_message"
+echo "                Use gnutls library : $tls_message"
diff -uNr ethereal-0.10.14/epan/dissectors/Makefile.common ethereal-0.10.14-patch/epan/dissectors/Makefile.common
--- ethereal-0.10.14/epan/dissectors/Makefile.common	2005-12-27 03:21:11.000000000 +0100
+++ ethereal-0.10.14-patch/epan/dissectors/Makefile.common	2006-02-02 20:12:16.000000000 +0100
@@ -558,6 +558,7 @@
 	packet-skinny.c	\
 	packet-slimp3.c	\
 	packet-sll.c	\
+	packet-ssl-utils.c	\
 	packet-slowprotocols.c	\
 	packet-slsk.c	\
 	packet-smb-browse.c	\
diff -uNr ethereal-0.10.14/epan/dissectors/packet-ssl.c ethereal-0.10.14-patch/epan/dissectors/packet-ssl.c
--- ethereal-0.10.14/epan/dissectors/packet-ssl.c	2005-12-27 03:21:14.000000000 +0100
+++ ethereal-0.10.14-patch/epan/dissectors/packet-ssl.c	2006-02-02 20:12:16.000000000 +0100
@@ -2,7 +2,7 @@
  * Routines for ssl dissection
  * Copyright (c) 2000-2001, Scott Renfro <scott@xxxxxxxxxx>
  *
- * $Id: packet-ssl.c 15218 2005-08-05 00:23:22Z jmayer $
+ * $Id: packet-ssl.c,v 1.13 2006/02/02 14:57:39 altoor Exp $
  *
  * Ethereal - Network traffic analyzer
  * By Gerald Combs <gerald@xxxxxxxxxxxx>
@@ -56,14 +56,7 @@
  *
  * Notes:
  *
- *   - Uses conversations in a no-malloc fashion.  Since we just want to
- *     remember the version of the conversation, we store the version
- *     integer directly in the void *data member of the conversation
- *     structure.  This means that we don't have to manage any memory,
- *     but will cause problems if anyone assumes that all data pointers
- *     are actually pointers to memory allocated by g_mem_chunk_alloc.
- *
- *   - Does not support decryption of encrypted frames, nor dissection
+ *   - Does not support dissection
  *     of frames that would require state maintained between frames
  *     (e.g., single ssl records spread across multiple tcp frames)
  *
@@ -82,6 +75,17 @@
  *       - Request Certificate
  *       - Client Certificate
  *
+ *    - Decryption is supported only for session that use RSA key exchange,
+ *      if the host private key is provided via preference. 
+ *    
+ *    - Decryption need to be performed 'sequentially', so it's done
+ *      at packet reception time. This may cause a significative packet capture
+ *      slow down. This also cause do dissect some ssl info that in previous
+ *      dissector version were dissected only when a proto_tree context was 
+ *      available
+ *
+ *     We are at Packet reception if time pinfo->fd->flags.visited == 0 
+ *
  */
 
 #ifdef HAVE_CONFIG_H
@@ -96,7 +100,12 @@
 
 #include <epan/conversation.h>
 #include <epan/prefs.h>
+#include <epan/inet_v6defs.h>
 #include <epan/dissectors/packet-x509af.h>
+#include <epan/emem.h>
+#include <epan/tap.h>
+#include "packet-ssl-utils.h"
+
 
 static gboolean ssl_desegment = TRUE;
 
@@ -108,12 +117,14 @@
  *********************************************************************/
 
 /* Initialize the protocol and registered fields */
+static int ssl_tap                           = -1;
 static int proto_ssl                         = -1;
 static int hf_ssl_record                     = -1;
 static int hf_ssl_record_content_type        = -1;
 static int hf_ssl_record_version             = -1;
 static int hf_ssl_record_length              = -1;
 static int hf_ssl_record_appdata             = -1;
+static int hf_ssl_record_appdata_decrypted   = -1;
 static int hf_ssl2_record                    = -1;
 static int hf_ssl2_record_is_escape          = -1;
 static int hf_ssl2_record_padding_length     = -1;
@@ -199,6 +210,321 @@
 static gint ett_pct_cert_suites		  = -1;
 static gint ett_pct_exch_suites		  = -1;
 
+typedef struct {
+    unsigned int ssl_port;
+    unsigned int decrypted_port;
+    dissector_handle_t handle;
+    char* info;
+} SslAssociation;
+
+#ifdef _WIN32
+#define TEST_DIR "\\Program Files\\Ethereal\\esp_data\\"
+#else
+#define TEST_DIR "/usr/share/ethereal-ssl-decrypt/"
+#endif
+static char* ssl_keys_list = "127.0.0.1:443:"TEST_DIR"server.key,"
+    "127.0.0.1:4433:"TEST_DIR"server.pem";
+static char* ssl_ports_list = NULL;
+
+typedef struct _SslService {
+    address addr;
+    guint port;
+} SslService;
+
+static GHashTable *ssl_session_hash = NULL;
+static GHashTable *ssl_key_hash = NULL;
+static GTree* ssl_associations = NULL;
+static dissector_handle_t ssl_handle = NULL;
+static StringInfo ssl_decrypted_data = {NULL, 0};
+
+/* Hash Functions for ssl sessions table and private keys table*/
+static gint  ssl_equal (gconstpointer v, gconstpointer v2)
+{
+    const StringInfo *val1 = (const StringInfo *)v;
+    const StringInfo *val2 = (const StringInfo *)v2;
+
+    if (val1->data_len == val2->data_len &&
+            !memcmp(val1->data, val2->data, val2->data_len)) {
+        return 1;
+    }
+    return 0;
+}
+
+static guint ssl_hash  (gconstpointer v)
+{    
+    guint l,hash = 0;
+    StringInfo* id = (StringInfo*) v;
+    guint* cur = (guint*) id->data;
+    for (l=4; (l<id->data_len); l+=4, cur++)
+        hash = hash ^ (*cur);
+        
+    return hash;
+}
+
+static gint  ssl_private_key_equal (gconstpointer v, gconstpointer v2)
+{
+    const SslService *val1 = (const SslService *)v;
+    const SslService *val2 = (const SslService *)v2;
+
+    if ((val1->port == val2->port) &&
+            ! CMP_ADDRESS(&val1->addr, &val2->addr)) {
+        return 1;
+    }
+    return 0;
+}
+
+static guint ssl_private_key_hash  (gconstpointer v)
+{    
+    const SslService *key = (const SslService *)v;
+    guint l,hash = key->port, len = key->addr.len;
+    
+    guint* cur = (guint*) key->addr.data;
+    for (l=4; (l<len); l+=4, cur++)
+        hash = hash ^ (*cur);
+        
+    return hash;
+}
+
+/* private key table entries have a scope 'larger' then packet capture,
+ * so we can't relay on se_alloc** function */
+static void ssl_private_key_free(gpointer id, gpointer key, gpointer dummy)
+{
+    g_free(id);
+    ssl_free_key((SSL_PRIVATE_KEY*) key);
+}
+
+/* handling of association between ssl ports and clear text protocol */
+static void ssl_association_add(unsigned int port, unsigned int ctport, 
+        const char* info)
+{
+    dissector_table_t tcp_dissectors = find_dissector_table( "tcp.port");
+    SslAssociation* assoc = g_malloc(sizeof(SslAssociation)+strlen(info)+1);
+
+    assoc->info = (char*) assoc+sizeof(SslAssociation);
+    strcpy(assoc->info, info);
+    assoc->ssl_port = port;
+    assoc->decrypted_port = ctport;
+    assoc->handle = dissector_get_port_handle(tcp_dissectors, ctport);
+    
+    ssl_debug_printf("ssl_association_add port %d ctport %d info %s handle %p\n",
+        port, ctport, info, assoc->handle);
+
+    dissector_add("tcp.port", port, ssl_handle);    
+    g_tree_insert(ssl_associations, (gpointer)port, assoc);
+}
+
+static gint ssl_association_cmp(gconstpointer a, gconstpointer b)
+{
+    return (gint)a-(gint)b;
+}
+
+static inline SslAssociation* ssl_association_find(unsigned int port)
+{
+    register SslAssociation* ret = g_tree_lookup(ssl_associations, (gpointer)port);
+    ssl_debug_printf("ssl_association_find: port %d found %p\n", port, ret);
+    return ret;
+}
+
+static gint ssl_association_remove_handle (gpointer key, 
+    gpointer  data, gpointer  user_data)
+{
+    SslAssociation* assoc = (SslAssociation*) data;
+    ssl_debug_printf("ssl_association_remove_handle removing ptr %p handle\n",
+        data, assoc->handle);
+    if (assoc->handle)
+        dissector_delete("tcp.port", assoc->ssl_port, assoc->handle);
+    g_free(data);
+    return 0;
+}
+
+static inline int ssl_packet_from_server(unsigned int port)
+{
+    register int ret = ssl_association_find(port) != 0;
+    ssl_debug_printf("ssl_packet_from_server: is from server %d\n", ret);    
+    return ret;
+}    
+
+/* initialize/reset per capture state data (ssl sessions cache) */
+static void ssl_init(void)
+{
+    if (ssl_session_hash)
+        g_hash_table_destroy(ssl_session_hash);
+    ssl_session_hash = g_hash_table_new(ssl_hash, ssl_equal);
+    if (ssl_decrypted_data.data)
+        g_free(ssl_decrypted_data.data);
+    ssl_decrypted_data.data = g_malloc0(32);
+    ssl_decrypted_data.data_len = 32;
+}
+
+/* parse ssl related preferences (private keys and ports association strings) */
+static void ssl_parse(void)
+{
+    if (ssl_key_hash)
+    {
+        g_hash_table_foreach(ssl_key_hash, ssl_private_key_free, NULL);
+        g_hash_table_destroy(ssl_key_hash);
+    }
+    if (ssl_associations)
+    {
+        g_tree_traverse(ssl_associations, ssl_association_remove_handle, G_IN_ORDER, NULL);
+        g_tree_destroy(ssl_associations);
+    }
+
+    /* parse private keys string, load available keys and put them in key hash*/
+    ssl_key_hash = g_hash_table_new(ssl_private_key_hash,ssl_private_key_equal);
+    ssl_associations = g_tree_new(ssl_association_cmp);
+    
+    if (ssl_keys_list && (ssl_keys_list[0] != 0)) 
+    {
+        char* end;
+        char* start = strdup(ssl_keys_list);
+        char* tmp = start;
+        
+        ssl_debug_printf("ssl_init keys string %s\n", start);
+        do {
+            char* addr, *port, *filename;
+            unsigned char* ip;
+            SslService* service;
+            SSL_PRIVATE_KEY * private_key;
+            FILE* fp;
+            
+            addr = start;
+            /* split ip/file couple with ',' separator*/
+            end = strchr(start, ',');
+            if (end) {
+                *end = 0;
+                start = end+1;
+            }
+            
+            /* for each entry split ip, port, filename with ':' separator */
+            ssl_debug_printf("ssl_init found host entry %s\n", addr);
+            port = strchr(addr, ':');
+            if (!port)
+                break;
+            *port = 0;
+            port++;
+            
+            filename = strchr(port,':');
+            if (!filename)
+                break;
+            *filename=0;
+            filename++;
+            
+            /* convert ip and port string to network rappresentation*/
+            service = g_malloc(sizeof(SslService) + 4);
+            service->addr.type = AT_IPv4;
+            service->addr.len = 4;
+            service->addr.data = ip = ((unsigned char*)service) + sizeof(SslService);
+            sscanf(addr, "%hhu.%hhu.%hhu.%hhu", &ip[0], &ip[1], &ip[2], &ip[3]);
+            service->port = atoi(port);
+            ssl_debug_printf("ssl_init addr %hhu.%hhu.%hhu.%hhu port %d filename %s\n", 
+                ip[0], ip[1], ip[2], ip[3], service->port, filename);
+    
+            /* try to load pen file*/
+            fp = fopen(filename, "rb");
+            if (!fp) {
+                fprintf(stderr, "can't open file %s \n",filename);
+                break;
+            }        
+            
+            private_key = ssl_load_key(fp);
+            if (!private_key) {
+                fprintf(stderr,"can't load private key from %s\n",
+                    filename);
+                break;
+            }
+            fclose(fp);
+            
+            ssl_debug_printf("ssl_init private key file %s successfully loaded\n", 
+                filename);
+            g_hash_table_insert(ssl_key_hash, service, private_key);
+               
+        } while (end != NULL);
+        free(tmp);
+    }
+
+    /* parse ssl ports string and add ssl dissector to specifed port[s]*/    
+    if (ssl_ports_list && (ssl_ports_list[0] != 0)) 
+    {
+        char* end;
+        char* start = strdup(ssl_ports_list);
+        char* tmp = start;
+        
+        ssl_debug_printf("ssl_init ports string %s\n", start);
+        do {
+            char* port, *ctport, *info;
+            unsigned int portn, ctportn;
+            
+            port = start;
+            /* split ip/file couple with ',' separator*/
+            end = strchr(start, ',');
+            if (end) {
+                *end = 0;
+                start = end+1;
+            }
+            
+            /* for each entry split ip, port, filename with ':' separator */
+            ssl_debug_printf("ssl_init found port entry %s\n", port);
+            ctport = strchr(port, ':');
+            if (!ctport)
+                break;
+            *ctport = 0;
+            ctport++;
+            
+            info = strchr(ctport,':');
+            if (!info)
+                break;
+            *info=0;
+            info++;
+            
+            /* add dissector to this port */
+            portn = atoi(port);
+            ctportn = atoi(ctport);
+            if (!portn || !ctportn)
+                break;
+            
+            ssl_debug_printf("ssl_init adding dissector to port %d (ct port %d)\n", portn, ctportn);
+            ssl_association_add(portn, ctportn, info);
+        } while (end != NULL);
+        free(tmp);
+    }
+
+    /* [re] add ssl dissection to defaults ports */
+    ssl_association_add(443, 80, "Hypertext transfer protocol");
+    ssl_association_add(636, 389, "Lightweight directory access protocol");
+    ssl_association_add(993, 143, "Interactive mail access protocol");
+    ssl_association_add(995, 110, "Post office protocol");    
+}
+
+/* store master secret into session data cache */
+static void ssl_save_session(SslDecryptSession* ssl)
+{
+    /* allocate stringinfo chunks for session id and master secret data*/
+    StringInfo* session_id = se_alloc0(sizeof(StringInfo) + ssl->session_id.data_len);
+    StringInfo* master_secret = se_alloc0(48 + sizeof(StringInfo));
+    
+    master_secret->data = ((unsigned char*)master_secret+sizeof(StringInfo));
+    session_id->data = ((unsigned char*)session_id+sizeof(StringInfo));
+    
+    ssl_data_set(session_id, ssl->session_id.data, ssl->session_id.data_len);
+    ssl_data_set(master_secret, ssl->master_secret.data, ssl->master_secret.data_len);
+    g_hash_table_insert(ssl_session_hash, session_id, master_secret);
+    ssl_print_string("ssl_save_session stored session id", session_id);
+    ssl_print_string("ssl_save_session stored master secret", master_secret);
+}
+
+static void ssl_restore_session(SslDecryptSession* ssl)
+{
+    StringInfo* ms = g_hash_table_lookup(ssl_session_hash, &ssl->session_id);
+    if (!ms) {
+        ssl_debug_printf("ssl_restore_session can't find stored session\n");
+        return;
+    }
+    ssl_data_set(&ssl->master_secret, ms->data, ms->data_len);
+    ssl->state |= SSL_MASTER_SECRET;    
+    ssl_debug_printf("ssl_restore_session master key retrived\n");
+}
+
 /* The TCP port to associate with by default */
 #define TCP_PORT_SSL                    443
 #define TCP_PORT_SSL_LDAP               636
@@ -704,7 +1030,8 @@
 static int dissect_ssl3_record(tvbuff_t *tvb, packet_info *pinfo,
                                proto_tree *tree, guint32 offset,
                                guint *conv_version,
-                               gboolean *need_desegmentation);
+                               gboolean *need_desegmentation,
+                               SslDecryptSession *conv_data);
 
 /* change cipher spec dissector */
 static void dissect_ssl3_change_cipher_spec(tvbuff_t *tvb,
@@ -721,16 +1048,19 @@
 static void dissect_ssl3_handshake(tvbuff_t *tvb, packet_info *pinfo,
                                    proto_tree *tree, guint32 offset,
                                    guint32 record_length,
-                                   guint *conv_version, guint8 content_type);
+                                   guint *conv_version,
+                                   SslDecryptSession *conv_data, guint8 content_type);
 
 
 static void dissect_ssl3_hnd_cli_hello(tvbuff_t *tvb,
                                        proto_tree *tree,
-                                       guint32 offset, guint32 length);
+                                       guint32 offset, guint32 length, 
+                                       SslDecryptSession* ssl);
 
 static void dissect_ssl3_hnd_srv_hello(tvbuff_t *tvb,
                                        proto_tree *tree,
-                                       guint32 offset, guint32 length);
+                                       guint32 offset, guint32 length, 
+                                       SslDecryptSession* ssl);
 
 static void dissect_ssl3_hnd_cert(tvbuff_t *tvb,
                                   proto_tree *tree, guint32 offset, packet_info *pinfo);
@@ -742,7 +1072,7 @@
 static void dissect_ssl3_hnd_finished(tvbuff_t *tvb,
                                       proto_tree *tree,
                                       guint32 offset,
-                                      guint *conv_version);
+                                      guint* conv_version);
 
 
 /*
@@ -754,12 +1084,14 @@
 static int dissect_ssl2_record(tvbuff_t *tvb, packet_info *pinfo,
                                proto_tree *tree, guint32 offset,
                                guint *conv_version,
-                               gboolean *need_desegmentation);
+                               gboolean *need_desegmentation,
+                               SslDecryptSession* ssl);
 
 /* client hello dissector */
 static void dissect_ssl2_hnd_client_hello(tvbuff_t *tvb,
                                           proto_tree *tree,
-                                          guint32 offset);
+                                          guint32 offset,
+                                          SslDecryptSession* ssl);
 
 static void dissect_pct_msg_client_hello(tvbuff_t *tvb,
                                           proto_tree *tree,
@@ -794,7 +1126,7 @@
  * Support Functions
  *
  */
-static void ssl_set_conv_version(packet_info *pinfo, guint version);
+/*static void ssl_set_conv_version(packet_info *pinfo, guint version);*/
 static int  ssl_is_valid_handshake_type(guint8 type);
 static int  ssl_is_valid_content_type(guint8 type);
 static int  ssl_is_valid_ssl_version(guint16 version);
@@ -809,7 +1141,6 @@
 static int  ssl_looks_like_valid_pct_handshake(tvbuff_t *tvb,
                                                guint32 offset,
                                                guint32 record_length);
-
 /*********************************************************************
  *
  * Main dissector
@@ -824,12 +1155,13 @@
 
     conversation_t *conversation;
     void *conv_data;
-    guint conv_version     = SSL_VER_UNKNOWN;
     proto_item *ti         = NULL;
     proto_tree *ssl_tree   = NULL;
     guint32 offset         = 0;
     gboolean first_record_in_frame = TRUE;
     gboolean need_desegmentation;
+    SslDecryptSession* ssl_session = NULL;
+    guint* conv_version;
 
     /* Track the version using conversations to reduce the
      * chance that a packet that simply *looks* like a v2 or
@@ -852,10 +1184,48 @@
                                         pinfo->srcport, pinfo->destport, 0);
     }
     conv_data = conversation_get_proto_data(conversation, proto_ssl);
+    
+    /* PAOLO: manage ssl decryption data */
+    /*get a valid ssl session pointer*/ 
     if (conv_data != NULL)
-    {
-        conv_version = GPOINTER_TO_UINT(conv_data);
-    }
+        ssl_session = conv_data;
+    else {
+        SslService dummy;
+
+        ssl_session = se_alloc0(sizeof(SslDecryptSession));
+        ssl_session_init(ssl_session);
+        ssl_session->version = SSL_VER_UNKNOWN;
+        conversation_add_proto_data(conversation, proto_ssl, ssl_session);
+            
+        /* we need to know witch side of conversation is speaking*/
+        if (ssl_packet_from_server(pinfo->srcport)) {
+            dummy.addr = pinfo->net_src;
+            dummy.port = pinfo->srcport;
+        }
+        else {
+            dummy.addr = pinfo->net_dst;
+            dummy.port = pinfo->destport;
+        }
+        ssl_debug_printf("dissect_ssl server %hhd.%hhd.%hhd.%hhd:%d\n", 
+            dummy.addr.data[0],
+            dummy.addr.data[1],dummy.addr.data[2],
+            dummy.addr.data[3],dummy.port);
+
+        /* try to retrive private key for this service. Do it now 'cause pinfo
+         * is not always available 
+         * Note that with HAVE_LIBGNUTLS undefined private_key is allways 0
+         * and thus decryption never engaged*/
+        ssl_session->private_key = g_hash_table_lookup(ssl_key_hash, &dummy);
+        if (!ssl_session->private_key) 
+            ssl_debug_printf("dissect_ssl can't find private key for this server!\n");
+    }
+    conv_version= & ssl_session->version;
+
+    /* try decryption only the first time we see this packet 
+     * (to keep cipher syncronized)and only if we have 
+     * the server private key*/
+    if (!ssl_session->private_key || pinfo->fd->flags.visited)
+        ssl_session = NULL;    
 
     /* Initialize the protocol column; we'll set it later when we
      * figure out what flavor of SSL it is (assuming we don't
@@ -910,12 +1280,13 @@
         /* first try to dispatch off the cached version
          * known to be associated with the conversation
          */
-        switch(conv_version) {
+        switch(*conv_version) {
         case SSL_VER_SSLv2:
         case SSL_VER_PCT:
             offset = dissect_ssl2_record(tvb, pinfo, ssl_tree,
-                                         offset, &conv_version,
-                                         &need_desegmentation);
+                                         offset, conv_version,
+                                         &need_desegmentation, 
+                                         ssl_session);
             break;
 
         case SSL_VER_SSLv3:
@@ -929,14 +1300,16 @@
             if (ssl_is_v2_client_hello(tvb, offset))
             {
                 offset = dissect_ssl2_record(tvb, pinfo, ssl_tree,
-                                             offset, &conv_version,
-                                             &need_desegmentation);
+                                             offset, conv_version,
+                                             &need_desegmentation,
+                                             ssl_session);
             }
             else
             {
                 offset = dissect_ssl3_record(tvb, pinfo, ssl_tree,
-                                             offset, &conv_version,
-                                             &need_desegmentation);
+                                             offset, conv_version,
+                                             &need_desegmentation,
+                                             ssl_session);
             }
             break;
 
@@ -948,15 +1321,17 @@
             {
                 /* looks like sslv2 or pct client hello */
                 offset = dissect_ssl2_record(tvb, pinfo, ssl_tree,
-                                             offset, &conv_version,
-                                             &need_desegmentation);
+                                             offset, conv_version,
+                                             &need_desegmentation, 
+                                             ssl_session);
             }
             else if (ssl_looks_like_sslv3(tvb, offset))
             {
                 /* looks like sslv3 or tls */
                 offset = dissect_ssl3_record(tvb, pinfo, ssl_tree,
-                                             offset, &conv_version,
-                                             &need_desegmentation);
+                                             offset, conv_version,
+                                             &need_desegmentation,
+                                             ssl_session);
             }
             else
             {
@@ -972,7 +1347,7 @@
                 if (check_col(pinfo->cinfo, COL_PROTOCOL))
                 {
                     col_set_str(pinfo->cinfo, COL_PROTOCOL,
-                         ssl_version_short_names[conv_version]);
+                         ssl_version_short_names[*conv_version]);
                 }
             }
             break;
@@ -981,22 +1356,96 @@
         /* Desegmentation return check */
         if (need_desegmentation)
           return;
-
-        /* If we haven't already set the version information for
-         * this conversation, do so. */
-        if (conv_data == NULL)
-        {
-            conv_data = GINT_TO_POINTER(conv_version);
-            conversation_add_proto_data(conversation, proto_ssl, conv_data);
-        }
-
         /* set up for next record in frame, if any */
         first_record_in_frame = FALSE;
     }
+    tap_queue_packet(ssl_tap, pinfo, (gpointer)proto_ssl);
+}
 
+static void 
+decrypt_ssl3_record(tvbuff_t *tvb, packet_info *pinfo, guint32 offset, 
+        guint32 record_length, guint8 content_type, SslDecryptSession* ssl,
+        gboolean save_plaintext)
+{
+    int len, direction;
+    SslDecoder* decoder;
+    
+    /* if we can decrypt and decryption have success
+    * add decrypted data to this packet info*/
+    ssl_debug_printf("decrypt_ssl3_record: app_data len %d ssl state %X\n", 
+        record_length, ssl->state);
+    if (!(ssl->state & SSL_HAVE_SESSION_KEY)) {
+        ssl_debug_printf("decrypt_ssl3_record: no session key\n");
+        return ;
+    }
+    
+    /* retrive decoder for this packet direction*/    
+    if ((direction = ssl_packet_from_server(pinfo->srcport)) != 0) {
+        ssl_debug_printf("decrypt_ssl3_record: using server decoder\n");
+        decoder = &ssl->server;
+    }
+    else { 
+        ssl_debug_printf("decrypt_ssl3_record: using client decoder\n");
+        decoder = &ssl->client;
+    }
+    
+    /* ensure we have enough storage space for decrypted data */
+    if (record_length > ssl_decrypted_data.data_len)
+    {
+        ssl_debug_printf("decrypt_ssl3_record: allocating %d bytes"
+                " for decrypt data (old len %d)\n", 
+                record_length + 32, ssl_decrypted_data.data_len);
+        ssl_decrypted_data.data = g_realloc(ssl_decrypted_data.data, 
+            record_length + 32);
+        ssl_decrypted_data.data_len = record_length + 32;
+    }
+    
+    /* run decryption and add decrypted payload to protocol data, if decryption 
+    * is successful*/
+    len = ssl_decrypted_data.data_len; 
+    if ((ssl_decrypt_record(ssl, decoder, 
+        content_type, tvb_get_ptr(tvb, offset, record_length),
+        record_length,  ssl_decrypted_data.data, &len) == 0) && 
+        save_plaintext)
+    {
+        StringInfo* data = p_get_proto_data(pinfo->fd, proto_ssl);
+        if (!data) 
+        {
+            ssl_debug_printf("decrypt_ssl3_record: allocating app_data %d "
+                "bytes for app data\n", len);
+            /* first app data record: allocate and put packet data*/
+            data = se_alloc(sizeof(StringInfo));
+            data->data = se_alloc(len);
+            data->data_len = len;
+            memcpy(data->data, ssl_decrypted_data.data, len);
+        }
+        else { 
+            unsigned char* store;
+            /* update previus record*/
+            ssl_debug_printf("decrypt_ssl3_record: reallocating app_data "
+                "%d bytes for app data (total %d appdata bytes)\n", 
+                len, data->data_len + len);
+            store = se_alloc(data->data_len + len);
+            memcpy(store, data->data, data->data_len);
+            memcpy(&store[data->data_len], ssl_decrypted_data.data, len);
+            data->data_len += len;
+            
+            /* old decrypted data ptr here appare to be leaked, but it's 
+             * collected by emem allocator */
+            data->data = store;
+            
+            /* data ptr is changed, so remove old one and re-add the new one*/
+            ssl_debug_printf("decrypt_ssl3_record: removing old app_data ptr\n");
+            p_rem_proto_data(pinfo->fd, proto_ssl);
+        }
+     
+        ssl_debug_printf("decrypt_ssl3_record: setting decrypted app_data ptr %p\n",data);
+        p_add_proto_data(pinfo->fd, proto_ssl, data);
+    }
 }
 
 
+
 /*********************************************************************
  *
  * SSL version 3 and TLS Dissection Routines
@@ -1005,7 +1454,8 @@
 static int
 dissect_ssl3_record(tvbuff_t *tvb, packet_info *pinfo,
                     proto_tree *tree, guint32 offset,
-                    guint *conv_version, gboolean *need_desegmentation)
+                    guint *conv_version, gboolean *need_desegmentation,
+                    SslDecryptSession* ssl)
 {
 
     /*
@@ -1033,6 +1483,8 @@
     proto_tree *ti              = NULL;
     proto_tree *ssl_record_tree = NULL;
     guint32 available_bytes     = 0;
+    StringInfo* decrypted;
+    SslAssociation* association;
 
     available_bytes = tvb_length_remaining(tvb, offset);
 
@@ -1152,12 +1604,21 @@
         if (version == 0x0300)
         {
             *conv_version = SSL_VER_SSLv3;
-            ssl_set_conv_version(pinfo, *conv_version);
+            if (ssl) {
+                ssl->version_netorder = version;
+                ssl->state |= SSL_VERSION;
+            }
+            /*ssl_set_conv_version(pinfo, ssl->version);*/
         }
         else if (version == 0x0301)
         {
+            
             *conv_version = SSL_VER_TLS;
-            ssl_set_conv_version(pinfo, *conv_version);
+            if (ssl) {
+                ssl->version_netorder = version;
+                ssl->state |= SSL_VERSION;
+            }
+            /*ssl_set_conv_version(pinfo, ssl->version);*/
         }
     }
     if (check_col(pinfo->cinfo, COL_PROTOCOL))
@@ -1182,6 +1643,11 @@
     /*
      * now dissect the next layer
      */
+    ssl_debug_printf("dissect_ssl3_record: content_type %d\n",content_type);
+    
+    /* PAOLO try to decrypt each record (we must keep ciphers "in sync") 
+     * store plain text only for app data */
+
     switch (content_type) {
     case SSL_ID_CHG_CIPHER_SPEC:
         if (check_col(pinfo->cinfo, COL_INFO))
@@ -1190,26 +1656,76 @@
                                         offset, conv_version, content_type);
         break;
     case SSL_ID_ALERT:
+        if (ssl)
+            decrypt_ssl3_record(tvb, pinfo, offset, 
+                record_length, content_type, ssl, FALSE);
         dissect_ssl3_alert(tvb, pinfo, ssl_record_tree, offset,
                            conv_version);
         break;
     case SSL_ID_HANDSHAKE:
+        if (ssl)
+            decrypt_ssl3_record(tvb, pinfo, offset, 
+                record_length, content_type, ssl, FALSE);
         dissect_ssl3_handshake(tvb, pinfo, ssl_record_tree, offset,
-                               record_length, conv_version, content_type);
+                               record_length, conv_version, ssl, content_type);
         break;
     case SSL_ID_APP_DATA:
+        if (ssl)
+            decrypt_ssl3_record(tvb, pinfo, offset, 
+                record_length, content_type, ssl, TRUE);
+        
+        /* show on info colum what we are decoding */
         if (check_col(pinfo->cinfo, COL_INFO))
             col_append_str(pinfo->cinfo, COL_INFO, "Application Data");
-        if (ssl_record_tree)
+                
+        if (!ssl_record_tree)
+            break;
+        
+        /* we need dissector information when the selected packet is shown.
+         * ssl session pointer is NULL at that time, so we can't access
+         * info cached there*/         
+        association = ssl_association_find(pinfo->srcport);
+        association = association ? association: ssl_association_find(pinfo->destport);
+
+        proto_item_set_text(ssl_record_tree,
+            "%s Record Layer: %s Protocol: %s",
+            ssl_version_short_names[*conv_version],
+            val_to_str(content_type, ssl_31_content_type, "unknown"),
+            association?association->info:"Application Data");
+     
+        /* show decrypted data info, if available */         
+        decrypted = p_get_proto_data(pinfo->fd, proto_ssl);
+        if (decrypted)
         {
-            proto_item_set_text(ssl_record_tree,
-                                "%s Record Layer: %s Protocol: Application Data",
-                                ssl_version_short_names[*conv_version],
-                                val_to_str(content_type, ssl_31_content_type, "unknown"));
+            tvbuff_t* new_tvb;
+            
+            /* try to dissect decrypted data*/
+            ssl_debug_printf("dissect_ssl3_record decrypted len %d\n", decrypted->data_len);
+            
+             /* create new tvbuff for the decrypted data */
+            new_tvb = tvb_new_real_data(decrypted->data, 
+                decrypted->data_len, decrypted->data_len);
+            tvb_set_free_cb(new_tvb, g_free);
+            //tvb_set_child_real_data_tvbuff(tvb, new_tvb);
+            
+            /* find out a dissector using server port*/
+            if (association && association->handle) {
+                ssl_debug_printf("dissect_ssl3_record found association %p\n", association);
+                ssl_print_text_data("decrypted app data",decrypted->data, 
+                    decrypted->data_len);
+                
+                call_dissector(association->handle, new_tvb, pinfo, ssl_record_tree);
+            }
+            /* add raw decrypted data only if a decoder is not found*/
+            else 
+                proto_tree_add_string(ssl_record_tree, hf_ssl_record_appdata_decrypted, tvb,
+                        offset, decrypted->data_len, (char*) decrypted->data);
+        }
+        else {
             tvb_ensure_bytes_exist(tvb, offset, record_length);
             proto_tree_add_item(ssl_record_tree, hf_ssl_record_appdata, tvb,
-                                offset, record_length, 0);
-        }
+                       offset, record_length, 0);
+        }     
         break;
 
     default:
@@ -1227,7 +1743,7 @@
 static void
 dissect_ssl3_change_cipher_spec(tvbuff_t *tvb,
                                 proto_tree *tree, guint32 offset,
-                                guint *conv_version, guint8 content_type)
+                                guint* conv_version, guint8 content_type)
 {
     /*
      * struct {
@@ -1250,7 +1766,7 @@
 static void
 dissect_ssl3_alert(tvbuff_t *tvb, packet_info *pinfo,
                    proto_tree *tree, guint32 offset,
-                   guint *conv_version)
+                   guint* conv_version)
 {
     /*     struct {
      *         AlertLevel level;
@@ -1294,7 +1810,7 @@
         if (check_col(pinfo->cinfo, COL_INFO))
             col_append_str(pinfo->cinfo, COL_INFO, "Encrypted Alert");
     }
-
+    
     if (tree)
     {
         if (level && desc)
@@ -1325,7 +1841,8 @@
 static void
 dissect_ssl3_handshake(tvbuff_t *tvb, packet_info *pinfo,
                        proto_tree *tree, guint32 offset,
-                       guint32 record_length, guint *conv_version, guint8 content_type)
+                       guint32 record_length, guint *conv_version,
+                       SslDecryptSession* ssl, guint8 content_type)
 {
     /*     struct {
      *         HandshakeType msg_type;
@@ -1367,6 +1884,8 @@
         msg_type_str = match_strval(msg_type, ssl_31_handshake_type);
         length   = tvb_get_ntoh24(tvb, offset + 1);
 
+        ssl_debug_printf("dissect_ssl3_handshake iteration %d type %d offset %d lenght %d "
+            "bytes, remaning %d \n", first_iteration, msg_type, offset, length, record_length);
         if (!msg_type_str && !first_iteration)
         {
             /* only dissect / report messages if they're
@@ -1425,17 +1944,18 @@
 
         /* if we don't have a valid handshake type, just quit dissecting */
         if (!msg_type_str)
-        {
             return;
-        }
-
-        if (ssl_hand_tree)
+                
+        /* PAOLO: if we are doing ssl decryption we must dissect some requests type */
+        if (ssl_hand_tree || ssl)
         {
             /* add nodes for the message type and message length */
-            proto_tree_add_item(ssl_hand_tree, hf_ssl_handshake_type,
-                                tvb, offset, 1, msg_type);
+            if (ssl_hand_tree)
+                proto_tree_add_item(ssl_hand_tree, hf_ssl_handshake_type,
+                                    tvb, offset, 1, msg_type);
             offset++;
-            proto_tree_add_uint(ssl_hand_tree, hf_ssl_handshake_length,
+            if (ssl_hand_tree)
+                proto_tree_add_uint(ssl_hand_tree, hf_ssl_handshake_length,
                                 tvb, offset, 3, length);
             offset += 3;
 
@@ -1446,11 +1966,11 @@
                 break;
 
             case SSL_HND_CLIENT_HELLO:
-                dissect_ssl3_hnd_cli_hello(tvb, ssl_hand_tree, offset, length);
+                dissect_ssl3_hnd_cli_hello(tvb, ssl_hand_tree, offset, length, ssl);
             break;
 
             case SSL_HND_SERVER_HELLO:
-                dissect_ssl3_hnd_srv_hello(tvb, ssl_hand_tree, offset, length);
+                dissect_ssl3_hnd_srv_hello(tvb, ssl_hand_tree, offset, length, ssl);
                 break;
 
             case SSL_HND_CERTIFICATE:
@@ -1473,8 +1993,51 @@
                 /* unimplemented */
                 break;
 
-            case SSL_HND_CLIENT_KEY_EXCHG:
-                /* unimplemented */
+            case SSL_HND_CLIENT_KEY_EXCHG: 
+                {
+                    /* PAOLO: here we can have all the data to build session key*/
+                    StringInfo encrypted_pre_master;
+                    int ret;
+    
+                    if (!ssl)
+                        break;
+                    
+                    /* check for required session data */
+                    ssl_debug_printf("dissect_ssl3_handshake found SSL_HND_CLIENT_KEY_EXCHG state %X\n",
+                        ssl->state);
+                    if ((ssl->state & (SSL_CIPHER|SSL_CLIENT_RANDOM|SSL_SERVER_RANDOM|SSL_VERSION)) !=
+                            (SSL_CIPHER|SSL_CLIENT_RANDOM|SSL_SERVER_RANDOM|SSL_VERSION)) {
+                        ssl_debug_printf("dissect_ssl3_handshake not enough data to generate key (required %X)\n",
+                            (SSL_CIPHER|SSL_CLIENT_RANDOM|SSL_SERVER_RANDOM|SSL_VERSION));
+                        break;
+                    }
+                                
+                    /* get encrypted data, we must skip tls record len && version and
+                     * 2 bytes of record data */
+                    encrypted_pre_master.data = se_alloc(length - 2);
+                    encrypted_pre_master.data_len = length-2;
+                    tvb_memcpy(tvb, encrypted_pre_master.data, offset+2, length-2);
+                    
+                    if (!ssl->private_key) {
+                        ssl_debug_printf("dissect_ssl3_handshake can't find private key\n");
+                        break;
+                    }
+                                
+                    /* go with ssl key processessing; encrypted_pre_master 
+                     * will be used for master secret store*/
+                    ret = ssl_decrypt_pre_master_secret(ssl, &encrypted_pre_master, ssl->private_key);
+                    if (ret < 0) {
+                        ssl_debug_printf("dissect_ssl3_handshake can't decrypt pre master secret\n");
+                        break;
+                    }
+                    if (ssl_generate_keyring_material(ssl)<0) {
+                        ssl_debug_printf("dissect_ssl3_handshake can't generate keyring material\n");
+                        break;
+                    }
+                    ssl->state |= SSL_HAVE_SESSION_KEY;
+                    ssl_save_session(ssl);
+                    ssl_debug_printf("dissect_ssl3_handshake session keys succesfully generated\n");
+                }
                 break;
 
             case SSL_HND_FINISHED:
@@ -1485,9 +2048,8 @@
 
         }
         else
-        {
-            offset += 4;        /* skip the handshake header */
-        }
+            offset += 4;        /* skip the handshake header when handshake is not processed*/
+
         offset += length;
         first_iteration = FALSE; /* set up for next pass, if any */
     }
@@ -1495,13 +2057,50 @@
 
 static int
 dissect_ssl3_hnd_hello_common(tvbuff_t *tvb, proto_tree *tree,
-                              guint32 offset)
+                              guint32 offset, SslDecryptSession* ssl, gint from_server)
 {
     /* show the client's random challenge */
-    guint32 initial_offset = offset;
     nstime_t gmt_unix_time;
     guint8  session_id_length = 0;
 
+    if (ssl) 
+    {
+        /* PAOLO: get proper peer information*/
+        StringInfo* rnd;
+        if (from_server) 
+            rnd = &ssl->server_random;
+        else 
+            rnd = &ssl->client_random;
+        
+        /* get provided random for keyring generation*/
+        tvb_memcpy(tvb, rnd->data, offset, 32);
+        rnd->data_len = 32;
+        if (from_server)
+            ssl->state |= SSL_SERVER_RANDOM;
+        else
+            ssl->state |= SSL_CLIENT_RANDOM;
+        ssl_debug_printf("dissect_ssl3_hnd_hello_common found random state %X\n", 
+            ssl->state);
+        
+        session_id_length = tvb_get_guint8(tvb, offset + 32);
+        /* check stored session id info */
+        if (from_server && (session_id_length == ssl->session_id.data_len) &&
+                 (tvb_memeql(tvb, offset+33, ssl->session_id.data, session_id_length) == 0))
+        {       
+            /* clinet/server id match: try to restore a previous cached session*/
+            ssl_restore_session(ssl); 
+        }
+        else {
+            /* reset state on renegotiation*/
+            if (!from_server)
+                ssl->state &= ~(SSL_HAVE_SESSION_KEY|SSL_MASTER_SECRET|
+                    SSL_CIPHER|SSL_SERVER_RANDOM);
+            
+            tvb_memcpy(tvb,ssl->session_id.data, offset+33, session_id_length);
+            ssl->session_id.data_len = session_id_length;
+        }                
+    }
+     
     if (tree)
     {
         /* show the time */
@@ -1533,7 +2132,9 @@
         }
 
     }
-    return offset - initial_offset;
+    
+    // XXXX
+    return session_id_length+33;
 }
 
 static int
@@ -1591,7 +2192,8 @@
 
 static void
 dissect_ssl3_hnd_cli_hello(tvbuff_t *tvb,
-                           proto_tree *tree, guint32 offset, guint32 length)
+       proto_tree *tree, guint32 offset, guint32 length,
+       SslDecryptSession*ssl)
 {
     /* struct {
      *     ProtocolVersion client_version;
@@ -1610,20 +2212,23 @@
     guint8  compression_method;
     guint16 start_offset = offset;
 
-    if (tree)
+    if (tree || ssl)
     {
         /* show the client version */
-        proto_tree_add_item(tree, hf_ssl_handshake_client_version, tvb,
+        if (tree)
+            proto_tree_add_item(tree, hf_ssl_handshake_client_version, tvb,
                             offset, 2, FALSE);
         offset += 2;
 
         /* show the fields in common with server hello */
-        offset += dissect_ssl3_hnd_hello_common(tvb, tree, offset);
+        offset += dissect_ssl3_hnd_hello_common(tvb, tree, offset, ssl, 0);
 
         /* tell the user how many cipher suites there are */
         cipher_suite_length = tvb_get_ntohs(tvb, offset);
+        if (!tree)
+            return;
         proto_tree_add_uint(tree, hf_ssl_handshake_cipher_suites_len,
-                            tvb, offset, 2, cipher_suite_length);
+                        tvb, offset, 2, cipher_suite_length);
         offset += 2;            /* skip opaque length */
 
         if (cipher_suite_length > 0)
@@ -1706,7 +2311,7 @@
 
 static void
 dissect_ssl3_hnd_srv_hello(tvbuff_t *tvb,
-                           proto_tree *tree, guint32 offset, guint32 length)
+                           proto_tree *tree, guint32 offset, guint32 length, SslDecryptSession* ssl)
 {
     /* struct {
      *     ProtocolVersion server_version;
@@ -1719,21 +2324,56 @@
      */
     guint16 start_offset = offset;
 
-    if (tree)
+    if (tree || ssl)
     {
         /* show the server version */
-        proto_tree_add_item(tree, hf_ssl_handshake_server_version, tvb,
+        if (tree)
+                proto_tree_add_item(tree, hf_ssl_handshake_server_version, tvb,
                             offset, 2, FALSE);
         offset += 2;
 
         /* first display the elements conveniently in
          * common with client hello
          */
-        offset += dissect_ssl3_hnd_hello_common(tvb, tree, offset);
+        offset += dissect_ssl3_hnd_hello_common(tvb, tree, offset, ssl, 1);
+
+        /* PAOLO: handle session cipher suite  */
+        if (ssl) {
+            /* store selected cipher suite for decryption */
+            ssl->cipher = tvb_get_ntohs(tvb, offset);
+            if (ssl_find_cipher(ssl->cipher,&ssl->cipher_suite) < 0) {
+                ssl_debug_printf("dissect_ssl3_hnd_srv_hello can't find cipher suite %X\n", ssl->cipher);
+                goto no_cipher;
+            }
+
+            ssl->state |= SSL_CIPHER;
+            ssl_debug_printf("dissect_ssl3_hnd_srv_hello found cipher %X, state %X\n", 
+                ssl->cipher, ssl->state);
+
+            /* if we have restored a session now we can have enought material 
+             * to build session key, check it out*/
+            if ((ssl->state & 
+                    (SSL_CIPHER|SSL_CLIENT_RANDOM|SSL_SERVER_RANDOM|SSL_VERSION|SSL_MASTER_SECRET)) !=
+                    (SSL_CIPHER|SSL_CLIENT_RANDOM|SSL_SERVER_RANDOM|SSL_VERSION|SSL_MASTER_SECRET)) {
+                ssl_debug_printf("dissect_ssl3_hnd_srv_hello not enough data to generate key (required %X)\n",
+                    (SSL_CIPHER|SSL_CLIENT_RANDOM|SSL_SERVER_RANDOM|SSL_VERSION|SSL_MASTER_SECRET));
+                goto no_cipher;
+            }
+            
+            ssl_debug_printf("dissect_ssl3_hnd_srv_hello trying to generate keys\n");
+            if (ssl_generate_keyring_material(ssl)<0) {
+                ssl_debug_printf("dissect_ssl3_hnd_srv_hello can't generate keyring material\n");
+                goto no_cipher;
+            }
+            ssl->state |= SSL_HAVE_SESSION_KEY;
+        }
+no_cipher:
+        if (!tree)
+            return;
 
         /* now the server-selected cipher suite */
         proto_tree_add_item(tree, hf_ssl_handshake_cipher_suite,
-                            tvb, offset, 2, FALSE);
+                    tvb, offset, 2, FALSE);
         offset += 2;
 
         /* and the server-selected compression method */
@@ -1910,7 +2550,7 @@
 static void
 dissect_ssl3_hnd_finished(tvbuff_t *tvb,
                           proto_tree *tree, guint32 offset,
-                          guint *conv_version)
+                          guint* conv_version)
 {
     /* For TLS:
      *     struct {
@@ -1957,8 +2597,9 @@
 /* record layer dissector */
 static int
 dissect_ssl2_record(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
-                    guint32 offset, guint *conv_version,
-                    gboolean *need_desegmentation)
+                    guint32 offset, guint* conv_version,
+                    gboolean *need_desegmentation,
+                    SslDecryptSession* ssl)
 {
     guint32 initial_offset       = offset;
     guint8  byte                 = 0;
@@ -2057,13 +2698,13 @@
                                                (initial_offset +
                                                 record_length_length),
                                                record_length)) {
-            *conv_version = SSL_VER_PCT;
-            ssl_set_conv_version(pinfo, *conv_version);
+            *conv_version = SSL_VER_PCT;                                                   
+            /*ssl_set_conv_version(pinfo, ssl->version);*/
         }
         else if (msg_type >= 2 && msg_type <= 8)
         {
             *conv_version = SSL_VER_SSLv2;
-            ssl_set_conv_version(pinfo, *conv_version);
+            /*ssl_set_conv_version(pinfo, ssl->version);*/
         }
     }
 
@@ -2136,7 +2777,7 @@
         proto_tree_add_boolean(ssl_record_tree,
                                hf_ssl2_record_is_escape, tvb,
                                initial_offset, 1, is_escape);
-    }
+        }
     if (ssl_record_tree && padding_length != -1)
     {
         proto_tree_add_uint(ssl_record_tree,
@@ -2166,7 +2807,7 @@
         /* dissect the message (only handle client hello right now) */
         switch (msg_type) {
         case SSL2_HND_CLIENT_HELLO:
-            dissect_ssl2_hnd_client_hello(tvb, ssl_record_tree, offset);
+            dissect_ssl2_hnd_client_hello(tvb, ssl_record_tree, offset, ssl);
             break;
 
         case SSL2_HND_CLIENT_MASTER_KEY:
@@ -2219,7 +2860,8 @@
 
 static void
 dissect_ssl2_hnd_client_hello(tvbuff_t *tvb,
-                              proto_tree *tree, guint32 offset)
+                              proto_tree *tree, guint32 offset,
+                              SslDecryptSession* ssl)
 {
     /* struct {
      *    uint8 msg_type;
@@ -2241,7 +2883,7 @@
     guint16 challenge_length;
 
     proto_tree *ti;
-    proto_tree *cs_tree;
+    proto_tree *cs_tree=0;
 
     version = tvb_get_ntohs(tvb, offset);
     if (!ssl_is_valid_ssl_version(version))
@@ -2250,46 +2892,54 @@
         return;
     }
 
-    if (tree)
+    if (tree || ssl)
     {
         /* show the version */
-        proto_tree_add_item(tree, hf_ssl_record_version, tvb,
+        if (tree)
+            proto_tree_add_item(tree, hf_ssl_record_version, tvb,
                             offset, 2, FALSE);
         offset += 2;
 
         cipher_spec_length = tvb_get_ntohs(tvb, offset);
-        proto_tree_add_item(tree, hf_ssl2_handshake_cipher_spec_len,
+        if (tree)
+            proto_tree_add_item(tree, hf_ssl2_handshake_cipher_spec_len,
                             tvb, offset, 2, FALSE);
         offset += 2;
 
         session_id_length = tvb_get_ntohs(tvb, offset);
-        proto_tree_add_item(tree, hf_ssl2_handshake_session_id_len,
+        if (tree)
+            proto_tree_add_item(tree, hf_ssl2_handshake_session_id_len,
                             tvb, offset, 2, FALSE);
         offset += 2;
 
         challenge_length = tvb_get_ntohs(tvb, offset);
-        proto_tree_add_item(tree, hf_ssl2_handshake_challenge_len,
+        if (tree)
+            proto_tree_add_item(tree, hf_ssl2_handshake_challenge_len,
                             tvb, offset, 2, FALSE);
         offset += 2;
 
-        /* tell the user how many cipher specs they've won */
-        tvb_ensure_bytes_exist(tvb, offset, cipher_spec_length);
-        ti = proto_tree_add_none_format(tree, hf_ssl_handshake_cipher_suites,
+        if (tree)
+        {
+            /* tell the user how many cipher specs they've won */
+            tvb_ensure_bytes_exist(tvb, offset, cipher_spec_length);
+            ti = proto_tree_add_none_format(tree, hf_ssl_handshake_cipher_suites,
                                         tvb, offset, cipher_spec_length,
                                         "Cipher Specs (%u specs)",
                                         cipher_spec_length/3);
 
-        /* make this a subtree and expand the actual specs below */
-        cs_tree = proto_item_add_subtree(ti, ett_ssl_cipher_suites);
-        if (!cs_tree)
-        {
-            cs_tree = tree;     /* failsafe */
+            /* make this a subtree and expand the actual specs below */
+            cs_tree = proto_item_add_subtree(ti, ett_ssl_cipher_suites);
+            if (!cs_tree)
+            {
+                cs_tree = tree;     /* failsafe */
+            }
         }
 
         /* iterate through the cipher specs, showing them */
         while (cipher_spec_length > 0)
         {
-            proto_tree_add_item(cs_tree, hf_ssl2_handshake_cipher_spec,
+            if (cs_tree)
+                proto_tree_add_item(cs_tree, hf_ssl2_handshake_cipher_spec,
                                 tvb, offset, 3, FALSE);
             offset += 3;        /* length of one cipher spec */
             cipher_spec_length -= 3;
@@ -2298,15 +2948,26 @@
         /* if there's a session id, show it */
         if (session_id_length > 0)
         {
-            tvb_ensure_bytes_exist(tvb, offset, session_id_length);
-            proto_tree_add_bytes_format(tree,
-                                         hf_ssl_handshake_session_id,
-                                         tvb, offset, session_id_length,
-                                         tvb_get_ptr(tvb, offset, session_id_length),
-                                         "Session ID (%u byte%s)",
-                                         session_id_length,
-                                         plurality(session_id_length, "", "s"));
-
+            if (tree)
+            {
+                tvb_ensure_bytes_exist(tvb, offset, session_id_length);
+                proto_tree_add_bytes_format(tree,
+                                             hf_ssl_handshake_session_id,
+                                             tvb, offset, session_id_length,
+                                             tvb_get_ptr(tvb, offset, session_id_length),
+                                             "Session ID (%u byte%s)",
+                                             session_id_length,
+                                             plurality(session_id_length, "", "s"));
+            }
+            
+            //PAOLO: get session id and reset session state for key [re]negotiation
+            if (ssl)
+            {
+                tvb_memcpy(tvb,ssl->session_id.data, offset, session_id_length);
+                ssl->session_id.data_len = session_id_length;
+                ssl->state &= ~(SSL_HAVE_SESSION_KEY|SSL_MASTER_SECRET|
+                        SSL_CIPHER|SSL_SERVER_RANDOM);        
+            }
             offset += session_id_length;
         }
 
@@ -2314,8 +2975,26 @@
         if (challenge_length > 0)
         {
             tvb_ensure_bytes_exist(tvb, offset, challenge_length);
-            proto_tree_add_item(tree, hf_ssl2_handshake_challenge,
+            
+            if (tree)
+                proto_tree_add_item(tree, hf_ssl2_handshake_challenge,
                                 tvb, offset, challenge_length, 0);
+            if (ssl)
+            {
+                //PAOLO: get client random data; we get at most 32 bytes from 
+                // challenge
+                int max = challenge_length > 32? 32: challenge_length;
+                
+                ssl_debug_printf("client random len: %d padded to 32\n",
+                    challenge_length);
+                
+                // client random is padded with zero and 'right' aligned
+                memset(ssl->client_random.data, 0, 32 - max);
+                tvb_memcpy(tvb, &ssl->client_random.data[32 - max], offset, max);
+                ssl->client_random.data_len = 32;
+                ssl->state |= SSL_CLIENT_RANDOM;
+                
+            }
             offset += challenge_length;
         }
     }
@@ -2864,7 +3543,7 @@
  * Support Functions
  *
  *********************************************************************/
-
+#if 0
 static void
 ssl_set_conv_version(packet_info *pinfo, guint version)
 {
@@ -2895,6 +3574,7 @@
     }
     conversation_add_proto_data(conversation, proto_ssl, GINT_TO_POINTER(version));
 }
+#endif
 
 static int
 ssl_is_valid_handshake_type(guint8 type)
@@ -3181,6 +3861,9 @@
  * Standard Ethereal Protocol Registration and housekeeping
  *
  *********************************************************************/
+#ifndef SSL_SUFFIX
+#define SSL_SUFFIX ""
+#endif
 void
 proto_register_ssl(void)
 {
@@ -3222,6 +3905,12 @@
             FT_NONE, BASE_NONE, NULL, 0x0,
             "Payload is application data", HFILL }
         },
+        { &hf_ssl_record_appdata_decrypted,
+          { "Application Data decrypted", "ssl.app_data_decrypted",
+            FT_STRING, BASE_NONE, NULL, 0x0,
+            "Payload is decrypted application data", HFILL }
+        },
+
         { & hf_ssl2_record,
           { "SSLv2/PCT Record Header", "ssl.record",
             FT_NONE, BASE_DEC, NULL, 0x0,
@@ -3497,61 +4186,61 @@
             FT_NONE, BASE_NONE, NULL, 0x0,
             "Server's challenge to client", HFILL }
         },
-		{ &hf_pct_handshake_cipher_spec,
-		  { "Cipher Spec", "pct.handshake.cipherspec",
-			FT_NONE, BASE_NONE, NULL, 0x0,
-			"PCT Cipher specification", HFILL }
-		},
-		{ &hf_pct_handshake_cipher,
-		  { "Cipher", "pct.handshake.cipher",
-			FT_UINT16, BASE_HEX, VALS(pct_cipher_type), 0x0, 
-			"PCT Ciper", HFILL }
+        { &hf_pct_handshake_cipher_spec,
+          { "Cipher Spec", "pct.handshake.cipherspec",
+                FT_NONE, BASE_NONE, NULL, 0x0,
+                "PCT Cipher specification", HFILL }
+        },
+        { &hf_pct_handshake_cipher,
+          { "Cipher", "pct.handshake.cipher",
+                FT_UINT16, BASE_HEX, VALS(pct_cipher_type), 0x0, 
+                "PCT Ciper", HFILL }
 	},
-		{ &hf_pct_handshake_hash_spec,
-		  { "Hash Spec", "pct.handshake.hashspec",
-			FT_NONE, BASE_NONE, NULL, 0x0,
-			"PCT Hash specification", HFILL }
-		},
-		{ &hf_pct_handshake_hash,
-		  { "Hash", "pct.handshake.hash",
-			FT_UINT16, BASE_HEX, VALS(pct_hash_type), 0x0,
-			"PCT Hash", HFILL }
-		},
-		{ &hf_pct_handshake_cert_spec,
-		  { "Cert Spec", "pct.handshake.certspec",
-			FT_NONE, BASE_NONE, NULL, 0x0,
-			"PCT Certificate specification", HFILL }
-		},
-		{ &hf_pct_handshake_cert,
-		  { "Cert", "pct.handshake.cert",
-			FT_UINT16, BASE_HEX, VALS(pct_cert_type), 0x0,
-			"PCT Certificate", HFILL }
-		},
-		{ &hf_pct_handshake_exch_spec,
-		  { "Exchange Spec", "pct.handshake.exchspec",
-			FT_NONE, BASE_NONE, NULL, 0x0,
-			"PCT Exchange specification", HFILL }
-		},
-		{ &hf_pct_handshake_exch,
-		  { "Exchange", "pct.handshake.exch",
-			FT_UINT16, BASE_HEX, VALS(pct_exch_type), 0x0,
-			"PCT Exchange", HFILL }
-		},
-		{ &hf_pct_handshake_sig,
-		  { "Sig Spec", "pct.handshake.sig",
-			FT_UINT16, BASE_HEX, VALS(pct_sig_type), 0x0,
-			"PCT Signature", HFILL }
-		},
-		{ &hf_pct_msg_error_type,
-		  { "PCT Error Code", "pct.msg_error_code",
-			FT_UINT16, BASE_HEX, VALS(pct_error_code), 0x0,
-			"PCT Error Code", HFILL }
-		},
-		{ &hf_pct_handshake_server_cert,
-		  { "Server Cert", "pct.handshake.server_cert",
-			FT_NONE, BASE_NONE, NULL , 0x0,
-			"PCT Server Certificate", HFILL }
-		},
+        { &hf_pct_handshake_hash_spec,
+          { "Hash Spec", "pct.handshake.hashspec",
+                FT_NONE, BASE_NONE, NULL, 0x0,
+                "PCT Hash specification", HFILL }
+        },
+        { &hf_pct_handshake_hash,
+          { "Hash", "pct.handshake.hash",
+                FT_UINT16, BASE_HEX, VALS(pct_hash_type), 0x0,
+                "PCT Hash", HFILL }
+        },
+        { &hf_pct_handshake_cert_spec,
+          { "Cert Spec", "pct.handshake.certspec",
+                FT_NONE, BASE_NONE, NULL, 0x0,
+                "PCT Certificate specification", HFILL }
+        },
+        { &hf_pct_handshake_cert,
+          { "Cert", "pct.handshake.cert",
+                FT_UINT16, BASE_HEX, VALS(pct_cert_type), 0x0,
+                "PCT Certificate", HFILL }
+        },
+        { &hf_pct_handshake_exch_spec,
+          { "Exchange Spec", "pct.handshake.exchspec",
+                FT_NONE, BASE_NONE, NULL, 0x0,
+                "PCT Exchange specification", HFILL }
+        },
+        { &hf_pct_handshake_exch,
+          { "Exchange", "pct.handshake.exch",
+                FT_UINT16, BASE_HEX, VALS(pct_exch_type), 0x0,
+                "PCT Exchange", HFILL }
+        },
+        { &hf_pct_handshake_sig,
+          { "Sig Spec", "pct.handshake.sig",
+                FT_UINT16, BASE_HEX, VALS(pct_sig_type), 0x0,
+                "PCT Signature", HFILL }
+        },
+        { &hf_pct_msg_error_type,
+          { "PCT Error Code", "pct.msg_error_code",
+                FT_UINT16, BASE_HEX, VALS(pct_error_code), 0x0,
+                "PCT Error Code", HFILL }
+        },
+        { &hf_pct_handshake_server_cert,
+          { "Server Cert", "pct.handshake.server_cert",
+                FT_NONE, BASE_NONE, NULL , 0x0,
+                "PCT Server Certificate", HFILL }
+        },
     };
 
     /* Setup protocol subtree array */
@@ -3573,8 +4262,8 @@
     };
 
     /* Register the protocol name and description */
-    proto_ssl = proto_register_protocol("Secure Socket Layer",
-                                        "SSL", "ssl");
+    proto_ssl = proto_register_protocol("Secure Socket Layer"SSL_SUFFIX,
+                                        "SSL"SSL_SUFFIX, "ssl"SSL_SUFFIX);
 
     /* Required function calls to register the header fields and
      * subtrees used */
@@ -3582,17 +4271,32 @@
     proto_register_subtree_array(ett, array_length(ett));
 
     {
-      module_t *ssl_module = prefs_register_protocol(proto_ssl, NULL);
+      module_t *ssl_module = prefs_register_protocol(proto_ssl, ssl_parse);
       prefs_register_bool_preference(ssl_module,
              "desegment_ssl_records",
              "Reassemble SSL records spanning multiple TCP segments",
              "Whether the SSL dissector should reassemble SSL records spanning multiple TCP segments. "
              "To use this option, you must also enable \"Allow subdissectors to reassemble TCP streams\" in the TCP protocol settings.",
              &ssl_desegment);
-    }
-
-    register_dissector("ssl", dissect_ssl, proto_ssl);
-
+       prefs_register_string_preference(ssl_module, "keys_list", "RSA keys list",
+             "comma separated list of private RSA keys used for SSL decryption; "
+             "each list entry must be in the form of <ip>:<port>:<key_file_name>\n"
+             "<key_file_name>   is the local file name of the RSA private key used by the specified server\n",
+             (const char **)&ssl_keys_list);
+        prefs_register_string_preference(ssl_module, "ports_list", "SSL ports list",
+             "comma separated list of tcp ports numbers to be dissectes as SSL; "
+             "each list entry must be in the form of <port>:<clear-text-port>\n"
+             "<clear-text-port>   is the port numbert associated with the protocol tunneled over SSL for this port\n",
+             (const char **)&ssl_ports_list);
+    }
+
+    register_dissector("ssl"SSL_SUFFIX, dissect_ssl, proto_ssl);
+    
+    register_init_routine(ssl_init);
+    ssl_lib_init();
+    ssl_tap = register_tap("ssl"SSL_SUFFIX);
+    ssl_debug_printf("proto_register_ssl: registered tap %s:%d\n",
+        "ssl"SSL_SUFFIX, ssl_tap);
 }
 
 /* If this dissector uses sub-dissector registration add a registration
@@ -3602,11 +4306,8 @@
 void
 proto_reg_handoff_ssl(void)
 {
-    dissector_handle_t ssl_handle;
-
-    ssl_handle = find_dissector("ssl");
-    dissector_add("tcp.port", TCP_PORT_SSL, ssl_handle);
-    dissector_add("tcp.port", TCP_PORT_SSL_LDAP, ssl_handle);
-    dissector_add("tcp.port", TCP_PORT_SSL_IMAP, ssl_handle);
-    dissector_add("tcp.port", TCP_PORT_SSL_POP, ssl_handle);
+    ssl_handle = find_dissector("ssl"SSL_SUFFIX);
+    
+    /* add now dissector to default ports.*/
+    ssl_parse();
 }
diff -uNr ethereal-0.10.14/epan/dissectors/packet-ssl-utils.c ethereal-0.10.14-patch/epan/dissectors/packet-ssl-utils.c
--- ethereal-0.10.14/epan/dissectors/packet-ssl-utils.c	1970-01-01 01:00:00.000000000 +0100
+++ ethereal-0.10.14-patch/epan/dissectors/packet-ssl-utils.c	2006-02-02 20:12:16.000000000 +0100
@@ -0,0 +1,1265 @@
+/* packet-ssl-utils.c
+ *
+ * $Id: packet-ssl-utils.c,v 1.11 2006/02/02 15:22:13 altoor Exp $
+ *
+ * ssl manipulation functions
+ * By Paolo Abeni <paolo.abeni@xxxxxxxxx>
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+ 
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "packet-ssl-utils.h"
+
+#ifdef HAVE_LIBGNUTLS
+
+/* hmac abstraction layer */
+#define SSL_HMAC gcry_md_hd_t
+
+inline void ssl_hmac_init(SSL_HMAC* md, const void * key, int len, int algo)
+{
+    if (*(md)) 
+        gcry_md_close(*(md)); 
+    gcry_md_open(md,algo, GCRY_MD_FLAG_HMAC); 
+    gcry_md_setkey (*(md), key, len);
+}
+
+inline void ssl_hmac_update(SSL_HMAC* md, const void* data, int len)
+{
+    gcry_md_write(*(md), data, len);
+}
+inline void ssl_hmac_final(SSL_HMAC* md, unsigned char* data, unsigned int* datalen)
+{ 
+    int algo = gcry_md_get_algo (*(md));
+    unsigned int len = gcry_md_get_algo_dlen(algo);
+    memcpy(data, gcry_md_read(*(md), algo), len);
+    *datalen =len;
+}
+inline void ssl_hmac_cleanup(SSL_HMAC* md) { gcry_md_close(*(md)); }
+
+/* memory digest abstraction layer*/
+#define SSL_MD gcry_md_hd_t
+
+inline void ssl_md_init(SSL_MD* md, int algo)
+{
+    if (*(md)) 
+        gcry_md_close(*(md));
+    gcry_md_open(md,algo, 0); 
+}
+inline void ssl_md_update(SSL_MD* md, unsigned char* data, int len) 
+{ 
+    gcry_md_write(*(md), data, len); 
+}
+inline void ssl_md_final(SSL_MD* md, unsigned char* data, unsigned int* datalen)
+{ 
+    int algo = gcry_md_get_algo (*(md));
+    int len = gcry_md_get_algo_dlen (algo);
+    memcpy(data, gcry_md_read(*(md),  algo), len);
+    *datalen = len;
+}
+inline void ssl_md_cleanup(SSL_MD* md) { gcry_md_close(*(md)); }
+
+/* md5 /sha abstraction layer */
+#define SSL_SHA_CTX gcry_md_hd_t
+#define SSL_MD5_CTX gcry_md_hd_t
+
+void ssl_sha_init(SSL_SHA_CTX* md)
+{
+    if (*(md)) 
+        gcry_md_close(*(md));
+    gcry_md_open(md,GCRY_MD_SHA1, 0); 
+}
+inline void ssl_sha_update(SSL_SHA_CTX* md, unsigned char* data, int len) 
+{ 
+    gcry_md_write(*(md), data, len);
+}
+inline void ssl_sha_final(unsigned char* buf, SSL_SHA_CTX* md)
+{
+    memcpy(buf, gcry_md_read(*(md),  GCRY_MD_SHA1), 
+        gcry_md_get_algo_dlen(GCRY_MD_SHA1));
+}
+
+inline int ssl_md5_init(SSL_MD5_CTX* md)
+{
+    if (*(md)) 
+        gcry_md_close(*(md));
+    return gcry_md_open(md,GCRY_MD_MD5, 0); 
+}
+inline void ssl_md5_update(SSL_MD5_CTX* md, unsigned char* data, int len)
+{
+    gcry_md_write(*(md), data, len);
+}
+inline void ssl_md5_final(unsigned char* buf, SSL_MD5_CTX* md)
+{
+    memcpy(buf, gcry_md_read(*(md),  GCRY_MD_MD5), 
+        gcry_md_get_algo_dlen(GCRY_MD_MD5));
+}
+
+
+/* stream cipher abstraction layer*/
+int ssl_cipher_init(gcry_cipher_hd_t *cipher, int algo, unsigned char* sk, 
+        unsigned char* iv, int mode)
+{
+    int gcry_modes[]={
+        GCRY_CIPHER_MODE_STREAM,
+        GCRY_CIPHER_MODE_CBC
+    };
+    int err = gcry_cipher_open(cipher, algo, gcry_modes[mode], 0); 
+    if (err !=0)
+        return  -1;
+    err = gcry_cipher_setkey(*(cipher), sk, gcry_cipher_get_algo_keylen (algo)); 
+    if (err != 0)
+        return -1;
+    err = gcry_cipher_setiv(*(cipher), iv, gcry_cipher_get_algo_blklen (algo));
+    if (err != 0)
+        return -1;
+    return 0;
+}
+inline int ssl_cipher_decrypt(gcry_cipher_hd_t *cipher, unsigned char * out, int outl, 
+                   const unsigned char * in,int inl)
+{
+    return gcry_cipher_decrypt ( *(cipher), out, outl, in, inl);
+}
+inline int ssl_get_digest_by_name(const char*name)
+{
+    return gcry_md_map_name(name);
+}
+inline int ssl_get_cipher_by_name(const char* name)
+{
+    return gcry_cipher_map_name(name);
+}
+
+/* private key abstraction layer */
+inline int ssl_get_key_len(SSL_PRIVATE_KEY* pk) {return gcry_pk_get_nbits (pk); }
+
+gcry_err_code_t
+_gcry_rsa_decrypt (int algo, gcry_mpi_t *result, gcry_mpi_t *data,
+                   gcry_mpi_t *skey, int flags);
+                   
+#define PUBKEY_FLAG_NO_BLINDING (1 << 0) 
+
+/* decrypt data with private key. Store decrypted data directly into input
+ * buffer */
+int ssl_private_decrypt(unsigned int len, unsigned char* encr_data, SSL_PRIVATE_KEY* pk)
+{
+    int rc;
+    size_t decr_len = 0;
+    gcry_sexp_t  s_data, s_plain;
+    gcry_mpi_t encr_mpi;
+    size_t i, encr_len = len;
+    unsigned char* decr_data_ptr;
+    gcry_mpi_t text=NULL;
+
+    /* build up a mpi rappresentation for encrypted data */
+    rc = gcry_mpi_scan(&encr_mpi, GCRYMPI_FMT_USG,encr_data, encr_len, &encr_len); 
+    if (rc != 0 ) {
+        ssl_debug_printf("pcry_private_decrypt: can't convert encr_data to mpi (size %d):%s\n", 
+            len, gcry_strerror(rc));
+        return 0;
+    }
+
+#ifndef SSL_FAST    
+    /* put the data into a simple list */
+    rc = gcry_sexp_build(&s_data, NULL, "(enc-val(rsa(a%m)))", encr_mpi);
+    if (rc != 0) {
+        ssl_debug_printf("pcry_private_decrypt: can't build encr_sexp:%s \n",
+             gcry_strerror(rc));
+        return 0;
+    }
+
+    /* pass it to libgcrypt */
+    rc = gcry_pk_decrypt(&s_plain, s_data, pk);
+    gcry_sexp_release(s_data);
+    if (rc != 0)
+    {
+        ssl_debug_printf("pcry_private_decrypt: can't decrypt key:%s\n", 
+            gcry_strerror(rc));
+        goto out;
+    }    
+    
+    /* convert plain text sexp to mpi format */
+    text = gcry_sexp_nth_mpi(s_plain, 0, 0);
+    
+    /* compute size requested for plaintext buffer */
+    decr_len = len;
+    if (gcry_mpi_print(GCRYMPI_FMT_USG, NULL, decr_len, &decr_len, text) != 0) {
+        ssl_debug_printf("pcry_private_decrypt: can't compute decr size:%s\n",
+            gcry_strerror(rc));
+        decr_len = 0;
+        goto out;
+    }
+    
+    /* sanity check on out buffer */
+    if (decr_len > len) {
+        ssl_debug_printf("pcry_private_decrypt: decrypted data is too long ?!? (%d max %d)\n",
+            decr_len, len);
+        return 0;
+    }
+
+    /* write plain text to encrypted data buffer */
+    decr_data_ptr = encr_data;
+    if (gcry_mpi_print( GCRYMPI_FMT_USG, decr_data_ptr, decr_len, &decr_len, 
+            text) != 0) {
+        ssl_debug_printf("pcry_private_decrypt: can't print decr data to mpi (size %d):%s\n", 
+            decr_len, gcry_strerror(rc));
+        g_free(decr_data_ptr);
+        decr_len = 0;
+        goto out;
+    }
+    
+    /* strip the padding*/
+    rc = 0;
+    for (i = 1; i < decr_len; i++) {
+        if (decr_data_ptr[i] == 0) {
+            rc = i+1;
+            break;
+        }
+    }
+    
+    ssl_debug_printf("pcry_private_decrypt: stripping %d bytes, decr_len %d\n", 
+        rc, decr_len);
+    ssl_print_data("decypted_unstrip_pre_master", decr_data_ptr, decr_len);
+    g_memmove(decr_data_ptr, &decr_data_ptr[rc], decr_len - rc);
+    decr_len -= rc;
+
+out:        
+    gcry_sexp_release(s_plain);
+#else    
+    rc = _gcry_rsa_decrypt(0, &text,  &encr_mpi, pk,0);
+    gcry_mpi_print( GCRYMPI_FMT_USG, 0, 0, &decr_len, text);
+    
+    /* sanity check on out buffer */
+    if (decr_len > len) {
+        ssl_debug_printf("pcry_private_decrypt: decrypted data is too long ?!? (%d max %d)\n",
+            decr_len, len);
+        return 0;
+    }
+    
+    /* write plain text to newly allocated buffer */
+    decr_data_ptr = encr_data;
+    if (gcry_mpi_print( GCRYMPI_FMT_USG, decr_data_ptr, decr_len, &decr_len, 
+            text) != 0) {
+        ssl_debug_printf("pcry_private_decrypt: can't print decr data to mpi (size %d):%s\n", 
+            decr_len, gcry_strerror(rc));
+        return 0;
+    }
+    
+    /* strip the padding*/
+    rc = 0;
+    for (i = 1; i < decr_len; i++) {
+        if (decr_data_ptr[i] == 0) {
+            rc = i+1;
+            break;
+        }
+    }
+    
+    ssl_debug_printf("pcry_private_decrypt: stripping %d bytes, decr_len %d\n", 
+        rc, decr_len);
+    ssl_print_data("decypted_unstrip_pre_master", decr_data_ptr, decr_len);
+    g_memmove(decr_data_ptr, &decr_data_ptr[rc], decr_len - rc);
+    decr_len -= rc;
+#endif    
+    gcry_mpi_release(text);
+    return decr_len;
+}
+
+
+#define PRF(ssl,secret,usage,rnd1,rnd2,out) ((ssl->version_netorder==SSLV3_VERSION)? \
+        ssl3_prf(secret,usage,rnd1,rnd2,out): \
+        tls_prf(secret,usage,rnd1,rnd2,out))
+
+static char *digests[]={
+     "MD5",
+     "SHA1"
+};
+
+static char *ciphers[]={
+     "DES",
+     "DES3",
+     "ARCFOUR", /* gnutls does not support rc4, but this should be 100% compatible*/
+     "RC2",
+     "IDEA",
+     "AES",
+     "AES256"
+};
+
+/* look in openssl/ssl/ssl_lib.c for a complete list of available cipersuite*/
+static SslCipherSuite cipher_suites[]={
+     {1,KEX_RSA,SIG_RSA,ENC_NULL,0,0,0,DIG_MD5,16,0, SSL_CIPHER_MODE_STREAM},
+     {2,KEX_RSA,SIG_RSA,ENC_NULL,0,0,0,DIG_SHA,20,0, SSL_CIPHER_MODE_STREAM},
+     {3,KEX_RSA,SIG_RSA,ENC_RC4,1,128,40,DIG_MD5,16,1, SSL_CIPHER_MODE_STREAM},
+     {4,KEX_RSA,SIG_RSA,ENC_RC4,1,128,128,DIG_MD5,16,0, SSL_CIPHER_MODE_STREAM},
+     {5,KEX_RSA,SIG_RSA,ENC_RC4,1,128,128,DIG_SHA,20,0, SSL_CIPHER_MODE_STREAM},
+     {6,KEX_RSA,SIG_RSA,ENC_RC2,8,128,40,DIG_SHA,20,1, SSL_CIPHER_MODE_STREAM},
+     {7,KEX_RSA,SIG_RSA,ENC_IDEA,8,128,128,DIG_SHA,20,0, SSL_CIPHER_MODE_STREAM},
+     {8,KEX_RSA,SIG_RSA,ENC_DES,8,64,40,DIG_SHA,20,1, SSL_CIPHER_MODE_STREAM},
+     {9,KEX_RSA,SIG_RSA,ENC_DES,8,64,64,DIG_SHA,20,0, SSL_CIPHER_MODE_STREAM},
+     {10,KEX_RSA,SIG_RSA,ENC_3DES,8,192,192,DIG_SHA,20,0, SSL_CIPHER_MODE_STREAM},
+     {11,KEX_DH,SIG_DSS,ENC_DES,8,64,40,DIG_SHA,20,1, SSL_CIPHER_MODE_STREAM},
+     {12,KEX_DH,SIG_DSS,ENC_DES,8,64,64,DIG_SHA,20,0, SSL_CIPHER_MODE_STREAM},
+     {13,KEX_DH,SIG_DSS,ENC_3DES,8,192,192,DIG_SHA,20,0, SSL_CIPHER_MODE_STREAM},
+     {14,KEX_DH,SIG_RSA,ENC_DES,8,64,40,DIG_SHA,20,1, SSL_CIPHER_MODE_STREAM},
+     {15,KEX_DH,SIG_RSA,ENC_DES,8,64,64,DIG_SHA,20,0, SSL_CIPHER_MODE_STREAM},
+     {16,KEX_DH,SIG_RSA,ENC_3DES,8,192,192,DIG_SHA,20,0, SSL_CIPHER_MODE_STREAM},
+     {17,KEX_DH,SIG_DSS,ENC_DES,8,64,40,DIG_SHA,20,1, SSL_CIPHER_MODE_STREAM},
+     {18,KEX_DH,SIG_DSS,ENC_DES,8,64,64,DIG_SHA,20,0, SSL_CIPHER_MODE_STREAM},
+     {19,KEX_DH,SIG_DSS,ENC_3DES,8,192,192,DIG_SHA,20,0, SSL_CIPHER_MODE_STREAM},
+     {20,KEX_DH,SIG_RSA,ENC_DES,8,64,40,DIG_SHA,20,1, SSL_CIPHER_MODE_STREAM},
+     {21,KEX_DH,SIG_RSA,ENC_DES,8,64,64,DIG_SHA,20,0, SSL_CIPHER_MODE_STREAM},
+     {22,KEX_DH,SIG_RSA,ENC_3DES,8,192,192,DIG_SHA,20,0, SSL_CIPHER_MODE_STREAM},
+     {23,KEX_DH,SIG_NONE,ENC_RC4,1,128,40,DIG_MD5,16,1, SSL_CIPHER_MODE_STREAM},
+     {24,KEX_DH,SIG_NONE,ENC_RC4,1,128,128,DIG_MD5,16,0, SSL_CIPHER_MODE_STREAM},
+     {25,KEX_DH,SIG_NONE,ENC_DES,8,64,40,DIG_MD5,16,1, SSL_CIPHER_MODE_STREAM},
+     {26,KEX_DH,SIG_NONE,ENC_DES,8,64,64,DIG_MD5,16,0, SSL_CIPHER_MODE_STREAM},
+     {27,KEX_DH,SIG_NONE,ENC_3DES,8,192,192,DIG_MD5,16,0, SSL_CIPHER_MODE_STREAM},
+     {47,KEX_RSA,SIG_RSA,ENC_AES,16,128,128,DIG_SHA,20,0, SSL_CIPHER_MODE_CBC},
+     {53,KEX_RSA,SIG_RSA,ENC_AES256,16,256,256,DIG_SHA,20,0, SSL_CIPHER_MODE_CBC},
+     {96,KEX_RSA,SIG_RSA,ENC_RC4,1,128,56,DIG_MD5,16,1, SSL_CIPHER_MODE_STREAM},
+     {97,KEX_RSA,SIG_RSA,ENC_RC2,1,128,56,DIG_MD5,16,1, SSL_CIPHER_MODE_STREAM},
+     {98,KEX_RSA,SIG_RSA,ENC_DES,8,64,64,DIG_SHA,20,1, SSL_CIPHER_MODE_STREAM},
+     {99,KEX_DH,SIG_DSS,ENC_DES,8,64,64,DIG_SHA,16,1, SSL_CIPHER_MODE_STREAM},
+     {100,KEX_RSA,SIG_RSA,ENC_RC4,1,128,56,DIG_SHA,20,1, SSL_CIPHER_MODE_STREAM},
+     {101,KEX_DH,SIG_DSS,ENC_RC4,1,128,56,DIG_SHA,20,1, SSL_CIPHER_MODE_STREAM},
+     {102,KEX_DH,SIG_DSS,ENC_RC4,1,128,128,DIG_SHA,20,0, SSL_CIPHER_MODE_STREAM},
+     {-1, 0,0,0,0,0,0,0,0,0, 0}
+};
+
+#define MAX_BLOCK_SIZE 16
+#define MAX_KEY_SIZE 32
+
+int ssl_find_cipher(int num,SslCipherSuite* cs)
+{
+    SslCipherSuite *c;
+    
+    for(c=cipher_suites;c->number!=-1;c++){
+        if(c->number==num){
+            *cs=*c;
+            return 0;
+        }
+    }
+    
+    return -1;
+}
+
+static int tls_hash(StringInfo* secret,
+        StringInfo* seed, int md, StringInfo* out)
+{
+    guint8 *ptr=out->data;
+    unsigned int left=out->data_len;
+    int tocpy;
+    guint8 *A;
+    guint8 _A[20],tmp[20];
+    unsigned int A_l,tmp_l;
+    SSL_HMAC hm;
+    
+    memset(&hm, 0, sizeof(hm));
+    ssl_print_string("tls_hash: hash secret", secret);
+    ssl_print_string("tls_hash: hash seed", seed);
+    A=seed->data;
+    A_l=seed->data_len;
+    
+    while(left){
+        ssl_hmac_init(&hm,secret->data,secret->data_len,md);
+        ssl_hmac_update(&hm,A,A_l);
+        ssl_hmac_final(&hm,_A,&A_l);
+        A=_A;
+        
+        ssl_hmac_init(&hm,secret->data,secret->data_len,md);
+        ssl_hmac_update(&hm,A,A_l);
+        ssl_hmac_update(&hm,seed->data,seed->data_len);
+        ssl_hmac_final(&hm,tmp,&tmp_l);
+        
+        tocpy=MIN(left,tmp_l);
+        memcpy(ptr,tmp,tocpy);
+        ptr+=tocpy;
+        left-=tocpy;
+    }
+    
+    ssl_hmac_cleanup(&hm);
+    ssl_print_string("hash out", out);
+    return (0);
+}    
+
+static int tls_prf(StringInfo* secret,char *usage,
+        StringInfo* rnd1, StringInfo* rnd2, StringInfo* out)
+{
+    StringInfo seed, sha_out, md5_out;
+    guint8 *ptr;
+    StringInfo s1, s2;
+    unsigned int i,s_l, r=-1;
+    int usage_len = strlen(usage);
+    
+    /* initalize buffer for sha, md5 random seed*/
+    if (ssl_data_alloc(&sha_out, MAX(out->data_len,20)) < 0)
+        return -1;
+    if (ssl_data_alloc(&md5_out, MAX(out->data_len,16)) < 0)
+        goto free_sha;
+    if (ssl_data_alloc(&seed, usage_len+rnd1->data_len+rnd2->data_len) < 0)
+        goto free_md5;
+
+    ptr=seed.data;
+    memcpy(ptr,usage,usage_len); ptr+=usage_len;
+    memcpy(ptr,rnd1->data,rnd1->data_len); ptr+=rnd1->data_len;
+    memcpy(ptr,rnd2->data,rnd2->data_len); ptr+=rnd2->data_len;    
+    
+    /* initalize buffer for client/server seeds*/
+    s_l=secret->data_len/2 + secret->data_len%2;
+    if (ssl_data_alloc(&s1, s_l) < 0)
+        goto free_seed;
+    if (ssl_data_alloc(&s2, s_l) < 0)
+        goto free_s1;
+    
+    memcpy(s1.data,secret->data,s_l);
+    memcpy(s2.data,secret->data + (secret->data_len - s_l),s_l);
+
+    ssl_debug_printf("tls_prf: tls_hash(md5 secret_len %d seed_len %d )\n", s1.data_len, seed.data_len);
+    if(tls_hash(&s1,&seed,ssl_get_digest_by_name("MD5"),&md5_out) != 0)
+        goto free_all;
+    ssl_debug_printf("tls_prf: tls_hash(sha)\n");
+    if(tls_hash(&s2,&seed,ssl_get_digest_by_name("SHA1"),&sha_out) != 0)
+        goto free_all;
+    
+    for(i=0;i<out->data_len;i++)
+      out->data[i]=md5_out.data[i] ^ sha_out.data[i];
+    r =0;
+    
+    ssl_print_string("PRF out",out);
+free_all:    
+    free(s2.data);
+free_s1:    
+    free(s1.data);
+free_seed:    
+    free(seed.data);
+free_md5:
+    free(md5_out.data);    
+free_sha:
+    free(sha_out.data);
+    return r;    
+}
+
+static int ssl3_generate_export_iv(StringInfo* r1,
+        StringInfo* r2, StringInfo* out)
+{
+    SSL_MD5_CTX md5;
+    guint8 tmp[16];
+    
+    ssl_md5_init(&md5);
+    ssl_md5_update(&md5,r1->data,r1->data_len);
+    ssl_md5_update(&md5,r2->data,r2->data_len);
+    ssl_md5_final(tmp,&md5);
+    
+    memcpy(out->data,tmp,out->data_len);
+    ssl_print_string("export iv", out);
+    
+    return(0);
+}
+
+static int ssl3_prf(StringInfo* secret, char* usage,
+        StringInfo* r1,
+        StringInfo* r2,StringInfo* out)
+{
+    SSL_MD5_CTX md5;
+    SSL_SHA_CTX sha;
+    StringInfo *rnd1,*rnd2;
+    unsigned int off;
+    int i=0,j;
+    guint8 buf[20];
+    
+    rnd1=r1; rnd2=r2;
+        
+    ssl_md5_init(&md5);
+    memset(&sha,0,sizeof(sha));
+    ssl_sha_init(&sha);
+    
+    for(off=0;off<out->data_len;off+=16){
+        unsigned char outbuf[16];
+        int tocpy;
+        i++;
+        
+        ssl_debug_printf("ssl3_prf: sha1_update(%d)\n",i);
+        /* A, BB, CCC,  ... */
+        for(j=0;j<i;j++){
+            buf[j]=64+i;
+        }
+        
+        ssl_sha_update(&sha,buf,i);
+        if (secret) 
+            ssl_sha_update(&sha,secret->data,secret->data_len);
+        
+        if(!strcmp(usage,"client write key") || !strcmp(usage,"server write key")){
+            ssl_sha_update(&sha,rnd2->data,rnd2->data_len);
+            ssl_sha_update(&sha,rnd1->data,rnd1->data_len);
+        }
+        else{
+            ssl_sha_update(&sha,rnd1->data,rnd1->data_len);
+            ssl_sha_update(&sha,rnd2->data,rnd2->data_len);
+        }
+        
+        ssl_sha_final(buf,&sha);
+        
+        ssl_sha_init(&sha);
+        
+        ssl_debug_printf("ssl3_prf: md5_update(%d)\n",i);
+        ssl_md5_update(&md5,secret->data,secret->data_len);
+        ssl_md5_update(&md5,buf,20);
+        ssl_md5_final(outbuf,&md5);
+        tocpy=MIN(out->data_len-off,16);
+        memcpy(out->data+off,outbuf,tocpy);
+        
+        ssl_md5_init(&md5);
+    }
+    
+    return(0);
+}
+
+int ssl_create_decoder(SslDecoder *dec, SslCipherSuite *cipher_suite, 
+        guint8 *mk, guint8 *sk, guint8 *iv)
+{
+    int ciph=0;
+
+    /* Find the SSLeay cipher */
+    if(cipher_suite->enc!=ENC_NULL) {
+        ssl_debug_printf("ssl_create_decoder CIPHER: %s\n", ciphers[cipher_suite->enc-0x30]);
+        ciph=ssl_get_cipher_by_name(ciphers[cipher_suite->enc-0x30]);
+    }
+    if (ciph == 0) {
+        ssl_debug_printf("ssl_create_decoder can't find cipher %s\n", 
+            ciphers[cipher_suite->enc-0x30]);
+        return -1;
+    }
+    
+    /* init mac buffer: mac storage is embedded into decoder struct to save a
+     memory allocation and waste samo more memory*/
+    dec->cipher_suite=cipher_suite;
+    dec->mac_key.data = dec->_mac_key;
+    ssl_data_set(&dec->mac_key, mk, cipher_suite->dig_len);
+
+    if (ssl_cipher_init(&dec->evp,ciph,sk,iv,cipher_suite->mode) < 0) {
+        ssl_debug_printf("ssl_create_decoder: can't create cipher id:%d mode:%d\n",
+            ciph, cipher_suite->mode);
+        return -1;
+    }
+
+    ssl_debug_printf("decoder initialized (digest len %d)\n", cipher_suite->dig_len);
+    return 0;    
+}
+
+int ssl_generate_keyring_material(SslDecryptSession*ssl_session)
+{
+    StringInfo key_block;
+    guint8 _iv_c[MAX_BLOCK_SIZE],_iv_s[MAX_BLOCK_SIZE];
+    guint8 _key_c[MAX_KEY_SIZE],_key_s[MAX_KEY_SIZE];
+    int needed;
+    guint8 *ptr,*c_wk,*s_wk,*c_mk,*s_mk,*c_iv = _iv_c,*s_iv = _iv_s;
+    
+    /* if master_key is not yet generate, create it now*/    
+    if (!(ssl_session->state & SSL_MASTER_SECRET)) {
+        ssl_debug_printf("ssl_generate_keyring_material:PRF(pre_master_secret)\n");
+        if (PRF(ssl_session,&ssl_session->pre_master_secret,"master secret",
+                &ssl_session->client_random,
+                &ssl_session->server_random, &ssl_session->master_secret)) {
+            ssl_debug_printf("ssl_generate_keyring_material can't generate master_secret\n");
+            return -1;
+        }
+        ssl_print_string("master secret",&ssl_session->master_secret);
+    }
+    
+    /* Compute the key block. First figure out how much data we need*/
+    needed=ssl_session->cipher_suite.dig_len*2;
+    needed+=ssl_session->cipher_suite.bits / 4;
+    if(ssl_session->cipher_suite.block>1) 
+        needed+=ssl_session->cipher_suite.block*2;
+    
+    key_block.data_len = needed;
+    key_block.data = g_malloc(needed);
+    if (!key_block.data) {
+        ssl_debug_printf("ssl_generate_keyring_material can't allacate key_block\n");
+        return -1;
+    }
+    ssl_debug_printf("ssl_generate_keyring_material sess key generation\n");
+    if (PRF(ssl_session,&ssl_session->master_secret,"key expansion",
+            &ssl_session->server_random,&ssl_session->client_random,
+            &key_block)) {
+        ssl_debug_printf("ssl_generate_keyring_material can't generate key_block\n");
+        goto fail;
+    }
+    ssl_print_string("key expansion", &key_block);
+    
+    ptr=key_block.data;
+    c_mk=ptr; ptr+=ssl_session->cipher_suite.dig_len;
+    s_mk=ptr; ptr+=ssl_session->cipher_suite.dig_len;
+    
+    c_wk=ptr; ptr+=ssl_session->cipher_suite.eff_bits/8;
+    s_wk=ptr; ptr+=ssl_session->cipher_suite.eff_bits/8;
+    
+    if(ssl_session->cipher_suite.block>1){
+        c_iv=ptr; ptr+=ssl_session->cipher_suite.block;
+        s_iv=ptr; ptr+=ssl_session->cipher_suite.block;
+    }
+    
+    if(ssl_session->cipher_suite.export){
+        StringInfo iv_c,iv_s;
+        StringInfo key_c,key_s;
+        StringInfo k;
+        
+        if(ssl_session->cipher_suite.block>1){
+            
+            /* We only have room for MAX_BLOCK_SIZE bytes IVs, but that's
+             all we should need. This is a sanity check */
+            if(ssl_session->cipher_suite.block>MAX_BLOCK_SIZE) {
+                ssl_debug_printf("ssl_generate_keyring_material cipher suite block must be at most %d nut is %d\n", 
+                    MAX_BLOCK_SIZE, ssl_session->cipher_suite.block);
+                goto fail;
+            }
+        
+            iv_c.data = _iv_c;
+            iv_c.data_len = ssl_session->cipher_suite.block;
+            iv_s.data = _iv_s;
+            iv_s.data_len = ssl_session->cipher_suite.block;
+            
+            if(ssl_session->version_netorder==SSLV3_VERSION){
+                ssl_debug_printf("ssl_generate_keyring_material ssl3_generate_export_iv\n");
+                if (ssl3_generate_export_iv(&ssl_session->client_random,
+                        &ssl_session->server_random,&iv_c)) {
+                    ssl_debug_printf("ssl_generate_keyring_material can't generate sslv3 client iv\n");
+                    goto fail;
+                }
+                ssl_debug_printf("ssl_generate_keyring_material ssl3_generate_export_iv(2)\n");
+                if (ssl3_generate_export_iv(&ssl_session->server_random,
+                        &ssl_session->client_random,&iv_s)) {
+                    ssl_debug_printf("ssl_generate_keyring_material can't generate sslv3 server iv\n");
+                    goto fail;
+                }            
+            }
+            else{
+                guint8 _iv_block[MAX_BLOCK_SIZE * 2];
+                StringInfo iv_block;
+                StringInfo key_null;
+                guint8 _key_null;
+                
+                key_null.data = &_key_null;
+                key_null.data_len = 0;
+                                
+                iv_block.data = _iv_block;
+                iv_block.data_len = ssl_session->cipher_suite.block*2;
+                
+                ssl_debug_printf("ssl_generate_keyring_material prf(iv_block)\n");
+                if(PRF(ssl_session,&key_null, "IV block",
+                        &ssl_session->client_random,
+                        &ssl_session->server_random,&iv_block)) {
+                    ssl_debug_printf("ssl_generate_keyring_material can't generate tls31 iv block\n");
+                    goto fail;
+                }
+                
+                memcpy(_iv_c,iv_block.data,ssl_session->cipher_suite.block);
+                memcpy(_iv_s,iv_block.data+ssl_session->cipher_suite.block,
+                    ssl_session->cipher_suite.block);
+            }
+            
+            c_iv=_iv_c;
+            s_iv=_iv_s;
+        }
+        
+        if (ssl_session->version_netorder==SSLV3_VERSION){
+            
+            SSL_MD5_CTX md5;
+            ssl_debug_printf("ssl_generate_keyring_material MD5(client_random)\n");
+            ssl_md5_init(&md5);
+            ssl_md5_update(&md5,c_wk,ssl_session->cipher_suite.eff_bits/8);
+            ssl_md5_update(&md5,ssl_session->client_random.data,
+                ssl_session->client_random.data_len);
+            ssl_md5_update(&md5,ssl_session->server_random.data,
+                ssl_session->server_random.data_len);        
+            ssl_md5_final(_key_c,&md5);
+            c_wk=_key_c;
+            
+            ssl_md5_init(&md5);
+            ssl_debug_printf("ssl_generate_keyring_material MD5(server_random)\n");
+            ssl_md5_update(&md5,s_wk,ssl_session->cipher_suite.eff_bits/8);
+            ssl_md5_update(&md5,ssl_session->server_random.data,
+                ssl_session->server_random.data_len);
+            ssl_md5_update(&md5,ssl_session->client_random.data,
+                ssl_session->client_random.data_len);
+            ssl_md5_final(_key_s,&md5);
+            s_wk=_key_s;
+        }
+        else{
+            key_c.data = _key_c;
+            key_c.data_len = sizeof(_key_c);
+            key_s.data = _key_s;
+            key_s.data_len = sizeof(_key_s);
+            
+            k.data = c_wk;
+            k.data_len = ssl_session->cipher_suite.eff_bits/8;
+            ssl_debug_printf("ssl_generate_keyring_material PRF(key_c)\n");
+            if (PRF(ssl_session,&k,"client write key",
+                    &ssl_session->client_random,
+                    &ssl_session->server_random, &key_c)) {
+                ssl_debug_printf("ssl_generate_keyring_material can't generate tll31 server key \n");        
+                goto fail;
+            }
+            c_wk=_key_c;
+            
+            k.data = s_wk;
+            k.data_len = ssl_session->cipher_suite.eff_bits/8;
+            ssl_debug_printf("ssl_generate_keyring_material PRF(key_s)\n");
+            if(PRF(ssl_session,&k,"server write key",
+                    &ssl_session->client_random,
+                    &ssl_session->server_random, &key_s)) {
+                ssl_debug_printf("ssl_generate_keyring_material can't generate tll31 client key \n");
+                goto fail;
+            }
+            s_wk=_key_s;
+        }
+    }
+    
+    /* show key material info */
+    ssl_print_data("Client MAC key",c_mk,ssl_session->cipher_suite.dig_len);
+    ssl_print_data("Server MAC key",s_mk,ssl_session->cipher_suite.dig_len);    
+    ssl_print_data("Client Write key",c_wk,ssl_session->cipher_suite.bits/8);
+    ssl_print_data("Server Write key",s_wk,ssl_session->cipher_suite.bits/8);    
+        
+    if(ssl_session->cipher_suite.block>1) {
+        ssl_print_data("Client Write IV",c_iv,ssl_session->cipher_suite.block);
+        ssl_print_data("Server Write IV",s_iv,ssl_session->cipher_suite.block);
+    }
+    else {
+        ssl_print_data("Client Write IV",c_iv,8);
+        ssl_print_data("Server Write IV",s_iv,8);
+    }
+    
+    /* create both client and server ciphers*/
+    ssl_debug_printf("ssl_generate_keyring_material ssl_create_decoder(client)\n");
+    if (ssl_create_decoder(&ssl_session->client,
+            &ssl_session->cipher_suite,c_mk,c_wk,c_iv)) {
+        ssl_debug_printf("ssl_generate_keyring_material can't init client decoder\n");        
+        goto fail;
+    }
+    ssl_debug_printf("ssl_generate_keyring_material ssl_create_decoder(server)\n");
+    if (ssl_create_decoder(&ssl_session->server,
+            &ssl_session->cipher_suite,s_mk,s_wk,s_iv)) {
+        ssl_debug_printf("ssl_generate_keyring_material can't init client decoder\n");        
+        goto fail;
+    }
+        
+    g_free(key_block.data);
+    return 0;
+    
+fail:
+    free(key_block.data);
+    return -1;
+}
+
+int ssl_decrypt_pre_master_secret(SslDecryptSession*ssl_session, 
+    StringInfo* entrypted_pre_master, SSL_PRIVATE_KEY *pk)
+{
+    int i;
+        
+    if(ssl_session->cipher_suite.kex!=KEX_RSA) {
+        ssl_debug_printf("ssl_decrypt_pre_master_secret key %d diferent from KEX_RSA(%d)\n",
+            ssl_session->cipher_suite.kex, KEX_RSA);
+        return(-1);
+    }
+
+#if 0 
+    /* can't find any place where ephemeral_rsa is set ...*/
+    if(d->ephemeral_rsa) {
+        ssl_debug_printf("ssl_decrypt_pre_master_secret ephimeral RSA\n");
+        return(-1);
+    }
+#endif    
+ 
+    /* with tls key loading will fail if not rsa type, so no need to check*/
+    ssl_print_string("pre master encrypted",entrypted_pre_master);
+    ssl_debug_printf("ssl_decrypt_pre_master_secret:RSA_private_decrypt\n");
+    i=ssl_private_decrypt(entrypted_pre_master->data_len,
+        entrypted_pre_master->data, pk);
+
+    if (i!=48) {
+        ssl_debug_printf("ssl_decrypt_pre_master_secret wrong "
+            "pre_master_secret lenght (%d, expected %d)\n", i, 48);
+        return -1;
+    }
+
+    /* the decrypted data has been written into the pre_master key buffer */
+    ssl_session->pre_master_secret.data = entrypted_pre_master->data;
+    ssl_session->pre_master_secret.data_len=48;  
+    ssl_print_string("pre master secret",&ssl_session->pre_master_secret);
+
+    /* Remove the master secret if it was there.
+       This force keying material regeneration in
+       case we're renegotiating */
+    ssl_session->state &= ~SSL_MASTER_SECRET;
+    return 0;
+}
+ 
+#define MSB(a) ((a>>8)&0xff)
+#define LSB(a) (a&0xff)
+
+/* convert network byte order 32 byte number to right-aligned host byte order *
+ * 8 bytes buffer */
+static int fmt_seq(guint32 num, guint8* buf)
+{
+    guint32 netnum;
+
+    memset(buf,0,8);
+    netnum=g_htonl(num);
+    memcpy(buf+4,&netnum,4);
+
+    return(0);
+}
+
+static int tls_check_mac(SslDecoder*decoder, int ct,int ver, guint8* data,
+        guint32 datalen, guint8* mac)
+{
+    SSL_HMAC hm;
+    int md;
+    guint32 l;
+    guint8 buf[20];
+
+    memset(&hm, 0, sizeof(hm));
+    md=ssl_get_digest_by_name(digests[decoder->cipher_suite->dig-0x40]);
+    ssl_debug_printf("tls_check_mac mac type:%s md %d\n",
+        digests[decoder->cipher_suite->dig-0x40], md);
+    
+    ssl_hmac_init(&hm,decoder->mac_key.data,decoder->mac_key.data_len,md);
+    
+    fmt_seq(decoder->seq,buf);
+    decoder->seq++;
+    ssl_hmac_update(&hm,buf,8);
+    
+    buf[0]=ct;
+    ssl_hmac_update(&hm,buf,1);
+
+    buf[0]=MSB(ver);
+    buf[1]=LSB(ver);
+    ssl_hmac_update(&hm,buf,2); 
+    
+    buf[0]=MSB(datalen);
+    buf[1]=LSB(datalen);
+    ssl_hmac_update(&hm,buf,2);
+
+    ssl_hmac_update(&hm,data,datalen);
+
+    ssl_hmac_final(&hm,buf,&l);
+    ssl_print_data("Mac", buf, l);
+    if(memcmp(mac,buf,l))
+        return -1;
+
+    ssl_hmac_cleanup(&hm);
+    return(0);
+}
+
+int ssl3_check_mac(SslDecoder*decoder,int ct,guint8* data,
+        guint32 datalen, guint8* mac)
+{
+    SSL_MD mc;
+    int md;
+    guint32 l;
+    guint8 buf[64],dgst[20];
+    int pad_ct;
+    
+    pad_ct=(decoder->cipher_suite->dig==DIG_SHA)?40:48;
+
+    /* get cipher used for digest comptuation */
+    md=ssl_get_digest_by_name(digests[decoder->cipher_suite->dig-0x40]);
+    ssl_debug_printf("ssl3_check_mac digest%s md %d\n",
+        digests[decoder->cipher_suite->dig-0x40], md);
+    memset(&mc, 0, sizeof(mc));
+    ssl_md_init(&mc,md);
+    ssl_debug_printf("ssl3_check_mac memory digest %p\n",mc);
+
+    /* do hash computation on data && padding */
+    ssl_md_update(&mc,decoder->mac_key.data,decoder->mac_key.data_len);
+
+    memset(buf,0x36,pad_ct);
+    ssl_md_update(&mc,buf,pad_ct);
+
+    fmt_seq(decoder->seq,buf);
+    decoder->seq++;
+    ssl_md_update(&mc,buf,8);
+
+    buf[0]=ct;
+    ssl_md_update(&mc,buf,1);
+
+    buf[0]=MSB(datalen);
+    buf[1]=LSB(datalen);
+    ssl_md_update(&mc,buf,2);
+    ssl_md_update(&mc,data,datalen);
+
+    ssl_md_final(&mc,dgst,&l);
+
+    ssl_md_init(&mc,md);
+
+    ssl_md_update(&mc,decoder->mac_key.data,decoder->mac_key.data_len);
+
+    memset(buf,0x5c,pad_ct);
+    ssl_md_update(&mc,buf,pad_ct);
+    ssl_md_update(&mc,dgst,l);
+
+    ssl_md_final(&mc,dgst,&l);
+
+    if(memcmp(mac,dgst,l))
+        return -1;
+
+    return(0);
+}
+  
+int ssl_decrypt_record(SslDecryptSession*ssl,SslDecoder* decoder, int ct,
+        const unsigned char* in, int inl,unsigned char*out,int* outl)
+{
+    int pad, worklen;
+    guint8 *mac;
+    
+    ssl_debug_printf("ssl_decrypt_record ciphertext len %d\n", inl);
+    ssl_print_data("Ciphertext",in, inl);
+    
+    /* First decrypt*/
+    if ((pad = ssl_cipher_decrypt(&decoder->evp,out,*outl,in,inl))!= 0)
+        ssl_debug_printf("ssl_decrypt_record: %s %s\n", gcry_strsource (pad),
+                    gcry_strerror (pad));
+
+    ssl_print_data("Plaintext",out,inl);
+    worklen=inl;
+
+    /* Now strip off the padding*/
+    if(decoder->cipher_suite->block!=1){
+        pad=out[inl-1];
+        worklen-=(pad+1);
+        ssl_debug_printf("ssl_decrypt_record found padding %d final len %d\n", 
+            pad, *outl);
+    }
+
+    /* And the MAC */
+    worklen-=decoder->cipher_suite->dig_len;
+    if (worklen < 0)
+    {
+        ssl_debug_printf("ssl_decrypt_record wrong record len/padding outlen %d\n work %d\n",*outl, worklen);
+        return -1;
+    }
+    mac=out+worklen;
+    /*ssl_print_data("Record data",out,*outl);*/
+
+    /* Now check the MAC */
+    ssl_debug_printf("checking mac (len %d, version %X, ct %d)\n", worklen,ssl->version_netorder, ct);
+    if(ssl->version_netorder==0x300){
+        if(ssl3_check_mac(decoder,ct,out,worklen,mac) < 0) {
+            ssl_debug_printf("ssl_decrypt_record: mac falied\n");
+            return -1;
+        }
+    }
+    else{
+        if(tls_check_mac(decoder,ct,ssl->version_netorder,out,worklen,mac)< 0) {
+            ssl_debug_printf("ssl_decrypt_record: mac falied\n");
+            return -1;
+        }
+    }
+    ssl_debug_printf("ssl_decrypt_record: mac ok\n");
+    *outl = worklen;
+    return(0);
+}
+
+/* old relase of gnutls does not define the appropriate macros, so get 
+ * them from the string*/
+void ssl_get_version(int* major, int* minor, int* patch)
+{
+    const char* str = gnutls_check_version(NULL);
+    
+    ssl_debug_printf("ssl_get_version: %s\n", str);
+    sscanf(str, "%d.%d.%d", major, minor, patch);
+}
+
+
+SSL_PRIVATE_KEY* ssl_load_key(FILE* fp)
+{    
+    /* gnutls make our work much harded, since we have to work internally with
+     * s-exp formatted data, but PEM loader export only in "gnutls_datum" 
+     * format, and a datum -> s-exp convertion function does not exist.
+     */
+    struct gnutls_x509_privkey_int* priv_key;
+    gnutls_datum key;
+    gnutls_datum m, e, d, p,q, u;
+    int size, major, minor, patch;
+    unsigned int bytes;
+    unsigned int tmp_size;
+#ifdef SSL_FAST
+    gcry_mpi_t* rsa_params = g_malloc(sizeof(gcry_mpi_t)*6);
+#else    
+    gcry_mpi_t rsa_params[6];
+#endif    
+    gcry_sexp_t rsa_priv_key;
+    
+    /* init private key data*/
+    gnutls_x509_privkey_init(&priv_key);
+    
+    /* compute file size and load all file contents into a datum buffer*/
+    if (fseek(fp, 0, SEEK_END) < 0) {
+        ssl_debug_printf("ssl_load_key: can't fseek file\n");
+        return NULL;
+    }
+    if ((size = ftell(fp)) < 0) {
+        ssl_debug_printf("ssl_load_key: can't ftell file\n");
+        return NULL;
+    }
+    if (fseek(fp, 0, SEEK_SET) < 0) {
+        ssl_debug_printf("ssl_load_key: can't refseek file\n");
+        return NULL;
+    }
+    key.data = g_malloc(size);
+    key.size = size;
+    bytes = fread(key.data, 1, key.size, fp);
+    if (bytes < key.size) {
+        ssl_debug_printf("ssl_load_key: can't read from file %d bytes, got %d\n", 
+            key.size, bytes);
+        return NULL;
+    }
+    
+    /* import PEM data*/
+    if (gnutls_x509_privkey_import(priv_key, &key, GNUTLS_X509_FMT_PEM)!=0) {
+        ssl_debug_printf("ssl_load_key: can't import pem data\n");
+        return NULL;
+    }
+    free(key.data);
+    
+    /* RSA get parameter */
+    if (gnutls_x509_privkey_export_rsa_raw(priv_key, &m, &e, &d, &p, &q, &u) != 0) {
+        ssl_debug_printf("ssl_load_key: can't export rsa param (is a rsa private key file ?!?)\n");
+        return NULL;
+    }
+    
+    /* convert each rsa parameter to mpi format*/
+    if (gcry_mpi_scan( &rsa_params[0], GCRYMPI_FMT_USG, m.data,  m.size, &tmp_size) !=0) {
+        ssl_debug_printf("ssl_load_key: can't convert m rsa param to int (size %d)\n", m.size);
+        return NULL;
+    }
+    
+    if (gcry_mpi_scan( &rsa_params[1], GCRYMPI_FMT_USG, e.data,  e.size, &tmp_size) != 0) {
+        ssl_debug_printf("ssl_load_key: can't convert e rsa param to int (size %d)\n", e.size);
+        return NULL;
+    }
+
+    // note: openssl and gnutls use 'p' and 'q' with opposite meaning:
+    // our 'p' must be equal to 'q' as provided from openssl and viceversa
+    if (gcry_mpi_scan( &rsa_params[2], GCRYMPI_FMT_USG, d.data,  d.size, &tmp_size) !=0) {
+        ssl_debug_printf("ssl_load_key: can't convert d rsa param to int (size %d)\n", d.size);
+        return NULL;
+    }
+    
+    if (gcry_mpi_scan( &rsa_params[3], GCRYMPI_FMT_USG, q.data,  q.size, &tmp_size) !=0) {
+        ssl_debug_printf("ssl_load_key: can't convert q rsa param to int (size %d)\n", q.size);
+        return NULL;
+    }
+
+    if (gcry_mpi_scan( &rsa_params[4], GCRYMPI_FMT_USG, p.data,  p.size, &tmp_size) !=0) {
+        ssl_debug_printf("ssl_load_key: can't convert p rsa param to int (size %d)\n", p.size);
+        return NULL;
+    }
+        
+    if (gcry_mpi_scan( &rsa_params[5], GCRYMPI_FMT_USG, u.data,  u.size, &tmp_size) !=0) {
+        ssl_debug_printf("ssl_load_key: can't convert u rsa param to int (size %d)\n", m.size);
+        return NULL; 
+    }
+    
+    ssl_get_version(&major, &minor, &patch);
+    
+    /* certain versions of gnutls require swap of rsa params 'p' and 'q' */
+    if ((major <= 1) && (minor <= 0) && (patch <=13))
+    {
+        gcry_mpi_t tmp;
+        ssl_debug_printf("ssl_load_key: swapping p and q parametes\n");
+        tmp = rsa_params[4];
+        rsa_params[4] = rsa_params[3];
+        rsa_params[3] = tmp;
+    }
+    
+    if  (gcry_sexp_build( &rsa_priv_key, NULL,
+            "(private-key(rsa((n%m)(e%m)(d%m)(p%m)(q%m)(u%m))))", rsa_params[0], 
+            rsa_params[1], rsa_params[2], rsa_params[3], rsa_params[4], 
+            rsa_params[5]) != 0) {
+        ssl_debug_printf("ssl_load_key: can't built rsa private key s-exp\n");
+        return NULL;
+    }
+
+#if SSL_FAST    
+    return rsa_params;
+#else
+    {
+        int i;
+        for (i=0; i< 6; i++)
+            gcry_mpi_release(rsa_params[i]);
+    }
+    return rsa_priv_key;
+#endif    
+}
+
+void ssl_free_key(SSL_PRIVATE_KEY* key)
+{
+#if SSL_FAST    
+    int i;
+    for (i=0; i< 6; i++)
+        gcry_mpi_release(key[i]);    
+#else
+    gcry_sexp_release(key);
+#endif    
+}
+
+static FILE* myout=NULL;
+void ssl_lib_init(void)
+{
+    gnutls_global_init();
+
+#ifdef _WIN32
+    /* we don't have standard I/O file available, open a log */
+    myout = fopen("ssl-decrypt.txt","w");
+    if (!myout)
+#endif
+        myout = stderr;
+}
+
+
+#else
+/* no libgnutl: dummy operation to keep interface consistent*/
+void ssl_lib_init(void)
+{
+}
+
+SSL_PRIVATE_KEY* ssl_load_key(FILE* fp) 
+{
+    ssl_debug_printf("ssl_load_key: impossible without glutls. fp %p\n",fp);
+    return NULL;
+}
+void ssl_free_key(SSL_PRIVATE_KEY* key)
+{
+}
+
+int ssl_find_cipher(int num,SslCipherSuite* cs) 
+{
+    ssl_debug_printf("ssl_find_cipher: dummy without glutls. num %d cs %p\n",
+        num,cs);
+    return 0; 
+}
+int ssl_generate_keyring_material(SslDecryptSession*ssl) 
+{
+    ssl_debug_printf("ssl_generate_keyring_material: impossible without glutls. ssl %p\n",
+        ssl);
+    return 0; 
+}
+int ssl_decrypt_pre_master_secret(SslDecryptSession* ssl_session, 
+    StringInfo* entrypted_pre_master, SSL_PRIVATE_KEY *pk)
+{
+    ssl_debug_printf("ssl_decrypt_pre_master_secret: impossible without glutls."
+        " ssl %p entrypted_pre_master %p pk %p\n", ssl_session,
+        entrypted_pre_master, pk);
+    return 0;
+}
+
+int ssl_decrypt_record(SslDecryptSession*ssl,SslDecoder* decoder, int ct, 
+        const unsigned char* in, int inl,unsigned char*out,int* outl)
+{
+    ssl_debug_printf("ssl_decrypt_record: impossible without gnutls. ssl %p"
+        "decoder %p ct %d, in %p inl %d out %p outl %d\n", ssl, decoder, ct,
+        in, inl, out, outl);
+    return 0;
+}
+
+#endif
+
+/* get ssl data for this session. if no ssl data is found allocate a new one*/
+void ssl_session_init(SslDecryptSession* ssl_session)
+{
+    ssl_debug_printf("ssl_session_init: initializing ptr %p size %d\n", 
+        ssl_session, sizeof(SslDecryptSession));
+
+    ssl_session->master_secret.data = ssl_session->_master_secret;
+    ssl_session->session_id.data = ssl_session->_session_id;
+    ssl_session->client_random.data = ssl_session->_client_random;
+    ssl_session->server_random.data = ssl_session->_server_random;
+    ssl_session->master_secret.data_len = 48;
+}
+
+int ssl_data_init(StringInfo* str, unsigned char* src, unsigned int len)
+{
+    str->data = g_realloc(str->data,len);
+    if (!str->data)
+        return -1;
+    if (src)
+        memcpy(str->data, src,len);
+    str->data_len = len;
+    return 0;
+}
+
+int ssl_data_alloc(StringInfo* str, unsigned int len)
+{
+    str->data = g_malloc(len);
+    if (!str->data)
+        return -1;
+    str->data_len = len;
+    return 0;
+}
+
+int ssl_data_set(StringInfo* str, unsigned char* data, unsigned int len)
+{
+    memcpy(str->data, data, len);
+    str->data_len = len;
+    return 0;
+}
+
+#ifdef SSL_DECRYPT_DEBUG
+void ssl_debug_printf(char* fmt, ...)
+{
+  va_list ap;
+  int ret=0;
+  va_start(ap, fmt);
+  ret += vfprintf(myout, fmt, ap);
+  va_end(ap);
+  fflush(myout);
+}
+
+
+void ssl_print_text_data(const char* name, const unsigned char* data, int len)
+{
+    int i;
+    fprintf(myout,"%s: ",name);
+    for (i=0; i< len; i++) {
+      fprintf(myout,"%c",data[i]);
+    }
+    fprintf(myout,"\n");
+    fflush(myout);
+}
+
+void ssl_print_data(const char* name, const unsigned char* data, int len)
+{
+    int i;
+    fprintf(myout,"%s[%d]:\n",name, len);
+    for (i=0; i< len; i++) {
+        if ((i>0) && (i%16 == 0))
+            fprintf(myout,"\n");
+        fprintf(myout,"%.2x ",data[i]&255);
+    }
+    fprintf(myout,"\n");
+    fflush(myout);
+}
+
+void ssl_print_string(const char* name, const StringInfo* data)
+{
+    ssl_print_data(name, data->data, data->data_len);
+}
+#endif
diff -uNr ethereal-0.10.14/epan/dissectors/packet-ssl-utils.h ethereal-0.10.14-patch/epan/dissectors/packet-ssl-utils.h
--- ethereal-0.10.14/epan/dissectors/packet-ssl-utils.h	1970-01-01 01:00:00.000000000 +0100
+++ ethereal-0.10.14-patch/epan/dissectors/packet-ssl-utils.h	2006-02-02 20:12:16.000000000 +0100
@@ -0,0 +1,172 @@
+/* packet-ss-utils.c
+ *
+ * $Id: packet-ssl-utils.h,v 1.6 2006/01/31 09:41:17 altoor Exp $
+ *
+ * ssl manipulation functions
+ * By Paolo Abeni <paolo.abeni@xxxxxxxxx>
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#ifndef __SSL_UTILS_H_
+#define __SSL_UTILS_H_
+
+#include <glib.h>
+
+#ifdef HAVE_LIBGNUTLS
+
+#ifdef _WIN32
+//#include <gnutls_conf.h>
+#include <gcrypt_conf.h>
+#endif
+
+#include <stdio.h>
+#include <gcrypt.h>
+#include <gnutls/x509.h>
+#include <gnutls/openssl.h>
+
+//#define SSL_FAST 1
+#define SSL_DECRYPT_DEBUG
+
+#define SSL_CIPHER_CTX gcry_cipher_hd_t
+#ifdef SSL_FAST
+#define SSL_PRIVATE_KEY gcry_mpi_t
+#else
+#define SSL_PRIVATE_KEY struct gcry_sexp
+#endif
+#else 
+#define SSL_CIPHER_CTX void*
+#define SSL_PRIVATE_KEY void
+#endif
+
+typedef struct _StringInfo {
+    unsigned char* data;
+    unsigned int data_len;
+} StringInfo;
+
+#define SSL_WRITE_KEY           1
+
+#define SSLV3_VERSION          0x300
+#define TLSV1_VERSION          0x301
+
+#define SSL_CLIENT_RANDOM       1
+#define SSL_SERVER_RANDOM       2
+#define SSL_CIPHER              4
+#define SSL_HAVE_SESSION_KEY    8    
+#define SSL_VERSION             0x10
+#define SSL_MASTER_SECRET       0x20
+
+#define SSL_CIPHER_MODE_STREAM  0
+#define SSL_CIPHER_MODE_CBC     1
+
+typedef struct _SslCipherSuite {
+     int number;
+     int kex;
+     int sig;
+     int enc;
+     int block;
+     int bits;
+     int eff_bits;
+     int dig;
+     int dig_len;
+     int export;
+     int mode;
+} SslCipherSuite;
+
+typedef struct _SslDecoder {
+    SslCipherSuite* cipher_suite;
+    unsigned char _mac_key[20];
+    StringInfo mac_key;
+    SSL_CIPHER_CTX evp;    
+    guint32 seq;
+} SslDecoder;
+
+#define KEX_RSA         0x10
+#define KEX_DH          0x11
+
+#define SIG_RSA         0x20
+#define SIG_DSS         0x21
+#define SIG_NONE        0x22
+
+#define ENC_DES         0x30
+#define ENC_3DES        0x31
+#define ENC_RC4         0x32
+#define ENC_RC2         0x33
+#define ENC_IDEA        0x34
+#define ENC_AES         0x35
+#define ENC_AES256      0x36
+#define ENC_NULL        0x37
+
+#define DIG_MD5         0x40
+#define DIG_SHA         0x41
+
+/*typedef struct _SslService {
+    address addr;
+    guint port;
+} SslService;*/
+
+typedef struct _SslDecryptSession {
+    unsigned char _master_secret[48];
+    unsigned char _session_id[256];
+    unsigned char _client_random[32];
+    unsigned char _server_random[32];
+    StringInfo session_id;
+    StringInfo server_random;
+    StringInfo client_random;
+    StringInfo master_secret;
+    StringInfo pre_master_secret;
+    
+    int cipher;
+    int state;
+    SslCipherSuite cipher_suite;
+    SslDecoder server;
+    SslDecoder client;
+    SSL_PRIVATE_KEY* private_key;
+    guint32 version;
+    guint16 version_netorder;  
+
+} SslDecryptSession;
+
+void ssl_lib_init();
+void ssl_session_init(SslDecryptSession*);
+int ssl_data_alloc(StringInfo* str, unsigned int len);
+int ssl_data_set(StringInfo* data, unsigned char* src, unsigned int len);
+
+SSL_PRIVATE_KEY* ssl_load_key(FILE* fp);
+void ssl_free_key(SSL_PRIVATE_KEY*);
+
+int ssl_find_cipher(int num,SslCipherSuite* cs);
+
+int ssl_generate_keyring_material(SslDecryptSession*ssl_session);
+
+int ssl_decrypt_pre_master_secret(SslDecryptSession*ssl_session, 
+    StringInfo* entrypted_pre_master, SSL_PRIVATE_KEY *pk);
+
+int ssl_decrypt_record(SslDecryptSession*ssl,SslDecoder* decoder, int ct, 
+        const unsigned char* in, int inl,unsigned char*out,int* outl);
+
+#ifdef SSL_DECRYPT_DEBUG
+void ssl_debug_printf(char* fmt,...);
+void ssl_print_data(const char* name, const unsigned char* data, int len);
+void ssl_print_string(const char* name, const StringInfo* data);
+void ssl_print_text_data(const char* name, const unsigned char* data, int len);
+#else
+static inline char* ssl_debug_printf(char* fmt,...) { return fmt; }
+#define ssl_print_data(a, b, c)
+#define ssl_print_string(a, b)
+#define ssl_print_text_data(a, b, c)
+#endif
+
+#endif
diff -uNr ethereal-0.10.14/gtk/main.c ethereal-0.10.14-patch/gtk/main.c
--- ethereal-0.10.14/gtk/main.c	2005-12-27 03:20:26.000000000 +0100
+++ ethereal-0.10.14-patch/gtk/main.c	2006-02-02 20:12:17.000000000 +0100
@@ -3434,6 +3434,8 @@
                          filter_te);
     set_menu_object_data("/Analyze/Follow TCP Stream", E_DFILTER_TE_KEY,
                          filter_te);
+    set_menu_object_data("/Analyze/Follow SSL Stream", E_DFILTER_TE_KEY,
+                         filter_te);                         
     set_menu_object_data("/Analyze/Apply as Filter/Selected", E_DFILTER_TE_KEY,
                          filter_te);
     set_menu_object_data("/Analyze/Apply as Filter/Not Selected", E_DFILTER_TE_KEY,
diff -uNr ethereal-0.10.14/gtk/Makefile.common ethereal-0.10.14-patch/gtk/Makefile.common
--- ethereal-0.10.14/gtk/Makefile.common	2005-12-27 03:20:26.000000000 +0100
+++ ethereal-0.10.14-patch/gtk/Makefile.common	2006-02-02 20:12:17.000000000 +0100
@@ -102,7 +102,8 @@
 	text_page.c	\
 	toolbar.c	\
 	voip_calls.c \
-	webbrowser.c
+	webbrowser.c \
+	ssl-dlg.c
 
 
 ETHEREAL_TAP_SRC = \
diff -uNr ethereal-0.10.14/gtk/menu.c ethereal-0.10.14-patch/gtk/menu.c
--- ethereal-0.10.14/gtk/menu.c	2005-12-27 03:20:25.000000000 +0100
+++ ethereal-0.10.14-patch/gtk/menu.c	2006-02-02 20:12:17.000000000 +0100
@@ -171,6 +171,8 @@
                     currently opened/captured file only.
 */
 
+void
+ssl_stream_cb(GtkWidget * w, gpointer data _U_);
 
 /* main menu */
 static GtkItemFactoryEntry menu_items[] =
@@ -377,6 +379,8 @@
     ITEM_FACTORY_ENTRY("/Analyze/<separator>", NULL, NULL, 0, "<Separator>", NULL),
     ITEM_FACTORY_ENTRY("/Analyze/_Follow TCP Stream", NULL,
                        follow_stream_cb, 0, NULL, NULL),    
+    ITEM_FACTORY_ENTRY("/Analyze/_Follow SSL Stream", NULL,
+                       ssl_stream_cb, 0, NULL, NULL),    
     ITEM_FACTORY_ENTRY("/_Statistics", NULL, NULL, 0, "<Branch>", NULL),
     ITEM_FACTORY_STOCK_ENTRY("/Statistics/_Summary", NULL, summary_open_cb, 0, GTK_STOCK_PROPERTIES),
     ITEM_FACTORY_ENTRY("/Statistics/_Protocol Hierarchy", NULL,
@@ -458,6 +462,8 @@
 
     ITEM_FACTORY_ENTRY("/Follow TCP Stream", NULL, follow_stream_cb,
                        0, NULL, NULL),
+    ITEM_FACTORY_ENTRY("/Follow SSL Stream", NULL, ssl_stream_cb,
+                       0, NULL, NULL),
 
     ITEM_FACTORY_ENTRY("/<separator>", NULL, NULL, 0, "<Separator>", NULL),
 
@@ -505,6 +511,8 @@
 
     ITEM_FACTORY_ENTRY("/Follow TCP Stream", NULL, follow_stream_cb,
                        0, NULL, NULL),
+    ITEM_FACTORY_ENTRY("/Follow SSL Stream", NULL, ssl_stream_cb,
+                       0, NULL, NULL),
 
     ITEM_FACTORY_ENTRY("/<separator>", NULL, NULL, 0, "<Separator>", NULL),
 
@@ -1988,6 +1996,12 @@
       cf->current_frame != NULL ? (cf->edt->pi.ipproto == IP_PROTO_TCP) : FALSE);
   set_menu_sensitivity(tree_view_menu_factory, "/Follow TCP Stream",
       cf->current_frame != NULL ? (cf->edt->pi.ipproto == IP_PROTO_TCP) : FALSE);
+  set_menu_sensitivity(main_menu_factory, "/Analyze/Follow SSL Stream",
+      cf->current_frame != NULL ? (cf->edt->pi.ipproto == IP_PROTO_TCP) : FALSE);
+  set_menu_sensitivity(packet_list_menu_factory, "/Follow SSL Stream",
+      cf->current_frame != NULL ? (cf->edt->pi.ipproto == IP_PROTO_TCP) : FALSE);
+  set_menu_sensitivity(tree_view_menu_factory, "/Follow SSL Stream",
+      cf->current_frame != NULL ? (cf->edt->pi.ipproto == IP_PROTO_TCP) : FALSE);
   set_menu_sensitivity(main_menu_factory, "/Analyze/Decode As...",
       cf->current_frame != NULL && decode_as_ok());
   set_menu_sensitivity(packet_list_menu_factory, "/Decode As...",
diff -uNr ethereal-0.10.14/gtk/ssl-dlg.c ethereal-0.10.14-patch/gtk/ssl-dlg.c
--- ethereal-0.10.14/gtk/ssl-dlg.c	1970-01-01 01:00:00.000000000 +0100
+++ ethereal-0.10.14-patch/gtk/ssl-dlg.c	2006-02-02 20:12:17.000000000 +0100
@@ -0,0 +1,1049 @@
+/* ssl_dlg.c
+ *
+ * $Id: ssl-dlg.c,v 1.3 2006/02/02 16:52:21 altoor Exp $
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include <gtk/gtk.h>
+
+#include <stdio.h>
+#include <string.h>
+
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <ctype.h>
+
+#include <color.h>
+#include <gtk/colors.h>
+#include <gtk/main.h>
+#include <epan/follow.h>
+#include <gtk/dlg_utils.h>
+#include <gtk/keys.h>
+#include <globals.h>
+#include <alert_box.h>
+#include <simple_dialog.h>
+#include <epan/dissectors/packet-ipv6.h>
+#include <epan/prefs.h>
+#include <epan/addr_resolv.h>
+#include <epan/charsets.h>
+#include <util.h>
+#include <gtk/gui_utils.h>
+#include <epan/epan_dissect.h>
+#include <epan/filesystem.h>
+#include <gtk/compat_macros.h>
+#include <epan/ipproto.h>
+#include <gtk/font_utils.h>
+#include <wiretap/file_util.h>
+#include <epan/tap.h>
+
+#ifdef SSL_PLUGIN
+#include "packet-ssl-utils.h"
+#else
+#include <epan/dissectors/packet-ssl-utils.h>
+#endif
+
+#ifndef SSL_SUFFIX
+#define SSL_SUFFIX ""
+#endif
+
+/* Show Stream */
+typedef enum {
+	FROM_CLIENT,
+	FROM_SERVER,
+	BOTH_HOSTS
+} show_stream_t;
+
+/* Show Type */
+typedef enum {
+	SHOW_ASCII,
+	SHOW_HEXDUMP,
+	SHOW_CARRAY,
+	SHOW_RAW
+} show_type_t;
+
+typedef struct {
+	show_stream_t	show_stream;
+	show_type_t	show_type;
+	GtkWidget	*text;
+	GtkWidget	*ascii_bt;
+	GtkWidget	*ebcdic_bt;
+	GtkWidget	*hexdump_bt;
+	GtkWidget	*carray_bt;
+	GtkWidget	*raw_bt;
+	GtkWidget	*follow_save_as_w;
+	gboolean        is_ipv6;
+	char		*filter_out_filter;
+	GtkWidget	*filter_te;
+	GtkWidget	*streamwindow;
+        GList           *ssl_decrypted_data;
+        guint           bytes_written[2];
+        guint           client_port;
+        char            client_ip[MAX_IPADDR_LEN];
+} follow_info_t;
+
+static void follow_destroy_cb(GtkWidget * win, gpointer data);
+static void follow_charset_toggle_cb(GtkWidget * w, gpointer parent_w);
+static void follow_load_text(follow_info_t *follow_info);
+static void follow_filter_out_stream(GtkWidget * w, gpointer parent_w);
+static void follow_save_as_cmd_cb(GtkWidget * w, gpointer data);
+static void follow_save_as_ok_cb(GtkWidget * w, gpointer fs);
+static void follow_save_as_destroy_cb(GtkWidget * win, gpointer user_data);
+static void follow_stream_om_both(GtkWidget * w, gpointer data);
+static void follow_stream_om_client(GtkWidget * w, gpointer data);
+static void follow_stream_om_server(GtkWidget * w, gpointer data);
+
+
+#define E_FOLLOW_INFO_KEY "follow_info_key"
+
+/* List of "follow_info_t" structures for all "Follow TCP Stream" windows,
+   so we can redraw them all if the colors or font changes. */
+static GList *follow_infos;
+
+typedef struct {
+    gboolean is_server;
+    StringInfo* data;
+} SslDecryptedRecord;
+
+/* Add a "follow_info_t" structure to the list. */
+static void
+remember_follow_info(follow_info_t *follow_info)
+{
+  follow_infos = g_list_append(follow_infos, follow_info);
+}
+
+/* Remove a "follow_info_t" structure from the list. */
+static void
+forget_follow_info(follow_info_t *follow_info)
+{
+  follow_infos = g_list_remove(follow_infos, follow_info);
+}
+
+static int
+ssl_queue_packet_data(void *tapdata, packet_info *pinfo, epan_dissect_t *edt, const void *ssl)
+{
+    follow_info_t* follow_info = tapdata;
+    SslDecryptedRecord* rec;
+    int proto_ssl = (int) ssl;
+    StringInfo* data = p_get_proto_data(pinfo->fd, proto_ssl);
+    /*ssl_debug_printf("ssl_queue_packet_data: pinfo %p proto_ssl %d data %p\n",
+        pinfo, proto_ssl, data);*/
+
+    /* skip packet without decrypted data payload*/    
+    if (!data)
+        return 0;
+    
+    /* compute packet direction */
+    rec = g_malloc(sizeof(SslDecryptedRecord));
+
+    if (follow_info->client_port == 0) {
+        follow_info->client_port = pinfo->srcport;
+        memcpy(follow_info->client_ip, pinfo->src.data, pinfo->src.len);
+    }
+    if (memcmp(follow_info->client_ip, pinfo->src.data, pinfo->src.len) == 0 &&
+        follow_info->client_port == pinfo->srcport) {
+        rec->is_server = 0;
+    }
+    else 
+        rec->is_server = 1;
+
+    /* update stream counter */
+    follow_info->bytes_written[rec->is_server] += data->data_len;
+    
+    /* extract decrypted data and queue it locally */    
+    rec->data = data;
+    follow_info->ssl_decrypted_data = g_list_append(
+        follow_info->ssl_decrypted_data,rec);
+    /*ssl_debug_printf("ssl_queue_packet_data: ssl_decrypted_data %p data len %d\n",
+        follow_info->ssl_decrypted_data, data->data_len);*/
+
+    return 0;
+}
+
+/* Follow the TCP stream, if any, to which the last packet that we called
+   a dissection routine on belongs (this might be the most recently
+   selected packet, or it might be the last packet in the file). */
+void
+ssl_stream_cb(GtkWidget * w, gpointer data _U_)
+{
+    GtkWidget	*streamwindow, *vbox, *txt_scrollw, *text, *filter_te;
+    GtkWidget	*hbox, *button_hbox, *button, *radio_bt;
+    GtkWidget   *stream_fr, *stream_vb;
+    GtkWidget	*stream_om, *stream_menu, *stream_mi;
+    GtkTooltips *tooltips;
+    gchar		*follow_filter;
+    const gchar	*previous_filter;
+    int		    filter_out_filter_len, previus_filter_len;
+    const char	*hostname0, *hostname1;
+    char		*port0, *port1;
+    char		string[128];
+    follow_tcp_stats_t stats;
+    follow_info_t	*follow_info;
+    GString* msg;
+
+    /* we got tcp so we can follow */
+    if (cfile.edt->pi.ipproto != 6) {
+            simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+                          "Error following stream.  Please make\n"
+                          "sure you have an SSL packet selected.");
+            return;
+    }
+
+    follow_info = g_new0(follow_info_t, 1);
+    
+    /* data will be passed via tap callback*/
+    msg = register_tap_listener("ssl"SSL_SUFFIX, follow_info, NULL,
+	NULL, ssl_queue_packet_data, NULL);
+    if (msg)
+    {
+        simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+            "Can't register ssl tap: %s\n",msg->str);
+        return;
+    }
+    
+    /* Create a new filter that matches all packets in the TCP stream,
+       and set the display filter entry accordingly */
+    reset_tcp_reassembly();
+    follow_filter = build_follow_filter(&cfile.edt->pi);
+
+    /* Set the display filter entry accordingly */
+    filter_te = OBJECT_GET_DATA(w, E_DFILTER_TE_KEY);
+
+    /* needed in follow_filter_out_stream(), is there a better way? */
+    follow_info->filter_te = filter_te;
+
+    /* save previous filter, const since we're not supposed to alter */
+    previous_filter =
+        (const gchar *)gtk_entry_get_text(GTK_ENTRY(filter_te));
+
+    /* allocate our new filter. API claims g_malloc terminates program on failure */
+    /* my calc for max alloc needed is really +10 but when did a few extra bytes hurt ? */
+    previus_filter_len = previous_filter?strlen(previous_filter):0;
+    filter_out_filter_len = strlen(follow_filter) + previus_filter_len + 16;
+    follow_info->filter_out_filter = (gchar *)g_malloc(filter_out_filter_len);
+
+    /* append the negation */
+    if(previus_filter_len) {
+        g_snprintf(follow_info->filter_out_filter, filter_out_filter_len,
+        "%s and !(%s)", previous_filter, follow_filter);
+    } else {
+        g_snprintf(follow_info->filter_out_filter, filter_out_filter_len,
+        "!(%s)", follow_filter);
+    }
+
+
+    gtk_entry_set_text(GTK_ENTRY(filter_te), follow_filter);
+
+    /* Run the display filter so it goes in effect - even if it's the
+       same as the previous display filter. */
+    main_filter_packets(&cfile, follow_filter, TRUE);
+
+    /* Free the filter string, as we're done with it. */
+    g_free(follow_filter);
+
+    /* The data_out_file should now be full of the streams information */
+    remove_tap_listener(follow_info);
+
+    /* The data_out_filename file now has all the text that was in the session */
+    streamwindow = dlg_window_new("Follow TCP stream");
+
+    /* needed in follow_filter_out_stream(), is there a better way? */
+    follow_info->streamwindow = streamwindow;
+
+    gtk_widget_set_name(streamwindow, "TCP stream window");
+    gtk_window_set_default_size(GTK_WINDOW(streamwindow), DEF_WIDTH, DEF_HEIGHT);
+    gtk_container_border_width(GTK_CONTAINER(streamwindow), 6);
+
+    /* setup the container */
+    tooltips = gtk_tooltips_new ();
+
+    vbox = gtk_vbox_new(FALSE, 6);
+    gtk_container_add(GTK_CONTAINER(streamwindow), vbox);
+
+    /* content frame */
+    if (incomplete_tcp_stream) {
+            stream_fr = gtk_frame_new("Stream Content (incomplete)");
+    } else {
+            stream_fr = gtk_frame_new("Stream Content");
+    }
+    gtk_container_add(GTK_CONTAINER(vbox), stream_fr);
+    gtk_widget_show(stream_fr);
+
+    stream_vb = gtk_vbox_new(FALSE, 6);
+    gtk_container_set_border_width( GTK_CONTAINER(stream_vb) , 6);
+    gtk_container_add(GTK_CONTAINER(stream_fr), stream_vb);
+
+    /* create a scrolled window for the text */
+    txt_scrollw = scrolled_window_new(NULL, NULL);
+#if GTK_MAJOR_VERSION >= 2
+    gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(txt_scrollw),
+                                        GTK_SHADOW_IN);
+#endif
+    gtk_box_pack_start(GTK_BOX(stream_vb), txt_scrollw, TRUE, TRUE, 0);
+
+    /* create a text box */
+#if GTK_MAJOR_VERSION < 2
+    text = gtk_text_new(NULL, NULL);
+    gtk_text_set_editable(GTK_TEXT(text), FALSE);
+#else
+    text = gtk_text_view_new();
+    gtk_text_view_set_editable(GTK_TEXT_VIEW(text), FALSE);
+#endif
+    gtk_container_add(GTK_CONTAINER(txt_scrollw), text);
+    follow_info->text = text;
+
+
+    /* stream hbox */
+    hbox = gtk_hbox_new(FALSE, 1);
+    gtk_box_pack_start(GTK_BOX(stream_vb), hbox, FALSE, FALSE, 0);
+
+    /* Create Save As Button */
+    button = BUTTON_NEW_FROM_STOCK(GTK_STOCK_SAVE_AS);
+    SIGNAL_CONNECT(button, "clicked", follow_save_as_cmd_cb, follow_info);
+    gtk_tooltips_set_tip (tooltips, button, "Save the content as currently displayed ", NULL);
+    gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
+
+    /* Stream to show */
+    follow_tcp_stats(&stats);
+
+    if (stats.is_ipv6) {
+      struct e_in6_addr ipaddr;
+      memcpy(&ipaddr, stats.ip_address[0], 16);
+      hostname0 = get_hostname6(&ipaddr);
+      memcpy(&ipaddr, stats.ip_address[0], 16);
+      hostname1 = get_hostname6(&ipaddr);
+    } else {
+      guint32 ipaddr;
+      memcpy(&ipaddr, stats.ip_address[0], 4);
+      hostname0 = get_hostname(ipaddr);
+      memcpy(&ipaddr, stats.ip_address[1], 4);
+      hostname1 = get_hostname(ipaddr);
+    }
+
+    port0 = get_tcp_port(stats.tcp_port[0]);
+    port1 = get_tcp_port(stats.tcp_port[1]);
+
+    follow_info->is_ipv6 = stats.is_ipv6;
+
+    stream_om = gtk_option_menu_new();
+    stream_menu = gtk_menu_new();
+
+    /* Both Stream Directions */
+    g_snprintf(string, sizeof(string),
+             "Entire conversation (%u bytes)",
+             follow_info->bytes_written[0] + follow_info->bytes_written[1]);
+    stream_mi = gtk_menu_item_new_with_label(string);
+    SIGNAL_CONNECT(stream_mi, "activate", follow_stream_om_both,
+                   follow_info);
+    gtk_menu_append(GTK_MENU(stream_menu), stream_mi);
+    gtk_widget_show(stream_mi);
+    follow_info->show_stream = BOTH_HOSTS;
+
+    /* Host 0 --> Host 1 */
+    g_snprintf(string, sizeof(string), "%s:%s --> %s:%s (%u bytes)",
+             hostname0, port0, hostname1, port1,
+             follow_info->bytes_written[0]);
+    stream_mi = gtk_menu_item_new_with_label(string);
+    SIGNAL_CONNECT(stream_mi, "activate", follow_stream_om_client,
+                   follow_info);
+    gtk_menu_append(GTK_MENU(stream_menu), stream_mi);
+    gtk_widget_show(stream_mi);
+
+    /* Host 1 --> Host 0 */
+    g_snprintf(string, sizeof(string), "%s:%s --> %s:%s (%u bytes)",
+             hostname1, port1, hostname0, port0,
+             follow_info->bytes_written[1]);
+    stream_mi = gtk_menu_item_new_with_label(string);
+    SIGNAL_CONNECT(stream_mi, "activate", follow_stream_om_server,
+                   follow_info);
+    gtk_menu_append(GTK_MENU(stream_menu), stream_mi);
+    gtk_widget_show(stream_mi);
+
+    gtk_option_menu_set_menu(GTK_OPTION_MENU(stream_om), stream_menu);
+    /* Set history to 0th item, i.e., the first item. */
+    gtk_option_menu_set_history(GTK_OPTION_MENU(stream_om), 0);
+    gtk_tooltips_set_tip (tooltips, stream_om,
+        "Select the stream direction to display", NULL);
+    gtk_box_pack_start(GTK_BOX(hbox), stream_om, FALSE, FALSE, 0);
+
+    /* ASCII radio button */
+    radio_bt = gtk_radio_button_new_with_label(NULL, "ASCII");
+    gtk_tooltips_set_tip (tooltips, radio_bt, "Stream data output in \"ASCII\" format", NULL);
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_bt), TRUE);
+    gtk_box_pack_start(GTK_BOX(hbox), radio_bt, FALSE, FALSE, 0);
+    SIGNAL_CONNECT(radio_bt, "toggled", follow_charset_toggle_cb,
+                   follow_info);
+    follow_info->ascii_bt = radio_bt;
+    follow_info->show_type = SHOW_ASCII;
+
+    /* HEX DUMP radio button */
+    radio_bt = gtk_radio_button_new_with_label(gtk_radio_button_group
+                                        (GTK_RADIO_BUTTON(radio_bt)),
+                                        "Hex Dump");
+    gtk_tooltips_set_tip (tooltips, radio_bt, "Stream data output in \"Hexdump\" format", NULL);
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_bt), FALSE);
+    gtk_box_pack_start(GTK_BOX(hbox), radio_bt, FALSE, FALSE, 0);
+    SIGNAL_CONNECT(radio_bt, "toggled", follow_charset_toggle_cb,
+                   follow_info);
+    follow_info->hexdump_bt = radio_bt;
+
+    /* C Array radio button */
+    radio_bt = gtk_radio_button_new_with_label(gtk_radio_button_group
+                                        (GTK_RADIO_BUTTON(radio_bt)),
+                                        "C Arrays");
+    gtk_tooltips_set_tip (tooltips, radio_bt, "Stream data output in \"C Array\" format", NULL);
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_bt), FALSE);
+    gtk_box_pack_start(GTK_BOX(hbox), radio_bt, FALSE, FALSE, 0);
+    SIGNAL_CONNECT(radio_bt, "toggled", follow_charset_toggle_cb,
+                   follow_info);
+    follow_info->carray_bt = radio_bt;
+
+    /* Raw radio button */
+    radio_bt = gtk_radio_button_new_with_label(gtk_radio_button_group
+                                        (GTK_RADIO_BUTTON(radio_bt)),
+                                        "Raw");
+    gtk_tooltips_set_tip (tooltips, radio_bt, "Stream data output in \"Raw\" (binary) format. "
+    "As this contains non printable characters, the screen output will be in ASCII format", NULL);
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_bt), FALSE);
+    gtk_box_pack_start(GTK_BOX(hbox), radio_bt, FALSE, FALSE, 0);
+    SIGNAL_CONNECT(radio_bt, "toggled", follow_charset_toggle_cb,
+                   follow_info);
+    follow_info->raw_bt = radio_bt;
+
+    /* button hbox */
+    button_hbox = gtk_hbutton_box_new();
+    gtk_box_pack_start(GTK_BOX(vbox), button_hbox, FALSE, FALSE, 0);
+    gtk_button_box_set_layout (GTK_BUTTON_BOX(button_hbox), GTK_BUTTONBOX_END);
+    gtk_button_box_set_spacing(GTK_BUTTON_BOX(button_hbox), 5);
+
+    /* Create exclude stream button */
+    button = gtk_button_new_with_label("Filter out this stream");
+    SIGNAL_CONNECT(button, "clicked", follow_filter_out_stream, follow_info);
+    gtk_tooltips_set_tip (tooltips, button,
+    "Build a display filter which cuts this stream from the capture", NULL);
+    gtk_box_pack_start(GTK_BOX(button_hbox), button, FALSE, FALSE, 0);
+
+    /* Create Close Button */
+    button = BUTTON_NEW_FROM_STOCK(GTK_STOCK_CLOSE);
+    gtk_tooltips_set_tip (tooltips, button,
+        "Close the dialog and keep the current display filter", NULL);
+    gtk_box_pack_start(GTK_BOX(button_hbox), button, FALSE, FALSE, 0);
+    GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
+
+    window_set_cancel_button(streamwindow, button, window_cancel_button_cb);
+
+    /* Tuck away the follow_info object into the window */
+    OBJECT_SET_DATA(streamwindow, E_FOLLOW_INFO_KEY, follow_info);
+
+    follow_load_text(follow_info);
+    remember_follow_info(follow_info);
+
+    SIGNAL_CONNECT(streamwindow, "delete_event", window_delete_event_cb, NULL);
+    SIGNAL_CONNECT(streamwindow, "destroy", follow_destroy_cb, NULL);
+
+    /* Make sure this widget gets destroyed if we quit the main loop,
+       so that if we exit, we clean up any temporary files we have
+       for "Follow TCP Stream" windows. */
+    gtk_quit_add_destroy(gtk_main_level(), GTK_OBJECT(streamwindow));
+
+    gtk_widget_show_all(streamwindow);
+    window_present(streamwindow);
+}
+
+/* The destroy call back has the responsibility of
+ * unlinking the temporary file
+ * and freeing the filter_out_filter */
+static void
+follow_destroy_cb(GtkWidget *w, gpointer data _U_)
+{
+    GList* cur;
+    follow_info_t	*follow_info;
+
+    follow_info = OBJECT_GET_DATA(w, E_FOLLOW_INFO_KEY);
+    g_free(follow_info->filter_out_filter);
+    forget_follow_info(follow_info);
+
+    /* free decrypted data list*/
+    for (cur = follow_info->ssl_decrypted_data; cur; cur = g_list_next(cur))
+        if (cur->data)
+        {
+            /*ssl_debug_printf("follow_destroy_cb: freeing chunk %p\n", cur->data);*/
+            g_free(cur->data);
+            cur->data = NULL;
+        }
+    g_list_free (follow_info->ssl_decrypted_data);
+    g_free(follow_info);
+}
+
+/* XXX - can I emulate follow_charset_toggle_cb() instead of having
+ * 3 different functions here?
+ * That might not be a bad idea, as it might mean we only reload
+ * the window once, not twice - see follow_charset_toggle_cb()
+ * for an explanation. */
+static void
+follow_stream_om_both(GtkWidget *w _U_, gpointer data)
+{
+	follow_info_t	*follow_info = data;
+	follow_info->show_stream = BOTH_HOSTS;
+	follow_load_text(follow_info);
+}
+
+static void
+follow_stream_om_client(GtkWidget *w _U_, gpointer data)
+{
+	follow_info_t	*follow_info = data;
+	follow_info->show_stream = FROM_CLIENT;
+	follow_load_text(follow_info);
+}
+
+static void
+follow_stream_om_server(GtkWidget *w _U_, gpointer data)
+{
+	follow_info_t	*follow_info = data;
+	follow_info->show_stream = FROM_SERVER;
+	follow_load_text(follow_info);
+}
+
+
+/* Handles the display style toggling */
+static void
+follow_charset_toggle_cb(GtkWidget * w _U_, gpointer data)
+{
+	follow_info_t	*follow_info = data;
+
+	/*
+	 * A radio button toggles when it goes on and when it goes
+	 * off, so when you click a radio button two signals are
+	 * delivered.  We only want to reprocess the display once,
+	 * so we do it only when the button goes on.
+	 */
+	if (GTK_TOGGLE_BUTTON(w)->active) {
+		if (w == follow_info->hexdump_bt)
+			follow_info->show_type = SHOW_HEXDUMP;
+		else if (w == follow_info->carray_bt)
+			follow_info->show_type = SHOW_CARRAY;
+		else if (w == follow_info->ascii_bt)
+			follow_info->show_type = SHOW_ASCII;
+		else if (w == follow_info->raw_bt)
+			follow_info->show_type = SHOW_RAW;
+		follow_load_text(follow_info);
+	}
+}
+
+#define FLT_BUF_SIZE 1024
+
+typedef enum {
+	FRS_OK,
+	FRS_OPEN_ERROR,
+	FRS_READ_ERROR,
+	FRS_PRINT_ERROR
+} frs_return_t;
+
+/*
+ * XXX - the routine pointed to by "print_line" doesn't get handed lines,
+ * it gets handed bufferfuls.  That's fine for "follow_write_raw()"
+ * and "follow_add_to_gtk_text()", but, as "follow_print_text()" calls
+ * the "print_line()" routine from "print.c", and as that routine might
+ * genuinely expect to be handed a line (if, for example, it's using
+ * some OS or desktop environment's printing API, and that API expects
+ * to be handed lines), "follow_print_text()" should probably accumulate
+ * lines in a buffer and hand them "print_line()".  (If there's a
+ * complete line in a buffer - i.e., there's nothing of the line in
+ * the previous buffer or the next buffer - it can just hand that to
+ * "print_line()" after filtering out non-printables, as an
+ * optimization.)
+ *
+ * This might or might not be the reason why C arrays display
+ * correctly but get extra blank lines very other line when printed.
+ */
+static frs_return_t
+follow_read_stream(follow_info_t *follow_info,
+		   gboolean (*print_line) (char *, size_t, gboolean, void *),
+		   void *arg)
+{
+    int			iplen;
+    guint32		current_pos, global_client_pos = 0, global_server_pos = 0;
+    guint32		*global_pos;
+    gboolean		skip;
+    gchar               initbuf[256];
+    guint32             server_packet_count = 0;
+    guint32             client_packet_count = 0;
+    static const gchar	hexchars[16] = "0123456789abcdef";
+    GList* cur;
+
+    iplen = (follow_info->is_ipv6) ? 16 : 4;
+    
+    /*ssl_debug_printf("follow_read_stream: iplen %d list %p\n", iplen,
+        follow_info->ssl_decrypted_data);*/
+
+    for (cur = follow_info->ssl_decrypted_data; cur; cur = g_list_next(cur)) {
+        SslDecryptedRecord* rec = cur->data;
+	skip = FALSE;
+	if (!rec->is_server) {
+	    global_pos = &global_client_pos;
+	    if (follow_info->show_stream == FROM_SERVER) {
+		skip = TRUE;
+	    }
+	}
+	else {
+	    global_pos = &global_server_pos;
+	    if (follow_info->show_stream == FROM_CLIENT) {
+		skip = TRUE;
+	    }
+	}
+
+        if (!skip) {
+            size_t nchars = rec->data->data_len;
+            char* buffer = (char*) rec->data->data;
+            
+            /*ssl_debug_printf("follow_read_stream: chunk len %d is_server %d\n", 
+                nchars, rec->is_server);*/
+            
+            switch (follow_info->show_type) {
+    
+            case SHOW_ASCII:
+                /* If our native arch is EBCDIC, call:
+                 * ASCII_TO_EBCDIC(buffer, nchars);
+                 */
+                if (!(*print_line) (buffer, nchars, rec->is_server, arg))
+                    goto print_error;
+                break;
+    
+            case SHOW_RAW:
+                /* Don't translate, no matter what the native arch
+                 * is.
+                 */
+                if (!(*print_line) (buffer, nchars, rec->is_server, arg))
+                    goto print_error;
+                break;
+    
+            case SHOW_HEXDUMP:
+                current_pos = 0;
+                while (current_pos < nchars) {
+                    gchar hexbuf[256];
+                    int i;
+                    gchar *cur = hexbuf, *ascii_start;
+    
+                    /* is_server indentation : put 78 spaces at the
+                     * beginning of the string */
+                    if (rec->is_server && follow_info->show_stream == BOTH_HOSTS) {
+                        memset(cur, ' ', 78);
+                        cur += 78;
+                    }
+                    cur += g_snprintf(cur, 20, "%08X  ", *global_pos);
+                    /* 49 is space consumed by hex chars */
+                    ascii_start = cur + 49;
+                    for (i = 0; i < 16 && current_pos + i < nchars; i++) {
+                        *cur++ =
+                            hexchars[(buffer[current_pos + i] & 0xf0) >> 4];
+                        *cur++ =
+                            hexchars[buffer[current_pos + i] & 0x0f];
+                        *cur++ = ' ';
+                        if (i == 7)
+                            *cur++ = ' ';
+                    }
+                    /* Fill it up if column isn't complete */
+                    while (cur < ascii_start)  
+                        *cur++ = ' ';
+    
+                    /* Now dump bytes as text */
+                    for (i = 0; i < 16 && current_pos + i < nchars; i++) {
+                        *cur++ =
+                            (isprint((guchar)buffer[current_pos + i]) ?
+                            buffer[current_pos + i] : '.' );
+                        if (i == 7) {
+                            *cur++ = ' ';
+                        }
+                    }
+                    current_pos += i;
+                    (*global_pos) += i;
+                    *cur++ = '\n';
+                    *cur = 0;
+                    if (!(*print_line) (hexbuf, strlen(hexbuf), rec->is_server, arg))
+                        goto print_error;
+                }
+                break;
+    
+            case SHOW_CARRAY:
+                current_pos = 0;
+                g_snprintf(initbuf, sizeof(initbuf), "char peer%d_%d[] = {\n", 
+                        rec->is_server ? 1 : 0, 
+                        rec->is_server ? server_packet_count++ : client_packet_count++);
+                if (!(*print_line) (initbuf, strlen(initbuf), rec->is_server, arg))
+                    goto print_error;
+                while (current_pos < nchars) {
+                    gchar hexbuf[256];
+                    int i, cur;
+    
+                    cur = 0;
+                    for (i = 0; i < 8 && current_pos + i < nchars; i++) {
+                      /* Prepend entries with "0x" */
+                      hexbuf[cur++] = '0';
+                      hexbuf[cur++] = 'x';
+                        hexbuf[cur++] =
+                            hexchars[(buffer[current_pos + i] & 0xf0) >> 4];
+                        hexbuf[cur++] =
+                            hexchars[buffer[current_pos + i] & 0x0f];
+    
+                        /* Delimit array entries with a comma */
+                        if (current_pos + i + 1 < nchars)
+                          hexbuf[cur++] = ',';
+    
+                        hexbuf[cur++] = ' ';
+                    }
+    
+                    /* Terminate the array if we are at the end */
+                    if (current_pos + i == nchars) {
+                        hexbuf[cur++] = '}';
+                        hexbuf[cur++] = ';';
+                    }
+    
+                    current_pos += i;
+                    (*global_pos) += i;
+                    hexbuf[cur++] = '\n';
+                    hexbuf[cur] = 0;
+                    if (!(*print_line) (hexbuf, strlen(hexbuf), rec->is_server, arg))
+                        goto print_error;
+                }
+                break;
+            }
+        }
+    }
+    return FRS_OK;
+
+print_error:
+    return FRS_PRINT_ERROR;
+}
+
+/*
+ * XXX - for text printing, we probably want to wrap lines at 80 characters;
+ * (PostScript printing is doing this already), and perhaps put some kind of 
+ * dingbat (to use the technical term) to indicate a wrapped line, along the 
+ * lines of what's done when displaying this in a window, as per Warren Young's 
+ * suggestion.
+ */
+static gboolean
+follow_print_text(char *buffer, size_t nchars, gboolean is_server _U_, void *arg)
+{
+    print_stream_t *stream = arg;
+    size_t i;
+    char *str;
+
+    /* convert non printable characters */
+    for (i = 0; i < nchars; i++) {
+        if (buffer[i] == '\n' || buffer[i] == '\r')
+            continue;
+        if (! isprint((guchar)buffer[i])) {
+            buffer[i] = '.';
+        }
+    }
+
+    /* convert unterminated char array to a zero terminated string */
+    str = g_malloc(nchars + 1);
+    memcpy(str, buffer, nchars);
+    str[nchars] = 0;
+    print_line(stream, /*indent*/ 0, str);
+    g_free(str);
+
+    return TRUE;
+}
+
+static gboolean
+follow_write_raw(char *buffer, size_t nchars, gboolean is_server _U_, void *arg)
+{
+    FILE *fh = arg;
+    size_t nwritten;
+
+    nwritten = fwrite(buffer, 1, nchars, fh);
+    if (nwritten != nchars)
+	return FALSE;
+
+    return TRUE;
+}
+
+static void
+follow_filter_out_stream(GtkWidget * w _U_, gpointer data)
+{
+    follow_info_t	*follow_info = data;
+
+    /* Lock out user from messing with us. (ie. don't free our data!) */
+    gtk_widget_set_sensitive(follow_info->streamwindow, FALSE);
+
+    /* Set the display filter. */
+    gtk_entry_set_text(GTK_ENTRY(follow_info->filter_te), follow_info->filter_out_filter);
+
+    /* Run the display filter so it goes in effect. */
+    main_filter_packets(&cfile, follow_info->filter_out_filter, FALSE);
+
+    /* we force a subsequent close */
+    window_destroy(follow_info->streamwindow);
+
+    return;
+}
+
+/* static variable declarations to speed up the performance
+ * of follow_load_text and follow_add_to_gtk_text
+ */
+static GdkColor server_fg, server_bg;
+static GdkColor client_fg, client_bg;
+#if GTK_MAJOR_VERSION >= 2
+static GtkTextTag *server_tag, *client_tag;
+#endif
+
+static gboolean
+follow_add_to_gtk_text(char *buffer, size_t nchars, gboolean is_server,
+		       void *arg)
+{
+    GtkWidget *text = arg;
+#if GTK_MAJOR_VERSION >= 2
+    GtkTextBuffer *buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
+    GtkTextIter    iter;
+#endif
+
+#if GTK_MAJOR_VERSION >= 2 || GTK_MINOR_VERSION >= 3
+    /* While our isprint() hack is in place, we
+     * have to use convert some chars to '.' in order
+     * to be able to see the data we *should* see
+     * in the GtkText widget.
+     */
+    size_t i;
+
+    for (i = 0; i < nchars; i++) {
+        if (buffer[i] == '\n' || buffer[i] == '\r')
+            continue;
+        if (! isprint(buffer[i])) {
+            buffer[i] = '.';
+        }
+    }
+#endif
+
+#if GTK_MAJOR_VERSION < 2
+    if (is_server) {
+	gtk_text_insert(GTK_TEXT(text), user_font_get_regular(), &server_fg, 
+			&server_bg, buffer, nchars);
+    } else {
+	gtk_text_insert(GTK_TEXT(text), user_font_get_regular(), &client_fg, 
+			&client_bg, buffer, nchars);
+    }
+#else
+    gtk_text_buffer_get_end_iter(buf, &iter);
+    if (is_server) {
+	gtk_text_buffer_insert_with_tags(buf, &iter, buffer, nchars, 
+					server_tag, NULL);
+    } else {
+	gtk_text_buffer_insert_with_tags(buf, &iter, buffer, nchars, 
+					client_tag, NULL);
+    }
+#endif
+    return TRUE;
+}
+
+static void
+follow_load_text(follow_info_t *follow_info)
+{
+#if GTK_MAJOR_VERSION < 2
+    int bytes_already;
+#else
+    GtkTextBuffer *buf;
+
+    buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(follow_info->text));
+#endif
+
+    /* prepare colors one time for repeated use by follow_add_to_gtk_text */
+    color_t_to_gdkcolor(&server_fg, &prefs.st_server_fg);
+    color_t_to_gdkcolor(&server_bg, &prefs.st_server_bg);
+    color_t_to_gdkcolor(&client_fg, &prefs.st_client_fg);
+    color_t_to_gdkcolor(&client_bg, &prefs.st_client_bg);
+
+    /* Delete any info already in text box */
+#if GTK_MAJOR_VERSION < 2
+    bytes_already = gtk_text_get_length(GTK_TEXT(follow_info->text));
+    if (bytes_already > 0) {
+	gtk_text_set_point(GTK_TEXT(follow_info->text), 0);
+	gtk_text_forward_delete(GTK_TEXT(follow_info->text), bytes_already);
+    }
+
+    /* stop the updates while we fill the text box */
+    gtk_text_freeze(GTK_TEXT(follow_info->text));
+#else
+    /* prepare tags one time for repeated use by follow_add_to_gtk_text */
+    server_tag = gtk_text_buffer_create_tag(buf, NULL, "foreground-gdk", &server_fg,
+                                     "background-gdk", &server_bg, "font-desc",
+                                     user_font_get_regular(), NULL);
+    client_tag = gtk_text_buffer_create_tag(buf, NULL, "foreground-gdk", &client_fg,
+                                     "background-gdk", &client_bg, "font-desc",
+                                     user_font_get_regular(), NULL);
+
+    gtk_text_buffer_set_text(buf, "", -1);
+#endif
+    follow_read_stream(follow_info, follow_add_to_gtk_text, follow_info->text);
+#if GTK_MAJOR_VERSION < 2
+    gtk_text_thaw(GTK_TEXT(follow_info->text));
+#endif
+}
+
+
+/*
+ * Keep a static pointer to the current "Save TCP Follow Stream As" window, if
+ * any, so that if somebody tries to do "Save"
+ * while there's already a "Save TCP Follow Stream" window up, we just pop
+ * up the existing one, rather than creating a new one.
+ */
+static void
+follow_save_as_cmd_cb(GtkWidget *w _U_, gpointer data)
+{
+    GtkWidget		*new_win;
+    follow_info_t	*follow_info = data;
+
+    if (follow_info->follow_save_as_w != NULL) {
+	/* There's already a dialog box; reactivate it. */
+	reactivate_window(follow_info->follow_save_as_w);
+	return;
+    }
+
+    new_win = file_selection_new("Ethereal: Save TCP Follow Stream As",
+                                 FILE_SELECTION_SAVE);
+    follow_info->follow_save_as_w = new_win;
+
+    /* Tuck away the follow_info object into the window */
+    OBJECT_SET_DATA(new_win, E_FOLLOW_INFO_KEY, follow_info);
+
+    SIGNAL_CONNECT(new_win, "destroy", follow_save_as_destroy_cb, follow_info);
+
+#if (GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 4) || GTK_MAJOR_VERSION > 2
+    if (gtk_dialog_run(GTK_DIALOG(new_win)) == GTK_RESPONSE_ACCEPT)
+    {
+        follow_save_as_ok_cb(new_win, new_win);
+    } else {
+        window_destroy(new_win);
+    }
+#else
+    /* Connect the ok_button to file_save_as_ok_cb function and pass along a
+       pointer to the file selection box widget */
+    SIGNAL_CONNECT(GTK_FILE_SELECTION(new_win)->ok_button, 
+        "clicked", follow_save_as_ok_cb, new_win);
+
+    window_set_cancel_button(new_win, 
+        GTK_FILE_SELECTION(new_win)->cancel_button, window_cancel_button_cb);
+
+    gtk_file_selection_set_filename(GTK_FILE_SELECTION(new_win), "");
+
+    SIGNAL_CONNECT(new_win, "delete_event", window_delete_event_cb, NULL);
+
+    gtk_widget_show_all(new_win);
+    window_present(new_win);
+#endif
+}
+
+
+static void
+follow_save_as_ok_cb(GtkWidget * w _U_, gpointer fs)
+{
+    gchar		*to_name;
+    follow_info_t	*follow_info;
+    FILE		*fh;
+    print_stream_t	*stream = NULL;
+    gchar		*dirname;
+
+#if (GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 4) || GTK_MAJOR_VERSION > 2
+    to_name = g_strdup(gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fs)));
+#else
+    to_name = g_strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION(fs)));
+#endif
+
+    /* Perhaps the user specified a directory instead of a file.
+       Check whether they did. */
+    if (test_for_directory(to_name) == EISDIR) {
+        /* It's a directory - set the file selection box to display that
+           directory, and leave the selection box displayed. */
+        set_last_open_dir(to_name);
+        g_free(to_name);
+        file_selection_set_current_folder(fs, get_last_open_dir());
+        return;
+    }
+
+    follow_info = OBJECT_GET_DATA(fs, E_FOLLOW_INFO_KEY);
+    if (follow_info->show_type == SHOW_RAW) {
+        /* Write the data out as raw binary data */
+        fh = eth_fopen(to_name, "wb");
+    } else {
+        /* Write it out as text */
+        fh = eth_fopen(to_name, "w");
+    }
+    if (fh == NULL) {
+        open_failure_alert_box(to_name, errno, TRUE);
+        g_free(to_name);
+        return;
+    }
+
+    gtk_widget_hide(GTK_WIDGET(fs));
+    window_destroy(GTK_WIDGET(fs));
+
+    if (follow_info->show_type == SHOW_RAW) {
+        switch (follow_read_stream(follow_info, follow_write_raw, fh)) {
+        case FRS_OK:
+            if (fclose(fh) == EOF)
+                write_failure_alert_box(to_name, errno);
+            break;
+
+        case FRS_OPEN_ERROR:
+        case FRS_READ_ERROR:
+            fclose(fh);
+            break;
+
+        case FRS_PRINT_ERROR:
+            write_failure_alert_box(to_name, errno);
+            fclose(fh);
+            break;
+        }
+    } else {
+	stream = print_stream_text_stdio_new(fh);
+	switch (follow_read_stream(follow_info, follow_print_text, stream)) {
+        case FRS_OK:
+            if (!destroy_print_stream(stream))
+                write_failure_alert_box(to_name, errno);
+            break;
+
+        case FRS_OPEN_ERROR:
+        case FRS_READ_ERROR:
+            destroy_print_stream(stream);
+            break;
+
+        case FRS_PRINT_ERROR:
+            write_failure_alert_box(to_name, errno);
+            destroy_print_stream(stream);
+            break;
+        }
+    }
+
+    /* Save the directory name for future file dialogs. */
+    dirname = get_dirname(to_name);  /* Overwrites to_name */
+    set_last_open_dir(dirname);
+    g_free(to_name);
+}
+
+static void
+follow_save_as_destroy_cb(GtkWidget * win _U_, gpointer data)
+{
+    follow_info_t	*follow_info = data;
+
+    /* Note that we no longer have a dialog box. */
+    follow_info->follow_save_as_w = NULL;
+}
diff -uNr ethereal-0.10.14/Makefile.am ethereal-0.10.14-patch/Makefile.am
--- ethereal-0.10.14/Makefile.am	2005-12-27 03:21:20.000000000 +0100
+++ ethereal-0.10.14-patch/Makefile.am	2006-02-02 20:12:16.000000000 +0100
@@ -283,7 +283,8 @@
 	@SNMP_LIBS@ @SSL_LIBS@ 		\
 	$(plugin_ldadd)			\
 	@PCRE_LIBS@			\
-	@PCAP_LIBS@ @GTK_LIBS@ @ADNS_LIBS@ @KRB5_LIBS@ @FRAMEWORKS@
+	@PCAP_LIBS@ @GTK_LIBS@ @ADNS_LIBS@ @KRB5_LIBS@ @FRAMEWORKS@ \
+	@LIBGNUTLS_LIBS@
 
 # Additional libs that I know how to build. These will be
 # linked into the tethereal executable.
@@ -305,7 +306,8 @@
 	$(plugin_ldadd)			\
 	@PCRE_LIBS@			\
 	@GLIB_LIBS@ -lm			\
-	@PCAP_LIBS@ @SOCKET_LIBS@ @NSL_LIBS@ @ADNS_LIBS@ @KRB5_LIBS@
+	@PCAP_LIBS@ @SOCKET_LIBS@ @NSL_LIBS@ @ADNS_LIBS@ @KRB5_LIBS@ \
+	@LIBGNUTLS_LIBS@
 
 if ENABLE_STATIC
 tethereal_LDFLAGS = -Wl,-static -all-static
@@ -421,7 +423,8 @@
 	$(plugin_ldadd)			\
 	@PCRE_LIBS@			\
 	@GLIB_LIBS@ -lm			\
-	@PCAP_LIBS@ @SOCKET_LIBS@ @NSL_LIBS@ @ADNS_LIBS@ @KRB5_LIBS@
+	@PCAP_LIBS@ @SOCKET_LIBS@ @NSL_LIBS@ @ADNS_LIBS@ @KRB5_LIBS@ \
+	@LIBGNUTLS_LIBS@
 
 dftest_LDFLAGS = -export-dynamic