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 "gstdtlsenc.h"
31 
32 #include "gstdtlsdec.h"
33 
34 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
35     GST_PAD_SINK,
36     GST_PAD_REQUEST,
37     GST_STATIC_CAPS_ANY);
38 
39 static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
40     GST_PAD_SRC,
41     GST_PAD_ALWAYS,
42     GST_STATIC_CAPS ("application/x-dtls")
43     );
44 
45 GST_DEBUG_CATEGORY_STATIC (gst_dtls_enc_debug);
46 #define GST_CAT_DEFAULT gst_dtls_enc_debug
47 
48 #define gst_dtls_enc_parent_class parent_class
49 G_DEFINE_TYPE_WITH_CODE (GstDtlsEnc, gst_dtls_enc, GST_TYPE_ELEMENT,
50     GST_DEBUG_CATEGORY_INIT (gst_dtls_enc_debug, "dtlsenc", 0, "DTLS Encoder"));
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_IS_CLIENT,
65 
66   PROP_ENCODER_KEY,
67   PROP_SRTP_CIPHER,
68   PROP_SRTP_AUTH,
69   NUM_PROPERTIES
70 };
71 
72 static GParamSpec *properties[NUM_PROPERTIES];
73 
74 #define DEFAULT_CONNECTION_ID NULL
75 #define DEFAULT_IS_CLIENT FALSE
76 
77 #define DEFAULT_ENCODER_KEY NULL
78 #define DEFAULT_SRTP_CIPHER 0
79 #define DEFAULT_SRTP_AUTH 0
80 
81 #define INITIAL_QUEUE_SIZE 64
82 
83 static void gst_dtls_enc_finalize (GObject *);
84 static void gst_dtls_enc_set_property (GObject *, guint prop_id,
85     const GValue *, GParamSpec *);
86 static void gst_dtls_enc_get_property (GObject *, guint prop_id, GValue *,
87     GParamSpec *);
88 
89 static GstStateChangeReturn gst_dtls_enc_change_state (GstElement *,
90     GstStateChange);
91 static GstPad *gst_dtls_enc_request_new_pad (GstElement *, GstPadTemplate *,
92     const gchar * name, const GstCaps *);
93 
94 static gboolean src_activate_mode (GstPad *, GstObject *, GstPadMode,
95     gboolean active);
96 static void src_task_loop (GstPad *);
97 
98 static GstFlowReturn sink_chain (GstPad *, GstObject *, GstBuffer *);
99 static gboolean sink_event (GstPad * pad, GstObject * parent, GstEvent * event);
100 
101 static void on_key_received (GstDtlsConnection *, gpointer key, guint cipher,
102     guint auth, GstDtlsEnc *);
103 static void on_send_data (GstDtlsConnection *, gconstpointer data, gint length,
104     GstDtlsEnc *);
105 
106 static void
gst_dtls_enc_class_init(GstDtlsEncClass * klass)107 gst_dtls_enc_class_init (GstDtlsEncClass * klass)
108 {
109   GObjectClass *gobject_class;
110   GstElementClass *element_class;
111 
112   gobject_class = (GObjectClass *) klass;
113   element_class = (GstElementClass *) klass;
114 
115   gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_dtls_enc_finalize);
116   gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_dtls_enc_set_property);
117   gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_dtls_enc_get_property);
118 
119   element_class->change_state = GST_DEBUG_FUNCPTR (gst_dtls_enc_change_state);
120   element_class->request_new_pad =
121       GST_DEBUG_FUNCPTR (gst_dtls_enc_request_new_pad);
122 
123   signals[SIGNAL_ON_KEY_RECEIVED] =
124       g_signal_new ("on-key-received", G_TYPE_FROM_CLASS (klass),
125       G_SIGNAL_RUN_LAST, 0, NULL, NULL,
126       g_cclosure_marshal_generic, G_TYPE_NONE, 0);
127 
128   properties[PROP_CONNECTION_ID] =
129       g_param_spec_string ("connection-id",
130       "Connection id",
131       "Every encoder/decoder pair should have the same, unique, connection-id",
132       DEFAULT_CONNECTION_ID, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
133 
134   properties[PROP_IS_CLIENT] =
135       g_param_spec_boolean ("is-client",
136       "Is client",
137       "Set to true if the decoder should act as "
138       "client and initiate the handshake",
139       DEFAULT_IS_CLIENT,
140       GST_PARAM_MUTABLE_READY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
141 
142   properties[PROP_ENCODER_KEY] =
143       g_param_spec_boxed ("encoder-key",
144       "Encoder key",
145       "Master key that should be used by the SRTP encoder",
146       GST_TYPE_BUFFER, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
147 
148   properties[PROP_SRTP_CIPHER] =
149       g_param_spec_uint ("srtp-cipher",
150       "SRTP cipher",
151       "The SRTP cipher selected in the DTLS handshake. "
152       "The value will be set to an GstDtlsSrtpCipher.",
153       0, GST_DTLS_SRTP_CIPHER_AES_128_ICM, DEFAULT_SRTP_CIPHER,
154       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
155 
156   properties[PROP_SRTP_AUTH] =
157       g_param_spec_uint ("srtp-auth",
158       "SRTP authentication",
159       "The SRTP authentication selected in the DTLS handshake. "
160       "The value will be set to an GstDtlsSrtpAuth.",
161       0, GST_DTLS_SRTP_AUTH_HMAC_SHA1_80, DEFAULT_SRTP_AUTH,
162       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
163 
164   g_object_class_install_properties (gobject_class, NUM_PROPERTIES, properties);
165 
166   gst_element_class_add_static_pad_template (element_class, &src_template);
167   gst_element_class_add_static_pad_template (element_class, &sink_template);
168 
169   gst_element_class_set_static_metadata (element_class,
170       "DTLS Encoder",
171       "Encoder/Network/DTLS",
172       "Encodes packets with DTLS",
173       "Patrik Oldsberg patrik.oldsberg@ericsson.com");
174 }
175 
176 static void
gst_dtls_enc_init(GstDtlsEnc * self)177 gst_dtls_enc_init (GstDtlsEnc * self)
178 {
179   self->connection_id = NULL;
180   self->connection = NULL;
181 
182   self->is_client = DEFAULT_IS_CLIENT;
183 
184   self->encoder_key = NULL;
185   self->srtp_cipher = DEFAULT_SRTP_CIPHER;
186   self->srtp_auth = DEFAULT_SRTP_AUTH;
187 
188   g_queue_init (&self->queue);
189   g_mutex_init (&self->queue_lock);
190   g_cond_init (&self->queue_cond_add);
191 
192   self->src = gst_pad_new_from_static_template (&src_template, "src");
193   g_return_if_fail (self->src);
194 
195   gst_pad_set_activatemode_function (self->src,
196       GST_DEBUG_FUNCPTR (src_activate_mode));
197 
198   gst_element_add_pad (GST_ELEMENT (self), self->src);
199 }
200 
201 static void
gst_dtls_enc_finalize(GObject * object)202 gst_dtls_enc_finalize (GObject * object)
203 {
204   GstDtlsEnc *self = GST_DTLS_ENC (object);
205 
206   if (self->encoder_key) {
207     gst_buffer_unref (self->encoder_key);
208     self->encoder_key = NULL;
209   }
210 
211   if (self->connection_id) {
212     g_free (self->connection_id);
213     self->connection_id = NULL;
214   }
215 
216   g_mutex_lock (&self->queue_lock);
217   g_queue_foreach (&self->queue, (GFunc) gst_buffer_unref, NULL);
218   g_queue_clear (&self->queue);
219   g_mutex_unlock (&self->queue_lock);
220 
221   g_mutex_clear (&self->queue_lock);
222   g_cond_clear (&self->queue_cond_add);
223 
224   GST_LOG_OBJECT (self, "finalized");
225 
226   G_OBJECT_CLASS (parent_class)->finalize (object);
227 }
228 
229 static void
gst_dtls_enc_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)230 gst_dtls_enc_set_property (GObject * object, guint prop_id,
231     const GValue * value, GParamSpec * pspec)
232 {
233   GstDtlsEnc *self = GST_DTLS_ENC (object);
234 
235   switch (prop_id) {
236     case PROP_CONNECTION_ID:
237       if (self->connection_id != NULL) {
238         g_free (self->connection_id);
239         self->connection_id = NULL;
240       }
241       self->connection_id = g_value_dup_string (value);
242       break;
243     case PROP_IS_CLIENT:
244       self->is_client = g_value_get_boolean (value);
245       break;
246     default:
247       G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec);
248   }
249 }
250 
251 static void
gst_dtls_enc_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)252 gst_dtls_enc_get_property (GObject * object, guint prop_id, GValue * value,
253     GParamSpec * pspec)
254 {
255   GstDtlsEnc *self = GST_DTLS_ENC (object);
256 
257   switch (prop_id) {
258     case PROP_CONNECTION_ID:
259       g_value_set_string (value, self->connection_id);
260       break;
261     case PROP_IS_CLIENT:
262       g_value_set_boolean (value, self->is_client);
263       break;
264     case PROP_ENCODER_KEY:
265       g_value_set_boxed (value, self->encoder_key);
266       break;
267     case PROP_SRTP_CIPHER:
268       g_value_set_uint (value, self->srtp_cipher);
269       break;
270     case PROP_SRTP_AUTH:
271       g_value_set_uint (value, self->srtp_auth);
272       break;
273     default:
274       G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec);
275   }
276 }
277 
278 static GstStateChangeReturn
gst_dtls_enc_change_state(GstElement * element,GstStateChange transition)279 gst_dtls_enc_change_state (GstElement * element, GstStateChange transition)
280 {
281   GstDtlsEnc *self = GST_DTLS_ENC (element);
282   GstStateChangeReturn ret;
283 
284   switch (transition) {
285     case GST_STATE_CHANGE_NULL_TO_READY:
286       if (self->connection_id) {
287         self->connection = gst_dtls_dec_fetch_connection (self->connection_id);
288 
289         if (!self->connection) {
290           GST_WARNING_OBJECT (self,
291               "invalid connection id: '%s', connection not found or already in use",
292               self->connection_id);
293           return GST_STATE_CHANGE_FAILURE;
294         }
295 
296         g_signal_connect_object (self->connection,
297             "on-encoder-key", G_CALLBACK (on_key_received), self, 0);
298 
299         gst_dtls_connection_set_send_callback (self->connection,
300             g_cclosure_new (G_CALLBACK (on_send_data), self, NULL));
301       } else {
302         GST_WARNING_OBJECT (self,
303             "trying to change state to ready without connection id");
304         return GST_STATE_CHANGE_FAILURE;
305       }
306       break;
307     case GST_STATE_CHANGE_PAUSED_TO_READY:
308       GST_DEBUG_OBJECT (self, "stopping connection %s", self->connection_id);
309 
310       gst_dtls_connection_stop (self->connection);
311       break;
312     case GST_STATE_CHANGE_READY_TO_NULL:
313       GST_DEBUG_OBJECT (self, "closing connection %s", self->connection_id);
314 
315       if (self->connection) {
316         gst_dtls_connection_close (self->connection);
317         gst_dtls_connection_set_send_callback (self->connection, NULL);
318         g_object_unref (self->connection);
319         self->connection = NULL;
320       }
321       break;
322     default:
323       break;
324   }
325 
326   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
327 
328   switch (transition) {
329     case GST_STATE_CHANGE_READY_TO_PAUSED:
330       GST_DEBUG_OBJECT (self, "starting connection %s", self->connection_id);
331       gst_dtls_connection_start (self->connection, self->is_client);
332       break;
333     default:
334       break;
335   }
336 
337   return ret;
338 }
339 
340 static GstPad *
gst_dtls_enc_request_new_pad(GstElement * element,GstPadTemplate * templ,const gchar * name,const GstCaps * caps)341 gst_dtls_enc_request_new_pad (GstElement * element,
342     GstPadTemplate * templ, const gchar * name, const GstCaps * caps)
343 {
344   GstPad *sink;
345   gboolean ret;
346 
347   GST_DEBUG_OBJECT (element, "sink pad requested");
348 
349   g_return_val_if_fail (templ->direction == GST_PAD_SINK, NULL);
350 
351   sink = gst_pad_new_from_template (templ, name);
352   g_return_val_if_fail (sink, NULL);
353 
354   if (caps) {
355     g_object_set (sink, "caps", caps, NULL);
356   }
357 
358   gst_pad_set_chain_function (sink, GST_DEBUG_FUNCPTR (sink_chain));
359   gst_pad_set_event_function (sink, GST_DEBUG_FUNCPTR (sink_event));
360 
361   ret = gst_pad_set_active (sink, TRUE);
362   g_warn_if_fail (ret);
363 
364   gst_element_add_pad (element, sink);
365 
366   return sink;
367 }
368 
369 static gboolean
src_activate_mode(GstPad * pad,GstObject * parent,GstPadMode mode,gboolean active)370 src_activate_mode (GstPad * pad, GstObject * parent, GstPadMode mode,
371     gboolean active)
372 {
373   GstDtlsEnc *self = GST_DTLS_ENC (parent);
374   gboolean success = TRUE;
375   g_return_val_if_fail (mode == GST_PAD_MODE_PUSH, FALSE);
376 
377   if (active) {
378     GST_DEBUG_OBJECT (self, "src pad activating in push mode");
379 
380     self->flushing = FALSE;
381     self->send_initial_events = TRUE;
382     success =
383         gst_pad_start_task (pad, (GstTaskFunction) src_task_loop, self->src,
384         NULL);
385     if (!success) {
386       GST_WARNING_OBJECT (self, "failed to activate pad task");
387     }
388   } else {
389     GST_DEBUG_OBJECT (self, "deactivating src pad");
390 
391     g_mutex_lock (&self->queue_lock);
392     g_queue_foreach (&self->queue, (GFunc) gst_buffer_unref, NULL);
393     g_queue_clear (&self->queue);
394     self->flushing = TRUE;
395     g_cond_signal (&self->queue_cond_add);
396     g_mutex_unlock (&self->queue_lock);
397     success = gst_pad_stop_task (pad);
398     if (!success) {
399       GST_WARNING_OBJECT (self, "failed to deactivate pad task");
400     }
401   }
402 
403   return success;
404 }
405 
406 static void
src_task_loop(GstPad * pad)407 src_task_loop (GstPad * pad)
408 {
409   GstDtlsEnc *self = GST_DTLS_ENC (GST_PAD_PARENT (pad));
410   GstFlowReturn ret;
411   GstBuffer *buffer;
412   gboolean check_connection_timeout = FALSE;
413 
414   GST_TRACE_OBJECT (self, "src loop: acquiring lock");
415   g_mutex_lock (&self->queue_lock);
416   GST_TRACE_OBJECT (self, "src loop: acquired lock");
417 
418   if (self->flushing) {
419     GST_LOG_OBJECT (self, "src task loop entered on inactive pad");
420     GST_TRACE_OBJECT (self, "src loop: releasing lock");
421     g_mutex_unlock (&self->queue_lock);
422     return;
423   }
424 
425   while (g_queue_is_empty (&self->queue)) {
426     GST_TRACE_OBJECT (self, "src loop: queue empty, waiting for add");
427     g_cond_wait (&self->queue_cond_add, &self->queue_lock);
428     GST_TRACE_OBJECT (self, "src loop: add signaled");
429 
430     if (self->flushing) {
431       GST_LOG_OBJECT (self, "pad inactive, task returning");
432       GST_TRACE_OBJECT (self, "src loop: releasing lock");
433       g_mutex_unlock (&self->queue_lock);
434       return;
435     }
436   }
437   GST_TRACE_OBJECT (self, "src loop: queue has element");
438 
439   buffer = g_queue_pop_head (&self->queue);
440   g_mutex_unlock (&self->queue_lock);
441 
442   if (self->send_initial_events) {
443     GstSegment segment;
444     gchar s_id[32];
445     GstCaps *caps;
446 
447     self->send_initial_events = FALSE;
448 
449     g_snprintf (s_id, sizeof (s_id), "dtlsenc-%08x", g_random_int ());
450     gst_pad_push_event (self->src, gst_event_new_stream_start (s_id));
451     caps = gst_caps_new_empty_simple ("application/x-dtls");
452     gst_pad_push_event (self->src, gst_event_new_caps (caps));
453     gst_caps_unref (caps);
454     gst_segment_init (&segment, GST_FORMAT_BYTES);
455     gst_pad_push_event (self->src, gst_event_new_segment (&segment));
456     check_connection_timeout = TRUE;
457   }
458 
459   GST_TRACE_OBJECT (self, "src loop: releasing lock");
460 
461   ret = gst_pad_push (self->src, buffer);
462   if (check_connection_timeout)
463     gst_dtls_connection_check_timeout (self->connection);
464 
465   if (G_UNLIKELY (ret != GST_FLOW_OK)) {
466     GST_WARNING_OBJECT (self, "failed to push buffer on src pad: %s",
467         gst_flow_get_name (ret));
468   }
469 }
470 
471 static GstFlowReturn
sink_chain(GstPad * pad,GstObject * parent,GstBuffer * buffer)472 sink_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
473 {
474   GstDtlsEnc *self = GST_DTLS_ENC (parent);
475   GstMapInfo map_info;
476   gint ret;
477 
478   gst_buffer_map (buffer, &map_info, GST_MAP_READ);
479 
480   if (map_info.size) {
481     ret =
482         gst_dtls_connection_send (self->connection, map_info.data,
483         map_info.size);
484     if (ret != map_info.size) {
485       GST_WARNING_OBJECT (self,
486           "error sending data: %d B were written, expected value was %"
487           G_GSIZE_FORMAT " B", ret, map_info.size);
488     }
489   }
490 
491   gst_buffer_unmap (buffer, &map_info);
492 
493   gst_buffer_unref (buffer);
494 
495   return GST_FLOW_OK;
496 }
497 
498 
499 static gboolean
sink_event(GstPad * pad,GstObject * parent,GstEvent * event)500 sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
501 {
502   gboolean ret = FALSE;
503 
504   switch (GST_EVENT_TYPE (event)) {
505       /* Drop segment, stream-start as we will push our own from the src pad
506        * task.
507        * FIXME: do we need any information from upstream for pushing our own? */
508     case GST_EVENT_SEGMENT:
509     case GST_EVENT_STREAM_START:
510       gst_event_unref (event);
511       ret = TRUE;
512       break;
513     default:
514       ret = gst_pad_event_default (pad, parent, event);
515       break;
516   }
517 
518   return ret;
519 }
520 
521 static void
on_key_received(GstDtlsConnection * connection,gpointer key,guint cipher,guint auth,GstDtlsEnc * self)522 on_key_received (GstDtlsConnection * connection, gpointer key, guint cipher,
523     guint auth, GstDtlsEnc * self)
524 {
525   gpointer key_dup;
526   gchar *key_str;
527 
528   g_return_if_fail (GST_IS_DTLS_ENC (self));
529   g_return_if_fail (GST_IS_DTLS_CONNECTION (connection));
530 
531   self->srtp_cipher = cipher;
532   self->srtp_auth = auth;
533 
534   key_dup = g_memdup (key, GST_DTLS_SRTP_MASTER_KEY_LENGTH);
535 
536   if (self->encoder_key) {
537     gst_buffer_unref (self->encoder_key);
538     self->encoder_key = NULL;
539   }
540 
541   self->encoder_key =
542       gst_buffer_new_wrapped (key_dup, GST_DTLS_SRTP_MASTER_KEY_LENGTH);
543 
544   key_str = g_base64_encode (key, GST_DTLS_SRTP_MASTER_KEY_LENGTH);
545   GST_INFO_OBJECT (self, "received key: %s", key_str);
546   g_free (key_str);
547 
548   g_signal_emit (self, signals[SIGNAL_ON_KEY_RECEIVED], 0);
549 }
550 
551 static void
on_send_data(GstDtlsConnection * connection,gconstpointer data,gint length,GstDtlsEnc * self)552 on_send_data (GstDtlsConnection * connection, gconstpointer data, gint length,
553     GstDtlsEnc * self)
554 {
555   GstBuffer *buffer;
556 
557   GST_DEBUG_OBJECT (self, "sending data from %s with length %d",
558       self->connection_id, length);
559 
560   buffer = gst_buffer_new_wrapped (g_memdup (data, length), length);
561 
562   GST_TRACE_OBJECT (self, "send data: acquiring lock");
563   g_mutex_lock (&self->queue_lock);
564   GST_TRACE_OBJECT (self, "send data: acquired lock");
565 
566   g_queue_push_tail (&self->queue, buffer);
567 
568   GST_TRACE_OBJECT (self, "send data: signaling add");
569   g_cond_signal (&self->queue_cond_add);
570 
571   GST_TRACE_OBJECT (self, "send data: releasing lock");
572   g_mutex_unlock (&self->queue_lock);
573 }
574