From Robin Seggelmann: Add support for RFC 6520.
[obnox/wireshark/wip.git] / epan / dissectors / packet-dtls.c
index 82b7fd097d54bc0ddaefb2eecddc41ccb3983281..dd3a75e6f42d20e9db3d4cf2e63a50ebd51e88da 100644 (file)
  * See RFC 4347 for details about DTLS specs.
  *
  * Notes :
- * This dissector is based on TLS one (packet-ssl.c) because of the proximity of DTLS and TLS, decryption works like him with RSA key exchange.
- * It uses the sames things (file, libraries) that SSL one (gnutls, packet-ssl-utils.h) to make it easily maintenable.
+ * This dissector is based on the TLS dissector (packet-ssl.c); Because of the similarity
+ *   of DTLS and TLS, decryption works like TLS with RSA key exchange.
+ * This dissector uses the sames things (file, libraries) as the SSL dissector (gnutls, packet-ssl-utils.h)
+ *  to make it easily maintainable.
  *
- * It was developped to dissect and decrypt OpenSSL v 0.9.8f DTLS implementation.
- * It is limited to this implementation  while there is no complete implementation.
+ * It was developed to dissect and decrypt the OpenSSL v 0.9.8f DTLS implementation.
+ * It is limited to this implementation; there is no complete implementation.
  *
  * Implemented :
  *  - DTLS dissection
 # include "config.h"
 #endif
 
-#ifdef HAVE_SYS_TYPES_H
-#include <sys/types.h>
-#endif
-
-#ifdef HAVE_SYS_SOCKET_H
-#include <sys/socket.h>
-#endif
-
-#ifdef HAVE_WINSOCK2_H
-#include <winsock2.h>
-#endif
-
-#include <stdio.h>
-
 #include <glib.h>
 
+#include <epan/packet.h>
 #include <epan/conversation.h>
 #include <epan/prefs.h>
 #include <epan/asn1.h>
 #include <epan/emem.h>
 #include <epan/tap.h>
 #include <epan/reassemble.h>
-#include "inet_v6defs.h"
 #include "packet-ssl-utils.h"
+#include <wsutil/file_util.h>
+#include <epan/uat.h>
+
+/* DTLS User Access Table */
+static ssldecrypt_assoc_t *dtlskeylist_uats = NULL;
+static guint ndtlsdecrypt = 0;
 
 /* we need to remember the top tree so that subdissectors we call are created
  * at the root and not deep down inside the DTLS decode
@@ -87,72 +81,82 @@ static proto_tree *top_tree;
  *********************************************************************/
 
 /* Initialize the protocol and registered fields */
-static gint dtls_tap                           = -1;
-static gint proto_dtls                         = -1;
-static gint hf_dtls_record                     = -1;
-static gint hf_dtls_record_content_type        = -1;
-static gint hf_dtls_record_version             = -1;
-static gint hf_dtls_record_epoch               = -1;
-static gint hf_dtls_record_sequence_number     = -1;
-static gint hf_dtls_record_length              = -1;
-static gint hf_dtls_record_appdata             = -1;
-static gint hf_dtls_change_cipher_spec         = -1;
-static gint hf_dtls_alert_message              = -1;
-static gint hf_dtls_alert_message_level        = -1;
-static gint hf_dtls_alert_message_description  = -1;
-static gint hf_dtls_handshake_protocol         = -1;
-static gint hf_dtls_handshake_type             = -1;
-static gint hf_dtls_handshake_length           = -1;
-static gint hf_dtls_handshake_message_seq      = -1;
-static gint hf_dtls_handshake_fragment_offset  = -1;
-static gint hf_dtls_handshake_fragment_length  = -1;
-static gint hf_dtls_handshake_client_version   = -1;
-static gint hf_dtls_handshake_server_version   = -1;
-static gint hf_dtls_handshake_random_time      = -1;
-static gint hf_dtls_handshake_random_bytes     = -1;
-static gint hf_dtls_handshake_cookie_len       = -1;
-static gint hf_dtls_handshake_cookie           = -1;
+static gint dtls_tap                            = -1;
+static gint proto_dtls                          = -1;
+static gint hf_dtls_record                      = -1;
+static gint hf_dtls_record_content_type         = -1;
+static gint hf_dtls_record_version              = -1;
+static gint hf_dtls_record_epoch                = -1;
+static gint hf_dtls_record_sequence_number      = -1;
+static gint hf_dtls_record_length               = -1;
+static gint hf_dtls_record_appdata              = -1;
+static gint hf_dtls_change_cipher_spec          = -1;
+static gint hf_dtls_alert_message               = -1;
+static gint hf_dtls_alert_message_level         = -1;
+static gint hf_dtls_alert_message_description   = -1;
+static gint hf_dtls_handshake_protocol          = -1;
+static gint hf_dtls_handshake_type              = -1;
+static gint hf_dtls_handshake_length            = -1;
+static gint hf_dtls_handshake_message_seq       = -1;
+static gint hf_dtls_handshake_fragment_offset   = -1;
+static gint hf_dtls_handshake_fragment_length   = -1;
+static gint hf_dtls_handshake_client_version    = -1;
+static gint hf_dtls_handshake_server_version    = -1;
+static gint hf_dtls_handshake_random_time       = -1;
+static gint hf_dtls_handshake_random_bytes      = -1;
+static gint hf_dtls_handshake_cookie_len        = -1;
+static gint hf_dtls_handshake_cookie            = -1;
 static gint hf_dtls_handshake_cipher_suites_len = -1;
-static gint hf_dtls_handshake_cipher_suites    = -1;
-static gint hf_dtls_handshake_cipher_suite     = -1;
-static gint hf_dtls_handshake_session_id       = -1;
-static gint hf_dtls_handshake_comp_methods_len = -1;
-static gint hf_dtls_handshake_comp_methods     = -1;
-static gint hf_dtls_handshake_comp_method      = -1;
-static gint hf_dtls_handshake_extensions_len   = -1;
-static gint hf_dtls_handshake_extension_type   = -1;
-static gint hf_dtls_handshake_extension_len    = -1;
-static gint hf_dtls_handshake_extension_data   = -1;
-static gint hf_dtls_handshake_certificates_len = -1;
-static gint hf_dtls_handshake_certificates     = -1;
-static gint hf_dtls_handshake_certificate      = -1;
-static gint hf_dtls_handshake_certificate_len  = -1;
-static gint hf_dtls_handshake_cert_types_count = -1;
-static gint hf_dtls_handshake_cert_types       = -1;
-static gint hf_dtls_handshake_cert_type        = -1;
-static gint hf_dtls_handshake_finished         = -1;
-static gint hf_dtls_handshake_md5_hash         = -1;
-static gint hf_dtls_handshake_sha_hash         = -1;
-static gint hf_dtls_handshake_session_id_len   = -1;
-static gint hf_dtls_handshake_dnames_len       = -1;
-static gint hf_dtls_handshake_dnames           = -1;
-static gint hf_dtls_handshake_dname_len        = -1;
-static gint hf_dtls_handshake_dname            = -1;
-
-static gint hf_dtls_fragments                  = -1;
-static gint hf_dtls_fragment                   = -1;
-static gint hf_dtls_fragment_overlap           = -1;
-static gint hf_dtls_fragment_overlap_conflicts = -1;
-static gint hf_dtls_fragment_multiple_tails    = -1;
-static gint hf_dtls_fragment_too_long_fragment = -1;
-static gint hf_dtls_fragment_error             = -1;
-static gint hf_dtls_reassembled_in             = -1;
+static gint hf_dtls_handshake_cipher_suites     = -1;
+static gint hf_dtls_handshake_cipher_suite      = -1;
+static gint hf_dtls_handshake_session_id        = -1;
+static gint hf_dtls_handshake_comp_methods_len  = -1;
+static gint hf_dtls_handshake_comp_methods      = -1;
+static gint hf_dtls_handshake_comp_method       = -1;
+static gint hf_dtls_handshake_extensions_len    = -1;
+static gint hf_dtls_handshake_extension_type    = -1;
+static gint hf_dtls_handshake_extension_len     = -1;
+static gint hf_dtls_handshake_extension_data    = -1;
+static gint hf_dtls_handshake_certificates_len  = -1;
+static gint hf_dtls_handshake_certificates      = -1;
+static gint hf_dtls_handshake_certificate       = -1;
+static gint hf_dtls_handshake_certificate_len   = -1;
+static gint hf_dtls_handshake_cert_types_count  = -1;
+static gint hf_dtls_handshake_cert_types        = -1;
+static gint hf_dtls_handshake_cert_type         = -1;
+static gint hf_dtls_handshake_finished          = -1;
+static gint hf_dtls_handshake_md5_hash          = -1;
+static gint hf_dtls_handshake_sha_hash          = -1;
+static gint hf_dtls_handshake_session_id_len    = -1;
+static gint hf_dtls_handshake_dnames_len        = -1;
+static gint hf_dtls_handshake_dnames            = -1;
+static gint hf_dtls_handshake_dname_len         = -1;
+static gint hf_dtls_handshake_dname             = -1;
+
+static gint hf_dtls_heartbeat_extension_mode          = -1;
+static gint hf_dtls_heartbeat_message                 = -1;
+static gint hf_dtls_heartbeat_message_type            = -1;
+static gint hf_dtls_heartbeat_message_payload_length  = -1;
+static gint hf_dtls_heartbeat_message_payload         = -1;
+static gint hf_dtls_heartbeat_message_padding         = -1;
+
+static gint hf_dtls_fragments                   = -1;
+static gint hf_dtls_fragment                    = -1;
+static gint hf_dtls_fragment_overlap            = -1;
+static gint hf_dtls_fragment_overlap_conflicts  = -1;
+static gint hf_dtls_fragment_multiple_tails     = -1;
+static gint hf_dtls_fragment_too_long_fragment  = -1;
+static gint hf_dtls_fragment_error              = -1;
+static gint hf_dtls_fragment_count              = -1;
+static gint hf_dtls_reassembled_in              = -1;
+static gint hf_dtls_reassembled_length          = -1;
 
 /* Initialize the subtree pointers */
 static gint ett_dtls                   = -1;
 static gint ett_dtls_record            = -1;
 static gint ett_dtls_alert             = -1;
 static gint ett_dtls_handshake         = -1;
