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