dtls: Keep track of the connection state and signal it through all the layers

This allows the application to keep track of the underlying DTLS
connection state and act accordingly.
This commit is contained in:
Sebastian Dröge 2020-01-12 16:18:09 +02:00 committed by GStreamer Merge Bot
parent d66aa872ca
commit 7fcfb6c6c5
6 changed files with 298 additions and 23 deletions

View file

@ -69,6 +69,7 @@ enum
{ {
PROP_0, PROP_0,
PROP_AGENT, PROP_AGENT,
PROP_CONNECTION_STATE,
NUM_PROPERTIES NUM_PROPERTIES
}; };
@ -87,9 +88,9 @@ struct _GstDtlsConnectionPrivate
gboolean is_alive; gboolean is_alive;
gboolean keys_exported; gboolean keys_exported;
GstDtlsConnectionState connection_state;
gboolean sent_close_notify; gboolean sent_close_notify;
gboolean received_close_notify; gboolean received_close_notify;
gboolean fatal_error;
GMutex mutex; GMutex mutex;
GCond condition; 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_finalize (GObject * gobject);
static void gst_dtls_connection_set_property (GObject *, guint prop_id, static void gst_dtls_connection_set_property (GObject *, guint prop_id,
const GValue *, GParamSpec *); 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 log_state (GstDtlsConnection *, const gchar * str);
static void export_srtp_keys (GstDtlsConnection *); 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, 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, static int openssl_verify_callback (int preverify_ok,
X509_STORE_CTX * x509_ctx); X509_STORE_CTX * x509_ctx);
@ -135,6 +139,7 @@ gst_dtls_connection_class_init (GstDtlsConnectionClass * klass)
GObjectClass *gobject_class = G_OBJECT_CLASS (klass); GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gobject_class->set_property = gst_dtls_connection_set_property; gobject_class->set_property = gst_dtls_connection_set_property;
gobject_class->get_property = gst_dtls_connection_get_property;
connection_ex_index = connection_ex_index =
SSL_get_ex_new_index (0, (gpointer) "gstdtlsagent connection index", NULL, 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, GST_TYPE_DTLS_AGENT,
G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); 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); g_object_class_install_properties (gobject_class, NUM_PROPERTIES, properties);
_gst_dtls_init_openssl (); _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 gboolean
gst_dtls_connection_start (GstDtlsConnection * self, gboolean is_client, gst_dtls_connection_start (GstDtlsConnection * self, gboolean is_client,
GError ** err) GError ** err)
{ {
GstDtlsConnectionPrivate *priv; GstDtlsConnectionPrivate *priv;
gboolean ret; gboolean ret;
gboolean notify_state = FALSE;
priv = self->priv; priv = self->priv;
@ -313,19 +344,26 @@ gst_dtls_connection_start (GstDtlsConnection * self, gboolean is_client,
priv->bio_buffer_offset = 0; priv->bio_buffer_offset = 0;
priv->keys_exported = FALSE; priv->keys_exported = FALSE;
priv->fatal_error = FALSE;
priv->sent_close_notify = FALSE; priv->sent_close_notify = FALSE;
priv->received_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; priv->is_client = is_client;
if (priv->is_client) { if (priv->is_client) {
priv->connection_state = GST_DTLS_CONNECTION_STATE_CONNECTING;
notify_state = TRUE;
SSL_set_connect_state (priv->ssl); SSL_set_connect_state (priv->ssl);
} else { } 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); SSL_set_accept_state (priv->ssl);
} }
log_state (self, "initial state set"); log_state (self, "initial state set");
ret = openssl_poll (self, err); ret = openssl_poll (self, &notify_state, err);
if (ret == GST_FLOW_EOS && err) { if (ret == GST_FLOW_EOS && err) {
*err = *err =
g_error_new_literal (GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_OPEN_WRITE, 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"); GST_TRACE_OBJECT (self, "unlocking @ start");
g_mutex_unlock (&priv->mutex); 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; return ret == GST_FLOW_OK;
} }
@ -346,6 +389,7 @@ handle_timeout (gpointer data, gpointer user_data)
GstDtlsConnection *self = user_data; GstDtlsConnection *self = user_data;
GstDtlsConnectionPrivate *priv; GstDtlsConnectionPrivate *priv;
gint ret; gint ret;
gboolean notify_state = FALSE;
priv = self->priv; priv = self->priv;
@ -361,11 +405,16 @@ handle_timeout (gpointer data, gpointer user_data)
GST_WARNING_OBJECT (self, "handling timeout failed"); GST_WARNING_OBJECT (self, "handling timeout failed");
} else if (ret > 0) { } else if (ret > 0) {
log_state (self, "handling timeout before poll"); log_state (self, "handling timeout before poll");
openssl_poll (self, NULL); openssl_poll (self, &notify_state, NULL);
log_state (self, "handling timeout after poll"); log_state (self, "handling timeout after poll");
} }
} }
g_mutex_unlock (&priv->mutex); g_mutex_unlock (&priv->mutex);
if (notify_state) {
g_object_notify_by_pspec (G_OBJECT (self),
properties[PROP_CONNECTION_STATE]);
}
} }
static gboolean static gboolean
@ -456,6 +505,8 @@ gst_dtls_connection_check_timeout (GstDtlsConnection * self)
void void
gst_dtls_connection_stop (GstDtlsConnection * self) gst_dtls_connection_stop (GstDtlsConnection * self)
{ {
gboolean notify_state = FALSE;
g_return_if_fail (GST_IS_DTLS_CONNECTION (self)); g_return_if_fail (GST_IS_DTLS_CONNECTION (self));
g_return_if_fail (self->priv->ssl); g_return_if_fail (self->priv->ssl);
g_return_if_fail (self->priv->bio); g_return_if_fail (self->priv->bio);
@ -467,6 +518,11 @@ gst_dtls_connection_stop (GstDtlsConnection * self)
GST_TRACE_OBJECT (self, "locked @ stop"); GST_TRACE_OBJECT (self, "locked @ stop");
self->priv->is_alive = FALSE; 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"); GST_TRACE_OBJECT (self, "signaling @ stop");
g_cond_signal (&self->priv->condition); g_cond_signal (&self->priv->condition);
GST_TRACE_OBJECT (self, "signaled @ stop"); GST_TRACE_OBJECT (self, "signaled @ stop");
@ -475,11 +531,18 @@ gst_dtls_connection_stop (GstDtlsConnection * self)
g_mutex_unlock (&self->priv->mutex); g_mutex_unlock (&self->priv->mutex);
GST_DEBUG_OBJECT (self, "stopped connection"); GST_DEBUG_OBJECT (self, "stopped connection");
if (notify_state) {
g_object_notify_by_pspec (G_OBJECT (self),
properties[PROP_CONNECTION_STATE]);
}
} }
void void
gst_dtls_connection_close (GstDtlsConnection * self) gst_dtls_connection_close (GstDtlsConnection * self)
{ {
gboolean notify_state = FALSE;
g_return_if_fail (GST_IS_DTLS_CONNECTION (self)); g_return_if_fail (GST_IS_DTLS_CONNECTION (self));
g_return_if_fail (self->priv->ssl); g_return_if_fail (self->priv->ssl);
g_return_if_fail (self->priv->bio); g_return_if_fail (self->priv->bio);
@ -495,10 +558,21 @@ gst_dtls_connection_close (GstDtlsConnection * self)
g_cond_signal (&self->priv->condition); 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"); GST_TRACE_OBJECT (self, "unlocking @ close");
g_mutex_unlock (&self->priv->mutex); g_mutex_unlock (&self->priv->mutex);
GST_DEBUG_OBJECT (self, "closed connection"); GST_DEBUG_OBJECT (self, "closed connection");
if (notify_state) {
g_object_notify_by_pspec (G_OBJECT (self),
properties[PROP_CONNECTION_STATE]);
}
} }
void void
@ -533,6 +607,7 @@ gst_dtls_connection_process (GstDtlsConnection * self, gpointer data, gsize len,
GstFlowReturn flow_ret = GST_FLOW_OK; GstFlowReturn flow_ret = GST_FLOW_OK;
GstDtlsConnectionPrivate *priv; GstDtlsConnectionPrivate *priv;
int ret; int ret;
gboolean notify_state = FALSE;
g_return_val_if_fail (GST_IS_DTLS_CONNECTION (self), 0); g_return_val_if_fail (GST_IS_DTLS_CONNECTION (self), 0);
g_return_val_if_fail (self->priv->ssl, 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; 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"); GST_ERROR_OBJECT (self, "Had a fatal error before");
g_mutex_unlock (&priv->mutex); g_mutex_unlock (&priv->mutex);
if (err) if (err)
@ -569,7 +644,7 @@ gst_dtls_connection_process (GstDtlsConnection * self, gpointer data, gsize len,
log_state (self, "process start"); log_state (self, "process start");
if (SSL_want_write (priv->ssl)) { 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"); log_state (self, "process want write, after poll");
if (flow_ret != GST_FLOW_OK) { if (flow_ret != GST_FLOW_OK) {
g_mutex_unlock (&priv->mutex); 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); ret = SSL_read (priv->ssl, data, len);
*written = ret >= 0 ? ret : 0; *written = ret >= 0 ? ret : 0;
GST_DEBUG_OBJECT (self, "read result: %d", ret); 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) { if (flow_ret == GST_FLOW_EOS) {
self->priv->received_close_notify = TRUE; 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 /* Notify about the connection being properly closed now if both
* sides did so */ * sides did so */
if (self->priv->sent_close_notify && self->priv->send_callback) if (self->priv->sent_close_notify && self->priv->send_callback)
self->priv->send_callback (self, NULL, 0, NULL); self->priv->send_callback (self, NULL, 0, NULL);
g_mutex_unlock (&priv->mutex); g_mutex_unlock (&priv->mutex);
if (notify_state) {
g_object_notify_by_pspec (G_OBJECT (self),
properties[PROP_CONNECTION_STATE]);
}
return flow_ret; return flow_ret;
} else if (flow_ret != GST_FLOW_OK) { } else if (flow_ret != GST_FLOW_OK) {
g_mutex_unlock (&priv->mutex); g_mutex_unlock (&priv->mutex);
if (notify_state) {
g_object_notify_by_pspec (G_OBJECT (self),
properties[PROP_CONNECTION_STATE]);
}
return flow_ret; return flow_ret;
} }
log_state (self, "process after read"); 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"); log_state (self, "process after poll");
GST_TRACE_OBJECT (self, "unlocking @ process"); GST_TRACE_OBJECT (self, "unlocking @ process");
g_mutex_unlock (&priv->mutex); g_mutex_unlock (&priv->mutex);
if (notify_state) {
g_object_notify_by_pspec (G_OBJECT (self),
properties[PROP_CONNECTION_STATE]);
}
return flow_ret; return flow_ret;
} }
@ -614,6 +721,7 @@ gst_dtls_connection_send (GstDtlsConnection * self, gconstpointer data,
{ {
GstFlowReturn flow_ret; GstFlowReturn flow_ret;
int ret = 0; int ret = 0;
gboolean notify_state = FALSE;
g_return_val_if_fail (GST_IS_DTLS_CONNECTION (self), 0); 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); g_mutex_lock (&self->priv->mutex);
GST_TRACE_OBJECT (self, "locked @ send"); 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"); GST_ERROR_OBJECT (self, "Had a fatal error before");
g_mutex_unlock (&self->priv->mutex); g_mutex_unlock (&self->priv->mutex);
if (err) if (err)
@ -644,7 +752,11 @@ gst_dtls_connection_send (GstDtlsConnection * self, gconstpointer data,
*written = 0; *written = 0;
GST_DEBUG_OBJECT (self, "Sending close_notify"); GST_DEBUG_OBJECT (self, "Sending close_notify");
ret = SSL_shutdown (self->priv->ssl); 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) { if (ret == 1) {
GST_LOG_OBJECT (self, "received peer close_notify already"); GST_LOG_OBJECT (self, "received peer close_notify already");
self->priv->received_close_notify = TRUE; 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"); GST_LOG_OBJECT (self, "did not receive peer close_notify yet");
flow_ret = GST_FLOW_OK; flow_ret = GST_FLOW_OK;
} else { } 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)) { } else if (SSL_is_init_finished (self->priv->ssl)) {
GST_DEBUG_OBJECT (self, "sending data of %" G_GSIZE_FORMAT " B", len); 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 (ret <= 0) {
if (written) if (written)
*written = 0; *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 { } else {
if (written) if (written)
*written = ret; *written = ret;
@ -682,6 +798,11 @@ gst_dtls_connection_send (GstDtlsConnection * self, gconstpointer data,
GST_TRACE_OBJECT (self, "unlocking @ send"); GST_TRACE_OBJECT (self, "unlocking @ send");
g_mutex_unlock (&self->priv->mutex); g_mutex_unlock (&self->priv->mutex);
if (notify_state) {
g_object_notify_by_pspec (G_OBJECT (self),
properties[PROP_CONNECTION_STATE]);
}
return flow_ret; return flow_ret;
} }
@ -829,7 +950,7 @@ ssl_err_cb (const char *str, size_t len, void *u)
static GstFlowReturn static GstFlowReturn
handle_error (GstDtlsConnection * self, int ret, GstResourceError error_type, handle_error (GstDtlsConnection * self, int ret, GstResourceError error_type,
GError ** err) gboolean * notify_state, GError ** err)
{ {
int error; int error;
@ -841,7 +962,10 @@ handle_error (GstDtlsConnection * self, int ret, GstResourceError error_type,
return GST_FLOW_OK; return GST_FLOW_OK;
case SSL_ERROR_SSL: case SSL_ERROR_SSL:
GST_ERROR_OBJECT (self, "Fatal SSL error"); 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); ERR_print_errors_cb (ssl_err_cb, self);
if (err) if (err)
*err = *err =
@ -879,12 +1003,18 @@ handle_error (GstDtlsConnection * self, int ret, GstResourceError error_type,
*err = *err =
g_error_new (GST_RESOURCE_ERROR, error_type, g_error_new (GST_RESOURCE_ERROR, error_type,
"Fatal SSL syscall error: errno %d: %s", syserror, message); "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; return GST_FLOW_ERROR;
} }
} }
default: 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); GST_ERROR_OBJECT (self, "Unknown SSL error: %d, ret: %d", error, ret);
if (err) if (err)
*err = *err =
@ -895,7 +1025,7 @@ handle_error (GstDtlsConnection * self, int ret, GstResourceError error_type,
} }
static GstFlowReturn static GstFlowReturn
openssl_poll (GstDtlsConnection * self, GError ** err) openssl_poll (GstDtlsConnection * self, gboolean * notify_state, GError ** err)
{ {
int ret; int ret;
GstFlowReturn flow_ret; GstFlowReturn flow_ret;
@ -913,6 +1043,13 @@ openssl_poll (GstDtlsConnection * self, GError ** err)
GST_INFO_OBJECT (self, GST_INFO_OBJECT (self,
"handshake just completed successfully, exporting keys"); "handshake just completed successfully, exporting keys");
export_srtp_keys (self); 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 { } else {
GST_INFO_OBJECT (self, "handshake is completed"); GST_INFO_OBJECT (self, "handshake is completed");
} }
@ -928,7 +1065,9 @@ openssl_poll (GstDtlsConnection * self, GError ** err)
break; 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); 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"); GST_LOG_OBJECT (GST_DTLS_CONNECTION (BIO_get_data (bio)), "BIO free");
return 0; 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;
}

View file

@ -65,6 +65,18 @@ typedef enum {
#define GST_DTLS_SRTP_MASTER_KEY_LENGTH 30 #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: * GstDtlsConnection:
* *

View file

@ -63,10 +63,10 @@ enum
PROP_CONNECTION_ID, PROP_CONNECTION_ID,
PROP_PEM, PROP_PEM,
PROP_PEER_PEM, PROP_PEER_PEM,
PROP_DECODER_KEY, PROP_DECODER_KEY,
PROP_SRTP_CIPHER, PROP_SRTP_CIPHER,
PROP_SRTP_AUTH, PROP_SRTP_AUTH,
PROP_CONNECTION_STATE,
NUM_PROPERTIES NUM_PROPERTIES
}; };
@ -171,6 +171,13 @@ gst_dtls_dec_class_init (GstDtlsDecClass * klass)
0, GST_DTLS_SRTP_AUTH_HMAC_SHA1_80, DEFAULT_SRTP_AUTH, 0, GST_DTLS_SRTP_AUTH_HMAC_SHA1_80, DEFAULT_SRTP_AUTH,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); 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); g_object_class_install_properties (gobject_class, NUM_PROPERTIES, properties);
gst_element_class_add_static_pad_template (element_class, &src_template); 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: case PROP_SRTP_AUTH:
g_value_set_uint (value, self->srtp_auth); g_value_set_uint (value, self->srtp_auth);
break; 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: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec);
} }
@ -743,6 +757,15 @@ gst_dtls_dec_fetch_connection (gchar * id)
return connection; return connection;
} }
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 static void
create_connection (GstDtlsDec * self, gchar * id) create_connection (GstDtlsDec * self, gchar * id)
{ {
@ -750,6 +773,8 @@ create_connection (GstDtlsDec * self, gchar * id)
g_return_if_fail (GST_IS_DTLS_AGENT (self->agent)); g_return_if_fail (GST_IS_DTLS_AGENT (self->agent));
if (self->connection) { if (self->connection) {
g_signal_handlers_disconnect_by_func (self->connection,
on_connection_state_changed, self);
g_object_unref (self->connection); g_object_unref (self->connection);
self->connection = NULL; self->connection = NULL;
} }
@ -769,6 +794,10 @@ create_connection (GstDtlsDec * self, gchar * id)
self->connection = self->connection =
g_object_new (GST_TYPE_DTLS_CONNECTION, "agent", self->agent, NULL); 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), g_object_weak_ref (G_OBJECT (self->connection),
(GWeakNotify) connection_weak_ref_notify, g_strdup (id)); (GWeakNotify) connection_weak_ref_notify, g_strdup (id));