+static gint ett_dtls_heartbeat         = -1;
 static gint ett_dtls_cipher_suites     = -1;
 static gint ett_dtls_comp_methods      = -1;
 static gint ett_dtls_extension         = -1;
@@ -163,20 +167,23 @@ static gint ett_dtls_dnames            = -1;
 static gint ett_dtls_fragment          = -1;
 static gint ett_dtls_fragments         = -1;
 
-static GHashTable *dtls_session_hash   = NULL;
-static GHashTable *dtls_key_hash       = NULL;
-static GHashTable *dtls_fragment_table = NULL;
-static GTree* dtls_associations = NULL;
-static dissector_handle_t dtls_handle  = NULL;
-static StringInfo dtls_compressed_data = {NULL, 0};
-static StringInfo dtls_decrypted_data  = {NULL, 0};
-static gint dtls_decrypted_data_avail  = 0;
-
-static gchar* dtls_keys_list = NULL;
+static GHashTable         *dtls_session_hash         = NULL;
+static GHashTable         *dtls_key_hash             = NULL;
+static GHashTable         *dtls_fragment_table       = NULL;
+static GTree*              dtls_associations         = NULL;
+static dissector_handle_t  dtls_handle               = NULL;
+static StringInfo          dtls_compressed_data      = {NULL, 0};
+static StringInfo          dtls_decrypted_data       = {NULL, 0};
+static gint                dtls_decrypted_data_avail = 0;
+
+static uat_t *dtlsdecrypt_uat      = NULL;
+static gchar *dtls_keys_list       = NULL;
 #ifdef HAVE_LIBGNUTLS
-static gchardtls_debug_file_name = NULL;
+static gchar *dtls_debug_file_name = NULL;
 #endif
 
