dtls: Keep track of the connection state and signal it through all the layers
authorSebastian Dröge <sebastian@centricular.com>
Sun, 12 Jan 2020 14:18:09 +0000 (16:18 +0200)
committerGStreamer Merge Bot <gitlab-merge-bot@gstreamer-foundation.org>
Sun, 19 Jan 2020 11:16:34 +0000 (11:16 +0000)
This allows the application to keep track of the underlying DTLS
connection state and act accordingly.

ext/dtls/gstdtlsconnection.c
ext/dtls/gstdtlsconnection.h
ext/dtls/gstdtlsdec.c
ext/dtls/gstdtlsenc.c
ext/dtls/gstdtlssrtpdec.c
ext/dtls/gstdtlssrtpenc.c

index a3dfeb7..e287030 100644 (file)
@@ -69,6 +69,7 @@ enum
 {
   PROP_0,
   PROP_AGENT,
+  PROP_CONNECTION_STATE,
   NUM_PROPERTIES
 };
 
@@ -87,9 +88,9 @@ struct _GstDtlsConnectionPrivate
   gboolean is_alive;
   gboolean keys_exported;
 
+  GstDtlsConnectionState connection_state;
   gboolean sent_close_notify;
   gboolean received_close_notify;
-  gboolean fatal_error;
 
   GMutex mutex;
   GCond condition;
@@ -113,12 +114,15 @@ G_DEFINE_TYPE_WITH_CODE (GstDtlsConnection, gst_dtls_connection, G_TYPE_OBJECT,
 static void gst_dtls_connection_finalize (GObject * gobject);
 static void gst_dtls_connection_set_property (GObject *, guint prop_id,
     const GValue *, GParamSpec *);
+static void gst_dtls_connection_get_property (GObject *, guint prop_id,
+    GValue *, GParamSpec *);
 
 static void log_state (GstDtlsConnection *, const gchar * str);
 static void export_srtp_keys (GstDtlsConnection *);
-static GstFlowReturn openssl_poll (GstDtlsConnection *, GError ** err);
+static GstFlowReturn openssl_poll (GstDtlsConnection *, gboolean * notify_state,
+    GError ** err);
 static GstFlowReturn handle_error (GstDtlsConnection * self, int ret,
-    GstResourceError error_type, GError ** err);
+    GstResourceError error_type, gboolean * notify_state, GError ** err);
 static int openssl_verify_callback (int preverify_ok,
     X509_STORE_CTX * x509_ctx);
 
@@ -135,6 +139,7 @@ gst_dtls_connection_class_init (GstDtlsConnectionClass * klass)
   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
 
   gobject_class->set_property = gst_dtls_connection_set_property;