View file

@ -62,10 +62,10 @@ enum
PROP_0, PROP_0,
PROP_CONNECTION_ID, PROP_CONNECTION_ID,
PROP_IS_CLIENT, PROP_IS_CLIENT,
PROP_ENCODER_KEY, PROP_ENCODER_KEY,
PROP_SRTP_CIPHER, PROP_SRTP_CIPHER,
PROP_SRTP_AUTH, PROP_SRTP_AUTH,
PROP_CONNECTION_STATE,
NUM_PROPERTIES NUM_PROPERTIES
}; };
@ -160,6 +160,13 @@ gst_dtls_enc_class_init (GstDtlsEncClass * klass)
0, GST_DTLS_SRTP_AUTH_HMAC_SHA1_80, DEFAULT_SRTP_AUTH, 0, GST_DTLS_SRTP_AUTH_HMAC_SHA1_80, DEFAULT_SRTP_AUTH,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); 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); g_object_class_install_properties (gobject_class, NUM_PROPERTIES, properties);
gst_element_class_add_static_pad_template (element_class, &src_template); 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: case PROP_SRTP_AUTH:
g_value_set_uint (value, self->srtp_auth); g_value_set_uint (value, self->srtp_auth);
break; 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: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec); 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 static GstStateChangeReturn
gst_dtls_enc_change_state (GstElement * element, GstStateChange transition) 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, g_signal_connect_object (self->connection,
"on-encoder-key", G_CALLBACK (on_key_received), self, 0); "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, gst_dtls_connection_set_send_callback (self->connection,
(GstDtlsConnectionSendCallback) on_send_data, self, NULL); (GstDtlsConnectionSendCallback) on_send_data, self, NULL);

