2 * Copyright (c) 2014, Ericsson AB. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without modification,
5 * are permitted provided that the following conditions are met:
7 * 1. Redistributions of source code must retain the above copyright notice, this
8 * list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright notice, this
11 * list of conditions and the following disclaimer in the documentation and/or other
12 * materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17 * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
18 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
21 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
30 #include "gstdtlsdec.h"
32 #include "gstdtlscertificate.h"
34 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
37 GST_STATIC_CAPS ("application/x-dtls")
40 static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
45 GST_DEBUG_CATEGORY_STATIC (gst_dtls_dec_debug);
46 #define GST_CAT_DEFAULT gst_dtls_dec_debug
48 #define gst_dtls_dec_parent_class parent_class
49 G_DEFINE_TYPE_WITH_CODE (GstDtlsDec, gst_dtls_dec, GST_TYPE_ELEMENT,
50 GST_DEBUG_CATEGORY_INIT (gst_dtls_dec_debug, "dtlsdec", 0, "DTLS Decoder"));
54 SIGNAL_ON_KEY_RECEIVED,
58 static guint signals[NUM_SIGNALS];
73 static GParamSpec *properties[NUM_PROPERTIES];
75 #define DEFAULT_CONNECTION_ID NULL
76 #define DEFAULT_PEM NULL
77 #define DEFAULT_PEER_PEM NULL
79 #define DEFAULT_DECODER_KEY NULL
80 #define DEFAULT_SRTP_CIPHER 0
81 #define DEFAULT_SRTP_AUTH 0
84 static void gst_dtls_dec_finalize (GObject *);
85 static void gst_dtls_dec_dispose (GObject *);
86 static void gst_dtls_dec_set_property (GObject *, guint prop_id,
87 const GValue *, GParamSpec *);
88 static void gst_dtls_dec_get_property (GObject *, guint prop_id, GValue *,
91 static GstStateChangeReturn gst_dtls_dec_change_state (GstElement *,
93 static GstPad *gst_dtls_dec_request_new_pad (GstElement *, GstPadTemplate *,
94 const gchar * name, const GstCaps *);
95 static void gst_dtls_dec_release_pad (GstElement *, GstPad *);
97 static void on_key_received (GstDtlsConnection *, gpointer key, guint cipher,
98 guint auth, GstDtlsDec *);
99 static gboolean on_peer_certificate_received (GstDtlsConnection *, gchar * pem,
101 static GstFlowReturn sink_chain (GstPad *, GstObject * parent, GstBuffer *);
102 static GstFlowReturn sink_chain_list (GstPad *, GstObject * parent,
105 static GstDtlsAgent *get_agent_by_pem (const gchar * pem);
106 static void agent_weak_ref_notify (gchar * pem, GstDtlsAgent *);
107 static void create_connection (GstDtlsDec *, gchar * id);
108 static void connection_weak_ref_notify (gchar * id, GstDtlsConnection *);
111 gst_dtls_dec_class_init (GstDtlsDecClass * klass)
113 GObjectClass *gobject_class;
114 GstElementClass *element_class;
116 gobject_class = (GObjectClass *) klass;
117 element_class = (GstElementClass *) klass;
119 gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_dtls_dec_finalize);
120 gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_dtls_dec_dispose);
121 gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_dtls_dec_set_property);
122 gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_dtls_dec_get_property);
124 element_class->change_state = GST_DEBUG_FUNCPTR (gst_dtls_dec_change_state);
125 element_class->request_new_pad =
126 GST_DEBUG_FUNCPTR (gst_dtls_dec_request_new_pad);
127 element_class->release_pad = GST_DEBUG_FUNCPTR (gst_dtls_dec_release_pad);
129 signals[SIGNAL_ON_KEY_RECEIVED] =
130 g_signal_new ("on-key-received", G_TYPE_FROM_CLASS (klass),
131 G_SIGNAL_RUN_LAST, 0, NULL, NULL,
132 g_cclosure_marshal_generic, G_TYPE_NONE, 0);
134 properties[PROP_CONNECTION_ID] =
135 g_param_spec_string ("connection-id",
137 "Every encoder/decoder pair should have the same, unique, connection-id",
138 DEFAULT_CONNECTION_ID, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
140 properties[PROP_PEM] =
141 g_param_spec_string ("pem",
143 "A string containing a X509 certificate and RSA private key in PEM format",
144 DEFAULT_PEM, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
146 properties[PROP_PEER_PEM] =
147 g_param_spec_string ("peer-pem",
149 "The X509 certificate received in the DTLS handshake, in PEM format",
150 DEFAULT_PEER_PEM, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
152 properties[PROP_DECODER_KEY] =
153 g_param_spec_boxed ("decoder-key",
155 "SRTP key that should be used by the decoder",
156 GST_TYPE_CAPS, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
158 properties[PROP_SRTP_CIPHER] =
159 g_param_spec_uint ("srtp-cipher",
161 "The SRTP cipher selected in the DTLS handshake. "
162 "The value will be set to an GstDtlsSrtpCipher.",
163 0, GST_DTLS_SRTP_CIPHER_AES_128_ICM, DEFAULT_SRTP_CIPHER,
164 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
166 properties[PROP_SRTP_AUTH] =
167 g_param_spec_uint ("srtp-auth",
168 "SRTP authentication",
169 "The SRTP authentication selected in the DTLS handshake. "
170 "The value will be set to an GstDtlsSrtpAuth.",
171 0, GST_DTLS_SRTP_AUTH_HMAC_SHA1_80, DEFAULT_SRTP_AUTH,
172 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
174 g_object_class_install_properties (gobject_class, NUM_PROPERTIES, properties);
176 gst_element_class_add_static_pad_template (element_class, &src_template);
177 gst_element_class_add_static_pad_template (element_class, &sink_template);
179 gst_element_class_set_static_metadata (element_class,
181 "Decoder/Network/DTLS",
182 "Decodes DTLS packets", "Patrik Oldsberg patrik.oldsberg@ericsson.com");
186 gst_dtls_dec_init (GstDtlsDec * self)
188 self->agent = get_agent_by_pem (NULL);
189 self->connection_id = NULL;
190 self->connection = NULL;
191 self->peer_pem = NULL;
193 self->decoder_key = NULL;
194 self->srtp_cipher = DEFAULT_SRTP_CIPHER;
195 self->srtp_auth = DEFAULT_SRTP_AUTH;
197 g_mutex_init (&self->src_mutex);
200 self->sink = gst_pad_new_from_static_template (&sink_template, "sink");
201 g_return_if_fail (self->sink);
203 gst_pad_set_chain_function (self->sink, GST_DEBUG_FUNCPTR (sink_chain));
204 gst_pad_set_chain_list_function (self->sink,
205 GST_DEBUG_FUNCPTR (sink_chain_list));
207 gst_element_add_pad (GST_ELEMENT (self), self->sink);
211 gst_dtls_dec_finalize (GObject * object)
213 GstDtlsDec *self = GST_DTLS_DEC (object);
215 if (self->decoder_key) {
216 gst_buffer_unref (self->decoder_key);
217 self->decoder_key = NULL;
220 g_free (self->connection_id);
221 self->connection_id = NULL;
223 g_free (self->peer_pem);
224 self->peer_pem = NULL;
226 g_mutex_clear (&self->src_mutex);
228 GST_LOG_OBJECT (self, "finalized");
230 G_OBJECT_CLASS (parent_class)->finalize (object);
234 gst_dtls_dec_dispose (GObject * object)
236 GstDtlsDec *self = GST_DTLS_DEC (object);
239 g_object_unref (self->agent);
243 if (self->connection) {
244 g_object_unref (self->connection);
245 self->connection = NULL;
248 G_OBJECT_CLASS (parent_class)->dispose (object);
252 gst_dtls_dec_set_property (GObject * object, guint prop_id,
253 const GValue * value, GParamSpec * pspec)
255 GstDtlsDec *self = GST_DTLS_DEC (object);
258 case PROP_CONNECTION_ID:
259 g_free (self->connection_id);
260 self->connection_id = g_value_dup_string (value);
261 g_return_if_fail (self->agent);
262 create_connection (self, self->connection_id);
266 g_object_unref (self->agent);
268 self->agent = get_agent_by_pem (g_value_get_string (value));
269 if (self->connection_id) {
270 create_connection (self, self->connection_id);
274 G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec);
279 gst_dtls_dec_get_property (GObject * object, guint prop_id, GValue * value,
282 GstDtlsDec *self = GST_DTLS_DEC (object);
285 case PROP_CONNECTION_ID:
286 g_value_set_string (value, self->connection_id);
289 g_value_take_string (value,
290 gst_dtls_agent_get_certificate_pem (self->agent));
293 g_value_set_string (value, self->peer_pem);
295 case PROP_DECODER_KEY:
296 g_value_set_boxed (value, self->decoder_key);
298 case PROP_SRTP_CIPHER:
299 g_value_set_uint (value, self->srtp_cipher);
302 g_value_set_uint (value, self->srtp_auth);
305 G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec);
309 static GstStateChangeReturn
310 gst_dtls_dec_change_state (GstElement * element, GstStateChange transition)
312 GstDtlsDec *self = GST_DTLS_DEC (element);
313 GstStateChangeReturn ret;
315 switch (transition) {
316 case GST_STATE_CHANGE_NULL_TO_READY:
317 if (self->connection) {
318 g_signal_connect_object (self->connection,
319 "on-decoder-key", G_CALLBACK (on_key_received), self, 0);
320 g_signal_connect_object (self->connection,
321 "on-peer-certificate", G_CALLBACK (on_peer_certificate_received),
324 GST_WARNING_OBJECT (self,
325 "trying to change state to ready without connection id and pem");
326 return GST_STATE_CHANGE_FAILURE;
333 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
339 forward_sticky_events (GstPad * pad, GstEvent ** event, gpointer user_data)
341 GstPad *srcpad = GST_PAD_CAST (user_data);
344 ret = gst_pad_store_sticky_event (srcpad, *event);
345 if (ret != GST_FLOW_OK) {
346 GST_DEBUG_OBJECT (srcpad, "storing sticky event %p (%s) failed: %s", *event,
347 GST_EVENT_TYPE_NAME (*event), gst_flow_get_name (ret));
354 gst_dtls_dec_request_new_pad (GstElement * element,
355 GstPadTemplate * tmpl, const gchar * name, const GstCaps * caps)
357 GstDtlsDec *self = GST_DTLS_DEC (element);
360 GST_DEBUG_OBJECT (element, "requesting pad");
362 g_return_val_if_fail (!self->src, NULL);
363 g_return_val_if_fail (tmpl->direction == GST_PAD_SRC, NULL);
365 g_mutex_lock (&self->src_mutex);
367 GST_ERROR_OBJECT (self, "Pad %s:%s exists already",
368 GST_DEBUG_PAD_NAME (self->src));
369 g_mutex_unlock (&self->src_mutex);
373 self->src = pad = gst_pad_new_from_template (tmpl, name);
375 g_mutex_unlock (&self->src_mutex);
377 gst_pad_set_active (pad, TRUE);
380 gst_pad_set_caps (pad, (GstCaps *) caps);
382 /* Forward sticky events to the new srcpad */
383 gst_pad_sticky_events_foreach (self->sink, forward_sticky_events, self->src);
385 gst_element_add_pad (element, pad);
391 gst_dtls_dec_release_pad (GstElement * element, GstPad * pad)
393 GstDtlsDec *self = GST_DTLS_DEC (element);
395 g_return_if_fail (self->src == pad);
397 g_mutex_lock (&self->src_mutex);
400 g_mutex_unlock (&self->src_mutex);
402 GST_DEBUG_OBJECT (self, "releasing src pad");
404 gst_element_remove_pad (element, pad);
408 on_key_received (GstDtlsConnection * connection, gpointer key, guint cipher,
409 guint auth, GstDtlsDec * self)
414 g_return_if_fail (GST_IS_DTLS_DEC (self));
416 self->srtp_cipher = cipher;
417 self->srtp_auth = auth;
419 key_dup = g_memdup (key, GST_DTLS_SRTP_MASTER_KEY_LENGTH);
421 if (self->decoder_key) {
422 gst_buffer_unref (self->decoder_key);
423 self->decoder_key = NULL;
427 gst_buffer_new_wrapped (key_dup, GST_DTLS_SRTP_MASTER_KEY_LENGTH);
429 key_str = g_base64_encode (key, GST_DTLS_SRTP_MASTER_KEY_LENGTH);
430 GST_INFO_OBJECT (self, "received key: %s", key_str);
433 g_signal_emit (self, signals[SIGNAL_ON_KEY_RECEIVED], 0);
437 on_peer_certificate_received (GstDtlsConnection * connection, gchar * pem,
440 g_return_val_if_fail (GST_IS_DTLS_DEC (self), TRUE);
442 GST_DEBUG_OBJECT (self, "Received peer certificate PEM: \n%s", pem);
444 if (self->peer_pem != NULL) {
445 g_free (self->peer_pem);
446 self->peer_pem = NULL;
448 self->peer_pem = g_strdup (pem);
450 g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_PEER_PEM]);
456 process_buffer (GstDtlsDec * self, GstBuffer * buffer)
461 if (!gst_buffer_map (buffer, &map_info, GST_MAP_READWRITE))
464 if (!map_info.size) {
465 gst_buffer_unmap (buffer, &map_info);
470 gst_dtls_connection_process (self->connection, map_info.data,
472 gst_buffer_unmap (buffer, &map_info);
477 gst_buffer_set_size (buffer, size);
483 process_buffer_from_list (GstBuffer ** buffer, guint idx, gpointer user_data)
485 GstDtlsDec *self = GST_DTLS_DEC (user_data);
488 *buffer = gst_buffer_make_writable (*buffer);
489 size = process_buffer (self, *buffer);
491 gst_buffer_replace (buffer, NULL);
497 sink_chain_list (GstPad * pad, GstObject * parent, GstBufferList * list)
499 GstDtlsDec *self = GST_DTLS_DEC (parent);
500 GstFlowReturn ret = GST_FLOW_OK;
503 list = gst_buffer_list_make_writable (list);
504 gst_buffer_list_foreach (list, process_buffer_from_list, self);
506 if (gst_buffer_list_length (list) == 0) {
507 GST_DEBUG_OBJECT (self, "Not produced any buffers");
508 gst_buffer_list_unref (list);
513 g_mutex_lock (&self->src_mutex);
514 other_pad = self->src;
516 gst_object_ref (other_pad);
517 g_mutex_unlock (&self->src_mutex);
520 GST_LOG_OBJECT (self, "decoded buffer list with length %u, pushing",
521 gst_buffer_list_length (list));
522 ret = gst_pad_push_list (other_pad, list);
523 gst_object_unref (other_pad);
525 GST_LOG_OBJECT (self, "dropped buffer list with length %d, not linked",
526 gst_buffer_list_length (list));
527 gst_buffer_list_unref (list);
534 sink_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
536 GstDtlsDec *self = GST_DTLS_DEC (parent);
537 GstFlowReturn ret = GST_FLOW_OK;
542 gst_buffer_unref (buffer);
546 GST_DEBUG_OBJECT (self,
547 "received buffer from %s with length %" G_GSIZE_FORMAT,
548 self->connection_id, gst_buffer_get_size (buffer));
550 buffer = gst_buffer_make_writable (buffer);
551 size = process_buffer (self, buffer);
554 gst_buffer_unref (buffer);
559 g_mutex_lock (&self->src_mutex);
560 other_pad = self->src;
562 gst_object_ref (other_pad);
563 g_mutex_unlock (&self->src_mutex);
566 GST_LOG_OBJECT (self, "decoded buffer with length %d, pushing", size);
567 ret = gst_pad_push (other_pad, buffer);
568 gst_object_unref (other_pad);
570 GST_LOG_OBJECT (self, "dropped buffer with length %d, not linked", size);
571 gst_buffer_unref (buffer);
577 static GHashTable *agent_table = NULL;
578 G_LOCK_DEFINE_STATIC (agent_table);
580 static GstDtlsAgent *generated_cert_agent = NULL;
582 static GstDtlsAgent *
583 get_agent_by_pem (const gchar * pem)
588 if (g_once_init_enter (&generated_cert_agent)) {
589 GstDtlsAgent *new_agent;
590 GObject *certificate;
592 certificate = g_object_new (GST_TYPE_DTLS_CERTIFICATE, NULL);
593 new_agent = g_object_new (GST_TYPE_DTLS_AGENT, "certificate",
595 g_object_unref (certificate);
597 GST_DEBUG_OBJECT (generated_cert_agent,
598 "no agent with generated cert found, creating new");
599 g_once_init_leave (&generated_cert_agent, new_agent);
601 GST_DEBUG_OBJECT (generated_cert_agent,
602 "using agent with generated cert");
605 agent = generated_cert_agent;
606 g_object_ref (agent);
608 G_LOCK (agent_table);
612 g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
615 agent = GST_DTLS_AGENT (g_hash_table_lookup (agent_table, pem));
618 GObject *certificate;
620 certificate = g_object_new (GST_TYPE_DTLS_CERTIFICATE, "pem", pem, NULL);
621 agent = g_object_new (GST_TYPE_DTLS_AGENT, "certificate", certificate,
623 g_object_unref (certificate);
625 g_object_weak_ref (G_OBJECT (agent), (GWeakNotify) agent_weak_ref_notify,
626 (gpointer) g_strdup (pem));
628 g_hash_table_insert (agent_table, g_strdup (pem), agent);
630 GST_DEBUG_OBJECT (agent, "no agent found, created new");
632 g_object_ref (agent);
633 GST_DEBUG_OBJECT (agent, "agent found");
636 G_UNLOCK (agent_table);
644 agent_weak_ref_notify (gchar * pem, GstDtlsAgent * agent)
646 G_LOCK (agent_table);
647 g_hash_table_remove (agent_table, pem);
648 G_UNLOCK (agent_table);
654 static GHashTable *connection_table = NULL;
655 G_LOCK_DEFINE_STATIC (connection_table);
658 gst_dtls_dec_fetch_connection (gchar * id)
660 GstDtlsConnection *connection;
661 g_return_val_if_fail (id, NULL);
663 GST_DEBUG ("fetching '%s' from connection table, size is %d",
664 id, g_hash_table_size (connection_table));
666 G_LOCK (connection_table);
668 connection = g_hash_table_lookup (connection_table, id);
671 g_object_ref (connection);
672 g_hash_table_remove (connection_table, id);
674 GST_WARNING ("no connection with id '%s' found", id);
677 G_UNLOCK (connection_table);
683 create_connection (GstDtlsDec * self, gchar * id)
685 g_return_if_fail (GST_IS_DTLS_DEC (self));
686 g_return_if_fail (GST_IS_DTLS_AGENT (self->agent));
688 if (self->connection) {
689 g_object_unref (self->connection);
690 self->connection = NULL;
693 G_LOCK (connection_table);
695 if (!connection_table) {
697 g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
700 if (g_hash_table_contains (connection_table, id)) {
701 G_UNLOCK (connection_table);
703 g_return_if_reached ();
707 g_object_new (GST_TYPE_DTLS_CONNECTION, "agent", self->agent, NULL);
709 g_object_weak_ref (G_OBJECT (self->connection),
710 (GWeakNotify) connection_weak_ref_notify, g_strdup (id));
712 g_hash_table_insert (connection_table, g_strdup (id), self->connection);
714 G_UNLOCK (connection_table);
718 connection_weak_ref_notify (gchar * id, GstDtlsConnection * connection)
720 G_LOCK (connection_table);
721 g_hash_table_remove (connection_table, id);
722 G_UNLOCK (connection_table);