Check for an infinite loop while processing cipher suites. Fixes bug 1582.
[metze/wireshark/wip.git] / epan / dissectors / packet-ssl.c
index 3597308db51ed86698b7fa1a5142571a16f61656..5b39280e4ca7a5b8a070b4637550831426dab061 100644 (file)
@@ -4,8 +4,8 @@
  *
  * $Id$
  *
- * Ethereal - Network traffic analyzer
- * By Gerald Combs <gerald@ethereal.com>
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
  * Copyright 1998 Gerald Combs
  *
  * This program is free software; you can redistribute it and/or
@@ -27,7 +27,7 @@
  *     http://www.netscape.com/eng/security/SSL_2.html
  *
  * for SSL 2.0 specs.
- * 
+ *
  * See
  *
  *     http://www.netscape.com/eng/ssl3/
@@ -42,7 +42,7 @@
  *
  * for PCT 1 draft specs.
  *
- * See 
+ * See
  *
  *     http://research.sun.com/projects/crypto/draft-ietf-tls-ecc-05.txt
  *
  *
  * 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)
  *
  *       - 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
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/stat.h>
+#include <errno.h>
+
+#include <sys/types.h>
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
 
 #include <glib.h>
 
 #include <epan/conversation.h>
+#include <epan/reassemble.h>
 #include <epan/prefs.h>
+#include <epan/emem.h>
+#include <epan/inet_v6defs.h>
+#include <epan/dissectors/packet-tcp.h>
 #include <epan/dissectors/packet-x509af.h>
+#include <epan/tap.h>
+#include <epan/filesystem.h>
+#include <epan/report_err.h>
+#include <epan/expert.h>
+#include "packet-ssl.h"
+#include "packet-ssl-utils.h"
+
 
 static gboolean ssl_desegment = TRUE;
+static gboolean ssl_desegment_app_data = TRUE;
 
 
 /*********************************************************************
@@ -108,80 +131,89 @@ static gboolean ssl_desegment = TRUE;
  *********************************************************************/
 
 /* Initialize the protocol and registered fields */
-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_ssl2_record                    = -1;
-static int hf_ssl2_record_is_escape          = -1;
-static int hf_ssl2_record_padding_length     = -1;
-static int hf_ssl2_msg_type                  = -1;
-static int hf_pct_msg_type                   = -1;
-static int hf_ssl_change_cipher_spec         = -1;
-static int hf_ssl_alert_message              = -1;
-static int hf_ssl_alert_message_level        = -1;
-static int hf_ssl_alert_message_description  = -1;
-static int hf_ssl_handshake_protocol         = -1;
-static int hf_ssl_handshake_type             = -1;
-static int hf_ssl_handshake_length           = -1;
-static int hf_ssl_handshake_client_version   = -1;
-static int hf_ssl_handshake_server_version   = -1;
-static int hf_ssl_handshake_random_time      = -1;
-static int hf_ssl_handshake_random_bytes     = -1;
-static int hf_ssl_handshake_cipher_suites_len = -1;
-static int hf_ssl_handshake_cipher_suites    = -1;
-static int hf_ssl_handshake_cipher_suite     = -1;
-static int hf_ssl_handshake_session_id       = -1;
-static int hf_ssl_handshake_comp_methods_len = -1;
-static int hf_ssl_handshake_comp_methods     = -1;
-static int hf_ssl_handshake_comp_method      = -1;
-static int hf_ssl_handshake_extensions_len   = -1;
-static int hf_ssl_handshake_extension_type   = -1;
-static int hf_ssl_handshake_extension_len    = -1;
-static int hf_ssl_handshake_extension_data   = -1;
-static int hf_ssl_handshake_certificates_len = -1;
-static int hf_ssl_handshake_certificates     = -1;
-static int hf_ssl_handshake_certificate      = -1;
-static int hf_ssl_handshake_certificate_len  = -1;
-static int hf_ssl_handshake_cert_types_count = -1;
-static int hf_ssl_handshake_cert_types       = -1;
-static int hf_ssl_handshake_cert_type        = -1;
-static int hf_ssl_handshake_finished         = -1;
-static int hf_ssl_handshake_md5_hash         = -1;
-static int hf_ssl_handshake_sha_hash         = -1;
-static int hf_ssl_handshake_session_id_len   = -1;
-static int hf_ssl_handshake_dnames_len       = -1;
-static int hf_ssl_handshake_dnames           = -1;
-static int hf_ssl_handshake_dname_len        = -1;
-static int hf_ssl_handshake_dname            = -1;
-static int hf_ssl2_handshake_cipher_spec_len = -1;
-static int hf_ssl2_handshake_session_id_len  = -1;
-static int hf_ssl2_handshake_challenge_len   = -1;
-static int hf_ssl2_handshake_cipher_spec     = -1;
-static int hf_ssl2_handshake_challenge       = -1;
-static int hf_ssl2_handshake_clear_key_len   = -1;
-static int hf_ssl2_handshake_enc_key_len     = -1;
-static int hf_ssl2_handshake_key_arg_len     = -1;
-static int hf_ssl2_handshake_clear_key       = -1;
-static int hf_ssl2_handshake_enc_key         = -1;
-static int hf_ssl2_handshake_key_arg         = -1;
-static int hf_ssl2_handshake_session_id_hit  = -1;
-static int hf_ssl2_handshake_cert_type       = -1;
-static int hf_ssl2_handshake_connection_id_len = -1;
-static int hf_ssl2_handshake_connection_id   = -1;
-static int hf_pct_handshake_cipher_spec        = -1;
-static int hf_pct_handshake_hash_spec  = -1;
-static int hf_pct_handshake_cert_spec  = -1;
-static int hf_pct_handshake_cert       = -1;
-static int hf_pct_handshake_server_cert        = -1;
-static int hf_pct_handshake_exch_spec  = -1;
-static int hf_pct_handshake_hash       = -1;
-static int hf_pct_handshake_cipher     = -1;
-static int hf_pct_handshake_exch       = -1;
-static int hf_pct_handshake_sig                = -1;
-static int hf_pct_msg_error_type       = -1;
+static gint ssl_tap                           = -1;
+static gint proto_ssl                         = -1;
+static gint hf_ssl_record                     = -1;
+static gint hf_ssl_record_content_type        = -1;
+static gint hf_ssl_record_version             = -1;
+static gint hf_ssl_record_length              = -1;
+static gint hf_ssl_record_appdata             = -1;
+static gint hf_ssl2_record                    = -1;
+static gint hf_ssl2_record_is_escape          = -1;
+static gint hf_ssl2_record_padding_length     = -1;
+static gint hf_ssl2_msg_type                  = -1;
+static gint hf_pct_msg_type                   = -1;
+static gint hf_ssl_change_cipher_spec         = -1;
+static gint hf_ssl_alert_message              = -1;
+static gint hf_ssl_alert_message_level        = -1;
+static gint hf_ssl_alert_message_description  = -1;
+static gint hf_ssl_handshake_protocol         = -1;
+static gint hf_ssl_handshake_type             = -1;
+static gint hf_ssl_handshake_length           = -1;
+static gint hf_ssl_handshake_client_version   = -1;
+static gint hf_ssl_handshake_server_version   = -1;
+static gint hf_ssl_handshake_random_time      = -1;
+static gint hf_ssl_handshake_random_bytes     = -1;
+static gint hf_ssl_handshake_cipher_suites_len = -1;
+static gint hf_ssl_handshake_cipher_suites    = -1;
+static gint hf_ssl_handshake_cipher_suite     = -1;
+static gint hf_ssl_handshake_session_id       = -1;
+static gint hf_ssl_handshake_comp_methods_len = -1;
+static gint hf_ssl_handshake_comp_methods     = -1;
+static gint hf_ssl_handshake_comp_method      = -1;
+static gint hf_ssl_handshake_extensions_len   = -1;
+static gint hf_ssl_handshake_extension_type   = -1;
+static gint hf_ssl_handshake_extension_len    = -1;
+static gint hf_ssl_handshake_extension_data   = -1;
+static gint hf_ssl_handshake_certificates_len = -1;
+static gint hf_ssl_handshake_certificates     = -1;
+static gint hf_ssl_handshake_certificate      = -1;
+static gint hf_ssl_handshake_certificate_len  = -1;
+static gint hf_ssl_handshake_cert_types_count = -1;
+static gint hf_ssl_handshake_cert_types       = -1;
+static gint hf_ssl_handshake_cert_type        = -1;
+static gint hf_ssl_handshake_finished         = -1;
+static gint hf_ssl_handshake_md5_hash         = -1;
+static gint hf_ssl_handshake_sha_hash         = -1;
+static gint hf_ssl_handshake_session_id_len   = -1;
+static gint hf_ssl_handshake_dnames_len       = -1;
+static gint hf_ssl_handshake_dnames           = -1;
+static gint hf_ssl_handshake_dname_len        = -1;
+static gint hf_ssl_handshake_dname            = -1;
+static gint hf_ssl2_handshake_cipher_spec_len = -1;
+static gint hf_ssl2_handshake_session_id_len  = -1;
+static gint hf_ssl2_handshake_challenge_len   = -1;
+static gint hf_ssl2_handshake_cipher_spec     = -1;
+static gint hf_ssl2_handshake_challenge       = -1;
+static gint hf_ssl2_handshake_clear_key_len   = -1;
+static gint hf_ssl2_handshake_enc_key_len     = -1;
+static gint hf_ssl2_handshake_key_arg_len     = -1;
+static gint hf_ssl2_handshake_clear_key       = -1;
+static gint hf_ssl2_handshake_enc_key         = -1;
+static gint hf_ssl2_handshake_key_arg         = -1;
+static gint hf_ssl2_handshake_session_id_hit  = -1;
+static gint hf_ssl2_handshake_cert_type       = -1;
+static gint hf_ssl2_handshake_connection_id_len = -1;
+static gint hf_ssl2_handshake_connection_id   = -1;
+static gint hf_pct_handshake_cipher_spec       = -1;
+static gint hf_pct_handshake_hash_spec = -1;
+static gint hf_pct_handshake_cert_spec = -1;
+static gint hf_pct_handshake_cert      = -1;
+static gint hf_pct_handshake_server_cert       = -1;
+static gint hf_pct_handshake_exch_spec = -1;
+static gint hf_pct_handshake_hash      = -1;
+static gint hf_pct_handshake_cipher    = -1;
+static gint hf_pct_handshake_exch      = -1;
+static gint hf_pct_handshake_sig               = -1;
+static gint hf_pct_msg_error_type      = -1;
+static int hf_ssl_reassembled_in = -1;
+static int hf_ssl_segments = -1;
+static int hf_ssl_segment = -1;
+static int hf_ssl_segment_overlap = -1;
+static int hf_ssl_segment_overlap_conflict = -1;
+static int hf_ssl_segment_multiple_tails = -1;
+static int hf_ssl_segment_too_long_fragment = -1;
+static int hf_ssl_segment_error = -1;
 
 /* Initialize the subtree pointers */
 static gint ett_ssl                   = -1;
@@ -194,501 +226,133 @@ static gint ett_ssl_extension         = -1;
 static gint ett_ssl_certs             = -1;
 static gint ett_ssl_cert_types        = -1;
 static gint ett_ssl_dnames            = -1;
+static gint ett_ssl_random            = -1;
 static gint ett_pct_cipher_suites        = -1;
 static gint ett_pct_hash_suites                  = -1;
 static gint ett_pct_cert_suites                  = -1;
 static gint ett_pct_exch_suites                  = -1;