View file

@ -28,7 +28,6 @@
#endif #endif
#include "gstdtlssrtpdec.h" #include "gstdtlssrtpdec.h"
#include "gstdtlsconnection.h" #include "gstdtlsconnection.h"
static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink", static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
@ -69,6 +68,7 @@ enum
PROP_0, PROP_0,
PROP_PEM, PROP_PEM,
PROP_PEER_PEM, PROP_PEER_PEM,
PROP_CONNECTION_STATE,
NUM_PROPERTIES 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", "The X509 certificate received in the DTLS handshake, in PEM format",
DEFAULT_PEER_PEM, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); 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); g_object_class_install_properties (gobject_class, NUM_PROPERTIES, properties);
gst_element_class_add_static_pad_template (element_class, &sink_template); gst_element_class_add_static_pad_template (element_class, &sink_template);
@ -146,6 +153,15 @@ gst_dtls_srtp_dec_class_init (GstDtlsSrtpDecClass * klass)
"Patrik Oldsberg patrik.oldsberg@ericsson.com"); "Patrik Oldsberg patrik.oldsberg@ericsson.com");
} }
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 static void
gst_dtls_srtp_dec_init (GstDtlsSrtpDec * self) gst_dtls_srtp_dec_init (GstDtlsSrtpDec * self)
{ {
@ -226,6 +242,8 @@ gst_dtls_srtp_dec_init (GstDtlsSrtpDec * self)
G_CALLBACK (on_decoder_request_key), self); G_CALLBACK (on_decoder_request_key), self);
g_signal_connect (self->bin.dtls_element, "notify::peer-pem", g_signal_connect (self->bin.dtls_element, "notify::peer-pem",
G_CALLBACK (on_peer_pem), self); 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 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"); GST_WARNING_OBJECT (self, "tried to get peer-pem after disabling DTLS");
} }
break; break;
case PROP_CONNECTION_STATE:
g_object_get_property (G_OBJECT (self->bin.dtls_element),
"connection-state", value);
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec);
} }

