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 "gstdtlssrtpenc.h"
31
32 #include <stdio.h>
33
34 static GstStaticPadTemplate rtp_sink_template =
35 GST_STATIC_PAD_TEMPLATE ("rtp_sink_%d",
36 GST_PAD_SINK,
37 GST_PAD_REQUEST,
38 GST_STATIC_CAPS ("application/x-rtp;application/x-rtcp")
39 );
40
41 static GstStaticPadTemplate rtcp_sink_template =
42 GST_STATIC_PAD_TEMPLATE ("rtcp_sink_%d",
43 GST_PAD_SINK,
44 GST_PAD_REQUEST,
45 GST_STATIC_CAPS ("application/x-rtp;application/x-rtcp")
46 );
47
48 static GstStaticPadTemplate data_sink_template =
49 GST_STATIC_PAD_TEMPLATE ("data_sink",
50 GST_PAD_SINK,
51 GST_PAD_REQUEST,
52 GST_STATIC_CAPS_ANY);
53
54 static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
55 GST_PAD_SRC,
56 GST_PAD_ALWAYS,
57 GST_STATIC_CAPS_ANY);
58
59 GST_DEBUG_CATEGORY_STATIC (gst_dtls_srtp_enc_debug);
60 #define GST_CAT_DEFAULT gst_dtls_srtp_enc_debug
61
62 #define gst_dtls_srtp_enc_parent_class parent_class
63 G_DEFINE_TYPE_WITH_CODE (GstDtlsSrtpEnc, gst_dtls_srtp_enc,
64 GST_TYPE_DTLS_SRTP_BIN, GST_DEBUG_CATEGORY_INIT (gst_dtls_srtp_enc_debug,
65 "dtlssrtpenc", 0, "DTLS Decoder"));
66
67 enum
68 {
69 SIGNAL_ON_KEY_SET,
70 NUM_SIGNALS
71 };
72
73 static guint signals[NUM_SIGNALS];
74
75 enum
76 {
77 PROP_0,
78 PROP_IS_CLIENT,
79 NUM_PROPERTIES
80 };
81
82 static GParamSpec *properties[NUM_PROPERTIES];
83
84 #define DEFAULT_IS_CLIENT FALSE
85
86 static gboolean transform_enum (GBinding *, const GValue * source_value,
87 GValue * target_value, GEnumClass *);
88
89 static void gst_dtls_srtp_enc_set_property (GObject *, guint prop_id,
90 const GValue *, GParamSpec *);
91 static void gst_dtls_srtp_enc_get_property (GObject *, guint prop_id,
92 GValue *, GParamSpec *);
93
94 static GstPad *add_ghost_pad (GstElement *, const gchar * name, GstPad *,
95 GstPadTemplate *);
96 static GstPad *gst_dtls_srtp_enc_request_new_pad (GstElement *,
97 GstPadTemplate *, const gchar * name, const GstCaps *);
98
99 static void on_key_received (GObject * encoder, GstDtlsSrtpEnc *);
100
101 static void gst_dtls_srtp_enc_remove_dtls_element (GstDtlsSrtpBin *);
102 static GstPadProbeReturn remove_dtls_encoder_probe_callback (GstPad *,
103 GstPadProbeInfo *, GstElement *);
104
105 static void
gst_dtls_srtp_enc_class_init(GstDtlsSrtpEncClass * klass)106 gst_dtls_srtp_enc_class_init (GstDtlsSrtpEncClass * klass)
107 {
108 GObjectClass *gobject_class;
109 GstElementClass *element_class;
110 GstDtlsSrtpBinClass *dtls_srtp_bin_class;
111
112 gobject_class = (GObjectClass *) klass;
113 element_class = (GstElementClass *) klass;
114 dtls_srtp_bin_class = (GstDtlsSrtpBinClass *) klass;
115
116 gobject_class->set_property =
117 GST_DEBUG_FUNCPTR (gst_dtls_srtp_enc_set_property);
118 gobject_class->get_property =
119 GST_DEBUG_FUNCPTR (gst_dtls_srtp_enc_get_property);
120
121 element_class->request_new_pad =
122 GST_DEBUG_FUNCPTR (gst_dtls_srtp_enc_request_new_pad);
123
124 dtls_srtp_bin_class->remove_dtls_element =
125 GST_DEBUG_FUNCPTR (gst_dtls_srtp_enc_remove_dtls_element);
126
127 signals[SIGNAL_ON_KEY_SET] =
128 g_signal_new ("on-key-set", G_TYPE_FROM_CLASS (klass),
129 G_SIGNAL_RUN_LAST, 0, NULL, NULL,
130 g_cclosure_marshal_generic, G_TYPE_NONE, 0);
131
132 properties[PROP_IS_CLIENT] =
133 g_param_spec_boolean ("is-client",
134 "Is client",
135 "Set to true if the decoder should act as "
136 "client and initiate the handshake",
137 DEFAULT_IS_CLIENT,
138 GST_PARAM_MUTABLE_READY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
139
140 g_object_class_install_properties (gobject_class, NUM_PROPERTIES, properties);
141
142 gst_element_class_add_static_pad_template (element_class, &rtp_sink_template);
143 gst_element_class_add_static_pad_template (element_class,
144 &rtcp_sink_template);
145 gst_element_class_add_static_pad_template (element_class,
146 &data_sink_template);
147 gst_element_class_add_static_pad_template (element_class, &src_template);
148
149 gst_element_class_set_static_metadata (element_class,
150 "DTLS-SRTP Encoder",
151 "Encoder/Network/DTLS/SRTP",
152 "Encodes SRTP packets with a key received from DTLS",
153 "Patrik Oldsberg patrik.oldsberg@ericsson.com");
154 }
155
156 static void
gst_dtls_srtp_enc_init(GstDtlsSrtpEnc * self)157 gst_dtls_srtp_enc_init (GstDtlsSrtpEnc * self)
158 {
159 GstElementClass *klass = GST_ELEMENT_GET_CLASS (GST_ELEMENT (self));
160 static GEnumClass *cipher_enum_class, *auth_enum_class;
161 gboolean ret;
162
163 /*
164 +--------------------+ +-----------------+
165 rtp_sink-R-o|rtp_sink rtp_src|o-R-o| |
166 | srtpenc | | |
167 rtcp_sink-R-o|srtcp_sink rtcp_src|o-R-o| |
168 +--------------------+ | funnel |o---src
169 | |
170 +--------------------+ | |
171 data_sink-R-o| dtlsenc |o---o| |
172 +--------------------+ +-----------------+
173 */
174
175 self->srtp_enc = gst_element_factory_make ("srtpenc", NULL);
176 if (!self->srtp_enc) {
177 GST_ERROR_OBJECT (self,
178 "failed to create srtp encoder, is the srtp plugin registered?");
179 return;
180 }
181 g_return_if_fail (self->srtp_enc);
182 self->bin.dtls_element = gst_element_factory_make ("dtlsenc", NULL);
183 if (!self->bin.dtls_element) {
184 GST_ERROR_OBJECT (self, "failed to create dtls encoder");
185 return;
186 }
187 self->funnel = gst_element_factory_make ("funnel", NULL);
188 if (!self->funnel) {
189 GST_ERROR_OBJECT (self, "failed to create funnel");
190 return;
191 }
192
193 gst_bin_add_many (GST_BIN (self), self->bin.dtls_element, self->srtp_enc,
194 self->funnel, NULL);
195
196 ret = gst_element_link (self->bin.dtls_element, self->funnel);
197 g_return_if_fail (ret);
198
199 add_ghost_pad (GST_ELEMENT (self), "src",
200 gst_element_get_static_pad (self->funnel, "src"),
201 gst_element_class_get_pad_template (klass, "src"));
202
203 g_signal_connect (self->bin.dtls_element, "on-key-received",
204 G_CALLBACK (on_key_received), self);
205
206 if (g_once_init_enter (&cipher_enum_class)) {
207 GType type = g_type_from_name ("GstSrtpCipherType");
208 g_assert (type);
209 g_once_init_leave (&cipher_enum_class, g_type_class_peek (type));
210 }
211 if (g_once_init_enter (&auth_enum_class)) {
212 GType type = g_type_from_name ("GstSrtpAuthType");
213 g_assert (type);
214 g_once_init_leave (&auth_enum_class, g_type_class_peek (type));
215 }
216
217 g_object_set (self->srtp_enc, "random-key", TRUE, NULL);
218
219 g_object_bind_property (G_OBJECT (self), "key", self->srtp_enc, "key",
220 G_BINDING_DEFAULT);
221 g_object_bind_property_full (G_OBJECT (self), "srtp-cipher", self->srtp_enc,
222 "rtp-cipher", G_BINDING_DEFAULT, (GBindingTransformFunc) transform_enum,
223 NULL, cipher_enum_class, NULL);
224 g_object_bind_property_full (G_OBJECT (self), "srtcp-cipher", self->srtp_enc,
225 "rtcp-cipher", G_BINDING_DEFAULT, (GBindingTransformFunc) transform_enum,
226 NULL, cipher_enum_class, NULL);
227 g_object_bind_property_full (G_OBJECT (self), "srtp-auth", self->srtp_enc,
228 "rtp-auth", G_BINDING_DEFAULT, (GBindingTransformFunc) transform_enum,
229 NULL, auth_enum_class, NULL);
230 g_object_bind_property_full (G_OBJECT (self), "srtcp-auth", self->srtp_enc,
231 "rtcp-auth", G_BINDING_DEFAULT, (GBindingTransformFunc) transform_enum,
232 NULL, auth_enum_class, NULL);
233 }
234
235 static gboolean
transform_enum(GBinding * binding,const GValue * source_value,GValue * target_value,GEnumClass * enum_class)236 transform_enum (GBinding * binding, const GValue * source_value,
237 GValue * target_value, GEnumClass * enum_class)
238 {
239 GEnumValue *enum_value;
240 const gchar *nick;
241
242 nick = g_value_get_string (source_value);
243 g_return_val_if_fail (nick, FALSE);
244
245 enum_value = g_enum_get_value_by_nick (enum_class, nick);
246 g_return_val_if_fail (enum_value, FALSE);
247
248 GST_DEBUG_OBJECT (g_binding_get_source (binding),
249 "transforming enum from %s to %d", nick, enum_value->value);
250
251 g_value_set_enum (target_value, enum_value->value);
252
253 return TRUE;
254 }
255
256 static void
gst_dtls_srtp_enc_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)257 gst_dtls_srtp_enc_set_property (GObject * object,
258 guint prop_id, const GValue * value, GParamSpec * pspec)
259 {
260 GstDtlsSrtpEnc *self = GST_DTLS_SRTP_ENC (object);
261
262 switch (prop_id) {
263 case PROP_IS_CLIENT:
264 if (self->bin.dtls_element) {
265 g_object_set_property (G_OBJECT (self->bin.dtls_element), "is-client",
266 value);
267 } else {
268 GST_WARNING_OBJECT (self,
269 "tried to set is-client after disabling DTLS");
270 }
271 break;
272 default:
273 G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec);
274 }
275 }
276
277 static void
gst_dtls_srtp_enc_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)278 gst_dtls_srtp_enc_get_property (GObject * object,
279 guint prop_id, GValue * value, GParamSpec * pspec)
280 {
281 GstDtlsSrtpEnc *self = GST_DTLS_SRTP_ENC (object);
282
283 switch (prop_id) {
284 case PROP_IS_CLIENT:
285 if (self->bin.dtls_element) {
286 g_object_get_property (G_OBJECT (self->bin.dtls_element), "is-client",
287 value);
288 } else {
289 GST_WARNING_OBJECT (self,
290 "tried to get is-client after disabling DTLS");
291 }
292 break;
293 default:
294 G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec);
295 }
296 }
297
298 static GstPad *
add_ghost_pad(GstElement * element,const gchar * name,GstPad * target,GstPadTemplate * templ)299 add_ghost_pad (GstElement * element,
300 const gchar * name, GstPad * target, GstPadTemplate * templ)
301 {
302 GstPad *pad;
303 gboolean ret;
304
305 pad = gst_ghost_pad_new_from_template (name, target, templ);
306 gst_object_unref (target);
307 target = NULL;
308
309 ret = gst_pad_set_active (pad, TRUE);
310 g_warn_if_fail (ret);
311
312 ret = gst_element_add_pad (element, pad);
313 g_warn_if_fail (ret);
314
315 return pad;
316 }
317
318 static GstPad *
gst_dtls_srtp_enc_request_new_pad(GstElement * element,GstPadTemplate * templ,const gchar * name,const GstCaps * caps)319 gst_dtls_srtp_enc_request_new_pad (GstElement * element,
320 GstPadTemplate * templ, const gchar * name, const GstCaps * caps)
321 {
322 GstDtlsSrtpEnc *self = GST_DTLS_SRTP_ENC (element);
323 GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
324 GstPad *target_pad;
325 GstPad *ghost_pad = NULL;
326 guint pad_n;
327 gchar *srtp_src_name;
328
329 GST_DEBUG_OBJECT (element, "pad requested");
330
331 g_return_val_if_fail (templ->direction == GST_PAD_SINK, NULL);
332 g_return_val_if_fail (self->srtp_enc, NULL);
333
334 if (templ == gst_element_class_get_pad_template (klass, "rtp_sink_%d")) {
335 target_pad = gst_element_get_request_pad (self->srtp_enc, name);
336 g_return_val_if_fail (target_pad, NULL);
337
338 sscanf (GST_PAD_NAME (target_pad), "rtp_sink_%d", &pad_n);
339 srtp_src_name = g_strdup_printf ("rtp_src_%d", pad_n);
340
341 gst_element_link_pads (self->srtp_enc, srtp_src_name, self->funnel, NULL);
342
343 g_free (srtp_src_name);
344
345 ghost_pad = add_ghost_pad (element, name, target_pad, templ);
346
347 GST_LOG_OBJECT (self, "added rtp sink pad");
348 } else if (templ == gst_element_class_get_pad_template (klass,
349 "rtcp_sink_%d")) {
350 target_pad = gst_element_get_request_pad (self->srtp_enc, name);
351 g_return_val_if_fail (target_pad, NULL);
352
353 sscanf (GST_PAD_NAME (target_pad), "rtcp_sink_%d", &pad_n);
354 srtp_src_name = g_strdup_printf ("rtcp_src_%d", pad_n);
355
356 gst_element_link_pads (self->srtp_enc, srtp_src_name, self->funnel, NULL);
357
358 g_free (srtp_src_name);
359
360 ghost_pad = add_ghost_pad (element, name, target_pad, templ);
361
362 GST_LOG_OBJECT (self, "added rtcp sink pad");
363 } else if (templ == gst_element_class_get_pad_template (klass, "data_sink")) {
364 g_return_val_if_fail (self->bin.dtls_element, NULL);
365 target_pad = gst_element_get_request_pad (self->bin.dtls_element, "sink");
366
367 ghost_pad = add_ghost_pad (element, name, target_pad, templ);
368
369 GST_LOG_OBJECT (self, "added data sink pad");
370 } else {
371 g_warn_if_reached ();
372 }
373
374 if (caps && ghost_pad) {
375 g_object_set (ghost_pad, "caps", caps, NULL);
376 }
377
378 return ghost_pad;
379 }
380
381 static void
on_key_received(GObject * encoder,GstDtlsSrtpEnc * self)382 on_key_received (GObject * encoder, GstDtlsSrtpEnc * self)
383 {
384 GstDtlsSrtpBin *bin = GST_DTLS_SRTP_BIN (self);
385 GstBuffer *buffer = NULL;
386 guint cipher, auth;
387
388 if (!(bin->key_is_set || bin->srtp_cipher || bin->srtp_auth
389 || bin->srtcp_cipher || bin->srtcp_auth)) {
390 g_object_get (encoder,
391 "encoder-key", &buffer,
392 "srtp-cipher", &cipher, "srtp-auth", &auth, NULL);
393
394 g_object_set (self->srtp_enc,
395 "rtp-cipher", cipher,
396 "rtcp-cipher", cipher,
397 "rtp-auth", auth,
398 "rtcp-auth", auth, "key", buffer, "random-key", FALSE, NULL);
399
400 gst_buffer_unref (buffer);
401
402 g_signal_emit (self, signals[SIGNAL_ON_KEY_SET], 0);
403 } else {
404 GST_DEBUG_OBJECT (self,
405 "ignoring keys received from DTLS handshake, key struct is set");
406 }
407 }
408
409 static void
gst_dtls_srtp_enc_remove_dtls_element(GstDtlsSrtpBin * bin)410 gst_dtls_srtp_enc_remove_dtls_element (GstDtlsSrtpBin * bin)
411 {
412 GstDtlsSrtpEnc *self = GST_DTLS_SRTP_ENC (bin);
413 GstPad *dtls_sink_pad, *peer_pad;
414 gulong id;
415 guint rtp_cipher = 1, rtcp_cipher = 1, rtp_auth = 1, rtcp_auth = 1;
416
417 if (!bin->dtls_element) {
418 return;
419 }
420
421 g_object_get (self->srtp_enc,
422 "rtp-cipher", &rtp_cipher,
423 "rtcp-cipher", &rtcp_cipher,
424 "rtp-auth", &rtp_auth, "rtcp-auth", &rtcp_auth, NULL);
425
426 if (!rtp_cipher && !rtcp_cipher && !rtp_auth && !rtcp_auth) {
427 g_object_set (self->srtp_enc, "random-key", FALSE, NULL);
428 }
429
430 dtls_sink_pad = gst_element_get_static_pad (bin->dtls_element, "sink");
431
432 if (!dtls_sink_pad) {
433 gst_element_set_state (GST_ELEMENT (bin->dtls_element), GST_STATE_NULL);
434 gst_bin_remove (GST_BIN (self), bin->dtls_element);
435 bin->dtls_element = NULL;
436 return;
437 }
438
439 peer_pad = gst_pad_get_peer (dtls_sink_pad);
440 g_return_if_fail (peer_pad);
441 gst_object_unref (dtls_sink_pad);
442 dtls_sink_pad = NULL;
443
444 id = gst_pad_add_probe (peer_pad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
445 (GstPadProbeCallback) remove_dtls_encoder_probe_callback,
446 bin->dtls_element, NULL);
447 g_return_if_fail (id);
448 bin->dtls_element = NULL;
449
450 gst_pad_push_event (peer_pad,
451 gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM,
452 gst_structure_new_empty ("dummy")));
453
454 gst_object_unref (peer_pad);
455 }
456
457 static GstPadProbeReturn
remove_dtls_encoder_probe_callback(GstPad * pad,GstPadProbeInfo * info,GstElement * element)458 remove_dtls_encoder_probe_callback (GstPad * pad,
459 GstPadProbeInfo * info, GstElement * element)
460 {
461 gst_pad_remove_probe (pad, GST_PAD_PROBE_INFO_ID (info));
462
463 gst_element_set_state (GST_ELEMENT (element), GST_STATE_NULL);
464 gst_bin_remove (GST_BIN (GST_ELEMENT_PARENT (element)), element);
465
466 return GST_PAD_PROBE_OK;
467 }
468