+  gobject_class->get_property = gst_dtls_connection_get_property;
 
   connection_ex_index =
       SSL_get_ex_new_index (0, (gpointer) "gstdtlsagent connection index", NULL,
@@ -161,6 +166,13 @@ gst_dtls_connection_class_init (GstDtlsConnectionClass * klass)
       GST_TYPE_DTLS_AGENT,
       G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
 
+  properties[PROP_CONNECTION_STATE] =
+      g_param_spec_enum ("connection-state",
+      "Connection State",
+      "Current connection state",
+      GST_DTLS_TYPE_CONNECTION_STATE,
+      GST_DTLS_CONNECTION_STATE_NEW, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
   g_object_class_install_properties (gobject_class, NUM_PROPERTIES, properties);
 
   _gst_dtls_init_openssl ();
@@ -290,12 +302,31 @@ gst_dtls_connection_set_property (GObject * object, guint prop_id,
   }
 }
 
+static void
+gst_dtls_connection_get_property (GObject * object, guint prop_id,
+    GValue * value, GParamSpec * pspec)
+{
+  GstDtlsConnection *self = GST_DTLS_CONNECTION (object);
+  GstDtlsConnectionPrivate *priv = self->priv;
+
+  switch (prop_id) {
+    case PROP_CONNECTION_STATE:
+      g_mutex_lock (&priv->mutex);
+      g_value_set_enum (value, priv->connection_state);
+      g_mutex_unlock (&priv->mutex);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec);
+  }
+}
+
 gboolean
 gst_dtls_connection_start (GstDtlsConnection * self, gboolean is_client,
     GError ** err)
 {
   GstDtlsConnectionPrivate *priv;
   gboolean ret;
+  gboolean notify_state = FALSE;
 
   priv = self->priv;
 
@@ -313,19 +344,26 @@ gst_dtls_connection_start (GstDtlsConnection * self, gboolean is_client,
   priv->bio_buffer_offset = 0;
   priv->keys_exported = FALSE;
 
-  priv->fatal_error = FALSE;
   priv->sent_close_notify = FALSE;
   priv->received_close_notify = FALSE;
 
+  /* Client immediately starts connecting, the server waits for a client to
+   * start the handshake process */
   priv->is_client = is_client;
   if (priv->is_client) {
+    priv->connection_state = GST_DTLS_CONNECTION_STATE_CONNECTING;
+    notify_state = TRUE;
     SSL_set_connect_state (priv->ssl);
   } else {
+    if (priv->connection_state != GST_DTLS_CONNECTION_STATE_NEW) {
+      priv->connection_state = GST_DTLS_CONNECTION_STATE_NEW;
+      notify_state = TRUE;
+    }
     SSL_set_accept_state (priv->ssl);
   }
   log_state (self, "initial state set");
 
-  ret = openssl_poll (self, err);
+  ret = openssl_poll (self, &notify_state, err);
   if (ret == GST_FLOW_EOS && err) {
     *err =
         g_error_new_literal (GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_OPEN_WRITE,
@@ -337,6 +375,11 @@ gst_dtls_connection_start (GstDtlsConnection * self, gboolean is_client,
   GST_TRACE_OBJECT (self, "unlocking @ start");
   g_mutex_unlock (&priv->mutex);
 
+  if (notify_state) {
+    g_object_notify_by_pspec (G_OBJECT (self),
+        properties[PROP_CONNECTION_STATE]);
+  }
+
   return ret == GST_FLOW_OK;
 }
 
@@ -346,6 +389,7 @@ handle_timeout (gpointer data, gpointer user_data)
   GstDtlsConnection *self = user_data;
   GstDtlsConnectionPrivate *priv;
   gint ret;
+  gboolean notify_state = FALSE;
 
   priv = self->priv;
 
@@ -361,11 +405,16 @@ handle_timeout (gpointer data, gpointer user_data)
       GST_WARNING_OBJECT (self, "handling timeout failed");
     } else if (ret > 0) {
       log_state (self, "handling timeout before poll");
-      openssl_poll (self, NULL);
+      openssl_poll (self, &notify_state, NULL);
       log_state (self, "handling timeout after poll");
     }
   }
   g_mutex_unlock (&priv->mutex);
+
+  if (notify_state) {
+    g_object_notify_by_pspec (G_OBJECT (self),
+        properties[PROP_CONNECTION_STATE]);
+  }
 }
 
 static gboolean
@@ -456,6 +505,8 @@ gst_dtls_connection_check_timeout (GstDtlsConnection * self)
 void
 gst_dtls_connection_stop (GstDtlsConnection * self)
 {
+  gboolean notify_state = FALSE;
+
   g_return_if_fail (GST_IS_DTLS_CONNECTION (self));
   g_return_if_fail (self->priv->ssl);
   g_return_if_fail (self->priv->bio);
@@ -467,6 +518,11 @@ gst_dtls_connection_stop (GstDtlsConnection * self)
   GST_TRACE_OBJECT (self, "locked @ stop");
 
   self->priv->is_alive = FALSE;
+  if (self->priv->connection_state != GST_DTLS_CONNECTION_STATE_FAILED
+      && self->priv->connection_state != GST_DTLS_CONNECTION_STATE_CLOSED) {
+    self->priv->connection_state = GST_DTLS_CONNECTION_STATE_CLOSED;
+    notify_state = TRUE;
+  }
   GST_TRACE_OBJECT (self, "signaling @ stop");
   g_cond_signal (&self->priv->condition);
   GST_TRACE_OBJECT (self, "signaled @ stop");
@@ -475,11 +531,18 @@ gst_dtls_connection_stop (GstDtlsConnection * self)
   g_mutex_unlock (&self->priv->mutex);
 
   GST_DEBUG_OBJECT (self, "stopped connection");
+
+  if (notify_state) {
+    g_object_notify_by_pspec (G_OBJECT (self),
+        properties[PROP_CONNECTION_STATE]);
+  }
 }
 
 void
 gst_dtls_connection_close (GstDtlsConnection * self)
 {
+  gboolean notify_state = FALSE;
+
   g_return_if_fail (GST_IS_DTLS_CONNECTION (self));
   g_return_if_fail (self->priv->ssl);
   g_return_if_fail (self->priv->bio);
@@ -495,10 +558,21 @@ gst_dtls_connection_close (GstDtlsConnection * self)
     g_cond_signal (&self->priv->condition);
   }
 
+  if (self->priv->connection_state != GST_DTLS_CONNECTION_STATE_FAILED
+      && self->priv->connection_state != GST_DTLS_CONNECTION_STATE_CLOSED) {
+    self->priv->connection_state = GST_DTLS_CONNECTION_STATE_CLOSED;
+    notify_state = TRUE;
+  }
+
   GST_TRACE_OBJECT (self, "unlocking @ close");
   g_mutex_unlock (&self->priv->mutex);
 
   GST_DEBUG_OBJECT (self, "closed connection");
+
+  if (notify_state) {
+    g_object_notify_by_pspec (G_OBJECT (self),
+        properties[PROP_CONNECTION_STATE]);
+  }
 }
 
 void