+static heur_dissector_list_t heur_subdissector_list;
+
 static const fragment_items dtls_frag_items = {
   /* Fragment subtrees */
   &ett_dtls_fragment,
@@ -189,8 +196,11 @@ static const fragment_items dtls_frag_items = {
   &hf_dtls_fragment_multiple_tails,
   &hf_dtls_fragment_too_long_fragment,
   &hf_dtls_fragment_error,
+  &hf_dtls_fragment_count,
   /* Reassembled in field */
   &hf_dtls_reassembled_in,
+  /* Reassembled length field */
+  &hf_dtls_reassembled_length,
   /* Tag */
   "Message fragments"
 };
@@ -199,22 +209,34 @@ static const fragment_items dtls_frag_items = {
 static void
 dtls_init(void)
 {
+  module_t *dtls_module = prefs_find_module("dtls");
+  pref_t   *keys_list_pref;
+
   ssl_common_init(&dtls_session_hash, &dtls_decrypted_data, &dtls_compressed_data);
   fragment_table_init (&dtls_fragment_table);
+
+  /* We should have loaded "keys_list" by now. Mark it obsolete */
+  if (dtls_module) {
+    keys_list_pref = prefs_find_preference(dtls_module, "keys_list");
+    if (! prefs_get_preference_obsolete(keys_list_pref)) {
+      prefs_set_preference_obsolete(keys_list_pref);
+    }
+  }
 }
 
 /* parse dtls related preferences (private keys and ports association strings) */
 static void
-dtls_parse(void)
+dtls_parse_uat(void)
 {
-  ep_stack_t tmp_stack;
-  SslAssociation *tmp_assoc;
+  ep_stack_t       tmp_stack;
+  SslAssociation  *tmp_assoc;
+  guint            i;
 
   if (dtls_key_hash)
-    {
+  {
       g_hash_table_foreach(dtls_key_hash, ssl_private_key_free, NULL);
       g_hash_table_destroy(dtls_key_hash);
-    }
+  }
 
   /* remove only associations created from key list */
   tmp_stack = ep_stack_new();
@@ -226,17 +248,49 @@ dtls_parse(void)
   /* parse private keys string, load available keys and put them in key hash*/
   dtls_key_hash = g_hash_table_new(ssl_private_key_hash, ssl_private_key_equal);
 
-  if (dtls_keys_list && (dtls_keys_list[0] != 0))
+  ssl_set_debug(dtls_debug_file_name);
+
+  if (ndtlsdecrypt > 0)
+  {
+    for (i = 0; i < ndtlsdecrypt; i++)
     {
-      ssl_parse_key_list(dtls_keys_list,dtls_key_hash,dtls_associations,dtls_handle,FALSE);
+      ssldecrypt_assoc_t *d = &(dtlskeylist_uats[i]);
+      ssl_parse_key_list(d, dtls_key_hash, dtls_associations, dtls_handle, FALSE);
     }
-
-  ssl_set_debug(dtls_debug_file_name);
+  }
 
   dissector_add_handle("sctp.port", dtls_handle);
   dissector_add_handle("udp.port", dtls_handle);
 }
 
+static void
+dtls_parse_old_keys(void)
+{
+  gchar          **old_keys, **parts, *err;
+  guint            i;
+  GString         *uat_entry = g_string_new("");
+
+  /* Import old-style keys */
+  if (dtlsdecrypt_uat && dtls_keys_list && dtls_keys_list[0]) {
+    old_keys = g_strsplit(dtls_keys_list, ";", 0);
+    for (i = 0; old_keys[i] != NULL; i++) {
+      parts = g_strsplit(old_keys[i], ",", 4);
+      if (parts[0] && parts[1] && parts[2] && parts[3]) {
+        g_string_printf(uat_entry, "\"%s\",\"%s\",\"%s\",\"%s\",\"\"",
+                        parts[0], parts[1], parts[2], parts[3]);
+        if (!uat_load_str(dtlsdecrypt_uat, uat_entry->str, &err)) {
+          ssl_debug_printf("dtls_parse: Can't load UAT string %s: %s\n",
+                           uat_entry->str, err);
+        }
+      }
+      g_strfreev(parts);
+    }
+    g_strfreev(old_keys);
+  }
+  g_string_free(uat_entry, TRUE);
+
+}
+
 /*
  * DTLS Dissection Routines
  *
@@ -266,18 +320,23 @@ static void dissect_dtls_handshake(tvbuff_t *tvb, packet_info *pinfo,
                                    guint *conv_version,
                                    SslDecryptSession *conv_data, guint8 content_type);
 
+/* heartbeat message dissector */
+static void dissect_dtls_heartbeat(tvbuff_t *tvb, packet_info *pinfo,
+                                   proto_tree *tree, guint32 offset,
+                                   guint *conv_version, guint32 record_length);
+
 
 static void dissect_dtls_hnd_cli_hello(tvbuff_t *tvb,
                                        proto_tree *tree,
                                        guint32 offset, guint32 length,
                                        SslDecryptSession* ssl);
 
-static void dissect_dtls_hnd_hello_verify_request(tvbuff_t *tvb,
+static int dissect_dtls_hnd_hello_verify_request(tvbuff_t *tvb,
                                                   proto_tree *tree,
                                                   guint32 offset,
                                                   SslDecryptSession* ssl);
 
-static void dissect_dtls_hnd_srv_hello(tvbuff_t *tvb,
+static int dissect_dtls_hnd_srv_hello(tvbuff_t *tvb,
                                        proto_tree *tree,
                                        guint32 offset, guint32 length,
                                        SslDecryptSession* ssl);
@@ -317,20 +376,22 @@ static void
 dissect_dtls(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
 {
 
-  conversation_t *conversation;
-  void *conv_data;
-  proto_item *ti;
-  proto_tree *dtls_tree;
-  guint32 offset;
-  gboolean first_record_in_frame;
-  SslDecryptSession* ssl_session;
-  guint* conv_version;
-  ti = NULL;
-  dtls_tree = NULL;
-  offset = 0;
+  conversation_t    *conversation;
+  void              *conv_data;
+  proto_item        *ti;
+  proto_tree        *dtls_tree;
+  guint32            offset;
+  gboolean           first_record_in_frame;
+  SslDecryptSession *ssl_session;
+  guint*             conv_version;
+  Ssl_private_key_t *private_key;
+
+  ti                    = NULL;
+  dtls_tree             = NULL;
+  offset                = 0;
   first_record_in_frame = TRUE;
-  ssl_session = NULL;
-  top_tree=tree;
+  ssl_session           = NULL;
+  top_tree              = tree;
 
   /* Track the version using conversations allows
    * us to more frequently set the protocol column properly
@@ -342,15 +403,8 @@ dissect_dtls(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
    *       the conv_version, must set the copy in the conversation
    *       in addition to conv_version
    */
-  conversation = find_conversation(pinfo->fd->num, &pinfo->src, &pinfo->dst, pinfo->ptype,
-                                   pinfo->srcport, pinfo->destport, 0);
-  if (!conversation)
-    {
-      /* create a new conversation */
-      conversation = conversation_new(pinfo->fd->num, &pinfo->src, &pinfo->dst, pinfo->ptype,
-                                      pinfo->srcport, pinfo->destport, 0);
-    }
-  conv_data = conversation_get_proto_data(conversation, proto_dtls);
+  conversation = find_or_create_conversation(pinfo);
+  conv_data    = conversation_get_proto_data(conversation, proto_dtls);
 
   /* manage dtls decryption data */
   /*get a valid ssl session pointer*/
@@ -380,16 +434,19 @@ dissect_dtls(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
      * 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(dtls_key_hash, &dummy);
-    if (!ssl_session->private_key)
+    private_key = g_hash_table_lookup(dtls_key_hash, &dummy);
+    if (!private_key) {
       ssl_debug_printf("dissect_dtls can't find private key for this server!\n");
+    }
+    else {
+      ssl_session->private_key = private_key->sexp_pkey;
+    }
   }
   conv_version= & ssl_session->version;
 
   /* try decryption only the first time we see this packet
-   * (to keep cipher synchronized)and only if we have
-   * the server private key*/
-  if (!ssl_session->private_key || pinfo->fd->flags.visited)
+   * (to keep cipher synchronized) */
+  if (pinfo->fd->flags.visited)
     ssl_session = NULL;
 
   /* Initialize the protocol column; we'll set it later when we
@@ -403,7 +460,7 @@ dissect_dtls(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
   /* Create display subtree for SSL as a whole */
   if (tree)
     {
-      ti = proto_tree_add_item(tree, proto_dtls, tvb, 0, -1, FALSE);
+      ti = proto_tree_add_item(tree, proto_dtls, tvb, 0, -1, ENC_NA);
       dtls_tree = proto_item_add_subtree(ti, ett_dtls);
     }
 
@@ -413,8 +470,7 @@ dissect_dtls(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
       /* on second and subsequent records per frame
        * add a delimiter on info column
        */
-      if (!first_record_in_frame
-          && check_col(pinfo->cinfo, COL_INFO))
+      if (!first_record_in_frame)
         {
           col_append_str(pinfo->cinfo, COL_INFO, ", ");
         }
@@ -462,27 +518,82 @@ dissect_dtls(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
   tap_queue_packet(dtls_tap, pinfo, NULL);
 }
 
+static gboolean
+dissect_dtls_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+  /* Stronger confirmation of DTLS packet is provided by verifying the
+   * payload length against the remainder of the UDP packet size. */
+  guint length = tvb_length(tvb);
+  guint offset = 0;
+
+  if (tvb_reported_length(tvb) == length) {
+    while (offset + 13 <= length && looks_like_dtls(tvb, offset)) {
+      /* Advance offset to the end of the current DTLS record */
+      offset += tvb_get_ntohs(tvb, offset + 11) + 13;
+      if (offset == length) {
+        dissect_dtls(tvb, pinfo, tree);
+        return TRUE;
+      }
+    }
+
+    if (pinfo->fragmented && offset >= 13) {
+      dissect_dtls(tvb, pinfo, tree);
+      return TRUE;
+    }
+    return FALSE;
+  }
+
+  /* We've got a truncated packet - do our best with what we've got. */
+  while (tvb_length_remaining(tvb, offset) >= 3) {
+    if (!looks_like_dtls(tvb, offset))
+      return FALSE;
+
+    offset += 3;
+    if (tvb_length_remaining(tvb, offset) >= 10 ) {
+      offset += tvb_get_ntohs(tvb, offset + 8) + 10;
+    } else {
+      /* Dissect what we've got, which might be as little as 3 bytes. */
+      dissect_dtls(tvb, pinfo, tree);
+      return TRUE;
+    }
+    if (offset == length) {
+      /* Can this ever happen?  Well, just in case ... */
+      dissect_dtls(tvb, pinfo, tree);
+      return TRUE;
+    }
+  }
+
+  /* One last check to see if the current offset is at least less than the
+   * original number of bytes present before truncation or we're dealing with
+   * a packet fragment that's also been truncated. */
+  if ((length >= 3) && (offset <= tvb_reported_length(tvb) || pinfo->fragmented)) {
+    dissect_dtls(tvb, pinfo, tree);
+    return TRUE;
+  }
+  return FALSE;
+}
+
 static gint
 decrypt_dtls_record(tvbuff_t *tvb, packet_info *pinfo, guint32 offset,
                     guint32 record_length, guint8 content_type, SslDecryptSession* ssl,
                     gboolean save_plaintext)
 {
-  gint ret;
-  gint direction;
-  SslDecoder* decoder;
+  gint        ret;
+  SslDecoder *decoder;
+
   ret = 0;
 
   /* if we can decrypt and decryption have success
    * add decrypted data to this packet info */
-  ssl_debug_printf("decrypt_dtls_record: app_data len %d ssl state %X\n",
+  ssl_debug_printf("decrypt_dtls_record: app_data len %d, ssl state %X\n",
                    record_length, ssl->state);
-  if (!(ssl->state & SSL_HAVE_SESSION_KEY)) {
+  if (!ssl || (!save_plaintext && !(ssl->state & SSL_HAVE_SESSION_KEY))) {
     ssl_debug_printf("decrypt_dtls_record: no session key\n");
     return ret;
   }
 
   /* retrive decoder for this packet direction */
-  if ((direction = ssl_packet_from_server(ssl, dtls_associations, pinfo)) != 0) {
+  if (ssl_packet_from_server(ssl, dtls_associations, pinfo) != 0) {
     ssl_debug_printf("decrypt_dtls_record: using server decoder\n");
     decoder = ssl->server;
   }
@@ -491,6 +602,11 @@ decrypt_dtls_record(tvbuff_t *tvb, packet_info *pinfo, guint32 offset,
     decoder = ssl->client;
   }
 
+  if (!decoder) {
+    ssl_debug_printf("decrypt_dtls_record: no decoder available\n");
+    return ret;
+  }
+
   /* ensure we have enough storage space for decrypted data */
   if (record_length > dtls_decrypted_data.data_len)
     {
@@ -505,14 +621,22 @@ decrypt_dtls_record(tvbuff_t *tvb, packet_info *pinfo, guint32 offset,
   /* run decryption and add decrypted payload to protocol data, if decryption
    * is successful*/
   dtls_decrypted_data_avail = dtls_decrypted_data.data_len;
-  if (ssl_decrypt_record(ssl, decoder,
-                         content_type, tvb_get_ptr(tvb, offset, record_length),
-                         record_length, &dtls_compressed_data, &dtls_decrypted_data, &dtls_decrypted_data_avail) == 0)
+  if (ssl->state & SSL_HAVE_SESSION_KEY) {
+    if (ssl_decrypt_record(ssl, decoder, content_type, tvb_get_ptr(tvb, offset, record_length), record_length,
+                           &dtls_compressed_data, &dtls_decrypted_data, &dtls_decrypted_data_avail) == 0)
+      ret = 1;
+  }
+  else if (ssl->cipher == 0x0001 || ssl->cipher == 0x0002) {
+    /* Non-encrypting cipher RSA-NULL-MD5 or RSA-NULL-SHA */
+    memcpy(dtls_decrypted_data.data, tvb_get_ptr(tvb, offset, record_length), record_length);
+    dtls_decrypted_data_avail = dtls_decrypted_data.data_len = record_length;
     ret = 1;
+  }
 
-    if (ret && save_plaintext) {
-      ssl_add_data_info(proto_dtls, pinfo, dtls_decrypted_data.data, dtls_decrypted_data_avail,  TVB_RAW_OFFSET(tvb)+offset, 0);
-    }
+  if (ret && save_plaintext) {
+    ssl_add_data_info(proto_dtls, pinfo, dtls_decrypted_data.data, dtls_decrypted_data_avail,
+                      tvb_raw_offset(tvb)+offset, 0);
+  }
 
   return ret;
 }
@@ -547,39 +671,39 @@ dissect_dtls_record(tvbuff_t *tvb, packet_info *pinfo,
    *    struct {
    *        ContentType type;
    *        ProtocolVersion version;
-   *       uint16 epoch;               // New field
-   *       uint48 sequence_number;       // New field
+   *        uint16 epoch;               // New field
+   *        uint48 sequence_number;     // New field
    *        uint16 length;
    *        opaque fragment[TLSPlaintext.length];
    *    } DTLSPlaintext;
    */
-  guint32 record_length;
-  guint16 version;
-  guint16 epoch;
-  gdouble sequence_number;
-  gint64 sequence_number_temp;
-  guint8 content_type;
-  guint8 next_byte;
-  proto_tree *ti;
-  proto_tree *dtls_record_tree;
-  guint32 available_bytes;
-  SslAssociation* association;
-  SslDataInfo *appl_data;
-  ti              = NULL;
+
+  guint32         record_length;
+  guint16         version;
+  guint16         epoch;
+  gdouble         sequence_number;
+  gint64          sequence_number_temp;
+  guint8          content_type;
+  guint8          next_byte;
+  proto_tree     *ti;
+  proto_tree     *dtls_record_tree;
+  SslAssociation *association;
+  SslDataInfo    *appl_data;
+
+  ti               = NULL;
   dtls_record_tree = NULL;
-  available_bytes = tvb_length_remaining(tvb, offset);
 
   /*
    * Get the record layer fields of interest
    */
-  content_type  = tvb_get_guint8(tvb, offset);
-  version       = tvb_get_ntohs(tvb, offset + 1);
-  epoch       = tvb_get_ntohs(tvb, offset + 3);
-  sequence_number  = tvb_get_ntohl(tvb, offset + 7);
-  sequence_number_temp=tvb_get_ntohs(tvb, offset + 5);
-  sequence_number_temp=sequence_number_temp<<32;
-  sequence_number+=sequence_number_temp;
-  record_length = tvb_get_ntohs(tvb, offset + 11);
+  content_type          = tvb_get_guint8(tvb, offset);
+  version               = tvb_get_ntohs(tvb, offset + 1);
+  epoch                 = tvb_get_ntohs(tvb, offset + 3);
+  sequence_number       = tvb_get_ntohl(tvb, offset + 7);
+  sequence_number_temp  = tvb_get_ntohs(tvb, offset + 5);
+  sequence_number_temp  = sequence_number_temp<<32;
+  sequence_number      += sequence_number_temp;
+  record_length         = tvb_get_ntohs(tvb, offset + 11);
 
   if(ssl){
     if(ssl_packet_from_server(ssl, dtls_associations, pinfo)){
@@ -616,7 +740,7 @@ dissect_dtls_record(tvbuff_t *tvb, packet_info *pinfo,
       /* add the record layer subtree header */
       tvb_ensure_bytes_exist(tvb, offset, 13 + record_length);
       ti = proto_tree_add_item(tree, hf_dtls_record, tvb,
-                               offset, 13 + record_length, 0);
+                               offset, 13 + record_length, ENC_NA);
       dtls_record_tree = proto_item_add_subtree(ti, ett_dtls_record);
     }
 
@@ -625,12 +749,12 @@ dissect_dtls_record(tvbuff_t *tvb, packet_info *pinfo,
 
       /* show the one-byte content type */
       proto_tree_add_item(dtls_record_tree, hf_dtls_record_content_type,
-                          tvb, offset, 1, FALSE);
+                          tvb, offset, 1, ENC_BIG_ENDIAN);
       offset++;
 
       /* add the version */
       proto_tree_add_item(dtls_record_tree, hf_dtls_record_version, tvb,
-                          offset, 2, FALSE);
+                          offset, 2, ENC_BIG_ENDIAN);
       offset += 2;
 
       /* show epoch */
@@ -684,7 +808,7 @@ dissect_dtls_record(tvbuff_t *tvb, packet_info *pinfo,
       if (version == DTLSV1DOT0_VERSION)
         {
           col_set_str(pinfo->cinfo, COL_PROTOCOL,
-                      ssl_version_short_names[SSL_VER_DTLS]);
+                      val_to_str_const(SSL_VER_DTLS, ssl_version_short_names, "SSL"));
         }
       else
         {
@@ -705,6 +829,7 @@ dissect_dtls_record(tvbuff_t *tvb, packet_info *pinfo,
     col_append_str(pinfo->cinfo, COL_INFO, "Change Cipher Spec");
     dissect_dtls_change_cipher_spec(tvb, dtls_record_tree,
                                     offset, conv_version, content_type);
+    if (ssl) ssl_change_cipher(ssl, ssl_packet_from_server(ssl, dtls_associations, pinfo));
     break;
   case SSL_ID_ALERT:
     {
@@ -716,13 +841,15 @@ dissect_dtls_record(tvbuff_t *tvb, packet_info *pinfo,
                             dtls_decrypted_data_avail, offset);
 
       /* try to retrive and use decrypted alert record, if any. */
-      decrypted = ssl_get_record_info(proto_dtls, pinfo, offset);
-      if (decrypted)
+      decrypted = ssl_get_record_info(tvb, proto_dtls, pinfo, offset);
+      if (decrypted) {
         dissect_dtls_alert(decrypted, pinfo, dtls_record_tree, 0,
                            conv_version);
-      else
+        add_new_data_source(pinfo, decrypted, "Decrypted SSL record");
+      } else {
         dissect_dtls_alert(tvb, pinfo, dtls_record_tree, offset,
                            conv_version);
+      }
       break;
     }
   case SSL_ID_HANDSHAKE:
@@ -739,13 +866,15 @@ dissect_dtls_record(tvbuff_t *tvb, packet_info *pinfo,
                             dtls_decrypted_data_avail, offset);
 
       /* try to retrive and use decrypted handshake record, if any. */
-      decrypted = ssl_get_record_info(proto_dtls, pinfo, offset);
-      if (decrypted)
+      decrypted = ssl_get_record_info(tvb, proto_dtls, pinfo, offset);
+      if (decrypted) {
         dissect_dtls_handshake(decrypted, pinfo, dtls_record_tree, 0,
-                               decrypted->length, conv_version, ssl, content_type);
-      else
+                               tvb_length(decrypted), conv_version, ssl, content_type);
+        add_new_data_source(pinfo, decrypted, "Decrypted SSL record");
+      } else {
         dissect_dtls_handshake(tvb, pinfo, dtls_record_tree, offset,
                                record_length, conv_version, ssl, content_type);
+      }
       break;
     }
   case SSL_ID_APP_DATA:
@@ -763,28 +892,29 @@ dissect_dtls_record(tvbuff_t *tvb, packet_info *pinfo,
      * ssl session pointer is NULL at that time, so we can't access
      * info cached there*/
     association = ssl_association_find(dtls_associations, pinfo->srcport, pinfo->ptype == PT_TCP);
-    association = association ? association: ssl_association_find(dtls_associations, pinfo->destport, pinfo->ptype == PT_TCP);
+    association = association ? association : ssl_association_find(dtls_associations, pinfo->destport, pinfo->ptype == PT_TCP);
 
     proto_item_set_text(dtls_record_tree,
                         "%s Record Layer: %s Protocol: %s",
-                        ssl_version_short_names[*conv_version],
+                        val_to_str_const(*conv_version, ssl_version_short_names, "SSL"),
                         val_to_str(content_type, ssl_31_content_type, "unknown"),
                         association?association->info:"Application Data");
 
-    proto_tree_add_item(dtls_record_tree, hf_dtls_record_appdata, tvb,
-                        offset, record_length, 0);
-
     /* show decrypted data info, if available */
-    appl_data = ssl_get_data_info(proto_dtls, pinfo, TVB_RAW_OFFSET(tvb)+offset);
+    appl_data = ssl_get_data_info(proto_dtls, pinfo, tvb_raw_offset(tvb)+offset);
     if (appl_data && (appl_data->plain_data.data_len > 0))
       {
         tvbuff_t *next_tvb;
+        gboolean  dissected;
         /* try to dissect decrypted data*/
         ssl_debug_printf("dissect_dtls_record decrypted len %d\n",
                          appl_data->plain_data.data_len);
 
         /* create a new TVB structure for desegmented data */
-        next_tvb = tvb_new_child_real_data(tvb, appl_data->plain_data.data, appl_data->plain_data.data_len, appl_data->plain_data.data_len);
+        next_tvb = tvb_new_child_real_data(tvb,
+                                           appl_data->plain_data.data,
+                                           appl_data->plain_data.data_len,
+                                           appl_data->plain_data.data_len);
 
         add_new_data_source(pinfo, next_tvb, "Decrypted DTLS data");
 
@@ -793,10 +923,40 @@ dissect_dtls_record(tvbuff_t *tvb, packet_info *pinfo,
           ssl_debug_printf("dissect_dtls_record found association %p\n", (void *)association);
           ssl_print_text_data("decrypted app data",appl_data->plain_data.data, appl_data->plain_data.data_len);
 
-          call_dissector(association->handle, next_tvb, pinfo, top_tree);
+          dissected = call_dissector_only(association->handle, next_tvb, pinfo, top_tree);
+        }
+        else {
+          /* try heuristic subdissectors */
+          dissected = dissector_try_heuristic(heur_subdissector_list, next_tvb, pinfo, top_tree);
         }
+        if (dissected)
+          break;
       }
+
+    proto_tree_add_item(dtls_record_tree, hf_dtls_record_appdata, tvb,
+                        offset, record_length, ENC_NA);
     break;
+  case SSL_ID_HEARTBEAT:
+  {
+    tvbuff_t* decrypted;
+
+    if (ssl && decrypt_dtls_record(tvb, pinfo, offset,
+                                   record_length, content_type, ssl, FALSE))
+      ssl_add_record_info(proto_dtls, pinfo, dtls_decrypted_data.data,
+                          dtls_decrypted_data_avail, offset);
+
+    /* try to retrive and use decrypted alert record, if any. */
+    decrypted = ssl_get_record_info(tvb, proto_dtls, pinfo, offset);
+    if (decrypted) {
+      dissect_dtls_heartbeat(decrypted, pinfo, dtls_record_tree, 0,
+                             conv_version, record_length);
+      add_new_data_source(pinfo, decrypted, "Decrypted SSL record");
+    } else {
+      dissect_dtls_heartbeat(tvb, pinfo, dtls_record_tree, offset,
+                             conv_version, record_length);
+    }
+    break;
+  }
 
   default:
     /* shouldn't get here since we check above for valid types */
@@ -824,10 +984,10 @@ dissect_dtls_change_cipher_spec(tvbuff_t *tvb,
     {
       proto_item_set_text(tree,
                           "%s Record Layer: %s Protocol: Change Cipher Spec",
-                          ssl_version_short_names[*conv_version],
+                          val_to_str_const(*conv_version, ssl_version_short_names, "SSL"),
                           val_to_str(content_type, ssl_31_content_type, "unknown"));
       proto_tree_add_item(tree, hf_dtls_change_cipher_spec, tvb,
-                          offset++, 1, FALSE);
+                          offset, 1, ENC_NA);
     }
 }
 
@@ -842,17 +1002,19 @@ dissect_dtls_alert(tvbuff_t *tvb, packet_info *pinfo,
    *         AlertDescription description;
    *     } Alert;
    */
-  proto_tree *ti;
-  proto_tree *ssl_alert_tree;
+
+  proto_tree  *ti;
+  proto_tree  *ssl_alert_tree;
   const gchar *level;
   const gchar *desc;
-  guint8 byte;
+  guint8       byte;
+
   ssl_alert_tree = NULL;
 
   if (tree)
     {
       ti = proto_tree_add_item(tree, hf_dtls_alert_message, tvb,
-                               offset, 2, 0);
+                               offset, 2, ENC_NA);
       ssl_alert_tree = proto_item_add_subtree(ti, ett_dtls_alert);
     }
 
@@ -861,11 +1023,11 @@ dissect_dtls_alert(tvbuff_t *tvb, packet_info *pinfo,
    */
 
   /* first lookup the names for the alert level and description */
-  byte = tvb_get_guint8(tvb, offset); /* grab the level byte */
+  byte  = tvb_get_guint8(tvb, offset); /* grab the level byte */
   level = match_strval(byte, ssl_31_alert_level);
 
-  byte = tvb_get_guint8(tvb, offset+1); /* grab the desc byte */
-  desc = match_strval(byte, ssl_31_alert_description);
+  byte  = tvb_get_guint8(tvb, offset+1); /* grab the desc byte */
+  desc  = match_strval(byte, ssl_31_alert_description);
 
   /* now set the text in the record layer line */
   if (level && desc)
@@ -886,19 +1048,19 @@ dissect_dtls_alert(tvbuff_t *tvb, packet_info *pinfo,
         {
           proto_item_set_text(tree, "%s Record Layer: Alert "
                               "(Level: %s, Description: %s)",
-                              ssl_version_short_names[*conv_version],
+                              val_to_str_const(*conv_version, ssl_version_short_names, "SSL"),
                               level, desc);
           proto_tree_add_item(ssl_alert_tree, hf_dtls_alert_message_level,
-                              tvb, offset++, 1, FALSE);
+                              tvb, offset++, 1, ENC_BIG_ENDIAN);
 
           proto_tree_add_item(ssl_alert_tree, hf_dtls_alert_message_description,
-                              tvb, offset++, 1, FALSE);
+                              tvb, offset, 1, ENC_BIG_ENDIAN);
         }
       else
         {
           proto_item_set_text(tree,
                               "%s Record Layer: Encrypted Alert",
-                              ssl_version_short_names[*conv_version]);
+                              val_to_str_const(*conv_version, ssl_version_short_names, "SSL"));
           proto_item_set_text(ssl_alert_tree,
                               "Alert Message: Encrypted Alert");
         }
@@ -917,8 +1079,8 @@ dissect_dtls_handshake(tvbuff_t *tvb, packet_info *pinfo,
    *         HandshakeType msg_type;
    *         uint24 length;
    *         uint16 message_seq;          //new field
-   *         uint24 fragment_offset;       //new field
-   *         uint24 fragment_length;        //new field
+   *         uint24 fragment_offset;      //new field
+   *         uint24 fragment_length;      //new field
    *         select (HandshakeType) {
    *             case hello_request:       HelloRequest;
    *             case client_hello:        ClientHello;
@@ -934,19 +1096,21 @@ dissect_dtls_handshake(tvbuff_t *tvb, packet_info *pinfo,
    *         } body;
    *     } Handshake;
    */
-  proto_tree *ti;
-  proto_tree *ssl_hand_tree;
+
+  proto_tree  *ti;
+  proto_tree  *ssl_hand_tree;
   const gchar *msg_type_str;
-  guint8 msg_type;
-  guint32 length;
-  guint16 message_seq;
-  guint32 fragment_offset;
-  guint32 fragment_length;
-  gboolean first_iteration;
-  ti               = NULL;
-  ssl_hand_tree    = NULL;
-  msg_type_str     = NULL;
-  first_iteration  = TRUE;
+  guint8       msg_type;
+  guint32      length;
+  guint16      message_seq;
+  guint32      fragment_offset;
+  guint32      fragment_length;
+  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
@@ -961,17 +1125,17 @@ dissect_dtls_handshake(tvbuff_t *tvb, packet_info *pinfo,
          first_iteration = FALSE) /* set up for next pass, if any */
     {
       fragment_data *frag_msg = NULL;
-      tvbuff_t *new_tvb = NULL;
-      const gchar *frag_str = NULL;
-      gboolean fragmented;
-
-      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);
-      message_seq = tvb_get_ntohs(tvb,offset + 4);
+      tvbuff_t      *new_tvb  = NULL;
+      const gchar   *frag_str = NULL;
+      gboolean       fragmented;
+
+      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);
+      message_seq     = tvb_get_ntohs(tvb,offset + 4);
       fragment_offset = tvb_get_ntoh24(tvb, offset + 6);
       fragment_length = tvb_get_ntoh24(tvb, offset + 9);
-      fragmented = fragment_length != length;
+      fragmented      = fragment_length != length;
 
       if (!msg_type_str && !first_iteration)
         {
@@ -1023,6 +1187,10 @@ dissect_dtls_handshake(tvbuff_t *tvb, packet_info *pinfo,
           if (frag_hand) {
             /* Fragmented handshake message */
             pinfo->fragmented = TRUE;
+
+            /* Don't pass the reassembly code data that doesn't exist */
+            tvb_ensure_bytes_exist(tvb, offset, fragment_length);
+
             frag_msg = fragment_add(tvb, offset+12, pinfo, message_seq,
                                     dtls_fragment_table,
                                     fragment_offset, fragment_length, TRUE);
@@ -1033,7 +1201,7 @@ dissect_dtls_handshake(tvbuff_t *tvb, packet_info *pinfo,
               {
                 /* Reassembled */
                 new_tvb = process_reassembled_data(tvb, offset+12, pinfo,
-                                                   "Reassembled Message",
+                                                   "Reassembled DTLS",
                                                    frag_msg,
                                                    &dtls_frag_items,
                                                    NULL, tree);
@@ -1055,7 +1223,7 @@ dissect_dtls_handshake(tvbuff_t *tvb, packet_info *pinfo,
           if (first_iteration)
             {
               proto_item_set_text(tree, "%s Record Layer: %s Protocol: %s%s",
-                                  ssl_version_short_names[*conv_version],
+                                  val_to_str_const(*conv_version, ssl_version_short_names, "SSL"),
                                   val_to_str(content_type, ssl_31_content_type, "unknown"),
                                   (msg_type_str!=NULL) ? msg_type_str :
                                   "Encrypted Handshake Message",
@@ -1064,7 +1232,7 @@ dissect_dtls_handshake(tvbuff_t *tvb, packet_info *pinfo,
           else
             {
               proto_item_set_text(tree, "%s Record Layer: %s Protocol: %s%s",
-                                  ssl_version_short_names[*conv_version],
+                                  val_to_str_const(*conv_version, ssl_version_short_names, "SSL"),
                                   val_to_str(content_type, ssl_31_content_type, "unknown"),
                                   "Multiple Handshake Messages",
                                   (frag_str!=NULL) ? frag_str : "");
@@ -1072,7 +1240,7 @@ dissect_dtls_handshake(tvbuff_t *tvb, packet_info *pinfo,
 
           /* add a subtree for the handshake protocol */
           ti = proto_tree_add_item(tree, hf_dtls_handshake_protocol, tvb,
-                                   offset, fragment_length + 12, 0);
+                                   offset, fragment_length + 12, ENC_NA);
           ssl_hand_tree = proto_item_add_subtree(ti, ett_dtls_handshake);
 
           if (ssl_hand_tree)
@@ -1096,7 +1264,7 @@ dissect_dtls_handshake(tvbuff_t *tvb, packet_info *pinfo,
 
           /* add nodes for the message type and message length */
           if (ssl_hand_tree)
-            proto_tree_add_item(ssl_hand_tree, hf_dtls_handshake_type,
+            proto_tree_add_uint(ssl_hand_tree, hf_dtls_handshake_type,
                                 tvb, offset, 1, msg_type);
           offset++;
           if (ssl_hand_tree)
@@ -1183,15 +1351,27 @@ dissect_dtls_handshake(tvbuff_t *tvb, packet_info *pinfo,
                 break;
 
               /* check for required session data */
-              ssl_debug_printf("dissect_dtls_handshake found SSL_HND_CLIENT_KEY_EXCHG state %X\n",
+              ssl_debug_printf("dissect_dtls_handshake found SSL_HND_CLIENT_KEY_EXCHG, state %X\n",
                                ssl->state);
               if ((ssl->state & (SSL_CIPHER|SSL_CLIENT_RANDOM|SSL_SERVER_RANDOM|SSL_VERSION)) !=
                   (SSL_CIPHER|SSL_CLIENT_RANDOM|SSL_SERVER_RANDOM|SSL_VERSION)) {
-                ssl_debug_printf("dissect_dtls_handshake not enough data to generate key (required %X)\n",
+                ssl_debug_printf("dissect_dtls_handshake not enough data to generate key (required state %X)\n",
                                  (SSL_CIPHER|SSL_CLIENT_RANDOM|SSL_SERVER_RANDOM|SSL_VERSION));
                 break;
               }
 
+              /* Skip leading two bytes length field. Older openssl's DTLS implementation seems not to have this field.
+               * See implementation note in RFC 4346 section 7.4.7.1
+               */
+              if (ssl->cipher_suite.kex==KEX_RSA && ssl->version_netorder != DTLSV1DOT0_VERSION_NOT) {
+                encrlen = tvb_get_ntohs(tvb, offset);
+                skip = 2;
+                if (encrlen > length - 2) {
+                  ssl_debug_printf("dissect_dtls_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);
@@ -1231,89 +1411,172 @@ dissect_dtls_handshake(tvbuff_t *tvb, packet_info *pinfo,
     }
 }
 
+/* dissects the heartbeat message, filling in the tree */
+static void
+dissect_dtls_heartbeat(tvbuff_t *tvb, packet_info *pinfo,
+                       proto_tree *tree, guint32 offset,
+                       guint* conv_version, guint32 record_length)
+{
+  /*     struct {
+   *         HeartbeatMessageType type;
+   *         uint16 payload_length;
+   *         opaque payload;
+   *         opaque padding;
+   *     } HeartbeatMessage;
+   */
+
+  proto_tree  *ti;
+  proto_tree  *dtls_heartbeat_tree;
+  const gchar *type;
+  guint8       byte;
+  guint16      payload_length;
+  guint16      padding_length;
+
+  dtls_heartbeat_tree = NULL;
+
+  if (tree) {
+    ti = proto_tree_add_item(tree, hf_dtls_heartbeat_message, tvb,
+                             offset, record_length - 32, ENC_NA);
+    dtls_heartbeat_tree = proto_item_add_subtree(ti, ett_dtls_heartbeat);
+  }
+
+  /*
+   * set the record layer label
+   */
+
+  /* first lookup the names for the message type and the payload length */
+  byte = tvb_get_guint8(tvb, offset);
+  type = match_strval(byte, tls_heartbeat_type);
+
+  payload_length = tvb_get_ntohs(tvb, offset + 1);
+  padding_length = record_length - 3 - payload_length;
+
+  /* now set the text in the record layer line */
+  if (type && (payload_length <= record_length - 16 - 3)) {
+    col_append_fstr(pinfo->cinfo, COL_INFO, "Heartbeat %s", type);
+  } else {
+    col_append_str(pinfo->cinfo, COL_INFO, "Encrypted Heartbeat");
+  }
+
+  if (tree) {
+    if (type && (payload_length <= record_length - 16 - 3)) {
+      proto_item_set_text(tree, "%s Record Layer: Heartbeat "
+                                "%s",
+                                val_to_str_const(*conv_version, ssl_version_short_names, "SSL"),
+                                type);
+      proto_tree_add_item(dtls_heartbeat_tree, hf_dtls_heartbeat_message_type,
+                          tvb, offset, 1, ENC_BIG_ENDIAN);
+      offset += 1;
+      proto_tree_add_uint(dtls_heartbeat_tree, hf_dtls_heartbeat_message_payload_length,
+                          tvb, offset, 2, payload_length);
+      offset += 2;
+      proto_tree_add_bytes_format(dtls_heartbeat_tree, hf_dtls_heartbeat_message_payload,
+                                  tvb, offset, payload_length,
+                                  NULL, "Payload (%u byte%s)",
+                                  payload_length,
+                                  plurality(payload_length, "", "s"));
+      offset += payload_length;
+      proto_tree_add_bytes_format(dtls_heartbeat_tree, hf_dtls_heartbeat_message_padding,
+                                  tvb, offset, padding_length,
+                                  NULL, "Padding and HMAC (%u byte%s)",
+                                  padding_length,
+                                  plurality(padding_length, "", "s"));
+    } else {
+      proto_item_set_text(tree,
+                         "%s Record Layer: Encrypted Heartbeat",
+                         val_to_str_const(*conv_version, ssl_version_short_names, "SSL"));
+      proto_item_set_text(dtls_heartbeat_tree,
+                          "Encrypted Heartbeat Message");
+    }
+  }
+}
+
 static gint
 dissect_dtls_hnd_hello_common(tvbuff_t *tvb, proto_tree *tree,
                               guint32 offset, SslDecryptSession* ssl, gint from_server)
 {
   /* show the client's random challenge */
   nstime_t gmt_unix_time;
-  guint8  session_id_length;
-  session_id_length = 0;
-  if (ssl)
-    {
-      /* get proper peer information*/
-      StringInfo* rnd;
-      if (from_server)
-        rnd = &ssl->server_random;
-      else
-        rnd = &ssl->client_random;
+  guint8   session_id_length;
 
-      /* 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_dtls_hnd_hello_common found random state %X\n",
-                       ssl->state);
-
-      session_id_length = tvb_get_guint8(tvb, offset + 32);
-      /* check stored session id info */
-      if (from_server && (session_id_length == ssl->session_id.data_len) &&
-          (tvb_memeql(tvb, offset+33, ssl->session_id.data, session_id_length) == 0))
-        {
-          /* clinet/server id match: try to restore a previous cached session*/
-          ssl_restore_session(ssl, dtls_session_hash);
+  if (tree || ssl)
+    {
+         if (ssl)
+               {
+                 /* 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_dtls_hnd_hello_common found random state %X\n",
+                                                  ssl->state);
         }
-      else {
-        tvb_memcpy(tvb,ssl->session_id.data, offset+33, session_id_length);
-        ssl->session_id.data_len = session_id_length;
-      }
-    }
 
-  if (tree)
-    {
       /* show the time */
-      gmt_unix_time.secs = tvb_get_ntohl(tvb, offset);
-      gmt_unix_time.nsecs = 0;
-      proto_tree_add_time(tree, hf_dtls_handshake_random_time,
-                          tvb, offset, 4, &gmt_unix_time);
+      if (tree)
+        {
+          gmt_unix_time.secs  = tvb_get_ntohl(tvb, offset);
+          gmt_unix_time.nsecs = 0;
+          proto_tree_add_time(tree, hf_dtls_handshake_random_time,
+                              tvb, offset, 4, &gmt_unix_time);
+        }
       offset += 4;
 
       /* show the random bytes */
-      proto_tree_add_item(tree, hf_dtls_handshake_random_bytes,
-                          tvb, offset, 28, 0);
+      if (tree)
+          proto_tree_add_item(tree, hf_dtls_handshake_random_bytes,
+                              tvb, offset, 28, ENC_NA);
       offset += 28;
 
       /* show the session id */
       session_id_length = tvb_get_guint8(tvb, offset);
-      proto_tree_add_item(tree, hf_dtls_handshake_session_id_len,
-                          tvb, offset++, 1, 0);
-      if (session_id_length > 0)
+      if (tree)
+          proto_tree_add_item(tree, hf_dtls_handshake_session_id_len,
+                              tvb, offset, 1, ENC_BIG_ENDIAN);
+         offset++;
+      if (ssl)
         {
-          proto_tree_add_bytes_format(tree, hf_dtls_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"));
-          offset += session_id_length;
+          /* check stored session id info */
+          if (from_server && (session_id_length == ssl->session_id.data_len) &&
+              (tvb_memeql(tvb, offset, ssl->session_id.data, session_id_length) == 0))
+            {
+              /* clinet/server id match: try to restore a previous cached session*/
+              ssl_restore_session(ssl, dtls_session_hash);
+            }
+          else {
+            tvb_memcpy(tvb,ssl->session_id.data, offset, session_id_length);
+            ssl->session_id.data_len = session_id_length;
+          }
         }
-
+         if (tree && session_id_length > 0)
+                 proto_tree_add_bytes_format(tree, hf_dtls_handshake_session_id,
+                                                                         tvb, offset, session_id_length,
+                                                                         NULL, "Session ID (%u byte%s)",
+                                                                         session_id_length,
+                                                                         plurality(session_id_length, "", "s"));
+         offset += session_id_length;
     }
 
   /* XXXX */
-  return session_id_length+33;
+  return offset;
 }
 
 static gint
 dissect_dtls_hnd_hello_ext(tvbuff_t *tvb,
                            proto_tree *tree, guint32 offset, guint32 left)
 {
-  guint16 extension_length;
-  guint16 ext_type;
-  guint16 ext_len;
+  guint16     extension_length;
+  guint16     ext_type;
+  guint16     ext_len;
   proto_item *pi;
   proto_tree *ext_tree;
 
@@ -1324,12 +1587,12 @@ dissect_dtls_hnd_hello_ext(tvbuff_t *tvb,
   proto_tree_add_uint(tree, hf_dtls_handshake_extensions_len,
                       tvb, offset, 2, extension_length);
   offset += 2;
-  left -= 2;
+  left   -= 2;
 
   while (left >= 4)
     {
       ext_type = tvb_get_ntohs(tvb, offset);
-      ext_len = tvb_get_ntohs(tvb, offset + 2);
+      ext_len  = tvb_get_ntohs(tvb, offset + 2);
 
       pi = proto_tree_add_text(tree, tvb, offset, 4 + ext_len,
                                "Extension: %s",
@@ -1348,13 +1611,21 @@ dissect_dtls_hnd_hello_ext(tvbuff_t *tvb,
                           tvb, offset, 2, ext_len);
       offset += 2;
 
-      proto_tree_add_bytes_format(ext_tree, hf_dtls_handshake_extension_data,
-                                  tvb, offset, ext_len,
-                                  tvb_get_ptr(tvb, offset, ext_len),
-                                  "Data (%u byte%s)",
-                                  ext_len, plurality(ext_len, "", "s"));
-      offset += ext_len;
-      left -= 2 + 2 + ext_len;
+      switch (ext_type) {
+      case SSL_HND_HELLO_EXT_HEARTBEAT:
+          proto_tree_add_item(ext_tree, hf_dtls_heartbeat_extension_mode,
+                              tvb, offset, 1, ENC_BIG_ENDIAN);
+          break;
+      default:
+          proto_tree_add_bytes_format(ext_tree, hf_dtls_handshake_extension_data,
+                                      tvb, offset, ext_len, NULL,
+                                      "Data (%u byte%s)",
+                                      ext_len, plurality(ext_len, "", "s"));
+          offset += ext_len;
+          break;
+      }
+
+      left   -= 2 + 2 + ext_len;
     }
 
   return offset;
@@ -1376,27 +1647,25 @@ dissect_dtls_hnd_cli_hello(tvbuff_t *tvb,
    * } ClientHello;
    *
    */
+
   proto_tree *ti;
   proto_tree *cs_tree;
-  guint16 cipher_suite_length;
-  guint8  compression_methods_length;
-  guint8  compression_method;
-  guint16 start_offset = offset;
-  guint8 cookie_length;
-  cipher_suite_length = 0;
-  compression_methods_length = 0;
-  cookie_length = 0;
+  guint16     cipher_suite_length;
+  guint8      compression_methods_length;
+  guint8      compression_method;
+  guint16     start_offset   = offset;
+  guint8      cookie_length;
 
   if (tree || ssl)
     {
       /* show the client version */
       if (tree)
         proto_tree_add_item(tree, hf_dtls_handshake_client_version, tvb,
-                            offset, 2, FALSE);
+                            offset, 2, ENC_BIG_ENDIAN);
       offset += 2;
 
       /* show the fields in common with server hello */
-      offset += dissect_dtls_hnd_hello_common(tvb, tree, offset, ssl, 0);
+      offset = dissect_dtls_hnd_hello_common(tvb, tree, offset, ssl, 0);
 
       /* look for a cookie */
       cookie_length = tvb_get_guint8(tvb, offset);
@@ -1411,8 +1680,7 @@ dissect_dtls_hnd_cli_hello(tvbuff_t *tvb,
         {
           proto_tree_add_bytes_format(tree, hf_dtls_handshake_cookie,
                                       tvb, offset, cookie_length,
-                                      tvb_get_ptr(tvb, offset, cookie_length),
-                                      "Cookie (%u byte%s)",
+                                      NULL, "Cookie (%u byte%s)",
                                       cookie_length,
                                       plurality(cookie_length, "", "s"));
           offset += cookie_length;
@@ -1445,7 +1713,7 @@ dissect_dtls_hnd_cli_hello(tvbuff_t *tvb,
           while (cipher_suite_length > 0)
             {
               proto_tree_add_item(cs_tree, hf_dtls_handshake_cipher_suite,
-                                  tvb, offset, 2, FALSE);
+                                  tvb, offset, 2, ENC_BIG_ENDIAN);
               offset += 2;
               cipher_suite_length -= 2;
             }
@@ -1496,7 +1764,7 @@ dissect_dtls_hnd_cli_hello(tvbuff_t *tvb,
 
       if (length > offset - start_offset)
         {
-          offset = dissect_dtls_hnd_hello_ext(tvb, tree, offset,
+          dissect_dtls_hnd_hello_ext(tvb, tree, offset,
                                               length -
                                               (offset - start_offset));
         }
@@ -1504,10 +1772,9 @@ dissect_dtls_hnd_cli_hello(tvbuff_t *tvb,
 }
 
 
-static void dissect_dtls_hnd_hello_verify_request(tvbuff_t *tvb,
-                                                  proto_tree *tree,
-                                                  guint32 offset,
-                                                  SslDecryptSession* ssl)
+static int
+dissect_dtls_hnd_hello_verify_request(tvbuff_t *tvb, proto_tree *tree,
+                                      guint32 offset, SslDecryptSession* ssl)
 {
   /*
    * struct {
@@ -1517,21 +1784,21 @@ static void dissect_dtls_hnd_hello_verify_request(tvbuff_t *tvb,
    */
 
   guint8 cookie_length;
-  cookie_length = 0;
+
 
   if (tree || ssl)
     {
       /* show the client version */
       if (tree)
         proto_tree_add_item(tree, hf_dtls_handshake_server_version, tvb,
-                            offset, 2, FALSE);
+                            offset, 2, ENC_BIG_ENDIAN);
       offset += 2;
 
 
       /* look for a cookie */
       cookie_length = tvb_get_guint8(tvb, offset);
       if (!tree)
-        return;
+        return offset;
 
       proto_tree_add_uint(tree, hf_dtls_handshake_cookie_len,
                           tvb, offset, 1, cookie_length);
@@ -1541,17 +1808,16 @@ static void dissect_dtls_hnd_hello_verify_request(tvbuff_t *tvb,
         {
           proto_tree_add_bytes_format(tree, hf_dtls_handshake_cookie,
                                       tvb, offset, cookie_length,
-                                      tvb_get_ptr(tvb, offset, cookie_length),
-                                      "Cookie (%u byte%s)",
+                                      NULL, "Cookie (%u byte%s)",
                                       cookie_length,
                                       plurality(cookie_length, "", "s"));
           offset += cookie_length;
         }
     }
-
+    return offset;
 }
 
-static void
+static int
 dissect_dtls_hnd_srv_hello(tvbuff_t *tvb,
                            proto_tree *tree, guint32 offset, guint32 length, SslDecryptSession* ssl)
 {
@@ -1564,7 +1830,9 @@ dissect_dtls_hnd_srv_hello(tvbuff_t *tvb,
    *     Extension server_hello_extension_list<0..2^16-1>;
    * } ServerHello;
    */
+
   guint16 start_offset;
+
   start_offset = offset;
 
   if (tree || ssl)
@@ -1572,13 +1840,13 @@ dissect_dtls_hnd_srv_hello(tvbuff_t *tvb,
       /* show the server version */
       if (tree)
         proto_tree_add_item(tree, hf_dtls_handshake_server_version, tvb,
-                            offset, 2, FALSE);
+                            offset, 2, ENC_BIG_ENDIAN);
       offset += 2;
 
       /* first display the elements conveniently in
        * common with client hello
        */
-      offset += dissect_dtls_hnd_hello_common(tvb, tree, offset, ssl, 1);
+      offset = dissect_dtls_hnd_hello_common(tvb, tree, offset, ssl, 1);
 
       /* PAOLO: handle session cipher suite  */
       if (ssl) {
@@ -1598,7 +1866,7 @@ dissect_dtls_hnd_srv_hello(tvbuff_t *tvb,
         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_dtls_hnd_srv_hello not enough data to generate key (required %X)\n",
+          ssl_debug_printf("dissect_dtls_hnd_srv_hello not enough data to generate key (required state %X)\n",
                            (SSL_CIPHER|SSL_CLIENT_RANDOM|SSL_SERVER_RANDOM|SSL_VERSION|SSL_MASTER_SECRET));
           goto no_cipher;
         }
@@ -1611,17 +1879,21 @@ dissect_dtls_hnd_srv_hello(tvbuff_t *tvb,
         ssl->state |= SSL_HAVE_SESSION_KEY;
       }
     no_cipher:
+      if (ssl) {
+        /* store selected compression method for decompression */
+        ssl->compression = tvb_get_guint8(tvb, offset+2);
+      }
       if (!tree)
-        return;
+        return offset;
 
       /* now the server-selected cipher suite */
       proto_tree_add_item(tree, hf_dtls_handshake_cipher_suite,
-                          tvb, offset, 2, FALSE);
+                          tvb, offset, 2, ENC_BIG_ENDIAN);
       offset += 2;
 
       /* and the server-selected compression method */
       proto_tree_add_item(tree, hf_dtls_handshake_comp_method,
-                          tvb, offset, 1, FALSE);
+                          tvb, offset, 1, ENC_BIG_ENDIAN);
       offset++;
 
       if (length > offset - start_offset)
@@ -1631,6 +1903,7 @@ dissect_dtls_hnd_srv_hello(tvbuff_t *tvb,
                                               (offset - start_offset));
         }
     }
+    return offset;
 }
 
 static void
@@ -1644,10 +1917,12 @@ dissect_dtls_hnd_cert(tvbuff_t *tvb,
    *     ASN.1Cert certificate_list<1..2^24-1>;
    * } Certificate;
    */
-  guint32 certificate_list_length;
+
+  guint32     certificate_list_length;
   proto_tree *ti;
   proto_tree *subtree;
-  asn1_ctx_t asn1_ctx;
+  asn1_ctx_t  asn1_ctx;
+
   asn1_ctx_init(&asn1_ctx, ASN1_ENC_BER, TRUE, pinfo);
 
   if (tree)
@@ -1683,7 +1958,7 @@ dissect_dtls_hnd_cert(tvbuff_t *tvb,
               certificate_list_length -= 3 + cert_length;
 
               proto_tree_add_item(subtree, hf_dtls_handshake_certificate_len,
-                                  tvb, offset, 3, FALSE);
+                                  tvb, offset, 3, ENC_BIG_ENDIAN);
               offset += 3;
 
               dissect_x509af_Certificate(FALSE, tvb, offset, &asn1_ctx, subtree, hf_dtls_handshake_certificate);
@@ -1712,12 +1987,11 @@ dissect_dtls_hnd_cert_req(tvbuff_t *tvb,
    *    } CertificateRequest;
    *
    */
+
   proto_tree *ti;
   proto_tree *subtree;
   guint8      cert_types_count;
-  gint         dnames_length;
-  cert_types_count = 0;
-  dnames_length = 0;
+  gint        dnames_length;
 
   if (tree)
     {
@@ -1743,7 +2017,7 @@ dissect_dtls_hnd_cert_req(tvbuff_t *tvb,
           while (cert_types_count > 0)
             {
               proto_tree_add_item(subtree, hf_dtls_handshake_cert_type,
-                                  tvb, offset, 1, FALSE);
+                                  tvb, offset, 1, ENC_BIG_ENDIAN);
               offset++;
               cert_types_count--;
             }
@@ -1776,13 +2050,12 @@ dissect_dtls_hnd_cert_req(tvbuff_t *tvb,
               dnames_length -= 2 + name_length;
 
               proto_tree_add_item(subtree, hf_dtls_handshake_dname_len,
-                                  tvb, offset, 2, FALSE);
+                                  tvb, offset, 2, ENC_BIG_ENDIAN);
               offset += 2;
 
               proto_tree_add_bytes_format(subtree,
                                           hf_dtls_handshake_dname,
-                                          tvb, offset, name_length,
-                                          tvb_get_ptr(tvb, offset, name_length),
+                                          tvb, offset, name_length, NULL,
                                           "Distinguished Name (%u byte%s)",
                                           name_length,
                                           plurality(name_length, "", "s"));
@@ -1794,8 +2067,7 @@ dissect_dtls_hnd_cert_req(tvbuff_t *tvb,
 }
 
 static void
-dissect_dtls_hnd_finished(tvbuff_t *tvb,
-                          proto_tree *tree, guint32 offset,
+dissect_dtls_hnd_finished(tvbuff_t *tvb, proto_tree *tree, guint32 offset,
                           guint* conv_version)
 {
   /*
@@ -1813,7 +2085,7 @@ dissect_dtls_hnd_finished(tvbuff_t *tvb,
   switch(*conv_version) {
   case SSL_VER_DTLS:
     proto_tree_add_item(tree, hf_dtls_handshake_finished,
-                        tvb, offset, 12, FALSE);
+                        tvb, offset, 12, ENC_NA);
     break;
   }
 }
@@ -1837,15 +2109,7 @@ ssl_set_conv_version(packet_info *pinfo, guint version)
       return;
     }
 
-  conversation = find_conversation(pinfo->fd->num, &pinfo->src, &pinfo->dst, pinfo->ptype,
-                                   pinfo->srcport, pinfo->destport, 0);
-
-  if (conversation == NULL)
-    {
-      /* create a new conversation */
-      conversation = conversation_new(pinfo->fd->num, &pinfo->src, &pinfo->dst, pinfo->ptype,
-                                      pinfo->srcport, pinfo->destport, 0);
-    }
+  conversation = find_or_create_conversation(pinfo);
 
   if (conversation_get_proto_data(conversation, proto_dtls) != NULL)
     {
@@ -1878,8 +2142,7 @@ dtls_is_valid_handshake_type(guint8 type)
 }
 
 static gint
-dtls_is_authoritative_version_message(guint8 content_type,
-                                      guint8 next_byte)
+dtls_is_authoritative_version_message(guint8 content_type, guint8 next_byte)
 {
   if (content_type == SSL_ID_HANDSHAKE
       && dtls_is_valid_handshake_type(next_byte))
@@ -1904,7 +2167,7 @@ looks_like_dtls(tvbuff_t *tvb, guint32 offset)
   /* have to have a valid content type followed by a valid
    * protocol version
    */
-  guint8 byte;
+  guint8  byte;
   guint16 version;
 
   /* see if the first byte is a valid content type */
@@ -1924,6 +2187,55 @@ looks_like_dtls(tvbuff_t *tvb, guint32 offset)
   return 1;
 }
 
+/* UAT */
+
+#ifdef HAVE_LIBGNUTLS
+static void
+dtlsdecrypt_free_cb(void* r)
+{
+  ssldecrypt_assoc_t* h = r;
+
+  g_free(h->ipaddr);
+  g_free(h->port);
+  g_free(h->protocol);
+  g_free(h->keyfile);
+  g_free(h->password);
+}
+#endif
+
+#if 0
+static void
+dtlsdecrypt_update_cb(void* r _U_, const char** err _U_)
+{
+  return;
+}
+#endif
+
+#ifdef HAVE_LIBGNUTLS
+static void *
+dtlsdecrypt_copy_cb(void* dest, const void* orig, size_t len _U_)
+{
+  const ssldecrypt_assoc_t* o = orig;
+  ssldecrypt_assoc_t*       d = dest;
+
+  d->ipaddr    = g_strdup(o->ipaddr);
+  d->port      = g_strdup(o->port);
+  d->protocol  = g_strdup(o->protocol);
+  d->keyfile   = g_strdup(o->keyfile);
+  d->password  = g_strdup(o->password);
+
+  return d;
+}
+
+UAT_CSTRING_CB_DEF(sslkeylist_uats,ipaddr,ssldecrypt_assoc_t)
+UAT_CSTRING_CB_DEF(sslkeylist_uats,port,ssldecrypt_assoc_t)
+UAT_CSTRING_CB_DEF(sslkeylist_uats,protocol,ssldecrypt_assoc_t)
+UAT_FILENAME_CB_DEF(sslkeylist_uats,keyfile,ssldecrypt_assoc_t)
+UAT_CSTRING_CB_DEF(sslkeylist_uats,password,ssldecrypt_assoc_t)
+#endif
+
+void proto_reg_handoff_dtls(void);
+
 /*********************************************************************
  *
  * Standard Wireshark Protocol Registration and housekeeping
@@ -1938,12 +2250,12 @@ proto_register_dtls(void)
     { &hf_dtls_record,
       { "Record Layer", "dtls.record",
         FT_NONE, BASE_NONE, NULL, 0x0,
-        "Record layer", HFILL }
+        NULL, HFILL }
     },
     { &hf_dtls_record_content_type,
       { "Content Type", "dtls.record.content_type",
         FT_UINT8, BASE_DEC, VALS(ssl_31_content_type), 0x0,
-        "Content type", HFILL}
+        NULL, HFILL}
     },
     { &hf_dtls_record_version,
       { "Version", "dtls.record.version",
@@ -1978,7 +2290,7 @@ proto_register_dtls(void)
     { & hf_dtls_alert_message,
       { "Alert Message", "dtls.alert_message",
         FT_NONE, BASE_NONE, NULL, 0x0,
-        "Alert message", HFILL }
+        NULL, HFILL }
     },
     { & hf_dtls_alert_message_level,
       { "Level", "dtls.alert_message.level",
@@ -2021,18 +2333,18 @@ proto_register_dtls(void)
         "Fragment length of handshake message", HFILL }
     },
     { &hf_dtls_handshake_client_version,
-      { "Version", "dtls.handshake.version",
+      { "Version", "dtls.handshake.client_version",
         FT_UINT16, BASE_HEX, VALS(ssl_versions), 0x0,
         "Maximum version supported by client", HFILL }
     },
     { &hf_dtls_handshake_server_version,
-      { "Version", "dtls.handshake.version",
+      { "Version", "dtls.handshake.server_version",
         FT_UINT16, BASE_HEX, VALS(ssl_versions), 0x0,
         "Version selected by server", HFILL }
     },
     { &hf_dtls_handshake_random_time,
       { "Random.gmt_unix_time", "dtls.handshake.random_time",
-        FT_ABSOLUTE_TIME, BASE_NONE, NULL, 0x0,
+        FT_ABSOLUTE_TIME, ABSOLUTE_TIME_LOCAL, NULL, 0x0,
         "Unix time field of random structure", HFILL }
     },
     { &hf_dtls_handshake_random_bytes,
@@ -2052,8 +2364,8 @@ proto_register_dtls(void)
     },
     { &hf_dtls_handshake_cipher_suite,
       { "Cipher Suite", "dtls.handshake.ciphersuite",
-        FT_UINT16, BASE_HEX, VALS(ssl_31_ciphersuite), 0x0,
-        "Cipher suite", HFILL }
+        FT_UINT16, BASE_HEX|BASE_EXT_STRING, &ssl_31_ciphersuite_ext, 0x0,
+        NULL, HFILL }
     },
     { &hf_dtls_handshake_cookie_len,
       { "Cookie Length", "dtls.handshake.cookie_length",
@@ -2180,6 +2492,33 @@ proto_register_dtls(void)
         FT_BYTES, BASE_NONE, NULL, 0x0,
         "Distinguished name of a CA that server trusts", HFILL }
     },
+    { &hf_dtls_heartbeat_extension_mode,
+      { "Mode", "dtls.handshake.extension.heartbeat.mode",
+        FT_UINT8, BASE_DEC, VALS(tls_heartbeat_mode), 0x0,
+        "Heartbeat extension mode", HFILL }
+    },
+    { &hf_dtls_heartbeat_message,
+      { "Heartbeat Message", "dtls.heartbeat_message",
+        FT_NONE, BASE_NONE, NULL, 0x0,
+        NULL, HFILL }
+    },
+    { &hf_dtls_heartbeat_message_type,
+      { "Type", "dtls.heartbeat_message.type",
+        FT_UINT8, BASE_DEC, VALS(tls_heartbeat_type), 0x0,
+        "Heartbeat message type", HFILL }
+    },
+    { &hf_dtls_heartbeat_message_payload_length,
+      { "Payload Length", "dtls.heartbeat_message.payload_length",
+        FT_UINT16, BASE_DEC, NULL, 0x00, NULL, HFILL }
+    },
+    { &hf_dtls_heartbeat_message_payload,
+      { "Payload Length", "dtls.heartbeat_message.payload",
+        FT_BYTES, BASE_NONE, NULL, 0x00, NULL, HFILL }
+    },
+    { &hf_dtls_heartbeat_message_padding,
+      { "Payload Length", "dtls.heartbeat_message.padding",
+        FT_BYTES, BASE_NONE, NULL, 0x00, NULL, HFILL }
+    },
     { &hf_dtls_fragments,
       { "Message fragments", "dtls.fragments",
         FT_NONE, BASE_NONE, NULL, 0x00, NULL, HFILL }
@@ -2210,10 +2549,18 @@ proto_register_dtls(void)
       { "Message defragmentation error", "dtls.fragment.error",
         FT_FRAMENUM, BASE_NONE, NULL, 0x00, NULL, HFILL }
     },
+    { &hf_dtls_fragment_count,
+      { "Message fragment count", "dtls.fragment.count",
+        FT_UINT32, BASE_DEC, NULL, 0x00, NULL, HFILL }
+    },
     { &hf_dtls_reassembled_in,
       { "Reassembled in", "dtls.reassembled.in",
         FT_FRAMENUM, BASE_NONE, NULL, 0x00, NULL, HFILL }
     },
+    { &hf_dtls_reassembled_length,
+      { "Reassembled DTLS length", "dtls.reassembled.length",
+        FT_UINT32, BASE_DEC, NULL, 0x00, NULL, HFILL }
+    },
   };
 
   /* Setup protocol subtree array */
@@ -2222,6 +2569,7 @@ proto_register_dtls(void)
     &ett_dtls_record,
     &ett_dtls_alert,
     &ett_dtls_handshake,
+    &ett_dtls_heartbeat,
     &ett_dtls_cipher_suites,
     &ett_dtls_comp_methods,
     &ett_dtls_extension,
@@ -2243,16 +2591,46 @@ proto_register_dtls(void)
 
 #ifdef HAVE_LIBGNUTLS
   {
-    module_t *dtls_module = prefs_register_protocol(proto_dtls, dtls_parse);
-    prefs_register_string_preference(dtls_module, "keys_list", "RSA keys list",
-                                     "semicolon separated list of private RSA keys used for DTLS 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\n",
-                                     (const gchar **)&dtls_keys_list);
+    module_t *dtls_module = prefs_register_protocol(proto_dtls, proto_reg_handoff_dtls);
+
+    static uat_field_t dtlskeylist_uats_flds[] = {
+      UAT_FLD_CSTRING_OTHER(sslkeylist_uats, ipaddr, "IP address", ssldecrypt_uat_fld_ip_chk_cb, "IPv4 or IPv6 address"),
+      UAT_FLD_CSTRING_OTHER(sslkeylist_uats, port, "Port", ssldecrypt_uat_fld_port_chk_cb, "Port Number"),
+      UAT_FLD_CSTRING_OTHER(sslkeylist_uats, protocol, "Protocol", ssldecrypt_uat_fld_protocol_chk_cb, "Protocol"),
+      UAT_FLD_FILENAME_OTHER(sslkeylist_uats, keyfile, "Key File", ssldecrypt_uat_fld_fileopen_chk_cb, "Path to the keyfile."),
+      UAT_FLD_CSTRING_OTHER(sslkeylist_uats, password," Password (p12 file)", ssldecrypt_uat_fld_password_chk_cb, "Password"),
+      UAT_END_FIELDS
+    };
+
+    dtlsdecrypt_uat = uat_new("DTLS RSA Keylist",
+                              sizeof(ssldecrypt_assoc_t),
+                              "dtlsdecrypttablefile",         /* filename */
+                              TRUE,                           /* from_profile */
+                              (void*) &dtlskeylist_uats,      /* data_ptr */
+                              &ndtlsdecrypt,                  /* numitems_ptr */
+                              UAT_CAT_FFMT,                   /* category */
+                              "ChK12ProtocolsSection",        /* TODO, need revision - help */
+                              dtlsdecrypt_copy_cb,
+                              NULL, /* dtlsdecrypt_update_cb? */
+                              dtlsdecrypt_free_cb,
+                              dtls_parse_uat,
+                              dtlskeylist_uats_flds);
+
+    prefs_register_uat_preference(dtls_module, "cfg",
+                                  "RSA keys list",
+                                  "A table of RSA keys for DTLS decryption",
+                                  dtlsdecrypt_uat);
+
     prefs_register_string_preference(dtls_module, "debug_file", "DTLS debug file",
                                      "redirect dtls debug to file name; leave empty to disable debug, "
                                      "use \"" SSL_DEBUG_USE_STDERR "\" to redirect output to stderr\n",
                                      (const gchar **)&dtls_debug_file_name);
+
+    prefs_register_string_preference(dtls_module, "keys_list", "RSA keys list (deprecated)",
+                                     "Semicolon-separated list of private RSA keys used for DTLS decryption. "
+                                     "Used by versions of Wireshark prior to 1.6",
+                                     (const gchar **)&dtls_keys_list);
+
   }
 #endif
 
@@ -2266,8 +2644,11 @@ proto_register_dtls(void)
   dtls_tap = register_tap("dtls");
   ssl_debug_printf("proto_register_dtls: registered tap %s:%d\n",
                    "dtls", dtls_tap);
+
+  register_heur_dissector_list("dtls", &heur_subdissector_list);
 }
 
+
 /* If this dissector uses sub-dissector registration add a registration
  * routine.  This format is required because a script is used to find
  * these routines and create the code that calls these routines.
@@ -2275,7 +2656,14 @@ proto_register_dtls(void)
 void
 proto_reg_handoff_dtls(void)
 {
+  static gboolean initialized = FALSE;
 
   /* add now dissector to default ports.*/
-  dtls_parse();
+  dtls_parse_uat();
+  dtls_parse_old_keys();
+
+  if (initialized == FALSE)
+    heur_dissector_add("udp", dissect_dtls_heur, proto_dtls);
+
+  initialized = TRUE;
 }