dtlsdec: Avoid duplicate ref when passing certificate property
[platform/upstream/gstreamer.git] / ext / dtls / gstdtlsdec.c
1 /*
2  * Copyright (c) 2014, Ericsson AB. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without modification,
5  * are permitted provided that the following conditions are met:
6  *
7  * 1. Redistributions of source code must retain the above copyright notice, this
8  * list of conditions and the following disclaimer.
9  *
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.
13  *
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
23  * OF SUCH DAMAGE.
24  */
25
26 #ifdef HAVE_CONFIG_H
27 #include "config.h"
28 #endif
29
30 #include "gstdtlsdec.h"
31
32 #include "gstdtlscertificate.h"
33
34 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
35     GST_PAD_SINK,
36     GST_PAD_ALWAYS,
37     GST_STATIC_CAPS ("application/x-dtls")
38     );
39
40 static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
41     GST_PAD_SRC,
42     GST_PAD_REQUEST,
43     GST_STATIC_CAPS_ANY);
44
45 GST_DEBUG_CATEGORY_STATIC (gst_dtls_dec_debug);
46 #define GST_CAT_DEFAULT gst_dtls_dec_debug
47
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"));
51
52 enum
53 {
54   SIGNAL_ON_KEY_RECEIVED,
55   NUM_SIGNALS
56 };
57
58 static guint signals[NUM_SIGNALS];
59
60 enum
61 {
62   PROP_0,
63   PROP_CONNECTION_ID,
64   PROP_PEM,
65   PROP_PEER_PEM,
66
67   PROP_DECODER_KEY,
68   PROP_SRTP_CIPHER,
69   PROP_SRTP_AUTH,
70   NUM_PROPERTIES
71 };
72
73 static GParamSpec *properties[NUM_PROPERTIES];
74
75 #define DEFAULT_CONNECTION_ID NULL
76 #define DEFAULT_PEM NULL
77 #define DEFAULT_PEER_PEM NULL
78
79 #define DEFAULT_DECODER_KEY NULL
80 #define DEFAULT_SRTP_CIPHER 0
81 #define DEFAULT_SRTP_AUTH 0
82
83
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 *,
89     GParamSpec *);
90
91 static GstStateChangeReturn gst_dtls_dec_change_state (GstElement *,
92     GstStateChange);
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 *);
96
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,
100     GstDtlsDec *);
101 static GstFlowReturn sink_chain (GstPad *, GstObject * parent, GstBuffer *);
102 static GstFlowReturn sink_chain_list (GstPad *, GstObject * parent,
103     GstBufferList *);
104
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 *);
109
110 static void
111 gst_dtls_dec_class_init (GstDtlsDecClass * klass)
112 {
113   GObjectClass *gobject_class;
114   GstElementClass *element_class;
115
116   gobject_class = (GObjectClass *) klass;
117   element_class = (GstElementClass *) klass;
118
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);
123
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);
128
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);
133
134   properties[PROP_CONNECTION_ID] =
135       g_param_spec_string ("connection-id",
136       "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);
139
140   properties[PROP_PEM] =
141       g_param_spec_string ("pem",
142       "PEM string",
143       "A string containing a X509 certificate and RSA private key in PEM format",
144       DEFAULT_PEM, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
145
146   properties[PROP_PEER_PEM] =
147       g_param_spec_string ("peer-pem",
148       "Peer PEM string",
149       "The X509 certificate received in the DTLS handshake, in PEM format",
150       DEFAULT_PEER_PEM, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
151
152   properties[PROP_DECODER_KEY] =
153       g_param_spec_boxed ("decoder-key",
154       "Decoder key",
155       "SRTP key that should be used by the decoder",
156       GST_TYPE_CAPS, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
157
158   properties[PROP_SRTP_CIPHER] =
159       g_param_spec_uint ("srtp-cipher",
160       "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);
165
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);
173
174   g_object_class_install_properties (gobject_class, NUM_PROPERTIES, properties);
175
176   gst_element_class_add_static_pad_template (element_class, &src_template);
177   gst_element_class_add_static_pad_template (element_class, &sink_template);
178
179   gst_element_class_set_static_metadata (element_class,
180       "DTLS Decoder",
181       "Decoder/Network/DTLS",
182       "Decodes DTLS packets", "Patrik Oldsberg patrik.oldsberg@ericsson.com");
183 }
184
185 static void
186 gst_dtls_dec_init (GstDtlsDec * self)
187 {
188   self->agent = get_agent_by_pem (NULL);
189   self->connection_id = NULL;
190   self->connection = NULL;
191   self->peer_pem = NULL;
192
193   self->decoder_key = NULL;
194   self->srtp_cipher = DEFAULT_SRTP_CIPHER;
195   self->srtp_auth = DEFAULT_SRTP_AUTH;
196
197   g_mutex_init (&self->src_mutex);
198
199   self->src = NULL;
200   self->sink = gst_pad_new_from_static_template (&sink_template, "sink");
201   g_return_if_fail (self->sink);
202
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));
206
207   gst_element_add_pad (GST_ELEMENT (self), self->sink);
208 }
209
210 static void
211 gst_dtls_dec_finalize (GObject * object)
212 {
213   GstDtlsDec *self = GST_DTLS_DEC (object);
214
215   if (self->decoder_key) {
216     gst_buffer_unref (self->decoder_key);
217     self->decoder_key = NULL;
218   }
219
220   g_free (self->connection_id);
221   self->connection_id = NULL;
222
223   g_free (self->peer_pem);
224   self->peer_pem = NULL;
225
226   g_mutex_clear (&self->src_mutex);
227
228   GST_LOG_OBJECT (self, "finalized");
229
230   G_OBJECT_CLASS (parent_class)->finalize (object);
231 }
232
233 static void
234 gst_dtls_dec_dispose (GObject * object)
235 {
236   GstDtlsDec *self = GST_DTLS_DEC (object);
237
238   if (self->agent) {
239     g_object_unref (self->agent);
240     self->agent = NULL;
241   }
242
243   if (self->connection) {
244     g_object_unref (self->connection);
245     self->connection = NULL;
246   }
247
248   G_OBJECT_CLASS (parent_class)->dispose (object);
249 }
250
251 static void
252 gst_dtls_dec_set_property (GObject * object, guint prop_id,
253     const GValue * value, GParamSpec * pspec)
254 {
255   GstDtlsDec *self = GST_DTLS_DEC (object);
256
257   switch (prop_id) {
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);
263       break;
264     case PROP_PEM:
265       if (self->agent) {
266         g_object_unref (self->agent);
267       }
268       self->agent = get_agent_by_pem (g_value_get_string (value));
269       if (self->connection_id) {
270         create_connection (self, self->connection_id);
271       }
272       break;
273     default:
274       G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec);
275   }
276 }
277
278 static void
279 gst_dtls_dec_get_property (GObject * object, guint prop_id, GValue * value,
280     GParamSpec * pspec)
281 {
282   GstDtlsDec *self = GST_DTLS_DEC (object);
283
284   switch (prop_id) {
285     case PROP_CONNECTION_ID:
286       g_value_set_string (value, self->connection_id);
287       break;
288     case PROP_PEM:
289       g_value_take_string (value,
290           gst_dtls_agent_get_certificate_pem (self->agent));
291       break;
292     case PROP_PEER_PEM:
293       g_value_set_string (value, self->peer_pem);
294       break;
295     case PROP_DECODER_KEY:
296       g_value_set_boxed (value, self->decoder_key);
297       break;
298     case PROP_SRTP_CIPHER:
299       g_value_set_uint (value, self->srtp_cipher);
300       break;
301     case PROP_SRTP_AUTH:
302       g_value_set_uint (value, self->srtp_auth);
303       break;
304     default:
305       G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec);
306   }
307 }
308
309 static GstStateChangeReturn
310 gst_dtls_dec_change_state (GstElement * element, GstStateChange transition)
311 {
312   GstDtlsDec *self = GST_DTLS_DEC (element);
313   GstStateChangeReturn ret;
314
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),
322             self, 0);
323       } else {
324         GST_WARNING_OBJECT (self,
325             "trying to change state to ready without connection id and pem");
326         return GST_STATE_CHANGE_FAILURE;
327       }
328       break;
329     default:
330       break;
331   }
332
333   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
334
335   return ret;
336 }
337
338 static gboolean
339 forward_sticky_events (GstPad * pad, GstEvent ** event, gpointer user_data)
340 {
341   GstPad *srcpad = GST_PAD_CAST (user_data);
342   GstFlowReturn ret;
343
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));
348   }
349
350   return TRUE;
351 }
352
353 static GstPad *
354 gst_dtls_dec_request_new_pad (GstElement * element,
355     GstPadTemplate * tmpl, const gchar * name, const GstCaps * caps)
356 {
357   GstDtlsDec *self = GST_DTLS_DEC (element);
358   GstPad *pad;
359
360   GST_DEBUG_OBJECT (element, "requesting pad");
361
362   g_return_val_if_fail (!self->src, NULL);
363   g_return_val_if_fail (tmpl->direction == GST_PAD_SRC, NULL);
364
365   g_mutex_lock (&self->src_mutex);
366   if (self->src) {
367     GST_ERROR_OBJECT (self, "Pad %s:%s exists already",
368         GST_DEBUG_PAD_NAME (self->src));
369     g_mutex_unlock (&self->src_mutex);
370     return NULL;
371   }
372
373   self->src = pad = gst_pad_new_from_template (tmpl, name);
374
375   g_mutex_unlock (&self->src_mutex);
376
377   gst_pad_set_active (pad, TRUE);
378
379   if (caps)
380     gst_pad_set_caps (pad, (GstCaps *) caps);
381
382   /* Forward sticky events to the new srcpad */
383   gst_pad_sticky_events_foreach (self->sink, forward_sticky_events, self->src);
384
385   gst_element_add_pad (element, pad);
386
387   return pad;
388 }
389
390 static void
391 gst_dtls_dec_release_pad (GstElement * element, GstPad * pad)
392 {
393   GstDtlsDec *self = GST_DTLS_DEC (element);
394
395   g_return_if_fail (self->src == pad);
396
397   g_mutex_lock (&self->src_mutex);
398
399   self->src = NULL;
400   g_mutex_unlock (&self->src_mutex);
401
402   GST_DEBUG_OBJECT (self, "releasing src pad");
403
404   gst_element_remove_pad (element, pad);
405 }
406
407 static void
408 on_key_received (GstDtlsConnection * connection, gpointer key, guint cipher,
409     guint auth, GstDtlsDec * self)
410 {
411   gpointer key_dup;
412   gchar *key_str;
413
414   g_return_if_fail (GST_IS_DTLS_DEC (self));
415
416   self->srtp_cipher = cipher;
417   self->srtp_auth = auth;
418
419   key_dup = g_memdup (key, GST_DTLS_SRTP_MASTER_KEY_LENGTH);
420
421   if (self->decoder_key) {
422     gst_buffer_unref (self->decoder_key);
423     self->decoder_key = NULL;
424   }
425
426   self->decoder_key =
427       gst_buffer_new_wrapped (key_dup, GST_DTLS_SRTP_MASTER_KEY_LENGTH);
428
429   key_str = g_base64_encode (key, GST_DTLS_SRTP_MASTER_KEY_LENGTH);
430   GST_INFO_OBJECT (self, "received key: %s", key_str);
431   g_free (key_str);
432
433   g_signal_emit (self, signals[SIGNAL_ON_KEY_RECEIVED], 0);
434 }
435
436 static gboolean
437 on_peer_certificate_received (GstDtlsConnection * connection, gchar * pem,
438     GstDtlsDec * self)
439 {
440   g_return_val_if_fail (GST_IS_DTLS_DEC (self), TRUE);
441
442   GST_DEBUG_OBJECT (self, "Received peer certificate PEM: \n%s", pem);
443
444   if (self->peer_pem != NULL) {
445     g_free (self->peer_pem);
446     self->peer_pem = NULL;
447   }
448   self->peer_pem = g_strdup (pem);
449
450   g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_PEER_PEM]);
451
452   return TRUE;
453 }
454
455 static gint
456 process_buffer (GstDtlsDec * self, GstBuffer * buffer)
457 {
458   GstMapInfo map_info;
459   gint size;
460
461   if (!gst_buffer_map (buffer, &map_info, GST_MAP_READWRITE))
462     return 0;
463
464   if (!map_info.size) {
465     gst_buffer_unmap (buffer, &map_info);
466     return 0;
467   }
468
469   size =
470       gst_dtls_connection_process (self->connection, map_info.data,
471       map_info.size);
472   gst_buffer_unmap (buffer, &map_info);
473
474   if (size <= 0)
475     return size;
476
477   gst_buffer_set_size (buffer, size);
478
479   return size;
480 }
481
482 static gboolean
483 process_buffer_from_list (GstBuffer ** buffer, guint idx, gpointer user_data)
484 {
485   GstDtlsDec *self = GST_DTLS_DEC (user_data);
486   gint size;
487
488   *buffer = gst_buffer_make_writable (*buffer);
489   size = process_buffer (self, *buffer);
490   if (size <= 0)
491     gst_buffer_replace (buffer, NULL);
492
493   return TRUE;
494 }
495
496 static GstFlowReturn
497 sink_chain_list (GstPad * pad, GstObject * parent, GstBufferList * list)
498 {
499   GstDtlsDec *self = GST_DTLS_DEC (parent);
500   GstFlowReturn ret = GST_FLOW_OK;
501   GstPad *other_pad;
502
503   list = gst_buffer_list_make_writable (list);
504   gst_buffer_list_foreach (list, process_buffer_from_list, self);
505
506   if (gst_buffer_list_length (list) == 0) {
507     GST_DEBUG_OBJECT (self, "Not produced any buffers");
508     gst_buffer_list_unref (list);
509
510     return GST_FLOW_OK;
511   }
512
513   g_mutex_lock (&self->src_mutex);
514   other_pad = self->src;
515   if (other_pad)
516     gst_object_ref (other_pad);
517   g_mutex_unlock (&self->src_mutex);
518
519   if (other_pad) {
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);
524   } else {
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);
528   }
529
530   return ret;
531 }
532
533 static GstFlowReturn
534 sink_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
535 {
536   GstDtlsDec *self = GST_DTLS_DEC (parent);
537   GstFlowReturn ret = GST_FLOW_OK;
538   gint size;
539   GstPad *other_pad;
540
541   if (!self->agent) {
542     gst_buffer_unref (buffer);
543     return GST_FLOW_OK;
544   }
545
546   GST_DEBUG_OBJECT (self,
547       "received buffer from %s with length %" G_GSIZE_FORMAT,
548       self->connection_id, gst_buffer_get_size (buffer));
549
550   buffer = gst_buffer_make_writable (buffer);
551   size = process_buffer (self, buffer);
552
553   if (size <= 0) {
554     gst_buffer_unref (buffer);
555
556     return GST_FLOW_OK;
557   }
558
559   g_mutex_lock (&self->src_mutex);
560   other_pad = self->src;
561   if (other_pad)
562     gst_object_ref (other_pad);
563   g_mutex_unlock (&self->src_mutex);
564
565   if (other_pad) {
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);
569   } else {
570     GST_LOG_OBJECT (self, "dropped buffer with length %d, not linked", size);
571     gst_buffer_unref (buffer);
572   }
573
574   return ret;
575 }
576
577 static GHashTable *agent_table = NULL;
578 G_LOCK_DEFINE_STATIC (agent_table);
579
580 static GstDtlsAgent *generated_cert_agent = NULL;
581
582 static GstDtlsAgent *
583 get_agent_by_pem (const gchar * pem)
584 {
585   GstDtlsAgent *agent;
586
587   if (!pem) {
588     if (g_once_init_enter (&generated_cert_agent)) {
589       GstDtlsAgent *new_agent;
590       GObject *certificate;
591
592       certificate = g_object_new (GST_TYPE_DTLS_CERTIFICATE, NULL);
593       new_agent = g_object_new (GST_TYPE_DTLS_AGENT, "certificate",
594           certificate, NULL);
595       g_object_unref (certificate);
596
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);
600     } else {
601       GST_DEBUG_OBJECT (generated_cert_agent,
602           "using agent with generated cert");
603     }
604
605     agent = generated_cert_agent;
606     g_object_ref (agent);
607   } else {
608     G_LOCK (agent_table);
609
610     if (!agent_table) {
611       agent_table =
612           g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
613     }
614
615     agent = GST_DTLS_AGENT (g_hash_table_lookup (agent_table, pem));
616
617     if (!agent) {
618       GObject *certificate;
619
620       certificate = g_object_new (GST_TYPE_DTLS_CERTIFICATE, "pem", pem, NULL);
621       agent = g_object_new (GST_TYPE_DTLS_AGENT, "certificate", certificate,
622           NULL);
623       g_object_unref (certificate);
624
625       g_object_weak_ref (G_OBJECT (agent), (GWeakNotify) agent_weak_ref_notify,
626           (gpointer) g_strdup (pem));
627
628       g_hash_table_insert (agent_table, g_strdup (pem), agent);
629
630       GST_DEBUG_OBJECT (agent, "no agent found, created new");
631     } else {
632       g_object_ref (agent);
633       GST_DEBUG_OBJECT (agent, "agent found");
634     }
635
636     G_UNLOCK (agent_table);
637   }
638
639
640   return agent;
641 }
642
643 static void
644 agent_weak_ref_notify (gchar * pem, GstDtlsAgent * agent)
645 {
646   G_LOCK (agent_table);
647   g_hash_table_remove (agent_table, pem);
648   G_UNLOCK (agent_table);
649
650   g_free (pem);
651   pem = NULL;
652 }
653
654 static GHashTable *connection_table = NULL;
655 G_LOCK_DEFINE_STATIC (connection_table);
656
657 GstDtlsConnection *
658 gst_dtls_dec_fetch_connection (gchar * id)
659 {
660   GstDtlsConnection *connection;
661   g_return_val_if_fail (id, NULL);
662
663   GST_DEBUG ("fetching '%s' from connection table, size is %d",
664       id, g_hash_table_size (connection_table));
665
666   G_LOCK (connection_table);
667
668   connection = g_hash_table_lookup (connection_table, id);
669
670   if (connection) {
671     g_object_ref (connection);
672     g_hash_table_remove (connection_table, id);
673   } else {
674     GST_WARNING ("no connection with id '%s' found", id);
675   }
676
677   G_UNLOCK (connection_table);
678
679   return connection;
680 }
681
682 static void
683 create_connection (GstDtlsDec * self, gchar * id)
684 {
685   g_return_if_fail (GST_IS_DTLS_DEC (self));
686   g_return_if_fail (GST_IS_DTLS_AGENT (self->agent));
687
688   if (self->connection) {
689     g_object_unref (self->connection);
690     self->connection = NULL;
691   }
692
693   G_LOCK (connection_table);
694
695   if (!connection_table) {
696     connection_table =
697         g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
698   }
699
700   if (g_hash_table_contains (connection_table, id)) {
701     G_UNLOCK (connection_table);
702
703     g_return_if_reached ();
704   }
705
706   self->connection =
707       g_object_new (GST_TYPE_DTLS_CONNECTION, "agent", self->agent, NULL);
708
709   g_object_weak_ref (G_OBJECT (self->connection),
710       (GWeakNotify) connection_weak_ref_notify, g_strdup (id));
711
712   g_hash_table_insert (connection_table, g_strdup (id), self->connection);
713
714   G_UNLOCK (connection_table);
715 }
716
717 static void
718 connection_weak_ref_notify (gchar * id, GstDtlsConnection * connection)
719 {
720   G_LOCK (connection_table);
721   g_hash_table_remove (connection_table, id);
722   G_UNLOCK (connection_table);
723
724   g_free (id);
725   id = NULL;
726 }