@@ -533,6 +607,7 @@ gst_dtls_connection_process (GstDtlsConnection * self, gpointer data, gsize len,
   GstFlowReturn flow_ret = GST_FLOW_OK;
   GstDtlsConnectionPrivate *priv;
   int ret;
+  gboolean notify_state = FALSE;
 
   g_return_val_if_fail (GST_IS_DTLS_CONNECTION (self), 0);
   g_return_val_if_fail (self->priv->ssl, 0);
@@ -552,7 +627,7 @@ gst_dtls_connection_process (GstDtlsConnection * self, gpointer data, gsize len,
     return GST_FLOW_EOS;
   }
 
-  if (self->priv->fatal_error) {
+  if (self->priv->connection_state == GST_DTLS_CONNECTION_STATE_FAILED) {
     GST_ERROR_OBJECT (self, "Had a fatal error before");
     g_mutex_unlock (&priv->mutex);
     if (err)
@@ -569,7 +644,7 @@ gst_dtls_connection_process (GstDtlsConnection * self, gpointer data, gsize len,
   log_state (self, "process start");
 
   if (SSL_want_write (priv->ssl)) {
-    flow_ret = openssl_poll (self, err);
+    flow_ret = openssl_poll (self, &notify_state, err);
     log_state (self, "process want write, after poll");
     if (flow_ret != GST_FLOW_OK) {
       g_mutex_unlock (&priv->mutex);
@@ -577,34 +652,66 @@ gst_dtls_connection_process (GstDtlsConnection * self, gpointer data, gsize len,
     }
   }
 
+  /* If we're a server and were in new state then by receiving the first data
+   * we would start the connection process */
+  if (!priv->is_client) {
+    if (self->priv->connection_state == GST_DTLS_CONNECTION_STATE_NEW) {
+      priv->connection_state = GST_DTLS_CONNECTION_STATE_CONNECTING;
+      notify_state = TRUE;
+    }
+  }
+
   ret = SSL_read (priv->ssl, data, len);
   *written = ret >= 0 ? ret : 0;
   GST_DEBUG_OBJECT (self, "read result: %d", ret);
 
-  flow_ret = handle_error (self, ret, GST_RESOURCE_ERROR_READ, err);
+  flow_ret =
+      handle_error (self, ret, GST_RESOURCE_ERROR_READ, &notify_state, err);
   if (flow_ret == GST_FLOW_EOS) {
     self->priv->received_close_notify = TRUE;
+    if (self->priv->connection_state != GST_DTLS_CONNECTION_STATE_FAILED
+        && self->priv->connection_state != GST_DTLS_CONNECTION_STATE_CLOSED) {
+      self->priv->connection_state = GST_DTLS_CONNECTION_STATE_CLOSED;
+      notify_state = TRUE;
+    }
     /* Notify about the connection being properly closed now if both
      * sides did so */
     if (self->priv->sent_close_notify && self->priv->send_callback)
       self->priv->send_callback (self, NULL, 0, NULL);
 
     g_mutex_unlock (&priv->mutex);
+
+    if (notify_state) {
+      g_object_notify_by_pspec (G_OBJECT (self),
+          properties[PROP_CONNECTION_STATE]);
+    }
+
     return flow_ret;
   } else if (flow_ret != GST_FLOW_OK) {
     g_mutex_unlock (&priv->mutex);
+
+    if (notify_state) {
+      g_object_notify_by_pspec (G_OBJECT (self),
+          properties[PROP_CONNECTION_STATE]);
+    }
+
     return flow_ret;
   }
 
   log_state (self, "process after read");
 
-  flow_ret = openssl_poll (self, err);
+  flow_ret = openssl_poll (self, &notify_state, err);
 
   log_state (self, "process after poll");
 
   GST_TRACE_OBJECT (self, "unlocking @ process");
   g_mutex_unlock (&priv->mutex);
 
+  if (notify_state) {
+    g_object_notify_by_pspec (G_OBJECT (self),
+        properties[PROP_CONNECTION_STATE]);
+  }
+
   return flow_ret;
 }
 
@@ -614,6 +721,7 @@ gst_dtls_connection_send (GstDtlsConnection * self, gconstpointer data,
 {
   GstFlowReturn flow_ret;
   int ret = 0;
+  gboolean notify_state = FALSE;
 
   g_return_val_if_fail (GST_IS_DTLS_CONNECTION (self), 0);
 
@@ -624,7 +732,7 @@ gst_dtls_connection_send (GstDtlsConnection * self, gconstpointer data,
   g_mutex_lock (&self->priv->mutex);
   GST_TRACE_OBJECT (self, "locked @ send");
 
-  if (self->priv->fatal_error) {
+  if (self->priv->connection_state == GST_DTLS_CONNECTION_STATE_FAILED) {
     GST_ERROR_OBJECT (self, "Had a fatal error before");
     g_mutex_unlock (&self->priv->mutex);
     if (err)
@@ -644,7 +752,11 @@ gst_dtls_connection_send (GstDtlsConnection * self, gconstpointer data,
       *written = 0;
     GST_DEBUG_OBJECT (self, "Sending close_notify");
     ret = SSL_shutdown (self->priv->ssl);
-    self->priv->sent_close_notify = TRUE;
+    if (self->priv->connection_state != GST_DTLS_CONNECTION_STATE_CLOSED &&
+        self->priv->connection_state != GST_DTLS_CONNECTION_STATE_FAILED) {
+      self->priv->connection_state = GST_DTLS_CONNECTION_STATE_CLOSED;
+      notify_state = TRUE;
+    }
     if (ret == 1) {
       GST_LOG_OBJECT (self, "received peer close_notify already");
       self->priv->received_close_notify = TRUE;
@@ -653,7 +765,9 @@ gst_dtls_connection_send (GstDtlsConnection * self, gconstpointer data,
       GST_LOG_OBJECT (self, "did not receive peer close_notify yet");
       flow_ret = GST_FLOW_OK;
     } else {
-      flow_ret = handle_error (self, ret, GST_RESOURCE_ERROR_WRITE, err);
+      flow_ret =
+          handle_error (self, ret, GST_RESOURCE_ERROR_WRITE, &notify_state,
+          err);
     }
   } else if (SSL_is_init_finished (self->priv->ssl)) {
     GST_DEBUG_OBJECT (self, "sending data of %" G_GSIZE_FORMAT " B", len);
@@ -661,7 +775,9 @@ gst_dtls_connection_send (GstDtlsConnection * self, gconstpointer data,
     if (ret <= 0) {
       if (written)
         *written = 0;
-      flow_ret = handle_error (self, ret, GST_RESOURCE_ERROR_WRITE, err);
+      flow_ret =
+          handle_error (self, ret, GST_RESOURCE_ERROR_WRITE, &notify_state,
+          err);
     } else {
       if (written)
         *written = ret;
@@ -682,6 +798,11 @@ gst_dtls_connection_send (GstDtlsConnection * self, gconstpointer data,
   GST_TRACE_OBJECT (self, "unlocking @ send");
   g_mutex_unlock (&self->priv->mutex);
 
+  if (notify_state) {
+    g_object_notify_by_pspec (G_OBJECT (self),
+        properties[PROP_CONNECTION_STATE]);
+  }
+
   return flow_ret;
 }
 
@@ -829,7 +950,7 @@ ssl_err_cb (const char *str, size_t len, void *u)
 
 static GstFlowReturn
 handle_error (GstDtlsConnection * self, int ret, GstResourceError error_type,
-    GError ** err)
+    gboolean * notify_state, GError ** err)
 {
   int error;
 
@@ -841,7 +962,10 @@ handle_error (GstDtlsConnection * self, int ret, GstResourceError error_type,
       return GST_FLOW_OK;
     case SSL_ERROR_SSL:
       GST_ERROR_OBJECT (self, "Fatal SSL error");
-      self->priv->fatal_error = TRUE;
+      if (self->priv->connection_state != GST_DTLS_CONNECTION_STATE_FAILED) {
+        self->priv->connection_state = GST_DTLS_CONNECTION_STATE_FAILED;
+        *notify_state = TRUE;
+      }
       ERR_print_errors_cb (ssl_err_cb, self);
       if (err)
         *err =
@@ -879,12 +1003,18 @@ handle_error (GstDtlsConnection * self, int ret, GstResourceError error_type,
           *err =
               g_error_new (GST_RESOURCE_ERROR, error_type,
               "Fatal SSL syscall error: errno %d: %s", syserror, message);
-        self->priv->fatal_error = TRUE;
+        if (self->priv->connection_state != GST_DTLS_CONNECTION_STATE_FAILED) {
+          self->priv->connection_state = GST_DTLS_CONNECTION_STATE_FAILED;
+          *notify_state = TRUE;
+        }
         return GST_FLOW_ERROR;
       }
     }
     default:
-      self->priv->fatal_error = TRUE;
+      if (self->priv->connection_state != GST_DTLS_CONNECTION_STATE_FAILED) {
+        self->priv->connection_state = GST_DTLS_CONNECTION_STATE_FAILED;
+        *notify_state = TRUE;
+      }
       GST_ERROR_OBJECT (self, "Unknown SSL error: %d, ret: %d", error, ret);
       if (err)
         *err =
@@ -895,7 +1025,7 @@ handle_error (GstDtlsConnection * self, int ret, GstResourceError error_type,
 }
 
 static GstFlowReturn
-openssl_poll (GstDtlsConnection * self, GError ** err)
+openssl_poll (GstDtlsConnection * self, gboolean * notify_state, GError ** err)
 {
   int ret;
   GstFlowReturn flow_ret;
@@ -913,6 +1043,13 @@ openssl_poll (GstDtlsConnection * self, GError ** err)
         GST_INFO_OBJECT (self,
             "handshake just completed successfully, exporting keys");
         export_srtp_keys (self);
+        if (self->priv->connection_state != GST_DTLS_CONNECTION_STATE_FAILED
+            && self->priv->connection_state != GST_DTLS_CONNECTION_STATE_CLOSED
+            && self->priv->connection_state !=
+            GST_DTLS_CONNECTION_STATE_CONNECTED) {
+          self->priv->connection_state = GST_DTLS_CONNECTION_STATE_CONNECTED;
+          *notify_state = TRUE;
+        }
       } else {
         GST_INFO_OBJECT (self, "handshake is completed");
       }
@@ -928,7 +1065,9 @@ openssl_poll (GstDtlsConnection * self, GError ** err)
       break;
   }
 
-  flow_ret = handle_error (self, ret, GST_RESOURCE_ERROR_OPEN_WRITE, err);
+  flow_ret =
+      handle_error (self, ret, GST_RESOURCE_ERROR_OPEN_WRITE, notify_state,
+      err);
 
   ERR_print_errors_cb (ssl_warn_cb, self);
 
@@ -1161,3 +1300,24 @@ bio_method_free (BIO * bio)
   GST_LOG_OBJECT (GST_DTLS_CONNECTION (BIO_get_data (bio)), "BIO free");
   return 0;
 }
+
+GType
+gst_dtls_connection_state_get_type (void)
+{
+  static GType type = 0;
+  static const GEnumValue values[] = {
+    {GST_DTLS_CONNECTION_STATE_NEW, "New connection", "new"},
+    {GST_DTLS_CONNECTION_STATE_CLOSED, "Closed connection on either side",
+        "closed"},
+    {GST_DTLS_CONNECTION_STATE_FAILED, "Failed connection", "failed"},
+    {GST_DTLS_CONNECTION_STATE_CONNECTING, "Connecting", "connecting"},
+    {GST_DTLS_CONNECTION_STATE_CONNECTED, "Successfully connected",
+        "connected"},
+    {0, NULL, NULL},
+  };
+
+  if (!type) {
+    type = g_enum_register_static ("GstDtlsConnectionState", values);
+  }
+  return type;
+}
index 6315ef1..b590486 100644 (file)
@@ -65,6 +65,18 @@ typedef enum {
 
 #define GST_DTLS_SRTP_MASTER_KEY_LENGTH 30
 
+typedef enum
+{
+  GST_DTLS_CONNECTION_STATE_NEW,
+  GST_DTLS_CONNECTION_STATE_CLOSED,
+  GST_DTLS_CONNECTION_STATE_FAILED,
+  GST_DTLS_CONNECTION_STATE_CONNECTING,
+  GST_DTLS_CONNECTION_STATE_CONNECTED,
+} GstDtlsConnectionState;
+
+GType gst_dtls_connection_state_get_type (void);
+#define GST_DTLS_TYPE_CONNECTION_STATE (gst_dtls_connection_state_get_type ())
+
 /*
  * GstDtlsConnection:
  *
index bba0035..7b370e2 100644 (file)
@@ -63,10 +63,10 @@ enum
   PROP_CONNECTION_ID,
   PROP_PEM,
   PROP_PEER_PEM,
-
   PROP_DECODER_KEY,
   PROP_SRTP_CIPHER,
   PROP_SRTP_AUTH,
+  PROP_CONNECTION_STATE,
   NUM_PROPERTIES
 };
 
@@ -171,6 +171,13 @@ gst_dtls_dec_class_init (GstDtlsDecClass * klass)
       0, GST_DTLS_SRTP_AUTH_HMAC_SHA1_80, DEFAULT_SRTP_AUTH,
       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
 
+  properties[PROP_CONNECTION_STATE] =
+      g_param_spec_enum ("connection-state",
+      "Connection State",
+      "Current connection state",
+      GST_DTLS_TYPE_CONNECTION_STATE,
+      GST_DTLS_CONNECTION_STATE_NEW, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
   g_object_class_install_properties (gobject_class, NUM_PROPERTIES, properties);
 
   gst_element_class_add_static_pad_template (element_class, &src_template);
@@ -301,6 +308,13 @@ gst_dtls_dec_get_property (GObject * object, guint prop_id, GValue * value,
     case PROP_SRTP_AUTH:
       g_value_set_uint (value, self->srtp_auth);
       break;
+    case PROP_CONNECTION_STATE:
+      if (self->connection)
+        g_object_get_property (G_OBJECT (self->connection), "connection-state",
+            value);
+      else
+        g_value_set_enum (value, GST_DTLS_CONNECTION_STATE_CLOSED);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec);
   }
@@ -744,12 +758,23 @@ gst_dtls_dec_fetch_connection (gchar * id)
 }
 
 static void
+on_connection_state_changed (GObject * object, GParamSpec * pspec,
+    gpointer user_data)
+{
+  GstDtlsDec *self = GST_DTLS_DEC (user_data);
+
+  g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_CONNECTION_STATE]);
+}
+
+static void
 create_connection (GstDtlsDec * self, gchar * id)
 {
   g_return_if_fail (GST_IS_DTLS_DEC (self));
   g_return_if_fail (GST_IS_DTLS_AGENT (self->agent));
 
   if (self->connection) {
+    g_signal_handlers_disconnect_by_func (self->connection,
+        on_connection_state_changed, self);
     g_object_unref (self->connection);
     self->connection = NULL;
   }
@@ -769,6 +794,10 @@ create_connection (GstDtlsDec * self, gchar * id)
 
   self->connection =
       g_object_new (GST_TYPE_DTLS_CONNECTION, "agent", self->agent, NULL);
+  g_signal_connect_object (self->connection,
+      "notify::connection-state", G_CALLBACK (on_connection_state_changed),
+      self, 0);
+  on_connection_state_changed (NULL, NULL, self);
 
   g_object_weak_ref (G_OBJECT (self->connection),
       (GWeakNotify) connection_weak_ref_notify, g_strdup (id));
index fd59722..e64ee4d 100644 (file)
@@ -62,10 +62,10 @@ enum
   PROP_0,
   PROP_CONNECTION_ID,
   PROP_IS_CLIENT,
-
   PROP_ENCODER_KEY,
   PROP_SRTP_CIPHER,
   PROP_SRTP_AUTH,
+  PROP_CONNECTION_STATE,
   NUM_PROPERTIES
 };
 
@@ -160,6 +160,13 @@ gst_dtls_enc_class_init (GstDtlsEncClass * klass)
       0, GST_DTLS_SRTP_AUTH_HMAC_SHA1_80, DEFAULT_SRTP_AUTH,
       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
 
+  properties[PROP_CONNECTION_STATE] =
+      g_param_spec_enum ("connection-state",
+      "Connection State",
+      "Current connection state",
+      GST_DTLS_TYPE_CONNECTION_STATE,
+      GST_DTLS_CONNECTION_STATE_NEW, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
   g_object_class_install_properties (gobject_class, NUM_PROPERTIES, properties);
 
   gst_element_class_add_static_pad_template (element_class, &src_template);
@@ -269,11 +276,27 @@ gst_dtls_enc_get_property (GObject * object, guint prop_id, GValue * value,
     case PROP_SRTP_AUTH:
       g_value_set_uint (value, self->srtp_auth);
       break;
+    case PROP_CONNECTION_STATE:
+      if (self->connection)
+        g_object_get_property (G_OBJECT (self->connection), "connection-state",
+            value);
+      else
+        g_value_set_enum (value, GST_DTLS_CONNECTION_STATE_CLOSED);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec);
   }
 }
 
+static void
+on_connection_state_changed (GObject * object, GParamSpec * pspec,
+    gpointer user_data)
+{
+  GstDtlsEnc *self = GST_DTLS_ENC (user_data);
+
+  g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_CONNECTION_STATE]);
+}
+
 static GstStateChangeReturn
 gst_dtls_enc_change_state (GstElement * element, GstStateChange transition)
 {
@@ -294,6 +317,10 @@ gst_dtls_enc_change_state (GstElement * element, GstStateChange transition)
 
         g_signal_connect_object (self->connection,
             "on-encoder-key", G_CALLBACK (on_key_received), self, 0);
+        g_signal_connect_object (self->connection,
+            "notify::connection-state",
+            G_CALLBACK (on_connection_state_changed), self, 0);
+        on_connection_state_changed (NULL, NULL, self);
 
         gst_dtls_connection_set_send_callback (self->connection,
             (GstDtlsConnectionSendCallback) on_send_data, self, NULL);
index 06e6ddd..72abfda 100644 (file)
@@ -28,7 +28,6 @@
 #endif
 
 #include "gstdtlssrtpdec.h"
-
 #include "gstdtlsconnection.h"
 
 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
@@ -69,6 +68,7 @@ enum
   PROP_0,
   PROP_PEM,
   PROP_PEER_PEM,
+  PROP_CONNECTION_STATE,
   NUM_PROPERTIES
 };
 
@@ -132,6 +132,13 @@ gst_dtls_srtp_dec_class_init (GstDtlsSrtpDecClass * klass)
       "The X509 certificate received in the DTLS handshake, in PEM format",
       DEFAULT_PEER_PEM, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
 
+  properties[PROP_CONNECTION_STATE] =
+      g_param_spec_enum ("connection-state",
+      "Connection State",
+      "Current connection state",
+      GST_DTLS_TYPE_CONNECTION_STATE,
+      GST_DTLS_CONNECTION_STATE_NEW, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
   g_object_class_install_properties (gobject_class, NUM_PROPERTIES, properties);
 
   gst_element_class_add_static_pad_template (element_class, &sink_template);
@@ -147,6 +154,15 @@ gst_dtls_srtp_dec_class_init (GstDtlsSrtpDecClass * klass)
 }
 
 static void
+on_connection_state_changed (GObject * object, GParamSpec * pspec,
+    gpointer user_data)
+{
+  GstDtlsSrtpDec *self = GST_DTLS_SRTP_DEC (user_data);
+
+  g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_CONNECTION_STATE]);
+}
+
+static void
 gst_dtls_srtp_dec_init (GstDtlsSrtpDec * self)
 {
   GstElementClass *klass = GST_ELEMENT_GET_CLASS (GST_ELEMENT (self));
@@ -226,6 +242,8 @@ gst_dtls_srtp_dec_init (GstDtlsSrtpDec * self)
       G_CALLBACK (on_decoder_request_key), self);
   g_signal_connect (self->bin.dtls_element, "notify::peer-pem",
       G_CALLBACK (on_peer_pem), self);
+  g_signal_connect (self->bin.dtls_element, "notify::connection-state",
+      G_CALLBACK (on_connection_state_changed), self);
 }
 
 static void
@@ -269,6 +287,10 @@ gst_dtls_srtp_dec_get_property (GObject * object,
         GST_WARNING_OBJECT (self, "tried to get peer-pem after disabling DTLS");
       }
       break;
+    case PROP_CONNECTION_STATE:
+      g_object_get_property (G_OBJECT (self->bin.dtls_element),
+          "connection-state", value);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec);
   }
index 5a2db43..d993549 100644 (file)
@@ -28,6 +28,7 @@
 #endif
 
 #include "gstdtlssrtpenc.h"
+#include "gstdtlsconnection.h"
 
 #include <stdio.h>
 
@@ -76,6 +77,7 @@ enum
 {
   PROP_0,
   PROP_IS_CLIENT,
+  PROP_CONNECTION_STATE,
   NUM_PROPERTIES
 };
 
@@ -136,6 +138,13 @@ gst_dtls_srtp_enc_class_init (GstDtlsSrtpEncClass * klass)
       DEFAULT_IS_CLIENT,
       GST_PARAM_MUTABLE_READY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
 
+  properties[PROP_CONNECTION_STATE] =
+      g_param_spec_enum ("connection-state",
+      "Connection State",
+      "Current connection state",
+      GST_DTLS_TYPE_CONNECTION_STATE,
+      GST_DTLS_CONNECTION_STATE_NEW, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
   g_object_class_install_properties (gobject_class, NUM_PROPERTIES, properties);
 
   gst_element_class_add_static_pad_template (element_class, &rtp_sink_template);
@@ -153,6 +162,15 @@ gst_dtls_srtp_enc_class_init (GstDtlsSrtpEncClass * klass)
 }
 
 static void
+on_connection_state_changed (GObject * object, GParamSpec * pspec,
+    gpointer user_data)
+{
+  GstDtlsSrtpEnc *self = GST_DTLS_SRTP_ENC (user_data);
+
+  g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_CONNECTION_STATE]);
+}
+
+static void
 gst_dtls_srtp_enc_init (GstDtlsSrtpEnc * self)
 {
   GstElementClass *klass = GST_ELEMENT_GET_CLASS (GST_ELEMENT (self));
@@ -215,6 +233,9 @@ gst_dtls_srtp_enc_init (GstDtlsSrtpEnc * self)
 
   g_object_set (self->srtp_enc, "random-key", TRUE, NULL);
 
+  g_signal_connect (self->bin.dtls_element, "notify::connection-state",
+      G_CALLBACK (on_connection_state_changed), self);
+
   g_object_bind_property (G_OBJECT (self), "key", self->srtp_enc, "key",
       G_BINDING_DEFAULT);
   g_object_bind_property_full (G_OBJECT (self), "srtp-cipher", self->srtp_enc,
@@ -289,6 +310,10 @@ gst_dtls_srtp_enc_get_property (GObject * object,
             "tried to get is-client after disabling DTLS");
       }
       break;
+    case PROP_CONNECTION_STATE:
+      g_object_get_property (G_OBJECT (self->bin.dtls_element),
+          "connection-state", value);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec);
   }