-
-/* The TCP port to associate with by default */
-#define TCP_PORT_SSL                    443
-#define TCP_PORT_SSL_LDAP               636
-#define TCP_PORT_SSL_IMAP               993
-#define TCP_PORT_SSL_POP                995
-
-/* version state tables */
-#define SSL_VER_UNKNOWN                   0
-#define SSL_VER_SSLv2                     1
-#define SSL_VER_SSLv3                     2
-#define SSL_VER_TLS                       3
-#define SSL_VER_PCT                       4
-
-/* corresponds to the #defines above */
-static gchar* ssl_version_short_names[] = {
-    "SSL",
-    "SSLv2",
-    "SSLv3",
-    "TLS",
-    "PCT"
+static gint ett_ssl_segments = -1;
+static gint ett_ssl_segment  = -1;
+
+
+/* not all of the hf_fields below make sense for SSL but we have to provide
+   them anyways to comply with the api (which was aimed for ip fragment
+   reassembly) */
+static const fragment_items ssl_segment_items = {
+       &ett_ssl_segment,
+       &ett_ssl_segments,
+       &hf_ssl_segments,
+       &hf_ssl_segment,
+       &hf_ssl_segment_overlap,
+       &hf_ssl_segment_overlap_conflict,
+       &hf_ssl_segment_multiple_tails,
+       &hf_ssl_segment_too_long_fragment,
+       &hf_ssl_segment_error,
+       &hf_ssl_reassembled_in,
+       "Segments"
 };
 
-/* other defines */
-#define SSL_ID_CHG_CIPHER_SPEC         0x14
-#define SSL_ID_ALERT                   0x15
-#define SSL_ID_HANDSHAKE               0x16
-#define SSL_ID_APP_DATA                0x17
-
-#define SSL_HND_HELLO_REQUEST          0
-#define SSL_HND_CLIENT_HELLO           1
-#define SSL_HND_SERVER_HELLO           2
-#define SSL_HND_CERTIFICATE            11
-#define SSL_HND_SERVER_KEY_EXCHG       12
-#define SSL_HND_CERT_REQUEST           13
-#define SSL_HND_SVR_HELLO_DONE         14
-#define SSL_HND_CERT_VERIFY            15
-#define SSL_HND_CLIENT_KEY_EXCHG       16
-#define SSL_HND_FINISHED               20
-
-#define SSL2_HND_ERROR                 0x00
-#define SSL2_HND_CLIENT_HELLO          0x01
-#define SSL2_HND_CLIENT_MASTER_KEY     0x02
-#define SSL2_HND_CLIENT_FINISHED       0x03
-#define SSL2_HND_SERVER_HELLO          0x04
-#define SSL2_HND_SERVER_VERIFY         0x05
-#define SSL2_HND_SERVER_FINISHED       0x06
-#define SSL2_HND_REQUEST_CERTIFICATE   0x07
-#define SSL2_HND_CLIENT_CERTIFICATE    0x08
-
-#define PCT_VERSION_1                                  0x8001
-
-#define PCT_MSG_CLIENT_HELLO                   0x01
-#define PCT_MSG_SERVER_HELLO                   0x02
-#define PCT_MSG_CLIENT_MASTER_KEY              0x03
-#define PCT_MSG_SERVER_VERIFY                  0x04
-#define PCT_MSG_ERROR                                  0x05
-
-#define PCT_CH_OFFSET_V1                               0xa
-
-#define PCT_CIPHER_DES                                 0x01
-#define PCT_CIPHER_IDEA                                        0x02
-#define PCT_CIPHER_RC2                                 0x03
-#define PCT_CIPHER_RC4                                 0x04
-#define PCT_CIPHER_DES_112                             0x05
-#define PCT_CIPHER_DES_168                             0x06
-
-#define PCT_HASH_MD5                                   0x0001
-#define PCT_HASH_MD5_TRUNC_64                  0x0002
-#define PCT_HASH_SHA                                   0x0003
-#define PCT_HASH_SHA_TRUNC_80                  0x0004
-#define PCT_HASH_DES_DM                                        0x0005
-
-#define PCT_CERT_NONE                                  0x00
-#define PCT_CERT_X509                                  0x01
-#define PCT_CERT_PKCS7                                 0x02
-
-#define PCT_SIG_NONE                                   0x0000
-#define PCT_SIG_RSA_MD5                                        0x0001
-#define PCT_SIG_RSA_SHA                                        0x0002
-#define PCT_SIG_DSA_SHA                                        0x0003
-
-#define PCT_EXCH_RSA_PKCS1                             0x01
-#define PCT_EXCH_RSA_PKCS1_TOKEN_DES   0x02
-#define PCT_EXCH_RSA_PKCS1_TOKEN_DES3  0x03
-#define PCT_EXCH_RSA_PKCS1_TOKEN_RC2   0x04
-#define PCT_EXCH_RSA_PKCS1_TOKEN_RC4   0x05
-#define PCT_EXCH_DH_PKCS3                              0x06
-#define PCT_EXCH_DH_PKCS3_TOKEN_DES            0x07
-#define PCT_EXCH_DH_PKCS3_TOKEN_DES3   0x08
-#define PCT_EXCH_FORTEZZA_TOKEN                        0x09
-
-#define PCT_ERR_BAD_CERTIFICATE                        0x01
-#define PCT_ERR_CLIENT_AUTH_FAILED             0x02
-#define PCT_ERR_ILLEGAL_MESSAGE                        0x03
-#define PCT_ERR_INTEGRITY_CHECK_FAILED 0x04
-#define PCT_ERR_SERVER_AUTH_FAILED             0x05
-#define PCT_ERR_SPECS_MISMATCH                 0x06
+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_compressed_data = {NULL, 0};
+static StringInfo ssl_decrypted_data = {NULL, 0};
+static gint ssl_decrypted_data_avail = 0;
 
-/*
- * Lookup tables
- *
- */
-static const value_string ssl_20_msg_types[] = {
-    { SSL2_HND_ERROR,               "Error" },
-    { SSL2_HND_CLIENT_HELLO,        "Client Hello" },
-    { SSL2_HND_CLIENT_MASTER_KEY,   "Client Master Key" },
-    { SSL2_HND_CLIENT_FINISHED,     "Client Finished" },
-    { SSL2_HND_SERVER_HELLO,        "Server Hello" },
-    { SSL2_HND_SERVER_VERIFY,       "Server Verify" },
-    { SSL2_HND_SERVER_FINISHED,     "Server Finished" },
-    { SSL2_HND_REQUEST_CERTIFICATE, "Request Certificate" },
-    { SSL2_HND_CLIENT_CERTIFICATE,  "Client Certificate" },
-    { 0x00, NULL },
-};
-
-static const value_string ssl_20_cipher_suites[] = {
-    { 0x010080, "SSL2_RC4_128_WITH_MD5" },
-    { 0x020080, "SSL2_RC4_128_EXPORT40_WITH_MD5" },
-    { 0x030080, "SSL2_RC2_CBC_128_CBC_WITH_MD5" },
-    { 0x040080, "SSL2_RC2_CBC_128_CBC_WITH_MD5" },
-    { 0x050080, "SSL2_IDEA_128_CBC_WITH_MD5" },
-    { 0x060040, "SSL2_DES_64_CBC_WITH_MD5" },
-    { 0x0700c0, "SSL2_DES_192_EDE3_CBC_WITH_MD5" },
-    { 0x080080, "SSL2_RC4_64_WITH_MD5" },
-    { 0x000000, "TLS_NULL_WITH_NULL_NULL" },
-    { 0x000001, "TLS_RSA_WITH_NULL_MD5" },
-    { 0x000002, "TLS_RSA_WITH_NULL_SHA" },
-    { 0x000003, "TLS_RSA_EXPORT_WITH_RC4_40_MD5" },
-    { 0x000004, "TLS_RSA_WITH_RC4_128_MD5" },
-    { 0x000005, "TLS_RSA_WITH_RC4_128_SHA" },
-    { 0x000006, "TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5" },
-    { 0x000007, "TLS_RSA_WITH_IDEA_CBC_SHA" },
-    { 0x000008, "TLS_RSA_EXPORT_WITH_DES40_CBC_SHA" },
-    { 0x000009, "TLS_RSA_WITH_DES_CBC_SHA" },
-    { 0x00000a, "TLS_RSA_WITH_3DES_EDE_CBC_SHA" },
-    { 0x00000b, "TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA" },
-    { 0x00000c, "TLS_DH_DSS_WITH_DES_CBC_SHA" },
-    { 0x00000d, "TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA" },
-    { 0x00000e, "TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA" },
-    { 0x00000f, "TLS_DH_RSA_WITH_DES_CBC_SHA" },
-    { 0x000010, "TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA" },
-    { 0x000011, "TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA" },
-    { 0x000012, "TLS_DHE_DSS_WITH_DES_CBC_SHA" },
-    { 0x000013, "TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA" },
-    { 0x000014, "TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA" },
-    { 0x000015, "TLS_DHE_RSA_WITH_DES_CBC_SHA" },
-    { 0x000016, "TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA" },
-    { 0x000017, "TLS_DH_anon_EXPORT_WITH_RC4_40_MD5" },
-    { 0x000018, "TLS_DH_anon_WITH_RC4_128_MD5" },
-    { 0x000019, "TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA" },
-    { 0x00001a, "TLS_DH_anon_WITH_DES_CBC_SHA" },
-    { 0x00001b, "TLS_DH_anon_WITH_3DES_EDE_CBC_SHA" },
-    { 0x00001c, "SSL_FORTEZZA_KEA_WITH_NULL_SHA" },
-    { 0x00001d, "SSL_FORTEZZA_KEA_WITH_FORTEZZA_CBC_SHA" },
-    { 0x00001e, "SSL_FORTEZZA_KEA_WITH_RC4_128_SHA" },
-    { 0x00002f, "TLS_RSA_WITH_AES_128_CBC_SHA" },
-    { 0x000030, "TLS_DH_DSS_WITH_AES_128_CBC_SHA" },
-    { 0x000031, "TLS_DH_RSA_WITH_AES_128_CBC_SHA" },
-    { 0x000032, "TLS_DHE_DSS_WITH_AES_128_CBC_SHA" },
-    { 0x000033, "TLS_DHE_RSA_WITH_AES_128_CBC_SHA" },
-    { 0x000034, "TLS_DH_anon_WITH_AES_128_CBC_SHA" },
-    { 0x000035, "TLS_RSA_WITH_AES_256_CBC_SHA" },
-    { 0x000036, "TLS_DH_DSS_WITH_AES_256_CBC_SHA" },
-    { 0x000037, "TLS_DH_RSA_WITH_AES_256_CBC_SHA" },
-    { 0x000038, "TLS_DHE_DSS_WITH_AES_256_CBC_SHA" },
-    { 0x000039, "TLS_DHE_RSA_WITH_AES_256_CBC_SHA" },
-    { 0x00003A, "TLS_DH_anon_WITH_AES_256_CBC_SHA" },
-    { 0x000041, "TLS_RSA_WITH_CAMELLIA_128_CBC_SHA" },
-    { 0x000042, "TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA" },
-    { 0x000043, "TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA" },
-    { 0x000044, "TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA" },
-    { 0x000045, "TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA" },
-    { 0x000046, "TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA" },
-    { 0x000047, "TLS_ECDH_ECDSA_WITH_NULL_SHA" },
-    { 0x000048, "TLS_ECDH_ECDSA_WITH_RC4_128_SHA" },
-    { 0x000049, "TLS_ECDH_ECDSA_WITH_DES_CBC_SHA" },
-    { 0x00004A, "TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA" },
-    { 0x00004B, "TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA" },
-    { 0x00004C, "TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA" },
-    { 0x000060, "TLS_RSA_EXPORT1024_WITH_RC4_56_MD5" },
-    { 0x000061, "TLS_RSA_EXPORT1024_WITH_RC2_CBC_56_MD5" },
-    { 0x000062, "TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA" },
-    { 0x000063, "TLS_DHE_DSS_EXPORT1024_WITH_DES_CBC_SHA" },
-    { 0x000064, "TLS_RSA_EXPORT1024_WITH_RC4_56_SHA" },
-    { 0x000065, "TLS_DHE_DSS_EXPORT1024_WITH_RC4_56_SHA" },
-    { 0x000066, "TLS_DHE_DSS_WITH_RC4_128_SHA" },
-    { 0x000084, "TLS_RSA_WITH_CAMELLIA_256_CBC_SHA" },
-    { 0x000085, "TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA" },
-    { 0x000086, "TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA" },
-    { 0x000087, "TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA" },
-    { 0x000088, "TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA" },
-    { 0x000089, "TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA" },
-    /* these from http://www.mozilla.org/projects/
-         security/pki/nss/ssl/fips-ssl-ciphersuites.html */
-    { 0x00fefe, "SSL_RSA_FIPS_WITH_DES_CBC_SHA"},
-    { 0x00feff, "SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA" },
-    { 0x00ffe0, "SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA" },
-    { 0x00ffe1, "SSL_RSA_FIPS_WITH_DES_CBC_SHA"},
-    /* Microsoft's old PCT protocol. These are from Eric Rescorla's
-       book "SSL and TLS" */
-    { 0x8f8001, "PCT_SSL_COMPAT | PCT_VERSION_1" },
-    { 0x800003, "PCT_SSL_CERT_TYPE | PCT1_CERT_X509_CHAIN" },
-    { 0x800001, "PCT_SSL_CERT_TYPE | PCT1_CERT_X509" },
-    { 0x810001, "PCT_SSL_HASH_TYPE | PCT1_HASH_MD5" },
-    { 0x810003, "PCT_SSL_HASH_TYPE | PCT1_HASH_SHA" },
-    { 0x820001, "PCT_SSL_EXCH_TYPE | PCT1_EXCH_RSA_PKCS1" },
-    { 0x830004, "PCT_SSL_CIPHER_TYPE_1ST_HALF | PCT1_CIPHER_RC4" },
-    { 0x848040, "PCT_SSL_CIPHER_TYPE_2ND_HALF | PCT1_ENC_BITS_128 | PCT1_MAC_BITS_128" },
-    { 0x842840, "PCT_SSL_CIPHER_TYPE_2ND_HALF | PCT1_ENC_BITS_40 | PCT1_MAC_BITS_128" },
-    /* note that ciphersuites of {0x00????} are TLS cipher suites in
-     * a sslv2 client hello message; the ???? above is the two-byte
-     * tls cipher suite id
-     */
-    { 0x00, NULL }
-};
-
-static const value_string ssl_20_certificate_type[] = {
-    { 0x00, "N/A" },
-    { 0x01, "X.509 Certificate" },
-    { 0x00, NULL },
-};
+static gchar* ssl_keys_list = NULL;
 
-static const value_string ssl_31_content_type[] = {
-    { 20, "Change Cipher Spec" },
-    { 21, "Alert" },
-    { 22, "Handshake" },
-    { 23, "Application Data" },
-    { 0x00, NULL }
-};
-
-static const value_string ssl_versions[] = {
-    { 0x0301, "TLS 1.0" },
-    { 0x0300, "SSL 3.0" },
-    { 0x0002, "SSL 2.0" },
-    { 0x00, NULL }
-};
-
-#if 0
-/* XXX - would be used if we dissected the body of a Change Cipher Spec
-   message. */
-static const value_string ssl_31_change_cipher_spec[] = {
-    { 1, "Change Cipher Spec" },
-    { 0x00, NULL },
-};
-#endif
-
-static const value_string ssl_31_alert_level[] = {
-    { 1, "Warning" },
-    { 2, "Fatal" },
-    { 0x00, NULL }
-};
-
-static const value_string ssl_31_alert_description[] = {
-    {  0,  "Close Notify" },
-    { 10,  "Unexpected Message" },
-    { 20,  "Bad Record MAC" },
-    { 21,  "Decryption Failed" },
-    { 22,  "Record Overflow" },
-    { 30,  "Decompression Failure" },
-    { 40,  "Handshake Failure" },
-    { 42,  "Bad Certificate" },
-    { 43,  "Unsupported Certificate" },
-    { 44,  "Certificate Revoked" },
-    { 45,  "Certificate Expired" },
-    { 46,  "Certificate Unknown" },
-    { 47,  "Illegal Parameter" },
-    { 48,  "Unknown CA" },
-    { 49,  "Access Denied" },
-    { 50,  "Decode Error" },
-    { 51,  "Decrypt Error" },
-    { 60,  "Export Restriction" },
-    { 70,  "Protocol Version" },
-    { 71,  "Insufficient Security" },
-    { 80,  "Internal Error" },
-    { 90,  "User Canceled" },
-    { 100, "No Renegotiation" },
-    { 0x00, NULL }
-};
-
-static const value_string ssl_31_handshake_type[] = {
-    { SSL_HND_HELLO_REQUEST,     "Hello Request" },
-    { SSL_HND_CLIENT_HELLO,      "Client Hello" },
-    { SSL_HND_SERVER_HELLO,      "Server Hello" },
-    { SSL_HND_CERTIFICATE,       "Certificate" },
-    { SSL_HND_SERVER_KEY_EXCHG,  "Server Key Exchange" },
-    { SSL_HND_CERT_REQUEST,      "Certificate Request" },
-    { SSL_HND_SVR_HELLO_DONE,    "Server Hello Done" },
-    { SSL_HND_CERT_VERIFY,       "Certificate Verify" },
-    { SSL_HND_CLIENT_KEY_EXCHG,  "Client Key Exchange" },
-    { SSL_HND_FINISHED,          "Finished" },
-    { 0x00, NULL }
-};
-
-static const value_string ssl_31_compression_method[] = {
-    { 0, "null" },
-    { 1, "ZLIB" },
-    { 64, "LZS" },
-    { 0x00, NULL }
-};
-
-#if 0
-/* XXX - would be used if we dissected a Signature, as would be
-   seen in a server key exchange or certificate verify message. */
-static const value_string ssl_31_key_exchange_algorithm[] = {
-    { 0, "RSA" },
-    { 1, "Diffie Hellman" },
-    { 0x00, NULL }
-};
-
-static const value_string ssl_31_signature_algorithm[] = {
-    { 0, "Anonymous" },
-    { 1, "RSA" },
-    { 2, "DSA" },
-    { 0x00, NULL }
-};
+#if defined(SSL_DECRYPT_DEBUG) || defined(HAVE_LIBGNUTLS)
+static gchar* ssl_debug_file_name = NULL;
 #endif
 
-static const value_string ssl_31_client_certificate_type[] = {
-    { 1, "RSA Sign" },
-    { 2, "DSS Sign" },
-    { 3, "RSA Fixed DH" },
-    { 4, "DSS Fixed DH" },
-    { 0x00, NULL }
-};
-
-#if 0
-/* XXX - would be used if we dissected exchnage keys, as would be
-   seen in a client key exchange message. */
-static const value_string ssl_31_public_value_encoding[] = {
-    { 0, "Implicit" },
-    { 1, "Explicit" },
-    { 0x00, NULL }
+const gchar* ssl_version_short_names[] = {
+    "SSL",
+    "SSLv2",
+    "SSLv3",
+    "TLSv1",
+    "TLSv1.1",
+    "DTLSv1.0",
+    "PCT"
 };
-#endif
 
-static const value_string ssl_31_ciphersuite[] = {
-    { 0x0000, "TLS_NULL_WITH_NULL_NULL" },
-    { 0x0001, "TLS_RSA_WITH_NULL_MD5" },
-    { 0x0002, "TLS_RSA_WITH_NULL_SHA" },
-    { 0x0003, "TLS_RSA_EXPORT_WITH_RC4_40_MD5" },
-    { 0x0004, "TLS_RSA_WITH_RC4_128_MD5" },
-    { 0x0005, "TLS_RSA_WITH_RC4_128_SHA" },
-    { 0x0006, "TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5" },
-    { 0x0007, "TLS_RSA_WITH_IDEA_CBC_SHA" },
-    { 0x0008, "TLS_RSA_EXPORT_WITH_DES40_CBC_SHA" },
-    { 0x0009, "TLS_RSA_WITH_DES_CBC_SHA" },
-    { 0x000a, "TLS_RSA_WITH_3DES_EDE_CBC_SHA" },
-    { 0x000b, "TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA" },
-    { 0x000c, "TLS_DH_DSS_WITH_DES_CBC_SHA" },
-    { 0x000d, "TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA" },
-    { 0x000e, "TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA" },
-    { 0x000f, "TLS_DH_RSA_WITH_DES_CBC_SHA" },
-    { 0x0010, "TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA" },
-    { 0x0011, "TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA" },
-    { 0x0012, "TLS_DHE_DSS_WITH_DES_CBC_SHA" },
-    { 0x0013, "TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA" },
-    { 0x0014, "TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA" },
-    { 0x0015, "TLS_DHE_RSA_WITH_DES_CBC_SHA" },
-    { 0x0016, "TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA" },
-    { 0x0017, "TLS_DH_anon_EXPORT_WITH_RC4_40_MD5" },
-    { 0x0018, "TLS_DH_anon_WITH_RC4_128_MD5" },
-    { 0x0019, "TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA" },
-    { 0x001a, "TLS_DH_anon_WITH_DES_CBC_SHA" },
-    { 0x001b, "TLS_DH_anon_WITH_3DES_EDE_CBC_SHA" },
-    { 0x001c, "SSL_FORTEZZA_KEA_WITH_NULL_SHA" },
-    { 0x001d, "SSL_FORTEZZA_KEA_WITH_FORTEZZA_CBC_SHA" },
-    { 0x001e, "SSL_FORTEZZA_KEA_WITH_RC4_128_SHA" },
-    { 0x002f, "TLS_RSA_WITH_AES_128_CBC_SHA" },
-    { 0x0030, "TLS_DH_DSS_WITH_AES_128_CBC_SHA" },
-    { 0x0031, "TLS_DH_RSA_WITH_AES_128_CBC_SHA" },
-    { 0x0032, "TLS_DHE_DSS_WITH_AES_128_CBC_SHA" },
-    { 0x0033, "TLS_DHE_RSA_WITH_AES_128_CBC_SHA" },
-    { 0x0034, "TLS_DH_anon_WITH_AES_128_CBC_SHA" },
-    { 0x0035, "TLS_RSA_WITH_AES_256_CBC_SHA" },
-    { 0x0036, "TLS_DH_DSS_WITH_AES_256_CBC_SHA" },
-    { 0x0037, "TLS_DH_RSA_WITH_AES_256_CBC_SHA" },
-    { 0x0038, "TLS_DHE_DSS_WITH_AES_256_CBC_SHA" },
-    { 0x0039, "TLS_DHE_RSA_WITH_AES_256_CBC_SHA" },
-    { 0x003A, "TLS_DH_anon_WITH_AES_256_CBC_SHA" },
-    { 0x0041, "TLS_RSA_WITH_CAMELLIA_128_CBC_SHA" },
-    { 0x0042, "TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA" },
-    { 0x0043, "TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA" },
-    { 0x0044, "TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA" },
-    { 0x0045, "TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA" },
-    { 0x0046, "TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA" },
-    { 0x0047, "TLS_ECDH_ECDSA_WITH_NULL_SHA" },
-    { 0x0048, "TLS_ECDH_ECDSA_WITH_RC4_128_SHA" },
-    { 0x0049, "TLS_ECDH_ECDSA_WITH_DES_CBC_SHA" },
-    { 0x004A, "TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA" },
-    { 0x004B, "TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA" },
-    { 0x004C, "TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA" },
-    { 0x0060, "TLS_RSA_EXPORT1024_WITH_RC4_56_MD5" },
-    { 0x0061, "TLS_RSA_EXPORT1024_WITH_RC2_CBC_56_MD5" },
-    { 0x0062, "TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA" },
-    { 0x0063, "TLS_DHE_DSS_EXPORT1024_WITH_DES_CBC_SHA" },
-    { 0x0064, "TLS_RSA_EXPORT1024_WITH_RC4_56_SHA" },
-    { 0x0065, "TLS_DHE_DSS_EXPORT1024_WITH_RC4_56_SHA" },
-    { 0x0066, "TLS_DHE_DSS_WITH_RC4_128_SHA" },
-    { 0x0084, "TLS_RSA_WITH_CAMELLIA_256_CBC_SHA" },
-    { 0x0085, "TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA" },
-    { 0x0086, "TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA" },
-    { 0x0087, "TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA" },
-    { 0x0088, "TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA" },
-    { 0x0089, "TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA" },
-    /* these from http://www.mozilla.org/projects/
-         security/pki/nss/ssl/fips-ssl-ciphersuites.html */
-    { 0xfefe, "SSL_RSA_FIPS_WITH_DES_CBC_SHA"},
-    { 0xfeff, "SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA" },
-    { 0xffe0, "SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA" },
-    { 0xffe1, "SSL_RSA_FIPS_WITH_DES_CBC_SHA"},
-    /* note that ciphersuites 0xff00 - 0xffff are private */
-    { 0x00, NULL }
-};
+/* Forward declaration we need below */
+void proto_reg_handoff_ssl(void);
 
-static const value_string pct_msg_types[] = {
-    { PCT_MSG_CLIENT_HELLO,         "Client Hello" },
-    { PCT_MSG_SERVER_HELLO,         "Server Hello" },
-    { PCT_MSG_CLIENT_MASTER_KEY,    "Client Master Key" },
-    { PCT_MSG_SERVER_VERIFY,        "Server Verify" },
-    { PCT_MSG_ERROR,                "Error" },
-    { 0x00, NULL },
-};
+/* Desegmentation of SSL streams */
+/* table to hold defragmented SSL streams */
+static GHashTable *ssl_fragment_table = NULL;
+static void
+ssl_fragment_init(void)
+{
+       fragment_table_init(&ssl_fragment_table);
+}
 
-static const value_string pct_cipher_type[] = {
-       { PCT_CIPHER_DES, "DES" },
-       { PCT_CIPHER_IDEA, "IDEA" },
-       { PCT_CIPHER_RC2, "RC2" },
-       { PCT_CIPHER_RC4, "RC4" },
-       { PCT_CIPHER_DES_112, "DES 112 bit" },
-       { PCT_CIPHER_DES_168, "DES 168 bit" },
-       { 0x00, NULL },
-};
+/* initialize/reset per capture state data (ssl sessions cache) */
+static void
+ssl_init(void)
+{
+  ssl_common_init(&ssl_session_hash, &ssl_decrypted_data, &ssl_compressed_data);
+  ssl_fragment_init();
+}
 
-static const value_string pct_hash_type[] = {
-       { PCT_HASH_MD5, "MD5" },
-       { PCT_HASH_MD5_TRUNC_64, "MD5_TRUNC_64"},
-       { PCT_HASH_SHA, "SHA"},
-       { PCT_HASH_SHA_TRUNC_80, "SHA_TRUNC_80"},
-       { PCT_HASH_DES_DM, "DES_DM"},
-       { 0x00, NULL },
-};
+/* parse ssl related preferences (private keys and ports association strings) */
+static void
+ssl_parse(void)
+{
+    ep_stack_t tmp_stack;
+    SslAssociation *tmp_assoc;
+    FILE *ssl_keys_file;
+    struct stat statb;
+    size_t size;
+    gchar *tmp_buf;
+    size_t nbytes;
+    gboolean read_failed;
+
+    ssl_set_debug(ssl_debug_file_name);
+
+    if (ssl_key_hash)
+    {
+        g_hash_table_foreach(ssl_key_hash, ssl_private_key_free, NULL);
+        g_hash_table_destroy(ssl_key_hash);
+    }
 
-static const value_string pct_cert_type[] = {
-       { PCT_CERT_NONE, "None" },
-       { PCT_CERT_X509, "X.509" },
-       { PCT_CERT_PKCS7, "PKCS #7" },
-       { 0x00, NULL },
-};
-static const value_string pct_sig_type[]  = {
-       { PCT_SIG_NONE, "None" },
-       { PCT_SIG_RSA_MD5, "MD5" },
-       { PCT_SIG_RSA_SHA, "RSA SHA" },
-       { PCT_SIG_DSA_SHA, "DSA SHA" },
-       { 0x00, NULL },
-};
+    /* remove only associations created from key list */
+    tmp_stack = ep_stack_new();
+    g_tree_traverse(ssl_associations, ssl_assoc_from_key_list, G_IN_ORDER, tmp_stack);
+    while ((tmp_assoc = ep_stack_pop(tmp_stack)) != NULL) {
+        ssl_association_remove(ssl_associations, tmp_assoc);
+    }
 
-static const value_string pct_exch_type[] = {
-       { PCT_EXCH_RSA_PKCS1, "RSA PKCS#1" },
-       { PCT_EXCH_RSA_PKCS1_TOKEN_DES, "RSA PKCS#1 Token DES" },
-       { PCT_EXCH_RSA_PKCS1_TOKEN_DES3, "RSA PKCS#1 Token 3DES" },     
-       { PCT_EXCH_RSA_PKCS1_TOKEN_RC2, "RSA PKCS#1 Token RC-2" },
-       { PCT_EXCH_RSA_PKCS1_TOKEN_RC4, "RSA PKCS#1 Token RC-4" },
-       { PCT_EXCH_DH_PKCS3, "DH PKCS#3" },
-       { PCT_EXCH_DH_PKCS3_TOKEN_DES, "DH PKCS#3 Token DES" },
-       { PCT_EXCH_DH_PKCS3_TOKEN_DES3, "DH PKCS#3 Token 3DES" },
-       { PCT_EXCH_FORTEZZA_TOKEN, "Fortezza" },
-       { 0x00, NULL },
-};
+    /* 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);
 
-static const value_string pct_error_code[] = {
-       { PCT_ERR_BAD_CERTIFICATE, "PCT_ERR_BAD_CERTIFICATE" },
-       { PCT_ERR_CLIENT_AUTH_FAILED, "PCT_ERR_CLIENT_AUTH_FAILE" },
-       { PCT_ERR_ILLEGAL_MESSAGE, "PCT_ERR_ILLEGAL_MESSAGE" },
-       { PCT_ERR_INTEGRITY_CHECK_FAILED, "PCT_ERR_INTEGRITY_CHECK_FAILED" },
-       { PCT_ERR_SERVER_AUTH_FAILED, "PCT_ERR_SERVER_AUTH_FAILED" },
-       { PCT_ERR_SPECS_MISMATCH, "PCT_ERR_SPECS_MISMATCH" },
-       { 0x00, NULL },
-};
+    if (ssl_keys_list && (ssl_keys_list[0] != 0))
+    {
+        if (file_exists(ssl_keys_list)) {
+            if ((ssl_keys_file = fopen(ssl_keys_list, "r"))) {
+                read_failed = FALSE;
+                fstat(fileno(ssl_keys_file), &statb);
+                size = statb.st_size;
+                tmp_buf = ep_alloc0(size + 1);
+                nbytes = fread(tmp_buf, 1, size, ssl_keys_file);
+                if (ferror(ssl_keys_file)) {
+                    report_read_failure(ssl_keys_list, errno);
+                    read_failed = TRUE;
+                }
+                fclose(ssl_keys_file);
+                tmp_buf[nbytes] = '\0';
+                if (!read_failed)
+                    ssl_parse_key_list(tmp_buf,ssl_key_hash,ssl_associations,ssl_handle,TRUE);
+            } else {
+                report_open_failure(ssl_keys_list, errno, FALSE);
+            }
+        } else {
+            ssl_parse_key_list(ssl_keys_list,ssl_key_hash,ssl_associations,ssl_handle,TRUE);
+        }
+    }
 
-/* RFC 3546 */
-static const value_string tls_hello_extension_types[] = {
-       { 0, "server_name" },
-       { 1, "max_fragment_length" },
-       { 2, "client_certificate_url" },
-       { 3, "trusted_ca_keys" },
-       { 4, "truncated_hmac" },
-       { 5, "status_request" },
-       { 35, "EAP-FAST PAC-Opaque" /* draft-cam-winget-eap-fast-00.txt */ },
-       { 0, NULL }
-};
+}
 
 /*********************************************************************
  *
@@ -701,16 +365,18 @@ static const value_string tls_hello_extension_types[] = {
  *
  */
 /* record layer dissector */
-static int dissect_ssl3_record(tvbuff_t *tvb, packet_info *pinfo,
+static gint 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,
+                               gboolean first_record_in_frame);
 
 /* change cipher spec dissector */
 static void dissect_ssl3_change_cipher_spec(tvbuff_t *tvb,
                                             proto_tree *tree,
                                             guint32 offset,
-                                            guint *conv_version);
+                                            guint *conv_version, guint8 content_type);
 
 /* alert message dissector */
 static void dissect_ssl3_alert(tvbuff_t *tvb, packet_info *pinfo,
@@ -721,16 +387,19 @@ static void dissect_ssl3_alert(tvbuff_t *tvb, packet_info *pinfo,
 static void dissect_ssl3_handshake(tvbuff_t *tvb, packet_info *pinfo,
                                    proto_tree *tree, guint32 offset,
                                    guint32 record_length,
-                                   guint *conv_version);
+                                   guint *conv_version,
+                                   SslDecryptSession *conv_data, guint8 content_type);
 
 
-static void dissect_ssl3_hnd_cli_hello(tvbuff_t *tvb,
+static void dissect_ssl3_hnd_cli_hello(tvbuff_t *tvb, packet_info *pinfo,
                                        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 +411,7 @@ static void dissect_ssl3_hnd_cert_req(tvbuff_t *tvb,
 static void dissect_ssl3_hnd_finished(tvbuff_t *tvb,
                                       proto_tree *tree,
                                       guint32 offset,
-                                      guint *conv_version);
+                                      guintconv_version);
 
 
 /*
@@ -751,15 +420,17 @@ static void dissect_ssl3_hnd_finished(tvbuff_t *tvb,
  */
 
 /* record layer dissector */
-static int dissect_ssl2_record(tvbuff_t *tvb, packet_info *pinfo,
+static gint 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,22 +465,20 @@ static void dissect_pct_msg_error(tvbuff_t *tvb,
  * Support Functions
  *
  */
-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);
-static int  ssl_is_authoritative_version_message(guint8 content_type,
+/*static void ssl_set_conv_version(packet_info *pinfo, guint version);*/
+static gint  ssl_is_valid_handshake_type(guint8 type);
+static gint  ssl_is_valid_ssl_version(guint16 version);
+static gint  ssl_is_authoritative_version_message(guint8 content_type,
                                                 guint8 next_byte);
-static int  ssl_is_v2_client_hello(tvbuff_t *tvb, guint32 offset);
-static int  ssl_looks_like_sslv2(tvbuff_t *tvb, guint32 offset);
-static int  ssl_looks_like_sslv3(tvbuff_t *tvb, guint32 offset);
-static int  ssl_looks_like_valid_v2_handshake(tvbuff_t *tvb,
+static gint  ssl_is_v2_client_hello(tvbuff_t *tvb, guint32 offset);
+static gint  ssl_looks_like_sslv2(tvbuff_t *tvb, guint32 offset);
+static gint  ssl_looks_like_sslv3(tvbuff_t *tvb, guint32 offset);
+static gint  ssl_looks_like_valid_v2_handshake(tvbuff_t *tvb,
                                               guint32 offset,
                                               guint32 record_length);
-static int  ssl_looks_like_valid_pct_handshake(tvbuff_t *tvb,
+static gint  ssl_looks_like_valid_pct_handshake(tvbuff_t *tvb,
                                                guint32 offset,
                                                guint32 record_length);
-
 /*********************************************************************
  *
  * Main dissector
@@ -824,12 +493,20 @@ dissect_ssl(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
 
     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;
+    proto_item *ti;
+    proto_tree *ssl_tree;
+    guint32 offset;
+    gboolean first_record_in_frame;
     gboolean need_desegmentation;
+    SslDecryptSession* ssl_session;
+    guint* conv_version;
+    ti = NULL;
+    ssl_tree   = NULL;
+    offset = 0;
+    first_record_in_frame = TRUE;
+    ssl_session = NULL;
+
+    ssl_debug_printf("\ndissect_ssl enter frame #%u (%s)\n", pinfo->fd->num, (pinfo->fd->flags.visited)?"already visited":"first time");
 
     /* Track the version using conversations to reduce the
      * chance that a packet that simply *looks* like a v2 or
@@ -845,6 +522,7 @@ dissect_ssl(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
      */
     conversation = find_conversation(pinfo->fd->num, &pinfo->src, &pinfo->dst, pinfo->ptype,
                                      pinfo->srcport, pinfo->destport, 0);
+
     if (!conversation)
     {
         /* create a new conversation */
@@ -852,10 +530,46 @@ dissect_ssl(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
                                         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(ssl_associations, pinfo->srcport, pinfo->ptype == PT_TCP)) {
+            dummy.addr = pinfo->src;
+            dummy.port = pinfo->srcport;
+        }
+        else {
+            dummy.addr = pinfo->dst;
+            dummy.port = pinfo->destport;
+        }
+        ssl_debug_printf("dissect_ssl server %s:%u\n",
+            address_to_str(&dummy.addr),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 (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
@@ -864,7 +578,6 @@ dissect_ssl(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
     {
         col_set_str(pinfo->cinfo, COL_PROTOCOL, "SSL");
     }
-
     /* clear the the info column */
     if (check_col(pinfo->cinfo, COL_INFO))
         col_clear(pinfo->cinfo, COL_INFO);
@@ -878,7 +591,7 @@ dissect_ssl(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
      * packets.
      *
      * Handling the single ssl record across multiple packets
-     * may be possible using ethereal conversations, but
+     * may be possible using wireshark conversations, but
      * probably not cleanly.  May have to wait for tcp stream
      * reassembly.
      */
@@ -889,7 +602,6 @@ dissect_ssl(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
         ti = proto_tree_add_item(tree, proto_ssl, tvb, 0, -1, FALSE);
         ssl_tree = proto_item_add_subtree(ti, ett_ssl);
     }
-
     /* iterate through the records in this tvbuff */
     while (tvb_reported_length_remaining(tvb, offset) != 0)
     {
@@ -910,12 +622,13 @@ dissect_ssl(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
         /* 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 +642,17 @@ dissect_ssl(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
             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,
+                                             first_record_in_frame);
             }
             break;
 
@@ -948,15 +664,18 @@ dissect_ssl(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
             {
                 /* 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,
+                                             first_record_in_frame);
             }
             else
             {
@@ -972,7 +691,7 @@ dissect_ssl(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
                 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,19 +700,528 @@ dissect_ssl(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
         /* 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;
     }
+    if (check_col(pinfo->cinfo, COL_INFO))
+       col_set_fence(pinfo->cinfo, COL_INFO);
+
+    tap_queue_packet(ssl_tap, pinfo, NULL);
+}
+
+static gint
+decrypt_ssl3_record(tvbuff_t *tvb, packet_info *pinfo, guint32 offset,
+        guint32 record_length, guint8 content_type, SslDecryptSession* ssl,
+        gboolean save_plaintext)
+{
+    gint ret;
+    gint direction;
+    StringInfo* data_for_iv;
+    gint data_for_iv_len;
+    SslDecoder* decoder;
+    ret = 0;
+    /* 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 0x%02X\n",
+        record_length, ssl->state);
+    direction = ssl_packet_from_server(ssl_associations, pinfo->srcport, pinfo->ptype == PT_TCP);
+
+    /* retrive decoder for this packet direction*/
+    if (direction != 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;
+    }
+
+    if (!decoder) {
+        ssl_debug_printf("decrypt_ssl3_record: no decoder available\n");
+        /* save data to update IV if decoder is available later */
+        data_for_iv = (direction != 0) ? &ssl->server_data_for_iv : &ssl->client_data_for_iv;
+        data_for_iv_len = (record_length < 24) ? record_length : 24;
+        ssl_data_set(data_for_iv, (guchar*)tvb_get_ptr(tvb, offset + record_length - data_for_iv_len, data_for_iv_len), data_for_iv_len);
+        return ret;
+    }
+
+    /* run decryption and add decrypted payload to protocol data, if decryption
+    * is successful*/
+    ssl_decrypted_data_avail = ssl_decrypted_data.data_len;
+    if (ssl_decrypt_record(ssl, decoder,
+          content_type, tvb_get_ptr(tvb, offset, record_length),
+          record_length, &ssl_compressed_data, &ssl_decrypted_data, &ssl_decrypted_data_avail) == 0)
+        ret = 1;
+    /*  */
+    if (!ret) {
+        /* save data to update IV if valid session key is obtained later */
+        data_for_iv = (direction != 0) ? &ssl->server_data_for_iv : &ssl->client_data_for_iv;
+        data_for_iv_len = (record_length < 24) ? record_length : 24;
+        ssl_data_set(data_for_iv, (guchar*)tvb_get_ptr(tvb, offset + record_length - data_for_iv_len, data_for_iv_len), data_for_iv_len);
+    }
+    if (ret && save_plaintext) {
+      ssl_add_data_info(proto_ssl, pinfo, ssl_decrypted_data.data, ssl_decrypted_data_avail,  TVB_RAW_OFFSET(tvb)+offset, decoder->flow);
+    }
+    return ret;
+}
+
+static void
+process_ssl_payload(tvbuff_t *tvb, volatile int offset, packet_info *pinfo,
+                   proto_tree *tree, SslAssociation* association);
+
+static void
+desegment_ssl(tvbuff_t *tvb, packet_info *pinfo, int offset,
+               guint32 seq, guint32 nxtseq,
+               SslAssociation* association,
+               proto_tree *root_tree, proto_tree *tree,
+               SslFlow *flow)
+{
+       fragment_data *ipfd_head;
+       gboolean must_desegment;
+       gboolean called_dissector;
+       int another_pdu_follows;
+       int deseg_offset;
+       guint32 deseg_seq;
+       gint nbytes;
+       proto_item *item;
+       proto_item *frag_tree_item;
+       proto_item *ssl_tree_item;
+    struct tcp_multisegment_pdu *msp;
+
+again:
+       ipfd_head=NULL;
+       must_desegment = FALSE;
+       called_dissector = FALSE;
+       another_pdu_follows = 0;
+       msp=NULL;
+
+       /*
+        * Initialize these to assume no desegmentation.
+        * If that's not the case, these will be set appropriately
+        * by the subdissector.
+        */
+       pinfo->desegment_offset = 0;
+       pinfo->desegment_len = 0;
+
+       /*
+        * Initialize this to assume that this segment will just be
+        * added to the middle of a desegmented chunk of data, so
+        * that we should show it all as data.
+        * If that's not the case, it will be set appropriately.
+        */
+       deseg_offset = offset;
+
+       /* find the most previous PDU starting before this sequence number */
+       msp=se_tree_lookup32_le(flow->multisegment_pdus, seq-1);
+       if(msp && msp->seq<=seq && msp->nxtpdu>seq){
+               int len;
+
+               if(!pinfo->fd->flags.visited){
+                       msp->last_frame=pinfo->fd->num;
+                       msp->last_frame_time=pinfo->fd->abs_ts;
+               }
+
+               /* OK, this PDU was found, which means the segment continues
+                  a higher-level PDU and that we must desegment it.
+               */
+               if(msp->flags&MSP_FLAGS_REASSEMBLE_ENTIRE_SEGMENT){
+                       /* The dissector asked for the entire segment */
+                       len=tvb_length_remaining(tvb, offset);
+               } else {
+                       len=MIN(nxtseq, msp->nxtpdu) - seq;
+               }
+
+               ipfd_head = fragment_add(tvb, offset, pinfo, msp->first_frame,
+                       ssl_fragment_table,
+                       seq - msp->seq,
+                       len,
+                       (LT_SEQ (nxtseq,msp->nxtpdu)) );
+
+               if(msp->flags&MSP_FLAGS_REASSEMBLE_ENTIRE_SEGMENT){
+                       msp->flags&=(~MSP_FLAGS_REASSEMBLE_ENTIRE_SEGMENT);
+
+                       /* If we consumed the entire segment there is no
+                        * other pdu starting anywhere inside this segment.
+                        * So update nxtpdu to point at least to the start
+                        * of the next segment.
+                        * (If the subdissector asks for even more data we
+                        * will advance nxtpdu even furhter later down in
+                        * the code.)
+                        */
+                       msp->nxtpdu=nxtseq;
+               }
+
+               if( (msp->nxtpdu<nxtseq)
+               &&  (msp->nxtpdu>=seq)
+               &&  (len>0) ){
+                       another_pdu_follows=msp->nxtpdu-seq;
+               }
+       } else {
+               /* This segment was not found in our table, so it doesn't
+                  contain a continuation of a higher-level PDU.
+                  Call the normal subdissector.
+               */
+               process_ssl_payload(tvb, offset, pinfo, tree, association);
+               called_dissector = TRUE;
+
+               /* Did the subdissector ask us to desegment some more data
+                  before it could handle the packet?
+                  If so we have to create some structures in our table but
+                  this is something we only do the first time we see this
+                  packet.
+               */
+               if(pinfo->desegment_len) {
+                       if (!pinfo->fd->flags.visited)
+                               must_desegment = TRUE;
+
+                       /*
+                        * Set "deseg_offset" to the offset in "tvb"
+                        * of the first byte of data that the
+                        * subdissector didn't process.
+                        */
+                       deseg_offset = offset + pinfo->desegment_offset;
+               }
+
+               /* Either no desegmentation is necessary, or this is
+                  segment contains the beginning but not the end of
+                  a higher-level PDU and thus isn't completely
+                  desegmented.
+               */
+               ipfd_head = NULL;
+       }
+
+
+       /* is it completely desegmented? */
+       if(ipfd_head){
+               /*
+                * Yes, we think it is.
+                * We only call subdissector for the last segment.
+                * Note that the last segment may include more than what
+                * we needed.
+                */
+               if(ipfd_head->reassembled_in==pinfo->fd->num){
+                       /*
+                        * OK, this is the last segment.
+                        * Let's call the subdissector with the desegmented
+                        * data.
+                        */
+                       tvbuff_t *next_tvb;
+                       int old_len;
+
+                       /* create a new TVB structure for desegmented data */
+                       next_tvb = tvb_new_real_data(ipfd_head->data,
+                                       ipfd_head->datalen, ipfd_head->datalen);
+
+                       /* add this tvb as a child to the original one */
+                       tvb_set_child_real_data_tvbuff(tvb, next_tvb);
+
+                       /* add desegmented data to the data source list */
+                       add_new_data_source(pinfo, next_tvb, "Reassembled SSL");
+
+                       /* call subdissector */
+                       process_ssl_payload(next_tvb, 0, pinfo, tree, association);
+                       called_dissector = TRUE;
+
+                       /*
+                        * OK, did the subdissector think it was completely
+                        * desegmented, or does it think we need even more
+                        * data?
+                        */
+                       old_len=(int)(tvb_reported_length(next_tvb)-tvb_reported_length_remaining(tvb, offset));
+                       if(pinfo->desegment_len &&
+                           pinfo->desegment_offset<=old_len){
+                               /*
+                                * "desegment_len" isn't 0, so it needs more
+                                * data for something - and "desegment_offset"
+                                * is before "old_len", so it needs more data
+                                * to dissect the stuff we thought was
+                                * completely desegmented (as opposed to the
+                                * stuff at the beginning being completely
+                                * desegmented, but the stuff at the end
+                                * being a new higher-level PDU that also
+                                * needs desegmentation).
+                                */
+                               fragment_set_partial_reassembly(pinfo,msp->first_frame,ssl_fragment_table);
+                               /* Update msp->nxtpdu to point to the new next
+                                * pdu boundary.
+                                */
+                               if(pinfo->desegment_len==DESEGMENT_ONE_MORE_SEGMENT){
+                                       /* We want reassembly of at least one
+                                        * more segment so set the nxtpdu
+                                        * boundary to one byte into the next
+                                        * segment.
+                                        * This means that the next segment
+                                        * will complete reassembly even if it
+                                        * is only one single byte in length.
+                                        */
+                                       msp->nxtpdu=seq+tvb_reported_length_remaining(tvb, offset) + 1;
+                                       msp->flags|=MSP_FLAGS_REASSEMBLE_ENTIRE_SEGMENT;
+                               } else {
+                                       msp->nxtpdu=seq+tvb_reported_length_remaining(tvb, offset) + pinfo->desegment_len;
+                               }
+                               /* Since we need at least some more data
+                                * there can be no pdu following in the
+                                * tail of this segment.
+                                */
+                               another_pdu_follows=0;
+                       } else {
+                               /*
+                                * Show the stuff in this TCP segment as
+                                * just raw TCP segment data.
+                                */
+                               nbytes =
+                                   tvb_reported_length_remaining(tvb, offset);
+                               proto_tree_add_text(tree, tvb, offset, -1,
+                                   "SSL segment data (%u byte%s)", nbytes,
+                                   plurality(nbytes, "", "s"));
+
+                               /*
+                                * The subdissector thought it was completely
+                                * desegmented (although the stuff at the
+                                * end may, in turn, require desegmentation),
+                                * so we show a tree with all segments.
+                                */
+                               show_fragment_tree(ipfd_head, &ssl_segment_items,
+                                       root_tree, pinfo, next_tvb, &frag_tree_item);
+                               /*
+                                * The toplevel fragment subtree is now
+                                * behind all desegmented data; move it
+                                * right behind the TCP tree.
+                                */
+                               ssl_tree_item = proto_tree_get_parent(tree);
+                               if(frag_tree_item && ssl_tree_item) {
+                                       proto_tree_move_item(root_tree, ssl_tree_item, frag_tree_item);
+                               }
+
+                               /* Did the subdissector ask us to desegment
+                                  some more data?  This means that the data
+                                  at the beginning of this segment completed
+                                  a higher-level PDU, but the data at the
+                                  end of this segment started a higher-level
+                                  PDU but didn't complete it.
+
+                                  If so, we have to create some structures
+                                  in our table, but this is something we
+                                  only do the first time we see this packet.
+                               */
+                               if(pinfo->desegment_len) {
+                                       if (!pinfo->fd->flags.visited)
+                                               must_desegment = TRUE;
+
+                                       /* The stuff we couldn't dissect
+                                          must have come from this segment,
+                                          so it's all in "tvb".
+
+                                          "pinfo->desegment_offset" is
+                                          relative to the beginning of
+                                          "next_tvb"; we want an offset
+                                          relative to the beginning of "tvb".
+
+                                          First, compute the offset relative
+                                          to the *end* of "next_tvb" - i.e.,
+                                          the number of bytes before the end
+                                          of "next_tvb" at which the
+                                          subdissector stopped.  That's the
+                                          length of "next_tvb" minus the
+                                          offset, relative to the beginning
+                                          of "next_tvb, at which the
+                                          subdissector stopped.
+                                       */
+                                       deseg_offset =
+                                           ipfd_head->datalen - pinfo->desegment_offset;
+
+                                       /* "tvb" and "next_tvb" end at the
+                                          same byte of data, so the offset
+                                          relative to the end of "next_tvb"
+                                          of the byte at which we stopped
+                                          is also the offset relative to
+                                          the end of "tvb" of the byte at
+                                          which we stopped.
+
+                                          Convert that back into an offset
+                                          relative to the beginninng of
+                                          "tvb", by taking the length of
+                                          "tvb" and subtracting the offset
+                                          relative to the end.
+                                       */
+                                       deseg_offset=tvb_reported_length(tvb) - deseg_offset;
+                               }
+                       }
+               }
+       }
 
+       if (must_desegment) {
+           /* If the dissector requested "reassemble until FIN"
+            * just set this flag for the flow and let reassembly
+            * proceed at normal.  We will check/pick up these
+            * reassembled PDUs later down in dissect_tcp() when checking
+            * for the FIN flag.
+            */
+           if(pinfo->desegment_len==DESEGMENT_UNTIL_FIN){
+                 flow->flags|=TCP_FLOW_REASSEMBLE_UNTIL_FIN;
+           }
+           /*
+            * The sequence number at which the stuff to be desegmented
+            * starts is the sequence number of the byte at an offset
+            * of "deseg_offset" into "tvb".
+            *
+            * The sequence number of the byte at an offset of "offset"
+            * is "seq", i.e. the starting sequence number of this
+            * segment, so the sequence number of the byte at
+            * "deseg_offset" is "seq + (deseg_offset - offset)".
+            */
+           deseg_seq = seq + (deseg_offset - offset);
+
+           if( ((nxtseq - deseg_seq) <= 1024*1024)
+           &&  (!pinfo->fd->flags.visited) ){
+               if(pinfo->desegment_len==DESEGMENT_ONE_MORE_SEGMENT){
+                       /* The subdissector asked to reassemble using the
+                        * entire next segment.
+                        * Just ask reassembly for one more byte
+                        * but set this msp flag so we can pick it up
+                        * above.
+                        */
+                       msp = pdu_store_sequencenumber_of_next_pdu(pinfo,
+                               deseg_seq, nxtseq+1, flow->multisegment_pdus);
+                       msp->flags|=MSP_FLAGS_REASSEMBLE_ENTIRE_SEGMENT;
+               } else {
+                       msp = pdu_store_sequencenumber_of_next_pdu(pinfo,
+                               deseg_seq, nxtseq+pinfo->desegment_len, flow->multisegment_pdus);
+               }
+
+               /* add this segment as the first one for this new pdu */
+               fragment_add(tvb, deseg_offset, pinfo, msp->first_frame,
+                       ssl_fragment_table,
+                       0,
+                       nxtseq - deseg_seq,
+                       LT_SEQ(nxtseq, msp->nxtpdu));
+               }
+       }
+
+       if (!called_dissector || pinfo->desegment_len != 0) {
+               if (ipfd_head != NULL && ipfd_head->reassembled_in != 0 &&
+                   !(ipfd_head->flags & FD_PARTIAL_REASSEMBLY)) {
+                       /*
+                        * We know what frame this PDU is reassembled in;
+                        * let the user know.
+                        */
+                       item=proto_tree_add_uint(tree, *ssl_segment_items.hf_reassembled_in,
+                           tvb, 0, 0, ipfd_head->reassembled_in);
+                       PROTO_ITEM_SET_GENERATED(item);
+               }
+
+               /*
+                * Either we didn't call the subdissector at all (i.e.,
+                * this is a segment that contains the middle of a
+                * higher-level PDU, but contains neither the beginning
+                * nor the end), or the subdissector couldn't dissect it
+                * all, as some data was missing (i.e., it set
+                * "pinfo->desegment_len" to the amount of additional
+                * data it needs).
+                */
+               if (pinfo->desegment_offset == 0) {
+                       /*
+                        * It couldn't, in fact, dissect any of it (the
+                        * first byte it couldn't dissect is at an offset
+                        * of "pinfo->desegment_offset" from the beginning
+                        * of the payload, and that's 0).
+                        * Just mark this as SSL.
+                        */
+                       if (check_col(pinfo->cinfo, COL_PROTOCOL)){
+                               col_set_str(pinfo->cinfo, COL_PROTOCOL, "SSL");
+                       }
+                       if (check_col(pinfo->cinfo, COL_INFO)){
+                               col_set_str(pinfo->cinfo, COL_INFO, "[SSL segment of a reassembled PDU]");
+                       }
+               }
+
+               /*
+                * Show what's left in the packet as just raw TCP segment
+                * data.
+                * XXX - remember what protocol the last subdissector
+                * was, and report it as a continuation of that, instead?
+                */
+               nbytes = tvb_reported_length_remaining(tvb, deseg_offset);
+               proto_tree_add_text(tree, tvb, deseg_offset, -1,
+                   "SSL segment data (%u byte%s)", nbytes,
+                   plurality(nbytes, "", "s"));
+       }
+       pinfo->can_desegment=0;
+       pinfo->desegment_offset = 0;
+       pinfo->desegment_len = 0;
+
+       if(another_pdu_follows){
+               /* there was another pdu following this one. */
+               pinfo->can_desegment=2;
+               /* we also have to prevent the dissector from changing the
+                * PROTOCOL and INFO colums since what follows may be an
+                * incomplete PDU and we dont want it be changed back from
+                *  <Protocol>   to <TCP>
+                * XXX There is no good way to block the PROTOCOL column
+                * from being changed yet so we set the entire row unwritable.
+                */
+               col_set_fence(pinfo->cinfo, COL_INFO);
+               col_set_writable(pinfo->cinfo, FALSE);
+               offset += another_pdu_follows;
+               seq += another_pdu_follows;
+               goto again;
+       }
+}
+
+static void
+process_ssl_payload(tvbuff_t *tvb, volatile int offset, packet_info *pinfo,
+                   proto_tree *tree, SslAssociation* association)
+{
+  tvbuff_t *next_tvb;
+
+  next_tvb = tvb_new_subset(tvb, offset, -1, -1);
+
+  if (association && association->handle) {
+    ssl_debug_printf("dissect_ssl3_record found association %p\n", association);
+    call_dissector(association->handle, next_tvb, pinfo, proto_tree_get_root(tree));
+  }
+}
+
+void
+dissect_ssl_payload(tvbuff_t *tvb, packet_info *pinfo, int offset, proto_tree *tree, SslAssociation* association)
+{
+  gboolean save_fragmented;
+  SslDataInfo *appl_data;
+  tvbuff_t *next_tvb;
+
+  /* show decrypted data info, if available */
+  appl_data = ssl_get_data_info(proto_ssl, pinfo, TVB_RAW_OFFSET(tvb)+offset);
+  if (!appl_data || !appl_data->plain_data.data_len) return;
+
+  /* try to dissect decrypted data*/
+  ssl_debug_printf("dissect_ssl3_record decrypted len %d\n", appl_data->plain_data.data_len);
+  ssl_print_text_data("decrypted app data fragment", appl_data->plain_data.data, appl_data->plain_data.data_len);
+
+  /* create a new TVB structure for desegmented data */
+  next_tvb = tvb_new_real_data(appl_data->plain_data.data, appl_data->plain_data.data_len, appl_data->plain_data.data_len);
+
+  /* add this tvb as a child to the original one */
+  tvb_set_child_real_data_tvbuff(tvb, next_tvb);
+
+  /* add desegmented data to the data source list */
+  add_new_data_source(pinfo, next_tvb, "Decrypted SSL data");
+
+  /* Can we desegment this segment? */
+  if (ssl_desegment_app_data) {
+    /* Yes. */
+    pinfo->can_desegment = 2;
+    desegment_ssl(next_tvb, pinfo, 0, appl_data->seq, appl_data->nxtseq, association, proto_tree_get_root(tree), tree, appl_data->flow);
+  } else if (association && association->handle) {
+    /* No - just call the subdissector.
+       Mark this as fragmented, so if somebody throws an exception,
+       we don't report it as a malformed frame. */
+    pinfo->can_desegment = 0;
+    save_fragmented = pinfo->fragmented;
+    pinfo->fragmented = TRUE;
+
+    process_ssl_payload(next_tvb, 0, pinfo, tree, association);
+    pinfo->fragmented = save_fragmented;
+  }
 }
 
 
@@ -1002,10 +1230,11 @@ dissect_ssl(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
  * SSL version 3 and TLS Dissection Routines
  *
  *********************************************************************/
-static int
+static gint
 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, gboolean first_record_in_frame _U_)
 {
 
     /*
@@ -1030,13 +1259,28 @@ dissect_ssl3_record(tvbuff_t *tvb, packet_info *pinfo,
     guint16 version;
     guint8 content_type;
     guint8 next_byte;
-    proto_tree *ti              = NULL;
-    proto_tree *ssl_record_tree = NULL;
-    guint32 available_bytes     = 0;
+    proto_tree *ti;
+    proto_tree *ssl_record_tree;
+    SslAssociation* association;
+    guint32 available_bytes;
+    ti = NULL;
+    ssl_record_tree = NULL;
+    available_bytes = 0;
 
     available_bytes = tvb_length_remaining(tvb, offset);
 
-    /*
+    /* TLS 1.0/1.1 just ignores unknown records - RFC 2246 chapter 6. The TLS Record Protocol */
+    if ((*conv_version==SSL_VER_TLS || *conv_version==SSL_VER_TLSv1DOT1) &&
+        (available_bytes >=1 ) && !ssl_is_valid_content_type(tvb_get_guint8(tvb, offset))) {
+      proto_tree_add_text(tree, tvb, offset, available_bytes, "Ignored Unknown Record");
+      if (check_col(pinfo->cinfo, COL_INFO))
+          col_append_str(pinfo->cinfo, COL_INFO, "Ignored Unknown Record");
+      if (check_col(pinfo->cinfo, COL_PROTOCOL))
+        col_set_str(pinfo->cinfo, COL_PROTOCOL, ssl_version_short_names[*conv_version]);
+      return offset + available_bytes;
+    }
+
+   /*
      * Can we do reassembly?
      */
     if (ssl_desegment && pinfo->can_desegment) {
@@ -1109,6 +1353,7 @@ dissect_ssl3_record(tvbuff_t *tvb, packet_info *pinfo,
     {
 
         /* add the record layer subtree header */
+        tvb_ensure_bytes_exist(tvb, offset, 5 + record_length);
         ti = proto_tree_add_item(tree, hf_ssl_record, tvb,
                                  offset, 5 + record_length, 0);
         ssl_record_tree = proto_item_add_subtree(ti, ett_ssl_record);
@@ -1148,65 +1393,138 @@ dissect_ssl3_record(tvbuff_t *tvb, packet_info *pinfo,
     if (*conv_version == SSL_VER_UNKNOWN
         && ssl_is_authoritative_version_message(content_type, next_byte))
     {
-        if (version == 0x0300)
+        if (version == SSLV3_VERSION)
         {
             *conv_version = SSL_VER_SSLv3;
-            ssl_set_conv_version(pinfo, *conv_version);
+            if (ssl) {
+                ssl->version_netorder = version;
+                ssl->state |= SSL_VERSION;
+                ssl_debug_printf("dissect_ssl3_record found version 0x%04X -> state 0x%02X\n", ssl->version_netorder, ssl->state);
+            }
+            /*ssl_set_conv_version(pinfo, ssl->version);*/
         }
-        else if (version == 0x0301)
+        else if (version == TLSV1_VERSION)
         {
+
             *conv_version = SSL_VER_TLS;
-            ssl_set_conv_version(pinfo, *conv_version);
+            if (ssl) {
+                ssl->version_netorder = version;
+                ssl->state |= SSL_VERSION;
+                ssl_debug_printf("dissect_ssl3_record found version 0x%04X -> state 0x%02X\n", ssl->version_netorder, ssl->state);
+            }
+            /*ssl_set_conv_version(pinfo, ssl->version);*/
+        }
+        else if (version == TLSV1DOT1_VERSION)
+        {
+
+            *conv_version = SSL_VER_TLSv1DOT1;
+            if (ssl) {
+                ssl->version_netorder = version;
+                ssl->state |= SSL_VERSION;
+                ssl_debug_printf("dissect_ssl3_record found version 0x%04X -> state 0x%02X\n", ssl->version_netorder, ssl->state);
+            }
+            /*ssl_set_conv_version(pinfo, ssl->version);*/
         }
     }
     if (check_col(pinfo->cinfo, COL_PROTOCOL))
     {
-        if (version == 0x0300)
-        {
-            col_set_str(pinfo->cinfo, COL_PROTOCOL,
-                        ssl_version_short_names[SSL_VER_SSLv3]);
-        }
-        else if (version == 0x0301)
-        {
-            col_set_str(pinfo->cinfo, COL_PROTOCOL,
-                        ssl_version_short_names[SSL_VER_TLS]);
-        }
-        else
-        {
             col_set_str(pinfo->cinfo, COL_PROTOCOL,
                         ssl_version_short_names[*conv_version]);
-        }
     }
 
     /*
      * 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:
+        ssl_debug_printf("dissect_ssl3_change_cipher_spec\n");
         if (check_col(pinfo->cinfo, COL_INFO))
             col_append_str(pinfo->cinfo, COL_INFO, "Change Cipher Spec");
         dissect_ssl3_change_cipher_spec(tvb, ssl_record_tree,
-                                        offset, conv_version);
+                                        offset, conv_version, content_type);
+        if (ssl) ssl_change_cipher(ssl, ssl_packet_from_server(ssl_associations, pinfo->srcport, pinfo->ptype == PT_TCP));
         break;
     case SSL_ID_ALERT:
-        dissect_ssl3_alert(tvb, pinfo, ssl_record_tree, offset,
-                           conv_version);
+      {
+       tvbuff_t* decrypted;
+       decrypted=0;
+       if (ssl&&decrypt_ssl3_record(tvb, pinfo, offset,
+                record_length, content_type, ssl, FALSE))
+         ssl_add_record_info(proto_ssl, pinfo, ssl_decrypted_data.data,
+                             ssl_decrypted_data_avail, offset);
+
+       /* try to retrive and use decrypted alert record, if any. */
+        decrypted = ssl_get_record_info(proto_ssl, pinfo, offset);
+        if (decrypted)
+         dissect_ssl3_alert(decrypted, pinfo, ssl_record_tree, 0,
+                            conv_version);
+       else
+         dissect_ssl3_alert(tvb, pinfo, ssl_record_tree, offset,
+                            conv_version);
         break;
+      }
     case SSL_ID_HANDSHAKE:
-        dissect_ssl3_handshake(tvb, pinfo, ssl_record_tree, offset,
-                               record_length, conv_version);
+    {
+        tvbuff_t* decrypted;
+       decrypted=0;
+        /* try to decrypt handshake record, if possible. Store decrypted
+         * record for later usage. The offset is used as 'key' to itentify
+         * this record into the packet (we can have multiple handshake records
+         * in the same frame) */
+        if (ssl && decrypt_ssl3_record(tvb, pinfo, offset,
+                record_length, content_type, ssl, FALSE))
+            ssl_add_record_info(proto_ssl, pinfo, ssl_decrypted_data.data,
+                ssl_decrypted_data_avail, offset);
+
+        /* try to retrive and use decrypted handshake record, if any. */
+        decrypted = ssl_get_record_info(proto_ssl, pinfo, offset);
+        if (decrypted) {
+                   /* add desegmented data to the data source list */
+                   add_new_data_source(pinfo, decrypted, "Decrypted SSL record");
+            dissect_ssl3_handshake(decrypted, pinfo, ssl_record_tree, 0,
+                 decrypted->length, conv_version, ssl, content_type);
+               } else {
+            dissect_ssl3_handshake(tvb, pinfo, ssl_record_tree, offset,
+                               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);
+           /* if application data desegmentation is allowed and needed */
+           /*if(ssl_desegment_app_data && *need_desegmentation)
+               ssl_desegment_ssl_app_data(ssl,pinfo);
+        */
+        }
+
+        /* 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)
-        {
-            proto_item_set_text(ssl_record_tree,
-                                "%s Record Layer: Application Data",
-                                ssl_version_short_names[*conv_version]);
-            proto_tree_add_item(ssl_record_tree, hf_ssl_record_appdata, tvb,
-                                offset, record_length, 0);
-        }
+
+        /* 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(ssl_associations, pinfo->srcport, pinfo->ptype == PT_TCP);
+        association = association ? association: ssl_association_find(ssl_associations, pinfo->destport, pinfo->ptype == PT_TCP);
+
+        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");
+
+        proto_tree_add_item(ssl_record_tree, hf_ssl_record_appdata, tvb,
+                       offset, record_length, 0);
+
+               dissect_ssl_payload(tvb, pinfo, offset, tree, association);
+
         break;
 
     default:
@@ -1224,7 +1542,7 @@ dissect_ssl3_record(tvbuff_t *tvb, packet_info *pinfo,
 static void
 dissect_ssl3_change_cipher_spec(tvbuff_t *tvb,
                                 proto_tree *tree, guint32 offset,
-                                guint *conv_version)
+                                guint* conv_version, guint8 content_type)
 {
     /*
      * struct {
@@ -1235,8 +1553,9 @@ dissect_ssl3_change_cipher_spec(tvbuff_t *tvb,
     if (tree)
     {
         proto_item_set_text(tree,
-                            "%s Record Layer: Change Cipher Spec",
-                            ssl_version_short_names[*conv_version]);
+                            "%s Record Layer: %s Protocol: Change Cipher Spec",
+                            ssl_version_short_names[*conv_version],
+                            val_to_str(content_type, ssl_31_content_type, "unknown"));
         proto_tree_add_item(tree, hf_ssl_change_cipher_spec, tvb,
                             offset++, 1, FALSE);
     }
@@ -1246,7 +1565,7 @@ dissect_ssl3_change_cipher_spec(tvbuff_t *tvb,
 static void
 dissect_ssl3_alert(tvbuff_t *tvb, packet_info *pinfo,
                    proto_tree *tree, guint32 offset,
-                   guint *conv_version)
+                   guintconv_version)
 {
     /*     struct {
      *         AlertLevel level;
@@ -1254,10 +1573,11 @@ dissect_ssl3_alert(tvbuff_t *tvb, packet_info *pinfo,
      *     } Alert;
      */
     proto_tree *ti;
-    proto_tree *ssl_alert_tree = NULL;
-    gchar *level;
-    gchar *desc;
+    proto_tree *ssl_alert_tree;
+    const gchar *level;
+    const gchar *desc;
     guint8 byte;
+    ssl_alert_tree = NULL;
     if (tree)
     {
         ti = proto_tree_add_item(tree, hf_ssl_alert_message, tvb,
@@ -1320,7 +1640,8 @@ dissect_ssl3_alert(tvbuff_t *tvb, packet_info *pinfo,
 static void
 dissect_ssl3_handshake(tvbuff_t *tvb, packet_info *pinfo,
                        proto_tree *tree, guint32 offset,
-                       guint32 record_length, guint *conv_version)
+                       guint32 record_length, guint *conv_version,
+                       SslDecryptSession* ssl, guint8 content_type)
 {
     /*     struct {
      *         HandshakeType msg_type;
@@ -1339,13 +1660,16 @@ dissect_ssl3_handshake(tvbuff_t *tvb, packet_info *pinfo,
      *         } body;
      *     } Handshake;
      */
-    proto_tree *ti            = NULL;
-    proto_tree *ssl_hand_tree = NULL;
-    gchar *msg_type_str       = NULL;
+    proto_tree *ti;
+    proto_tree *ssl_hand_tree;
+    const gchar *msg_type_str;
     guint8 msg_type;
     guint32 length;
-    gboolean first_iteration  = TRUE;
-
+    gboolean first_iteration;
+    ti = NULL;
+    ssl_hand_tree = NULL;
+    msg_type_str = NULL;
+    first_iteration = TRUE;
 
     /* just as there can be multiple records per packet, there
      * can be multiple messages per record as long as they have
@@ -1359,9 +1683,21 @@ dissect_ssl3_handshake(tvbuff_t *tvb, packet_info *pinfo,
     while (offset < record_length)
     {
         msg_type = tvb_get_guint8(tvb, offset);
-        msg_type_str = match_strval(msg_type, ssl_31_handshake_type);
         length   = tvb_get_ntoh24(tvb, offset + 1);
 
+       /* Check the length in the handshake message. Assume it's an
+        * encrypted handshake message if the message would pass
+        * the record_length boundary. This is a workaround for the
+        * situation where the first octet of the encrypted handshake
+        * message is actually a known handshake message type.
+        */
+       if ( offset + length <= record_length )
+          msg_type_str = match_strval(msg_type, ssl_31_handshake_type);
+       else
+          msg_type_str = NULL;
+
+        ssl_debug_printf("dissect_ssl3_handshake iteration %d type %d offset %d length %d "
+            "bytes, remaining %d \n", first_iteration, msg_type, offset, length, record_length);
         if (!msg_type_str && !first_iteration)
         {
             /* only dissect / report messages if they're
@@ -1390,15 +1726,17 @@ dissect_ssl3_handshake(tvbuff_t *tvb, packet_info *pinfo,
             /* set the label text on the record layer expanding node */
             if (first_iteration)
             {
-                proto_item_set_text(tree, "%s Record Layer: %s",
+                proto_item_set_text(tree, "%s Record Layer: %s Protocol: %s",
                                     ssl_version_short_names[*conv_version],
+                                    val_to_str(content_type, ssl_31_content_type, "unknown"),
                                     (msg_type_str!=NULL) ? msg_type_str :
-                                    "Encrypted Handshake Message");
+                                        "Encrypted Handshake Message");
             }
             else
             {
-                proto_item_set_text(tree, "%s Record Layer: %s",
+                proto_item_set_text(tree, "%s Record Layer: %s Protocol: %s",
                                     ssl_version_short_names[*conv_version],
+                                    val_to_str(content_type, ssl_31_content_type, "unknown"),
                                     "Multiple Handshake Messages");
             }
 
@@ -1418,17 +1756,18 @@ dissect_ssl3_handshake(tvbuff_t *tvb, packet_info *pinfo,
 
         /* 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;
 
@@ -1439,11 +1778,11 @@ dissect_ssl3_handshake(tvbuff_t *tvb, packet_info *pinfo,
                 break;
 
             case SSL_HND_CLIENT_HELLO:
-                dissect_ssl3_hnd_cli_hello(tvb, ssl_hand_tree, offset, length);
+                dissect_ssl3_hnd_cli_hello(tvb, pinfo, 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:
@@ -1467,7 +1806,65 @@ dissect_ssl3_handshake(tvbuff_t *tvb, packet_info *pinfo,
                 break;
 
             case SSL_HND_CLIENT_KEY_EXCHG:
-                /* unimplemented */
+                {
+                    /* PAOLO: here we can have all the data to build session key*/
+                    StringInfo encrypted_pre_master;
+                    gint ret;
+                    guint encrlen, skip;
+                   encrlen = length;
+                   skip = 0;
+
+                    if (!ssl)
+                        break;
+
+                    /* check for required session data */
+                    ssl_debug_printf("dissect_ssl3_handshake found SSL_HND_CLIENT_KEY_EXCHG state 0x%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 0x%02X)\n",
+                            (SSL_CIPHER|SSL_CLIENT_RANDOM|SSL_SERVER_RANDOM|SSL_VERSION));
+                        break;
+                    }
+
+                    /* get encrypted data, on tls1 we have to skip two bytes
+                     * (it's the encrypted len and should be equal to record len - 2)
+                     */
+                    if (ssl->version == SSL_VER_TLS||ssl->version == SSL_VER_TLSv1DOT1)
+                    {
+                        encrlen  = tvb_get_ntohs(tvb, offset);
+                        skip = 2;
+                        if (encrlen > length - 2)
+                        {
+                            ssl_debug_printf("dissect_ssl3_handshake wrong encrypted length (%d max %d)\n",
+                                encrlen, length);
+                            break;
+                        }
+                    }
+                    encrypted_pre_master.data = se_alloc(encrlen);
+                    encrypted_pre_master.data_len = encrlen;
+                    tvb_memcpy(tvb, encrypted_pre_master.data, offset+skip, encrlen);
+
+                    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_session_hash);
+                    ssl_debug_printf("dissect_ssl3_handshake session keys succesfully generated\n");
+                }
                 break;
 
             case SSL_HND_FINISHED:
@@ -1478,35 +1875,73 @@ dissect_ssl3_handshake(tvbuff_t *tvb, packet_info *pinfo,
 
         }
         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 */
     }
 }
 
-static int
+static gint
 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;
+    guint8  session_id_length;
+       proto_item *ti_rnd;
+       proto_tree *ssl_rnd_tree;
+
+    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 %s RANDOM -> state 0x%02X\n",
+            (from_server)?"SERVER":"CLIENT", 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, ssl_session_hash);
+        }
+        else {
+            tvb_memcpy(tvb,ssl->session_id.data, offset+33, session_id_length);
+            ssl->session_id.data_len = session_id_length;
+        }
+    }
 
     if (tree)
     {
+               ti_rnd = proto_tree_add_text(tree, tvb, offset, 32, "Random");
+               ssl_rnd_tree = proto_item_add_subtree(ti_rnd, ett_ssl_random);
+
         /* show the time */
         gmt_unix_time.secs = tvb_get_ntohl(tvb, offset);
         gmt_unix_time.nsecs = 0;
-        proto_tree_add_time(tree, hf_ssl_handshake_random_time,
+        proto_tree_add_time(ssl_rnd_tree, hf_ssl_handshake_random_time,
                                      tvb, offset, 4, &gmt_unix_time);
         offset += 4;
 
         /* show the random bytes */
-        proto_tree_add_item(tree, hf_ssl_handshake_random_bytes,
-                            tvb, offset, 28, 0);
+        proto_tree_add_item(ssl_rnd_tree, hf_ssl_handshake_random_bytes,
+                            tvb, offset, 28, FALSE);
         offset += 28;
 
         /* show the session id */
@@ -1515,20 +1950,20 @@ dissect_ssl3_hnd_hello_common(tvbuff_t *tvb, proto_tree *tree,
                             tvb, offset++, 1, 0);
         if (session_id_length > 0)
         {
-            proto_tree_add_bytes_format(tree, hf_ssl_handshake_session_id,
+            tvb_ensure_bytes_exist(tvb, offset, session_id_length);
+            proto_tree_add_bytes(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"));
+                                         tvb_get_ptr(tvb, offset, session_id_length));
             offset += session_id_length;
         }
 
     }
-    return offset - initial_offset;
+
+    /* XXXX */
+    return session_id_length+33;
 }
 
-static int
+static gint
 dissect_ssl3_hnd_hello_ext(tvbuff_t *tvb,
                            proto_tree *tree, guint32 offset, guint32 left)
 {
@@ -1582,8 +2017,9 @@ dissect_ssl3_hnd_hello_ext(tvbuff_t *tvb,
 }
 
 static void
-dissect_ssl3_hnd_cli_hello(tvbuff_t *tvb,
-                           proto_tree *tree, guint32 offset, guint32 length)
+dissect_ssl3_hnd_cli_hello(tvbuff_t *tvb, packet_info *pinfo,
+       proto_tree *tree, guint32 offset, guint32 length,
+       SslDecryptSession*ssl)
 {
     /* struct {
      *     ProtocolVersion client_version;
@@ -1597,35 +2033,51 @@ dissect_ssl3_hnd_cli_hello(tvbuff_t *tvb,
      */
     proto_tree *ti;
     proto_tree *cs_tree;
-    guint16 cipher_suite_length = 0;
-    guint8  compression_methods_length = 0;
+    gint cipher_suite_length;
+    guint8  compression_methods_length;
     guint8  compression_method;
-    guint16 start_offset = offset;
+    guint16 start_offset;
 
-    if (tree)
+    cipher_suite_length = 0;
+    compression_methods_length = 0;
+    start_offset = offset;
+
+    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)
         {
+            tvb_ensure_bytes_exist(tvb, offset, cipher_suite_length);
             ti = proto_tree_add_none_format(tree,
                                             hf_ssl_handshake_cipher_suites,
                                             tvb, offset, cipher_suite_length,
-                                            "Cipher Suites (%u suite%s)",
+                                            "Cipher Suites (%d suite%s)",
                                             cipher_suite_length / 2,
                                             plurality(cipher_suite_length/2, "", "s"));
+            if (cipher_suite_length % 2) {
+                proto_tree_add_text(tree, tvb, offset, 2,
+                    "Invalid cipher suite length: %d", cipher_suite_length);
+                expert_add_info_format(pinfo, NULL, PI_MALFORMED, PI_ERROR,
+                    "Cipher suite length (%d) must be a multiple of 2",
+                    cipher_suite_length);
+                return;
+            }
 
             /* make this a subtree */
             cs_tree = proto_item_add_subtree(ti, ett_ssl_cipher_suites);
@@ -1651,6 +2103,7 @@ dissect_ssl3_hnd_cli_hello(tvbuff_t *tvb,
 
         if (compression_methods_length > 0)
         {
+            tvb_ensure_bytes_exist(tvb, offset, compression_methods_length);
             ti = proto_tree_add_none_format(tree,
                                             hf_ssl_handshake_comp_methods,
                                             tvb, offset, compression_methods_length,
@@ -1696,7 +2149,7 @@ dissect_ssl3_hnd_cli_hello(tvbuff_t *tvb,
 
 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;
@@ -1707,25 +2160,63 @@ dissect_ssl3_hnd_srv_hello(tvbuff_t *tvb,
      *     Extension server_hello_extension_list<0..2^16-1>;
      * } ServerHello;
      */
-    guint16 start_offset = offset;
+    guint16 start_offset;
+    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 0x%X\n", ssl->cipher);
+                goto no_cipher;
+            }
+
+            ssl->state |= SSL_CIPHER;
+            ssl_debug_printf("dissect_ssl3_hnd_srv_hello found CIPHER 0x%04X -> state 0x%02X\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 0x%02X)\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:
 
         /* 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;
 
+      if (ssl) {
+          /* store selected compression method for decryption */
+          ssl->compression = tvb_get_guint8(tvb, offset);
+      }
         /* and the server-selected compression method */
         proto_tree_add_item(tree, hf_ssl_handshake_comp_method,
                             tvb, offset, 1, FALSE);
@@ -1764,6 +2255,7 @@ dissect_ssl3_hnd_cert(tvbuff_t *tvb,
 
         if (certificate_list_length > 0)
         {
+            tvb_ensure_bytes_exist(tvb, offset, certificate_list_length);
             ti = proto_tree_add_none_format(tree,
                                             hf_ssl_handshake_certificates,
                                             tvb, offset, certificate_list_length,
@@ -1783,7 +2275,8 @@ dissect_ssl3_hnd_cert(tvbuff_t *tvb,
             while (certificate_list_length > 0)
             {
                 /* get the length of the current certificate */
-                guint32 cert_length = tvb_get_ntoh24(tvb, offset);
+                guint32 cert_length;
+               cert_length = tvb_get_ntoh24(tvb, offset);
                 certificate_list_length -= 3 + cert_length;
 
                 proto_tree_add_item(subtree, hf_ssl_handshake_certificate_len,
@@ -1818,8 +2311,10 @@ dissect_ssl3_hnd_cert_req(tvbuff_t *tvb,
      */
     proto_tree *ti;
     proto_tree *subtree;
-    guint8      cert_types_count = 0;
-    int         dnames_length = 0;
+    guint8      cert_types_count;
+    gint         dnames_length;
+    cert_types_count = 0;
+    dnames_length = 0;
 
     if (tree)
     {
@@ -1858,6 +2353,7 @@ dissect_ssl3_hnd_cert_req(tvbuff_t *tvb,
 
         if (dnames_length > 0)
         {
+            tvb_ensure_bytes_exist(tvb, offset, dnames_length);
             ti = proto_tree_add_none_format(tree,
                                             hf_ssl_handshake_dnames,
                                             tvb, offset, dnames_length,
@@ -1873,13 +2369,15 @@ dissect_ssl3_hnd_cert_req(tvbuff_t *tvb,
             while (dnames_length > 0)
             {
                 /* get the length of the current certificate */
-                guint16 name_length = tvb_get_ntohs(tvb, offset);
+                guint16 name_length;
+               name_length = tvb_get_ntohs(tvb, offset);
                 dnames_length -= 2 + name_length;
 
                 proto_tree_add_item(subtree, hf_ssl_handshake_dname_len,
                                     tvb, offset, 2, FALSE);
                 offset += 2;
 
+                tvb_ensure_bytes_exist(tvb, offset, name_length);
                 proto_tree_add_bytes_format(subtree,
                                             hf_ssl_handshake_dname,
                                             tvb, offset, name_length,
@@ -1897,7 +2395,7 @@ dissect_ssl3_hnd_cert_req(tvbuff_t *tvb,
 static void
 dissect_ssl3_hnd_finished(tvbuff_t *tvb,
                           proto_tree *tree, guint32 offset,
-                          guint *conv_version)
+                          guintconv_version)
 {
     /* For TLS:
      *     struct {
@@ -1919,6 +2417,7 @@ dissect_ssl3_hnd_finished(tvbuff_t *tvb,
 
     switch(*conv_version) {
     case SSL_VER_TLS:
+    case SSL_VER_TLSv1DOT1:
         proto_tree_add_item(tree, hf_ssl_handshake_finished,
                             tvb, offset, 12, FALSE);
         break;
@@ -1942,25 +2441,36 @@ dissect_ssl3_hnd_finished(tvbuff_t *tvb,
 
 
 /* record layer dissector */
-static int
+static gint
 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;
-    guint8  record_length_length = 0;
-    guint32 record_length        = 0;
-    gint    is_escape            = -1;
-    gint16  padding_length       = -1;
-    guint8  msg_type             = 0;
-    gchar   *msg_type_str        = NULL;
-    guint32 available_bytes      = 0;
-
+    guint32 initial_offset;
+    guint8  byte;
+    guint8  record_length_length;
+    guint32 record_length;
+    gint    is_escape;
+    gint16  padding_length;
+    guint8  msg_type;
+    const gchar *msg_type_str;
+    guint32 available_bytes;
     proto_tree *ti;
-    proto_tree *ssl_record_tree = NULL;
-
-    /* pull first byte; if high bit is set, then record
+    proto_tree *ssl_record_tree;
+
+    initial_offset       = offset;
+    byte                 = 0;
+    record_length_length = 0;
+    record_length        = 0;
+    is_escape            = -1;
+    padding_length       = -1;
+    msg_type             = 0;
+    msg_type_str         = NULL;
+    available_bytes      = 0;
+    ssl_record_tree      = NULL;
+
+    /* pull first byte; if high bit is unset, then record
      * length is three bytes due to padding; otherwise
      * record length is two bytes
      */
@@ -2045,12 +2555,12 @@ dissect_ssl2_record(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                                                 record_length_length),
                                                record_length)) {
             *conv_version = SSL_VER_PCT;
-            ssl_set_conv_version(pinfo, *conv_version);
+            /*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);*/
         }
     }
 
@@ -2086,7 +2596,18 @@ dissect_ssl2_record(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                                 (*conv_version == SSL_VER_PCT)
                                 ? "PCT" : "SSLv2",
                                 "Encrypted Data");
+
+           /* Unlike SSLv3, the SSLv2 record layer does not have a
+            * version field. To make it possible to filter on record
+            * layer version we create a generated field with ssl
+            * record layer version 0x0002
+            */
+           ti = proto_tree_add_uint(ssl_record_tree,
+                                    hf_ssl_record_version, tvb,
+                                    initial_offset, 0, 0x0002);
+           PROTO_ITEM_SET_GENERATED(ti);
         }
+
         if (check_col(pinfo->cinfo, COL_INFO))
             col_append_str(pinfo->cinfo, COL_INFO, "Encrypted Data");
         return initial_offset + record_length_length + record_length;
@@ -2111,7 +2632,18 @@ dissect_ssl2_record(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
      */
     if (ssl_record_tree)
     {
+       /* Unlike SSLv3, the SSLv2 record layer does not have a
+        * version field. To make it possible to filter on record
+        * layer version we create a generated field with ssl
+        * record layer version 0x0002
+        */
+       ti = proto_tree_add_uint(ssl_record_tree,
+                                hf_ssl_record_version, tvb,
+                                initial_offset, 0, 0x0002);
+       PROTO_ITEM_SET_GENERATED(ti);
+
         /* add the record length */
+        tvb_ensure_bytes_exist(tvb, offset, record_length_length);
         ti = proto_tree_add_uint (ssl_record_tree,
                                   hf_ssl_record_length, tvb,
                                   initial_offset, record_length_length,
@@ -2122,7 +2654,7 @@ dissect_ssl2_record(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
         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,
@@ -2152,7 +2684,7 @@ dissect_ssl2_record(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
         /* 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:
@@ -2205,7 +2737,8 @@ dissect_ssl2_record(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
 
 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;
@@ -2228,6 +2761,7 @@ dissect_ssl2_hnd_client_hello(tvbuff_t *tvb,
 
     proto_tree *ti;
     proto_tree *cs_tree;
+    cs_tree=0;
 
     version = tvb_get_ntohs(tvb, offset);
     if (!ssl_is_valid_ssl_version(version))
@@ -2236,45 +2770,54 @@ dissect_ssl2_hnd_client_hello(tvbuff_t *tvb,
         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_handshake_client_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 */
-        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;
@@ -2283,22 +2826,54 @@ dissect_ssl2_hnd_client_hello(tvbuff_t *tvb,
         /* if there's a session id, show it */
         if (session_id_length > 0)
         {
-            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;
         }
 
         /* if there's a challenge, show it */
         if (challenge_length > 0)
         {
-            proto_tree_add_item(tree, hf_ssl2_handshake_challenge,
+            tvb_ensure_bytes_exist(tvb, offset, challenge_length);
+
+            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 */
+                gint max;
+                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;
         }
     }
@@ -2308,7 +2883,7 @@ static void
 dissect_pct_msg_client_hello(tvbuff_t *tvb,
                                                        proto_tree *tree, guint32 offset)
 {
-       guint16 CH_CLIENT_VERSION, CH_OFFSET, CH_CIPHER_SPECS_LENGTH, CH_HASH_SPECS_LENGTH, CH_CERT_SPECS_LENGTH, CH_EXCH_SPECS_LENGTH, CH_KEY_ARG_LENGTH; 
+       guint16 CH_CLIENT_VERSION, CH_OFFSET, CH_CIPHER_SPECS_LENGTH, CH_HASH_SPECS_LENGTH, CH_CERT_SPECS_LENGTH, CH_EXCH_SPECS_LENGTH, CH_KEY_ARG_LENGTH;
        proto_item *CH_CIPHER_SPECS_ti, *CH_HASH_SPECS_ti, *CH_CERT_SPECS_ti, *CH_EXCH_SPECS_ti;
        proto_tree *CH_CIPHER_SPECS_tree, *CH_HASH_SPECS_tree, *CH_CERT_SPECS_tree, *CH_EXCH_SPECS_tree;
        gint i;
@@ -2319,47 +2894,48 @@ dissect_pct_msg_client_hello(tvbuff_t *tvb,
        else
                proto_tree_add_text(tree, tvb, offset, 2, "Client Version (%x)", PCT_VERSION_1);
        offset += 2;
-       
+
        proto_tree_add_text(tree, tvb, offset, 1, "PAD");
        offset += 1;
-       
+
        proto_tree_add_text(tree, tvb, offset, 32, "Client Session ID Data (32 bytes)");
        offset += 32;
-       
+
        proto_tree_add_text(tree, tvb, offset, 32, "Challange Data(32 bytes)");
        offset += 32;
-       
+
        CH_OFFSET = tvb_get_ntohs(tvb, offset);
        if(CH_OFFSET != PCT_CH_OFFSET_V1)
                proto_tree_add_text(tree, tvb, offset, 2, "CH_OFFSET: %d, should be %d in PCT version 1", CH_OFFSET, PCT_CH_OFFSET_V1);
        else
                proto_tree_add_text(tree, tvb, offset, 2, "CH_OFFSET: %d", CH_OFFSET);
        offset += 2;
-       
+
        CH_CIPHER_SPECS_LENGTH = tvb_get_ntohs(tvb, offset);
        proto_tree_add_text(tree, tvb, offset, 2, "CIPHER_SPECS Length: %d", CH_CIPHER_SPECS_LENGTH);
        offset += 2;
-       
+
        CH_HASH_SPECS_LENGTH = tvb_get_ntohs(tvb, offset);
        proto_tree_add_text(tree, tvb, offset, 2, "HASH_SPECS Length: %d", CH_HASH_SPECS_LENGTH);
        offset += 2;
-       
+
        CH_CERT_SPECS_LENGTH = tvb_get_ntohs(tvb, offset);
        proto_tree_add_text(tree, tvb, offset, 2, "CERT_SPECS Length: %d", CH_CERT_SPECS_LENGTH);
        offset += 2;
-       
+
        CH_EXCH_SPECS_LENGTH = tvb_get_ntohs(tvb, offset);
        proto_tree_add_text(tree, tvb, offset, 2, "EXCH_SPECS Length: %d", CH_EXCH_SPECS_LENGTH);
        offset += 2;
-       
+
        CH_KEY_ARG_LENGTH = tvb_get_ntohs(tvb, offset);
        proto_tree_add_text(tree, tvb, offset, 2, "IV Length: %d", CH_KEY_ARG_LENGTH);
        offset += 2;
-       
+
        if(CH_CIPHER_SPECS_LENGTH) {
+                tvb_ensure_bytes_exist(tvb, offset, CH_CIPHER_SPECS_LENGTH);
                CH_CIPHER_SPECS_ti = proto_tree_add_item(tree, hf_pct_handshake_cipher_spec, tvb, offset, CH_CIPHER_SPECS_LENGTH, FALSE);
                CH_CIPHER_SPECS_tree = proto_item_add_subtree(CH_CIPHER_SPECS_ti, ett_pct_cipher_suites);
-               
+
                for(i=0; i<(CH_CIPHER_SPECS_LENGTH/4); i++) {
                        proto_tree_add_item(CH_CIPHER_SPECS_tree, hf_pct_handshake_cipher, tvb, offset, 2, FALSE);
                        offset += 2;
@@ -2369,38 +2945,42 @@ dissect_pct_msg_client_hello(tvbuff_t *tvb,
                        offset += 1;
                }
        }
-       
+
        if(CH_HASH_SPECS_LENGTH) {
+                tvb_ensure_bytes_exist(tvb, offset, CH_HASH_SPECS_LENGTH);
                CH_HASH_SPECS_ti = proto_tree_add_item(tree, hf_pct_handshake_hash_spec, tvb, offset, CH_HASH_SPECS_LENGTH, FALSE);
                CH_HASH_SPECS_tree = proto_item_add_subtree(CH_HASH_SPECS_ti, ett_pct_hash_suites);
-               
+
                for(i=0; i<(CH_HASH_SPECS_LENGTH/2); i++) {
                        proto_tree_add_item(CH_HASH_SPECS_tree, hf_pct_handshake_hash, tvb, offset, 2, FALSE);
                        offset += 2;
                }
        }
-       
+
        if(CH_CERT_SPECS_LENGTH) {
+                tvb_ensure_bytes_exist(tvb, offset, CH_CERT_SPECS_LENGTH);
                CH_CERT_SPECS_ti = proto_tree_add_item(tree, hf_pct_handshake_cert_spec, tvb, offset, CH_CERT_SPECS_LENGTH, FALSE);
                CH_CERT_SPECS_tree = proto_item_add_subtree(CH_CERT_SPECS_ti, ett_pct_cert_suites);
-               
+
                for(i=0; i< (CH_CERT_SPECS_LENGTH/2); i++) {
                        proto_tree_add_item(CH_CERT_SPECS_tree, hf_pct_handshake_cert, tvb, offset, 2, FALSE);
                        offset += 2;
                }
        }
-       
+
        if(CH_EXCH_SPECS_LENGTH) {
+                tvb_ensure_bytes_exist(tvb, offset, CH_EXCH_SPECS_LENGTH);
                CH_EXCH_SPECS_ti = proto_tree_add_item(tree, hf_pct_handshake_exch_spec, tvb, offset, CH_EXCH_SPECS_LENGTH, FALSE);
                CH_EXCH_SPECS_tree = proto_item_add_subtree(CH_EXCH_SPECS_ti, ett_pct_exch_suites);
-               
+
                for(i=0; i<(CH_EXCH_SPECS_LENGTH/2); i++) {
                        proto_tree_add_item(CH_EXCH_SPECS_tree, hf_pct_handshake_exch, tvb, offset, 2, FALSE);
                        offset += 2;
                }
        }
-       
+
        if(CH_KEY_ARG_LENGTH) {
+                tvb_ensure_bytes_exist(tvb, offset, CH_KEY_ARG_LENGTH);
                proto_tree_add_text(tree, tvb, offset, CH_KEY_ARG_LENGTH, "IV data (%d bytes)", CH_KEY_ARG_LENGTH);
                offset += CH_KEY_ARG_LENGTH;
        }
@@ -2409,7 +2989,7 @@ dissect_pct_msg_client_hello(tvbuff_t *tvb,
 static void
 dissect_pct_msg_server_hello(tvbuff_t *tvb, proto_tree *tree, guint32 offset, packet_info *pinfo)
 {
-/* structure: 
+/* structure:
 char SH_MSG_SERVER_HELLO
 char SH_PAD
 char SH_SERVER_VERSION_MSB
@@ -2436,7 +3016,7 @@ char SH_RESPONSE_DATA[MSB<<8|LSB]
 
 */
 
-       guint16 SH_SERVER_VERSION, SH_CERT_LENGTH, SH_CERT_SPECS_LENGTH, SH_CLIENT_SIG_LENGTH, SH_RESPONSE_LENGTH; 
+       guint16 SH_SERVER_VERSION, SH_CERT_LENGTH, SH_CERT_SPECS_LENGTH, SH_CLIENT_SIG_LENGTH, SH_RESPONSE_LENGTH;
 
        proto_tree_add_text(tree, tvb, offset, 1, "PAD");
        offset += 1;
@@ -2447,13 +3027,13 @@ char SH_RESPONSE_DATA[MSB<<8|LSB]
        else
                proto_tree_add_text(tree, tvb, offset, 2, "Server Version (%x)", PCT_VERSION_1);
        offset += 2;
-       
+
        proto_tree_add_text(tree, tvb, offset, 1, "SH_RESTART_SESSION_OK flag");
        offset += 1;
 
        proto_tree_add_text(tree, tvb, offset, 1, "SH_CLIENT_AUTH_REQ flag");
        offset += 1;
-       
+
        proto_tree_add_item(tree, hf_pct_handshake_cipher, tvb, offset, 2, FALSE);
        offset += 2;
        proto_tree_add_text(tree, tvb, offset, 1, "Encryption key length: %d", tvb_get_guint8(tvb, offset));
@@ -2476,35 +3056,38 @@ char SH_RESPONSE_DATA[MSB<<8|LSB]
        SH_CERT_LENGTH = tvb_get_ntohs(tvb, offset);
        proto_tree_add_text(tree, tvb, offset, 2, "Server Certificate Length: %d", SH_CERT_LENGTH);
        offset += 2;
-       
+
        SH_CERT_SPECS_LENGTH = tvb_get_ntohs(tvb, offset);
        proto_tree_add_text(tree, tvb, offset, 2, "Client CERT_SPECS Length: %d", SH_CERT_SPECS_LENGTH);
        offset += 2;
-       
+
        SH_CLIENT_SIG_LENGTH = tvb_get_ntohs(tvb, offset);
        proto_tree_add_text(tree, tvb, offset, 2, "Client SIG_SPECS Length: %d", SH_CLIENT_SIG_LENGTH);
        offset += 2;
-       
+
        SH_RESPONSE_LENGTH = tvb_get_ntohs(tvb, offset);
        proto_tree_add_text(tree, tvb, offset, 2, "Response Length: %d", SH_RESPONSE_LENGTH);
        offset += 2;
-       
+
        if(SH_CERT_LENGTH) {
                dissect_x509af_Certificate(FALSE, tvb, offset, pinfo, tree, hf_pct_handshake_server_cert);
                offset += SH_CERT_LENGTH;
        }
 
        if(SH_CERT_SPECS_LENGTH) {
+                tvb_ensure_bytes_exist(tvb, offset, SH_CERT_SPECS_LENGTH);
                proto_tree_add_text(tree, tvb, offset, SH_CERT_SPECS_LENGTH, "Client CERT_SPECS (%d bytes)", SH_CERT_SPECS_LENGTH);
                offset += SH_CERT_SPECS_LENGTH;
        }
 
        if(SH_CLIENT_SIG_LENGTH) {
+                tvb_ensure_bytes_exist(tvb, offset, SH_CLIENT_SIG_LENGTH);
                proto_tree_add_text(tree, tvb, offset, SH_CLIENT_SIG_LENGTH, "Client Signature (%d bytes)", SH_CLIENT_SIG_LENGTH);
                offset += SH_CLIENT_SIG_LENGTH;
        }
 
        if(SH_RESPONSE_LENGTH) {
+                tvb_ensure_bytes_exist(tvb, offset, SH_RESPONSE_LENGTH);
                proto_tree_add_text(tree, tvb, offset, SH_RESPONSE_LENGTH, "Server Response (%d bytes)", SH_RESPONSE_LENGTH);
                offset += SH_RESPONSE_LENGTH;
        }
@@ -2550,26 +3133,32 @@ dissect_pct_msg_client_master_key(tvbuff_t *tvb, proto_tree *tree, guint32 offse
        offset += 2;
 
        if(CMK_CLEAR_KEY_LENGTH) {
+                tvb_ensure_bytes_exist(tvb, offset, CMK_CLEAR_KEY_LENGTH);
                proto_tree_add_text(tree, tvb, offset, CMK_CLEAR_KEY_LENGTH, "Clear Key data (%d bytes)", CMK_CLEAR_KEY_LENGTH);
                offset += CMK_CLEAR_KEY_LENGTH;
        }
        if(CMK_ENCRYPTED_KEY_LENGTH) {
+                tvb_ensure_bytes_exist(tvb, offset, CMK_ENCRYPTED_KEY_LENGTH);
                proto_tree_add_text(tree, tvb, offset, CMK_ENCRYPTED_KEY_LENGTH, "Encrypted Key data (%d bytes)", CMK_ENCRYPTED_KEY_LENGTH);
                offset += CMK_ENCRYPTED_KEY_LENGTH;
        }
        if(CMK_KEY_ARG_LENGTH) {
+                tvb_ensure_bytes_exist(tvb, offset, CMK_KEY_ARG_LENGTH);
                proto_tree_add_text(tree, tvb, offset, CMK_KEY_ARG_LENGTH, "IV data (%d bytes)", CMK_KEY_ARG_LENGTH);
                offset += CMK_KEY_ARG_LENGTH;
        }
        if(CMK_VERIFY_PRELUDE) {
+                tvb_ensure_bytes_exist(tvb, offset, CMK_VERIFY_PRELUDE);
                proto_tree_add_text(tree, tvb, offset, CMK_VERIFY_PRELUDE, "Verify Prelude data (%d bytes)", CMK_VERIFY_PRELUDE);
                offset += CMK_VERIFY_PRELUDE;
        }
        if(CMK_CLIENT_CERT_LENGTH) {
+                tvb_ensure_bytes_exist(tvb, offset, CMK_CLIENT_CERT_LENGTH);
                proto_tree_add_text(tree, tvb, offset, CMK_CLIENT_CERT_LENGTH, "Client Certificate data (%d bytes)", CMK_CLIENT_CERT_LENGTH);
                offset += CMK_CLIENT_CERT_LENGTH;
        }
        if(CMK_RESPONSE_LENGTH) {
+                tvb_ensure_bytes_exist(tvb, offset, CMK_RESPONSE_LENGTH);
                proto_tree_add_text(tree, tvb, offset, CMK_RESPONSE_LENGTH, "Response data (%d bytes)", CMK_RESPONSE_LENGTH);
                offset += CMK_RESPONSE_LENGTH;
        }
@@ -2592,6 +3181,7 @@ dissect_pct_msg_server_verify(tvbuff_t *tvb,
        offset += 2;
 
        if(SV_RESPONSE_LENGTH) {
+                tvb_ensure_bytes_exist(tvb, offset, SV_RESPONSE_LENGTH);
                proto_tree_add_text(tree, tvb, offset, SV_RESPONSE_LENGTH, "Server Response (%d bytes)", SV_RESPONSE_LENGTH);
                offset += SV_RESPONSE_LENGTH;
        }
@@ -2684,6 +3274,7 @@ dissect_ssl2_hnd_client_master_key(tvbuff_t *tvb,
     /* show the variable length fields */
     if (clear_key_length > 0)
     {
+        tvb_ensure_bytes_exist(tvb, offset, clear_key_length);
         proto_tree_add_item(tree, hf_ssl2_handshake_clear_key,
                             tvb, offset, clear_key_length, FALSE);
         offset += clear_key_length;
@@ -2691,6 +3282,7 @@ dissect_ssl2_hnd_client_master_key(tvbuff_t *tvb,
 
     if (encrypted_key_length > 0)
     {
+        tvb_ensure_bytes_exist(tvb, offset, encrypted_key_length);
         proto_tree_add_item(tree, hf_ssl2_handshake_enc_key,
                             tvb, offset, encrypted_key_length, FALSE);
         offset += encrypted_key_length;
@@ -2698,6 +3290,7 @@ dissect_ssl2_hnd_client_master_key(tvbuff_t *tvb,
 
     if (key_arg_length > 0)
     {
+        tvb_ensure_bytes_exist(tvb, offset, key_arg_length);
         proto_tree_add_item(tree, hf_ssl2_handshake_key_arg,
                             tvb, offset, key_arg_length, FALSE);
         offset += key_arg_length;
@@ -2788,6 +3381,7 @@ dissect_ssl2_hnd_server_hello(tvbuff_t *tvb,
     if (cipher_spec_length > 0)
     {
         /* provide a collapsing node for the cipher specs */
+        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,
@@ -2812,6 +3406,7 @@ dissect_ssl2_hnd_server_hello(tvbuff_t *tvb,
 
     if (connection_id_length > 0)
     {
+        tvb_ensure_bytes_exist(tvb, offset, connection_id_length);
         proto_tree_add_item(tree, hf_ssl2_handshake_connection_id,
                             tvb, offset, connection_id_length, FALSE);
         offset += connection_id_length;
@@ -2820,6 +3415,134 @@ dissect_ssl2_hnd_server_hello(tvbuff_t *tvb,
 }
 
 
+void ssl_set_master_secret(guint32 frame_num, address *addr_srv, address *addr_cli,
+                           port_type ptype, guint32 port_srv, guint32 port_cli,
+                           guint32 version, gint cipher, const guchar *_master_secret,
+                           const guchar *_client_random, const guchar *_server_random,
+                           guint32 client_seq, guint32 server_seq)
+{
+  conversation_t *conversation = NULL;
+  void *conv_data = NULL;
+  SslDecryptSession *ssl = NULL;
+  guint iv_len;
+
+  ssl_debug_printf("\nssl_set_master_secret enter frame #%u\n", frame_num);
+
+  conversation = find_conversation(frame_num, addr_srv, addr_cli, ptype, port_srv, port_cli, 0);
+
+  if (!conversation) {
+    /* create a new conversation */
+    conversation = conversation_new(frame_num, addr_srv, addr_cli, ptype, port_srv, port_cli, 0);
+  }
+  conv_data = conversation_get_proto_data(conversation, proto_ssl);
+
+  if (conv_data) {
+    ssl = conv_data;
+  } else {
+    ssl = se_alloc0(sizeof(SslDecryptSession));
+    ssl_session_init(ssl);
+    ssl->version = SSL_VER_UNKNOWN;
+    conversation_add_proto_data(conversation, proto_ssl, ssl);
+  }
+
+  /* version */
+  if ((ssl->version==SSL_VER_UNKNOWN) && (version!=SSL_VER_UNKNOWN)) {
+    switch (version) {
+      case SSL_VER_SSLv3:
+        ssl->version = SSL_VER_SSLv3;
+        ssl->version_netorder = SSLV3_VERSION;
+        ssl->state |= SSL_VERSION;
+        ssl_debug_printf("ssl_set_master_secret set version 0x%04X -> state 0x%02X\n", ssl->version_netorder, ssl->state);
+        break;
+
+      case SSL_VER_TLS:
+        ssl->version = SSL_VER_TLS;
+        ssl->version_netorder = TLSV1_VERSION;
+        ssl->state |= SSL_VERSION;
+        ssl_debug_printf("ssl_set_master_secret set version 0x%04X -> state 0x%02X\n", ssl->version_netorder, ssl->state);
+        break;
+
+      case SSL_VER_TLSv1DOT1:
+        ssl->version = SSL_VER_TLSv1DOT1;
+        ssl->version_netorder = TLSV1DOT1_VERSION;
+        ssl->state |= SSL_VERSION;
+        ssl_debug_printf("ssl_set_master_secret set version 0x%04X -> state 0x%02X\n", ssl->version_netorder, ssl->state);
+        break;
+    }
+  }
+
+  /* cipher */
+  if (cipher > 0) {
+    ssl->cipher = cipher;
+    if (ssl_find_cipher(ssl->cipher,&ssl->cipher_suite) < 0) {
+        ssl_debug_printf("ssl_set_master_secret can't find cipher suite 0x%X\n", ssl->cipher);
+    } else {
+        ssl->state |= SSL_CIPHER;
+        ssl_debug_printf("ssl_set_master_secret set CIPHER 0x%04X -> state 0x%02X\n", ssl->cipher, ssl->state);
+    }
+  }
+
+  /* client random */
+  if (_client_random) {
+    ssl_data_set(&ssl->client_random, _client_random, 32);
+    ssl->state |= SSL_CLIENT_RANDOM;
+    ssl_debug_printf("ssl_set_master_secret set CLIENT RANDOM -> state 0x%02X\n", ssl->state);
+  }
+
+  /* server random */
+  if (_server_random) {
+    ssl_data_set(&ssl->server_random, _server_random, 32);
+    ssl->state |= SSL_SERVER_RANDOM;
+    ssl_debug_printf("ssl_set_master_secret set SERVER RANDOM -> state 0x%02X\n", ssl->state);
+  }
+
+  /* master secret */
+  if (_master_secret) {
+    ssl_data_set(&ssl->master_secret, _master_secret, 48);
+    ssl->state |= SSL_MASTER_SECRET;
+    ssl_debug_printf("ssl_set_master_secret set MASTER SECRET -> state 0x%02X\n", ssl->state);
+  }
+
+  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("ssl_set_master_secret not enough data to generate key (has 0x%02X but required 0x%02X)\n",
+          ssl->state, (SSL_CIPHER|SSL_CLIENT_RANDOM|SSL_SERVER_RANDOM|SSL_VERSION|SSL_MASTER_SECRET));
+      return;
+  }
+
+  ssl_debug_printf("ssl_set_master_secret trying to generate keys\n");
+  if (ssl_generate_keyring_material(ssl)<0) {
+      ssl_debug_printf("ssl_set_master_secret can't generate keyring material\n");
+      return;
+  }
+  ssl->state |= SSL_HAVE_SESSION_KEY;
+
+  /* chenge ciphers immediately */
+  ssl_change_cipher(ssl, TRUE);
+  ssl_change_cipher(ssl, FALSE);
+
+  /* update seq numbers is available */
+  if (ssl->client && (client_seq != (guint32)-1)) {
+    ssl->client->seq = client_seq;
+    ssl_debug_printf("ssl_set_master_secret client->seq updated to %u\n", ssl->client->seq);
+  }
+  if (ssl->server && (server_seq != (guint32)-1)) {
+    ssl->server->seq = server_seq;
+    ssl_debug_printf("ssl_set_master_secret server->seq updated to %u\n", ssl->server->seq);
+  }
+
+  /* update IV from last data */
+  iv_len = (ssl->cipher_suite.block>1) ? ssl->cipher_suite.block : 8;
+  if (ssl->client && ((ssl->client->seq > 0) || (ssl->client_data_for_iv.data_len > iv_len))) {
+    ssl_cipher_setiv(&ssl->client->evp, ssl->client_data_for_iv.data + ssl->client_data_for_iv.data_len - iv_len, iv_len);
+    ssl_print_data("ssl_set_master_secret client IV updated",ssl->client_data_for_iv.data + ssl->client_data_for_iv.data_len - iv_len, iv_len);
+  }
+  if (ssl->server && ((ssl->server->seq > 0) || (ssl->server_data_for_iv.data_len > iv_len))) {
+    ssl_cipher_setiv(&ssl->server->evp, ssl->server_data_for_iv.data + ssl->server_data_for_iv.data_len - iv_len, iv_len);
+    ssl_print_data("ssl_set_master_secret server IV updated",ssl->server_data_for_iv.data + ssl->server_data_for_iv.data_len - iv_len, iv_len);
+  }
+}
 
 
 /*********************************************************************
@@ -2827,7 +3550,7 @@ dissect_ssl2_hnd_server_hello(tvbuff_t *tvb,
  * Support Functions
  *
  *********************************************************************/
-
+#if 0
 static void
 ssl_set_conv_version(packet_info *pinfo, guint version)
 {
@@ -2858,8 +3581,9 @@ ssl_set_conv_version(packet_info *pinfo, guint version)
     }
     conversation_add_proto_data(conversation, proto_ssl, GINT_TO_POINTER(version));
 }
+#endif
 
-static int
+static gint
 ssl_is_valid_handshake_type(guint8 type)
 {
 
@@ -2879,25 +3603,15 @@ ssl_is_valid_handshake_type(guint8 type)
     return 0;
 }
 
-static int
-ssl_is_valid_content_type(guint8 type)
-{
-    if (type >= 0x14 && type <= 0x17)
-    {
-        return 1;
-    }
-
-    return 0;
-}
-
-static int
+static gint
 ssl_is_valid_ssl_version(guint16 version)
 {
-    gchar *version_str = match_strval(version, ssl_versions);
+    const gchar *version_str;
+    version_str = match_strval(version, ssl_versions);
     return version_str != NULL;
 }
 
-static int
+static gint
 ssl_is_authoritative_version_message(guint8 content_type,
                                      guint8 next_byte)
 {
@@ -2914,7 +3628,7 @@ ssl_is_authoritative_version_message(guint8 content_type,
     return 0;
 }
 
-static int
+static gint
 ssl_is_v2_client_hello(tvbuff_t *tvb, guint32 offset)
 {
     guint8 byte;
@@ -2940,7 +3654,7 @@ ssl_is_v2_client_hello(tvbuff_t *tvb, guint32 offset)
  * valid sslv2 record.  this isn't really possible,
  * but we'll try to do a reasonable job anyway.
  */
-static int
+static gint
 ssl_looks_like_sslv2(tvbuff_t *tvb, guint32 offset)
 {
     /* here's the current approach:
@@ -2952,7 +3666,9 @@ ssl_looks_like_sslv2(tvbuff_t *tvb, guint32 offset)
      */
 
     /* get the first byte; must have high bit set */
-    guint8 byte = tvb_get_guint8(tvb, offset);
+    guint8 byte;
+    byte = tvb_get_guint8(tvb, offset);
+
     if (byte < 0x80)
     {
         return 0;
@@ -2980,7 +3696,7 @@ ssl_looks_like_sslv2(tvbuff_t *tvb, guint32 offset)
  * valid sslv3 record.  this is somewhat more reliable
  * than sslv2 due to the structure of the v3 protocol
  */
-static int
+static gint
 ssl_looks_like_sslv3(tvbuff_t *tvb, guint32 offset)
 {
     /* have to have a valid content type followed by a valid
@@ -2998,7 +3714,7 @@ ssl_looks_like_sslv3(tvbuff_t *tvb, guint32 offset)
 
     /* now check to see if the version byte appears valid */
     version = tvb_get_ntohs(tvb, offset + 1);
-    if (version != 0x0300 && version != 0x0301)
+    if (version != SSLV3_VERSION && version != TLSV1_VERSION && version != TLSV1DOT1_VERSION)
     {
         return 0;
     }
@@ -3013,7 +3729,7 @@ ssl_looks_like_sslv3(tvbuff_t *tvb, guint32 offset)
  * data apart from a valid message without state,
  * we try to help the odds.
  */
-static int
+static gint
 ssl_looks_like_valid_v2_handshake(tvbuff_t *tvb, guint32 offset,
                                   guint32 record_length)
 {
@@ -3074,7 +3790,7 @@ ssl_looks_like_valid_v2_handshake(tvbuff_t *tvb, guint32 offset,
  * data apart from a valid message without state,
  * we try to help the odds.
  */
-static int
+static gint
 ssl_looks_like_valid_pct_handshake(tvbuff_t *tvb, guint32 offset,
                                   guint32 record_length)
 {
@@ -3141,7 +3857,7 @@ ssl_looks_like_valid_pct_handshake(tvbuff_t *tvb, guint32 offset,
 
 /*********************************************************************
  *
- * Standard Ethereal Protocol Registration and housekeeping
+ * Standard Wireshark Protocol Registration and housekeeping
  *
  *********************************************************************/
 void
@@ -3181,10 +3897,11 @@ proto_register_ssl(void)
             "Length of SSL record data", HFILL }
         },
         { &hf_ssl_record_appdata,
-          { "Application Data", "ssl.app_data",
-            FT_NONE, BASE_NONE, NULL, 0x0,
-            "Payload is application data", HFILL }
+          { "Encrypted Application Data", "ssl.app_data",
+            FT_BYTES, BASE_HEX, NULL, 0x0,
+            "Payload is encrypted application data", HFILL }
         },
+
         { & hf_ssl2_record,
           { "SSLv2/PCT Record Header", "ssl.record",
             FT_NONE, BASE_DEC, NULL, 0x0,
@@ -3246,13 +3963,13 @@ proto_register_ssl(void)
             "Version selected by server", HFILL }
         },
         { &hf_ssl_handshake_random_time,
-          { "Random.gmt_unix_time", "ssl.handshake.random_time",
-            FT_ABSOLUTE_TIME, BASE_NONE, NULL, 0x0,
+          { "gmt_unix_time", "ssl.handshake.random_time",
+            FT_ABSOLUTE_TIME, 0, NULL, 0x0,
             "Unix time field of random structure", HFILL }
         },
         { &hf_ssl_handshake_random_bytes,
-          { "Random.bytes", "ssl.handshake.random",
-            FT_NONE, BASE_NONE, NULL, 0x0,
+          { "random_bytes", "ssl.handshake.random_bytes",
+            FT_BYTES, 0, NULL, 0x0,
             "Random challenge used to authenticate server", HFILL }
         },
         { &hf_ssl_handshake_cipher_suites_len,
@@ -3460,61 +4177,92 @@ proto_register_ssl(void)
             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 }
+        },
+               { &hf_ssl_segment_overlap,
+               { "Segment overlap",    "ssl.segment.overlap", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
+                       "Segment overlaps with other segments", HFILL }},
+
+               { &hf_ssl_segment_overlap_conflict,
+               { "Conflicting data in segment overlap",        "ssl.segment.overlap.conflict", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
+                       "Overlapping segments contained conflicting data", HFILL }},
+
+               { &hf_ssl_segment_multiple_tails,
+               { "Multiple tail segments found",       "ssl.segment.multipletails", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
+                       "Several tails were found when reassembling the pdu", HFILL }},
+
+               { &hf_ssl_segment_too_long_fragment,
+               { "Segment too long",   "ssl.segment.toolongfragment", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
+                       "Segment contained data past end of the pdu", HFILL }},
+
+               { &hf_ssl_segment_error,
+               { "Reassembling error", "ssl.segment.error", FT_FRAMENUM, BASE_NONE, NULL, 0x0,
+                       "Reassembling error due to illegal segments", HFILL }},
+
+               { &hf_ssl_segment,
+               { "SSL Segment", "ssl.segment", FT_FRAMENUM, BASE_NONE, NULL, 0x0,
+                       "SSL Segment", HFILL }},
+
+               { &hf_ssl_segments,
+               { "Reassembled SSL Segments", "ssl.segments", FT_NONE, BASE_NONE, NULL, 0x0,
+                       "SSL Segments", HFILL }},
+
+               { &hf_ssl_reassembled_in,
+               { "Reassembled PDU in frame", "ssl.reassembled_in", FT_FRAMENUM, BASE_NONE, NULL, 0x0,
+                       "The PDU that doesn't end in this segment is reassembled in this frame", HFILL }},
     };
 
     /* Setup protocol subtree array */
@@ -3529,10 +4277,13 @@ proto_register_ssl(void)
         &ett_ssl_certs,
         &ett_ssl_cert_types,
         &ett_ssl_dnames,
+        &ett_ssl_random,
        &ett_pct_cipher_suites,
        &ett_pct_hash_suites,
        &ett_pct_cert_suites,
        &ett_pct_exch_suites,
+               &ett_ssl_segments,
+               &ett_ssl_segment,
     };
 
     /* Register the protocol name and description */
@@ -3545,17 +4296,42 @@ proto_register_ssl(void)
     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, proto_reg_handoff_ssl);
       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);
+      prefs_register_bool_preference(ssl_module,
+             "desegment_ssl_application_data",
+             "Reassemble SSL Application Data spanning multiple SSL records",
+             "Whether the SSL dissector should reassemble SSL Application Data spanning multiple SSL records. ",
+             &ssl_desegment_app_data);
+#ifdef HAVE_LIBGNUTLS
+       prefs_register_string_preference(ssl_module, "keys_list", "RSA keys list",
+             "Semicolon-separated list of private RSA keys used for SSL decryption; "
+             "each list entry must be in the form of <ip>,<port>,<protocol>,<key_file_name>. "
+             "<key_file_name> is the local file name of the RSA private key used by the specified server "
+             "(or name of the file containing such a list)",
+             (const gchar **)&ssl_keys_list);
+        prefs_register_string_preference(ssl_module, "debug_file", "SSL debug file",
+             "redirect ssl debug to file name; leave empty to disable debug, "
+             "use \"" SSL_DEBUG_USE_STDERR "\" to redirect output to stderr\n",
+             (const gchar **)&ssl_debug_file_name);
+#endif
     }
 
     register_dissector("ssl", dissect_ssl, proto_ssl);
+    ssl_handle = find_dissector("ssl");
 
+    ssl_associations = g_tree_new(ssl_association_cmp);
+
+    register_init_routine(ssl_init);
+    ssl_lib_init();
+    ssl_tap = register_tap("ssl");
+    ssl_debug_printf("proto_register_ssl: registered tap %s:%d\n",
+        "ssl", ssl_tap);
 }
 
 /* If this dissector uses sub-dissector registration add a registration
@@ -3565,11 +4341,37 @@ proto_register_ssl(void)
 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);
+    /* parse key list */
+    ssl_parse();
+
+    /* add ssl dissection to defaults ports */
+    ssl_dissector_add(443, "http", TRUE);
+    ssl_dissector_add(636, "ldap", TRUE);
+    ssl_dissector_add(993, "imap", TRUE);
+    ssl_dissector_add(995, "pop", TRUE);
+}
+
+void
+ssl_dissector_add(guint port, const gchar *protocol, gboolean tcp)
+{
+       SslAssociation *assoc;
+
+       assoc = ssl_association_find(ssl_associations, port, tcp);
+       if (assoc) {
+               ssl_association_remove(ssl_associations, assoc);
+       }
+
+    ssl_association_add(ssl_associations, ssl_handle, port, protocol, tcp, FALSE);
+}
+
+void
+ssl_dissector_delete(guint port, const gchar *protocol, gboolean tcp)
+{
+       SslAssociation *assoc;
+
+       assoc = ssl_association_find(ssl_associations, port, tcp);
+       if (assoc && (assoc->handle == find_dissector(protocol))) {
+               ssl_association_remove(ssl_associations, assoc);
+       }
 }