View file

@ -28,6 +28,7 @@
#endif #endif
#include "gstdtlssrtpenc.h" #include "gstdtlssrtpenc.h"
#include "gstdtlsconnection.h"
#include <stdio.h> #include <stdio.h>
@ -76,6 +77,7 @@ enum
{ {
PROP_0, PROP_0,
PROP_IS_CLIENT, PROP_IS_CLIENT,
PROP_CONNECTION_STATE,
NUM_PROPERTIES NUM_PROPERTIES
}; };
@ -136,6 +138,13 @@ gst_dtls_srtp_enc_class_init (GstDtlsSrtpEncClass * klass)
DEFAULT_IS_CLIENT, DEFAULT_IS_CLIENT,
GST_PARAM_MUTABLE_READY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); 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); g_object_class_install_properties (gobject_class, NUM_PROPERTIES, properties);
gst_element_class_add_static_pad_template (element_class, &rtp_sink_template); gst_element_class_add_static_pad_template (element_class, &rtp_sink_template);
@ -152,6 +161,15 @@ gst_dtls_srtp_enc_class_init (GstDtlsSrtpEncClass * klass)
"Patrik Oldsberg patrik.oldsberg@ericsson.com"); "Patrik Oldsberg patrik.oldsberg@ericsson.com");
} }
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 static void
gst_dtls_srtp_enc_init (GstDtlsSrtpEnc * self) gst_dtls_srtp_enc_init (GstDtlsSrtpEnc * self)
{ {
@ -215,6 +233,9 @@ gst_dtls_srtp_enc_init (GstDtlsSrtpEnc * self)
g_object_set (self->srtp_enc, "random-key", TRUE, NULL); 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_object_bind_property (G_OBJECT (self), "key", self->srtp_enc, "key",
G_BINDING_DEFAULT); G_BINDING_DEFAULT);
g_object_bind_property_full (G_OBJECT (self), "srtp-cipher", self->srtp_enc, 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"); "tried to get is-client after disabling DTLS");
} }
break; break;
case PROP_CONNECTION_STATE:
g_object_get_property (G_OBJECT (self->bin.dtls_element),
"connection-state", value);
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec);
} }