mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-07-29 09:45:07 +00:00
608 lines
18 KiB
C
608 lines
18 KiB
C
|
/*
|
||
|
* Copyright (c) 2014, Ericsson AB. All rights reserved.
|
||
|
*
|
||
|
* Redistribution and use in source and binary forms, with or without modification,
|
||
|
* are permitted provided that the following conditions are met:
|
||
|
*
|
||
|
* 1. Redistributions of source code must retain the above copyright notice, this
|
||
|
* list of conditions and the following disclaimer.
|
||
|
*
|
||
|
* 2. Redistributions in binary form must reproduce the above copyright notice, this
|
||
|
* list of conditions and the following disclaimer in the documentation and/or other
|
||
|
* materials provided with the distribution.
|
||
|
*
|
||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||
|
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||
|
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||
|
* OF SUCH DAMAGE.
|
||
|
*/
|
||
|
|
||
|
#ifdef HAVE_CONFIG_H
|
||
|
#include "config.h"
|
||
|
#endif
|
||
|
|
||
|
#include "gstdtlsdec.h"
|
||
|
|
||
|
#include "gstdtlscertificate.h"
|
||
|
|
||
|
static GstStaticPadTemplate sink_template =
|
||
|
GST_STATIC_PAD_TEMPLATE("sink",
|
||
|
GST_PAD_SINK,
|
||
|
GST_PAD_ALWAYS,
|
||
|
GST_STATIC_CAPS("application/x-dtls")
|
||
|
);
|
||
|
|
||
|
static GstStaticPadTemplate src_template =
|
||
|
GST_STATIC_PAD_TEMPLATE("src",
|
||
|
GST_PAD_SRC,
|
||
|
GST_PAD_REQUEST,
|
||
|
GST_STATIC_CAPS_ANY
|
||
|
);
|
||
|
|
||
|
GST_DEBUG_CATEGORY_STATIC(er_dtls_dec_debug);
|
||
|
#define GST_CAT_DEFAULT er_dtls_dec_debug
|
||
|
|
||
|
#define gst_er_dtls_dec_parent_class parent_class
|
||
|
G_DEFINE_TYPE_WITH_CODE(GstErDtlsDec, gst_er_dtls_dec, GST_TYPE_ELEMENT,
|
||
|
GST_DEBUG_CATEGORY_INIT(er_dtls_dec_debug, "erdtlsdec", 0, "Ericsson DTLS Decoder"));
|
||
|
|
||
|
#define UNUSED(param) while (0) { (void)(param); }
|
||
|
|
||
|
enum {
|
||
|
SIGNAL_ON_KEY_RECEIVED,
|
||
|
NUM_SIGNALS
|
||
|
};
|
||
|
|
||
|
static guint signals[NUM_SIGNALS];
|
||
|
|
||
|
enum {
|
||
|
PROP_0,
|
||
|
PROP_CONNECTION_ID,
|
||
|
PROP_PEM,
|
||
|
PROP_PEER_PEM,
|
||
|
|
||
|
PROP_DECODER_KEY,
|
||
|
PROP_SRTP_CIPHER,
|
||
|
PROP_SRTP_AUTH,
|
||
|
NUM_PROPERTIES
|
||
|
};
|
||
|
|
||
|
static GParamSpec *properties[NUM_PROPERTIES];
|
||
|
|
||
|
#define DEFAULT_CONNECTION_ID NULL
|
||
|
#define DEFAULT_PEM NULL
|
||
|
#define DEFAULT_PEER_PEM NULL
|
||
|
|
||
|
#define DEFAULT_DECODER_KEY NULL
|
||
|
#define DEFAULT_SRTP_CIPHER 0
|
||
|
#define DEFAULT_SRTP_AUTH 0
|
||
|
|
||
|
|
||
|
static void gst_er_dtls_dec_finalize(GObject *);
|
||
|
static void gst_er_dtls_dec_dispose(GObject *);
|
||
|
static void gst_er_dtls_dec_set_property(GObject *, guint prop_id, const GValue *, GParamSpec *);
|
||
|
static void gst_er_dtls_dec_get_property(GObject *, guint prop_id, GValue *, GParamSpec *);
|
||
|
|
||
|
static GstStateChangeReturn gst_er_dtls_dec_change_state(GstElement *, GstStateChange);
|
||
|
static GstPad *gst_er_dtls_dec_request_new_pad(GstElement *, GstPadTemplate *, const gchar *name, const GstCaps *);
|
||
|
static void gst_er_dtls_dec_release_pad(GstElement *, GstPad *);
|
||
|
|
||
|
static void on_key_received(ErDtlsConnection *, gpointer key, guint cipher, guint auth, GstErDtlsDec *);
|
||
|
static gboolean on_peer_certificate_received(ErDtlsConnection *, gchar *pem, GstErDtlsDec *);
|
||
|
static GstFlowReturn sink_chain(GstPad *, GstObject *parent, GstBuffer *);
|
||
|
|
||
|
static ErDtlsAgent *get_agent_by_pem(const gchar *pem);
|
||
|
static void agent_weak_ref_notify(gchar *pem, ErDtlsAgent *);
|
||
|
static void create_connection(GstErDtlsDec *, gchar *id);
|
||
|
static void connection_weak_ref_notify(gchar *id, ErDtlsConnection *);
|
||
|
|
||
|
static void gst_er_dtls_dec_class_init(GstErDtlsDecClass *klass)
|
||
|
{
|
||
|
GObjectClass *gobject_class;
|
||
|
GstElementClass *element_class;
|
||
|
|
||
|
gobject_class = (GObjectClass *) klass;
|
||
|
element_class = (GstElementClass *) klass;
|
||
|
|
||
|
gobject_class->finalize = GST_DEBUG_FUNCPTR(gst_er_dtls_dec_finalize);
|
||
|
gobject_class->dispose = GST_DEBUG_FUNCPTR(gst_er_dtls_dec_dispose);
|
||
|
gobject_class->set_property = GST_DEBUG_FUNCPTR(gst_er_dtls_dec_set_property);
|
||
|
gobject_class->get_property = GST_DEBUG_FUNCPTR(gst_er_dtls_dec_get_property);
|
||
|
|
||
|
element_class->change_state = GST_DEBUG_FUNCPTR(gst_er_dtls_dec_change_state);
|
||
|
element_class->request_new_pad = GST_DEBUG_FUNCPTR(gst_er_dtls_dec_request_new_pad);
|
||
|
element_class->release_pad = GST_DEBUG_FUNCPTR(gst_er_dtls_dec_release_pad);
|
||
|
|
||
|
signals[SIGNAL_ON_KEY_RECEIVED] =
|
||
|
g_signal_new("on-key-received", G_TYPE_FROM_CLASS(klass),
|
||
|
G_SIGNAL_RUN_LAST, 0, NULL, NULL,
|
||
|
g_cclosure_marshal_generic, G_TYPE_NONE, 0);
|
||
|
|
||
|
properties[PROP_CONNECTION_ID] =
|
||
|
g_param_spec_string("connection-id",
|
||
|
"Connection id",
|
||
|
"Every encoder/decoder pair should have the same, unique, connection-id",
|
||
|
DEFAULT_CONNECTION_ID,
|
||
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
|
||
|
|
||
|
properties[PROP_PEM] =
|
||
|
g_param_spec_string("pem",
|
||
|
"PEM string",
|
||
|
"A string containing a X509 certificate and RSA private key in PEM format",
|
||
|
DEFAULT_PEM,
|
||
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
|
||
|
|
||
|
properties[PROP_PEER_PEM] =
|
||
|
g_param_spec_string("peer-pem",
|
||
|
"Peer PEM string",
|
||
|
"The X509 certificate received in the DTLS handshake, in PEM format",
|
||
|
DEFAULT_PEER_PEM,
|
||
|
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
|
||
|
|
||
|
properties[PROP_DECODER_KEY] =
|
||
|
g_param_spec_boxed("decoder-key",
|
||
|
"Decoder key",
|
||
|
"SRTP key that should be used by the decider",
|
||
|
GST_TYPE_CAPS,
|
||
|
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
|
||
|
|
||
|
properties[PROP_SRTP_CIPHER] =
|
||
|
g_param_spec_uint("srtp-cipher",
|
||
|
"SRTP cipher",
|
||
|
"The SRTP cipher selected in the DTLS handshake. "
|
||
|
"The value will be set to an ErDtlsSrtpCipher.",
|
||
|
0, ER_DTLS_SRTP_CIPHER_AES_128_ICM, DEFAULT_SRTP_CIPHER,
|
||
|
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
|
||
|
|
||
|
properties[PROP_SRTP_AUTH] =
|
||
|
g_param_spec_uint("srtp-auth",
|
||
|
"SRTP authentication",
|
||
|
"The SRTP authentication selected in the DTLS handshake. "
|
||
|
"The value will be set to an ErDtlsSrtpAuth.",
|
||
|
0, ER_DTLS_SRTP_AUTH_HMAC_SHA1_80, DEFAULT_SRTP_AUTH,
|
||
|
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
|
||
|
|
||
|
g_object_class_install_properties(gobject_class, NUM_PROPERTIES, properties);
|
||
|
|
||
|
gst_element_class_add_pad_template(element_class,
|
||
|
gst_static_pad_template_get(&src_template));
|
||
|
gst_element_class_add_pad_template(element_class,
|
||
|
gst_static_pad_template_get(&sink_template));
|
||
|
|
||
|
gst_element_class_set_static_metadata(element_class,
|
||
|
"DTLS Decoder",
|
||
|
"Decoder/Network/DTLS",
|
||
|
"Decodes DTLS packets",
|
||
|
"Patrik Oldsberg patrik.oldsberg@ericsson.com");
|
||
|
}
|
||
|
|
||
|
static void gst_er_dtls_dec_init(GstErDtlsDec *self)
|
||
|
{
|
||
|
GstPad *sink;
|
||
|
self->agent = get_agent_by_pem(NULL);
|
||
|
self->connection_id = NULL;
|
||
|
self->connection = NULL;
|
||
|
self->peer_pem = NULL;
|
||
|
|
||
|
self->decoder_key = NULL;
|
||
|
self->srtp_cipher = DEFAULT_SRTP_CIPHER;
|
||
|
self->srtp_auth = DEFAULT_SRTP_AUTH;
|
||
|
|
||
|
g_mutex_init(&self->src_mutex);
|
||
|
|
||
|
self->src = NULL;
|
||
|
sink = gst_pad_new_from_static_template(&sink_template, "sink");
|
||
|
g_return_if_fail(sink);
|
||
|
|
||
|
gst_pad_set_chain_function(sink, GST_DEBUG_FUNCPTR(sink_chain));
|
||
|
|
||
|
gst_element_add_pad(GST_ELEMENT(self), sink);
|
||
|
}
|
||
|
|
||
|
static void gst_er_dtls_dec_finalize(GObject *object)
|
||
|
{
|
||
|
GstErDtlsDec *self = GST_ER_DTLS_DEC(object);
|
||
|
|
||
|
if (self->decoder_key) {
|
||
|
gst_buffer_unref(self->decoder_key);
|
||
|
self->decoder_key = NULL;
|
||
|
}
|
||
|
|
||
|
g_free(self->connection_id);
|
||
|
self->connection_id = NULL;
|
||
|
|
||
|
g_free(self->peer_pem);
|
||
|
self->peer_pem = NULL;
|
||
|
|
||
|
g_mutex_clear(&self->src_mutex);
|
||
|
|
||
|
GST_LOG_OBJECT(self, "finalized");
|
||
|
|
||
|
G_OBJECT_CLASS(parent_class)->finalize(object);
|
||
|
}
|
||
|
|
||
|
static void gst_er_dtls_dec_dispose(GObject *object)
|
||
|
{
|
||
|
GstErDtlsDec *self = GST_ER_DTLS_DEC(object);
|
||
|
|
||
|
if (self->agent) {
|
||
|
g_object_unref(self->agent);
|
||
|
self->agent = NULL;
|
||
|
}
|
||
|
|
||
|
if (self->connection) {
|
||
|
g_object_unref(self->connection);
|
||
|
self->connection = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void gst_er_dtls_dec_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
|
||
|
{
|
||
|
GstErDtlsDec *self = GST_ER_DTLS_DEC(object);
|
||
|
|
||
|
switch (prop_id) {
|
||
|
case PROP_CONNECTION_ID:
|
||
|
g_free(self->connection_id);
|
||
|
self->connection_id = g_value_dup_string(value);
|
||
|
g_return_if_fail(self->agent);
|
||
|
create_connection(self, self->connection_id);
|
||
|
break;
|
||
|
case PROP_PEM:
|
||
|
if (self->agent) {
|
||
|
g_object_unref(self->agent);
|
||
|
}
|
||
|
self->agent = get_agent_by_pem(g_value_get_string(value));
|
||
|
if (self->connection_id) {
|
||
|
create_connection(self, self->connection_id);
|
||
|
}
|
||
|
break;
|
||
|
default:
|
||
|
G_OBJECT_WARN_INVALID_PROPERTY_ID(self, prop_id, pspec);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void gst_er_dtls_dec_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
|
||
|
{
|
||
|
GstErDtlsDec *self = GST_ER_DTLS_DEC(object);
|
||
|
|
||
|
switch (prop_id) {
|
||
|
case PROP_CONNECTION_ID:
|
||
|
g_value_set_string(value, self->connection_id);
|
||
|
break;
|
||
|
case PROP_PEM:
|
||
|
g_value_take_string(value, er_dtls_agent_get_certificate_pem(self->agent));
|
||
|
break;
|
||
|
case PROP_PEER_PEM:
|
||
|
g_value_set_string(value, self->peer_pem);
|
||
|
break;
|
||
|
case PROP_DECODER_KEY:
|
||
|
g_value_set_boxed(value, self->decoder_key);
|
||
|
break;
|
||
|
case PROP_SRTP_CIPHER:
|
||
|
g_value_set_uint(value, self->srtp_cipher);
|
||
|
break;
|
||
|
case PROP_SRTP_AUTH:
|
||
|
g_value_set_uint(value, self->srtp_auth);
|
||
|
break;
|
||
|
default:
|
||
|
G_OBJECT_WARN_INVALID_PROPERTY_ID(self, prop_id, pspec);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static GstStateChangeReturn gst_er_dtls_dec_change_state(GstElement *element, GstStateChange transition)
|
||
|
{
|
||
|
GstErDtlsDec *self = GST_ER_DTLS_DEC(element);
|
||
|
GstStateChangeReturn ret;
|
||
|
|
||
|
switch (transition) {
|
||
|
case GST_STATE_CHANGE_NULL_TO_READY:
|
||
|
if (self->connection) {
|
||
|
g_signal_connect_object(self->connection,
|
||
|
"on-decoder-key", G_CALLBACK(on_key_received), self, 0);
|
||
|
g_signal_connect_object(self->connection,
|
||
|
"on-peer-certificate", G_CALLBACK(on_peer_certificate_received), self, 0);
|
||
|
} else {
|
||
|
GST_WARNING_OBJECT(self, "trying to change state to ready without connection id and pem");
|
||
|
return GST_STATE_CHANGE_FAILURE;
|
||
|
}
|
||
|
break;
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
ret = GST_ELEMENT_CLASS(parent_class)->change_state(element, transition);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static GstPad *gst_er_dtls_dec_request_new_pad(GstElement *element,
|
||
|
GstPadTemplate *tmpl, const gchar *name, const GstCaps *caps)
|
||
|
{
|
||
|
GstErDtlsDec *self = GST_ER_DTLS_DEC(element);
|
||
|
|
||
|
GST_DEBUG_OBJECT(element, "requesting pad");
|
||
|
|
||
|
g_return_val_if_fail(!self->src, NULL);
|
||
|
g_return_val_if_fail(tmpl->direction == GST_PAD_SRC, NULL);
|
||
|
|
||
|
g_mutex_lock(&self->src_mutex);
|
||
|
|
||
|
self->src = gst_pad_new_from_template(tmpl, name);
|
||
|
g_return_val_if_fail(self->src, NULL);
|
||
|
|
||
|
if (caps) {
|
||
|
g_object_set(self->src, "caps", caps, NULL);
|
||
|
}
|
||
|
|
||
|
gst_pad_set_active(self->src, TRUE);
|
||
|
gst_element_add_pad(element, self->src);
|
||
|
|
||
|
g_mutex_unlock(&self->src_mutex);
|
||
|
|
||
|
return self->src;
|
||
|
}
|
||
|
|
||
|
static void gst_er_dtls_dec_release_pad(GstElement *element, GstPad *pad)
|
||
|
{
|
||
|
GstErDtlsDec *self = GST_ER_DTLS_DEC(element);
|
||
|
|
||
|
g_mutex_lock(&self->src_mutex);
|
||
|
|
||
|
g_return_if_fail(self->src == pad);
|
||
|
gst_element_remove_pad(element, self->src);
|
||
|
self->src = NULL;
|
||
|
|
||
|
GST_DEBUG_OBJECT(self, "releasing src pad");
|
||
|
|
||
|
g_mutex_unlock(&self->src_mutex);
|
||
|
|
||
|
GST_ELEMENT_GET_CLASS(element)->release_pad(element, pad);
|
||
|
}
|
||
|
|
||
|
static void on_key_received(ErDtlsConnection *connection, gpointer key, guint cipher, guint auth, GstErDtlsDec *self)
|
||
|
{
|
||
|
gpointer key_dup;
|
||
|
gchar *key_str;
|
||
|
|
||
|
UNUSED(connection);
|
||
|
g_return_if_fail(GST_IS_ER_DTLS_DEC(self));
|
||
|
|
||
|
self->srtp_cipher = cipher;
|
||
|
self->srtp_auth = auth;
|
||
|
|
||
|
key_dup = g_memdup(key, ER_DTLS_SRTP_MASTER_KEY_LENGTH);
|
||
|
self->decoder_key = gst_buffer_new_wrapped(key_dup, ER_DTLS_SRTP_MASTER_KEY_LENGTH);
|
||
|
|
||
|
key_str = g_base64_encode(key, ER_DTLS_SRTP_MASTER_KEY_LENGTH);
|
||
|
GST_INFO_OBJECT(self, "received key: %s", key_str);
|
||
|
g_free(key_str);
|
||
|
|
||
|
g_signal_emit(self, signals[SIGNAL_ON_KEY_RECEIVED], 0);
|
||
|
}
|
||
|
|
||
|
static gboolean signal_peer_certificate_received(GWeakRef *ref)
|
||
|
{
|
||
|
GstErDtlsDec *self;
|
||
|
|
||
|
self = g_weak_ref_get(ref);
|
||
|
g_weak_ref_clear(ref);
|
||
|
g_free(ref);
|
||
|
ref = NULL;
|
||
|
|
||
|
if (self) {
|
||
|
g_object_notify_by_pspec(G_OBJECT(self), properties[PROP_PEER_PEM]);
|
||
|
g_object_unref(self);
|
||
|
self = NULL;
|
||
|
}
|
||
|
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
static gboolean on_peer_certificate_received(ErDtlsConnection *connection, gchar *pem, GstErDtlsDec *self)
|
||
|
{
|
||
|
GWeakRef *ref;
|
||
|
|
||
|
UNUSED(connection);
|
||
|
g_return_val_if_fail(GST_IS_ER_DTLS_DEC(self), TRUE);
|
||
|
|
||
|
GST_DEBUG_OBJECT(self, "Received peer certificate PEM: \n%s", pem);
|
||
|
|
||
|
self->peer_pem = g_strdup(pem);
|
||
|
|
||
|
ref = g_new(GWeakRef, 1);
|
||
|
g_weak_ref_init(ref, self);
|
||
|
|
||
|
g_idle_add((GSourceFunc) signal_peer_certificate_received, ref);
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
static GstFlowReturn sink_chain(GstPad *pad, GstObject *parent, GstBuffer *buffer)
|
||
|
{
|
||
|
GstErDtlsDec *self = GST_ER_DTLS_DEC(parent);
|
||
|
GstFlowReturn ret = GST_FLOW_OK;
|
||
|
GstMapInfo map_info = GST_MAP_INFO_INIT;
|
||
|
gint size;
|
||
|
|
||
|
if (!self->agent) {
|
||
|
gst_buffer_unref(buffer);
|
||
|
return GST_FLOW_OK;
|
||
|
}
|
||
|
|
||
|
GST_DEBUG_OBJECT(self, "received buffer from %s with length %zd",
|
||
|
self->connection_id, gst_buffer_get_size(buffer));
|
||
|
|
||
|
gst_buffer_map(buffer, &map_info, GST_MAP_READWRITE);
|
||
|
|
||
|
if (!map_info.size) {
|
||
|
gst_buffer_unmap(buffer, &map_info);
|
||
|
return GST_FLOW_OK;
|
||
|
}
|
||
|
|
||
|
size = er_dtls_connection_process(self->connection, map_info.data, map_info.size);
|
||
|
gst_buffer_unmap(buffer, &map_info);
|
||
|
|
||
|
if (size <= 0) {
|
||
|
gst_buffer_unref(buffer);
|
||
|
|
||
|
return GST_FLOW_OK;
|
||
|
}
|
||
|
|
||
|
g_mutex_lock(&self->src_mutex);
|
||
|
|
||
|
if (self->src) {
|
||
|
gst_buffer_set_size(buffer, size);
|
||
|
GST_LOG_OBJECT(self, "decoded buffer with length %d, pushing", size);
|
||
|
ret = gst_pad_push(self->src, buffer);
|
||
|
} else {
|
||
|
GST_LOG_OBJECT(self, "dropped buffer with length %d, not linked", size);
|
||
|
gst_buffer_unref(buffer);
|
||
|
}
|
||
|
|
||
|
g_mutex_unlock(&self->src_mutex);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static GHashTable *agent_table = NULL;
|
||
|
G_LOCK_DEFINE_STATIC(agent_table);
|
||
|
|
||
|
static ErDtlsAgent *generated_cert_agent = NULL;
|
||
|
|
||
|
static ErDtlsAgent *get_agent_by_pem(const gchar *pem)
|
||
|
{
|
||
|
ErDtlsAgent *agent;
|
||
|
|
||
|
if (!pem) {
|
||
|
if (g_once_init_enter (&generated_cert_agent)) {
|
||
|
ErDtlsAgent *new_agent;
|
||
|
|
||
|
new_agent = g_object_new(ER_TYPE_DTLS_AGENT, "certificate",
|
||
|
g_object_new(ER_TYPE_DTLS_CERTIFICATE, NULL), NULL);
|
||
|
|
||
|
GST_DEBUG_OBJECT(generated_cert_agent, "no agent with generated cert found, creating new");
|
||
|
g_once_init_leave (&generated_cert_agent, new_agent);
|
||
|
} else {
|
||
|
GST_DEBUG_OBJECT(generated_cert_agent, "using agent with generated cert");
|
||
|
}
|
||
|
|
||
|
agent = generated_cert_agent;
|
||
|
g_object_ref(agent);
|
||
|
} else {
|
||
|
G_LOCK(agent_table);
|
||
|
|
||
|
if (!agent_table) {
|
||
|
agent_table = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
|
||
|
}
|
||
|
|
||
|
agent = ER_DTLS_AGENT(g_hash_table_lookup(agent_table, pem));
|
||
|
|
||
|
if (!agent) {
|
||
|
agent = g_object_new(ER_TYPE_DTLS_AGENT,
|
||
|
"certificate", g_object_new(ER_TYPE_DTLS_CERTIFICATE, "pem", pem, NULL), NULL);
|
||
|
|
||
|
g_object_weak_ref(G_OBJECT(agent), (GWeakNotify) agent_weak_ref_notify, (gpointer) g_strdup(pem));
|
||
|
|
||
|
g_hash_table_insert(agent_table, g_strdup(pem), agent);
|
||
|
|
||
|
GST_DEBUG_OBJECT(agent, "no agent found, created new");
|
||
|
} else {
|
||
|
g_object_ref(agent);
|
||
|
GST_DEBUG_OBJECT(agent, "agent found");
|
||
|
}
|
||
|
|
||
|
G_UNLOCK(agent_table);
|
||
|
}
|
||
|
|
||
|
|
||
|
return agent;
|
||
|
}
|
||
|
|
||
|
static void agent_weak_ref_notify(gchar *pem, ErDtlsAgent *agent)
|
||
|
{
|
||
|
UNUSED(agent);
|
||
|
|
||
|
G_LOCK(agent_table);
|
||
|
g_hash_table_remove(agent_table, pem);
|
||
|
G_UNLOCK(agent_table);
|
||
|
|
||
|
g_free(pem);
|
||
|
pem = NULL;
|
||
|
}
|
||
|
|
||
|
static GHashTable *connection_table = NULL;
|
||
|
G_LOCK_DEFINE_STATIC(connection_table);
|
||
|
|
||
|
ErDtlsConnection *gst_er_dtls_dec_fetch_connection(gchar *id)
|
||
|
{
|
||
|
ErDtlsConnection *connection;
|
||
|
g_return_val_if_fail(id, NULL);
|
||
|
|
||
|
GST_DEBUG("fetching '%s' from connection table, size is %d",
|
||
|
id, g_hash_table_size(connection_table));
|
||
|
|
||
|
G_LOCK(connection_table);
|
||
|
|
||
|
connection = g_hash_table_lookup(connection_table, id);
|
||
|
|
||
|
if (connection) {
|
||
|
g_object_ref(connection);
|
||
|
g_hash_table_remove(connection_table, id);
|
||
|
} else {
|
||
|
GST_WARNING("no connection with id '%s' found", id);
|
||
|
}
|
||
|
|
||
|
G_UNLOCK(connection_table);
|
||
|
|
||
|
return connection;
|
||
|
}
|
||
|
|
||
|
static void create_connection(GstErDtlsDec *self, gchar *id)
|
||
|
{
|
||
|
g_return_if_fail(GST_IS_ER_DTLS_DEC(self));
|
||
|
g_return_if_fail(ER_IS_DTLS_AGENT(self->agent));
|
||
|
|
||
|
if (self->connection) {
|
||
|
g_object_unref(self->connection);
|
||
|
self->connection = NULL;
|
||
|
}
|
||
|
|
||
|
G_LOCK(connection_table);
|
||
|
|
||
|
if (!connection_table) {
|
||
|
connection_table = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
|
||
|
}
|
||
|
|
||
|
if (g_hash_table_contains(connection_table, id)) {
|
||
|
G_UNLOCK(connection_table);
|
||
|
|
||
|
g_return_if_reached();
|
||
|
}
|
||
|
|
||
|
self->connection = g_object_new(ER_TYPE_DTLS_CONNECTION, "agent", self->agent, NULL);
|
||
|
|
||
|
g_object_weak_ref(G_OBJECT(self->connection), (GWeakNotify) connection_weak_ref_notify, g_strdup(id));
|
||
|
|
||
|
g_hash_table_insert(connection_table, g_strdup(id), self->connection);
|
||
|
|
||
|
G_UNLOCK(connection_table);
|
||
|
}
|
||
|
|
||
|
static void connection_weak_ref_notify(gchar *id, ErDtlsConnection *connection)
|
||
|
{
|
||
|
UNUSED(connection);
|
||
|
|
||
|
G_LOCK(connection_table);
|
||
|
g_hash_table_remove(connection_table, id);
|
||
|
G_UNLOCK(connection_table);
|
||
|
|
||
|
g_free(id);
|
||
|
id = NULL;
|
||
|
}
|