• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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