• 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 "gstdtlselements.h"
31 #include "gstdtlssrtpdec.h"
32 #include "gstdtlsconnection.h"
33 
34 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
35     GST_PAD_SINK,
36     GST_PAD_ALWAYS,
37     GST_STATIC_CAPS_ANY);
38 
39 static GstStaticPadTemplate rtp_src_template =
40 GST_STATIC_PAD_TEMPLATE ("rtp_src",
41     GST_PAD_SRC,
42     GST_PAD_ALWAYS,
43     GST_STATIC_CAPS ("application/x-rtp")
44     );
45 
46 static GstStaticPadTemplate rtcp_src_template =
47 GST_STATIC_PAD_TEMPLATE ("rtcp_src",
48     GST_PAD_SRC,
49     GST_PAD_ALWAYS,
50     GST_STATIC_CAPS ("application/x-rtcp")
51     );
52 
53 static GstStaticPadTemplate data_src_template =
54 GST_STATIC_PAD_TEMPLATE ("data_src",
55     GST_PAD_SRC,
56     GST_PAD_REQUEST,
57     GST_STATIC_CAPS_ANY);
58 
59 GST_DEBUG_CATEGORY_STATIC (gst_dtls_srtp_dec_debug);
60 #define GST_CAT_DEFAULT gst_dtls_srtp_dec_debug
61 
62 #define gst_dtls_srtp_dec_parent_class parent_class
63 G_DEFINE_TYPE_WITH_CODE (GstDtlsSrtpDec, gst_dtls_srtp_dec,
64     GST_TYPE_DTLS_SRTP_BIN, GST_DEBUG_CATEGORY_INIT (gst_dtls_srtp_dec_debug,
65         "dtlssrtpdec", 0, "DTLS Decoder"));
66 GST_ELEMENT_REGISTER_DEFINE_WITH_CODE (dtlssrtpdec, "dtlssrtpdec",
67     GST_RANK_NONE, GST_TYPE_DTLS_SRTP_DEC, dtls_element_init (plugin));
68 
69 enum
70 {
71   PROP_0,
72   PROP_PEM,
73   PROP_PEER_PEM,
74   PROP_CONNECTION_STATE,
75   NUM_PROPERTIES
76 };
77 
78 static GParamSpec *properties[NUM_PROPERTIES];
79 
80 #define DEFAULT_PEM NULL
81 #define DEFAULT_PEER_PEM NULL
82 
83 static void gst_dtls_srtp_dec_set_property (GObject *, guint prop_id,
84     const GValue *, GParamSpec *);
85 static void gst_dtls_srtp_dec_get_property (GObject *, guint prop_id,
86     GValue *, GParamSpec *);
87 
88 static GstPad *gst_dtls_srtp_dec_request_new_pad (GstElement *,
89     GstPadTemplate *, const gchar * name, const GstCaps *);
90 static void gst_dtls_srtp_dec_release_pad (GstElement *, GstPad *);
91 
92 static GstCaps *on_decoder_request_key (GstElement * srtp_decoder, guint ssrc,
93     GstDtlsSrtpBin *);
94 static void on_peer_pem (GstElement * srtp_decoder, GParamSpec * pspec,
95     GstDtlsSrtpDec * self);
96 
97 static void gst_dtls_srtp_dec_remove_dtls_element (GstDtlsSrtpBin *);
98 static GstPadProbeReturn remove_dtls_decoder_probe_callback (GstPad *,
99     GstPadProbeInfo *, GstElement *);
100 
101 static void
gst_dtls_srtp_dec_class_init(GstDtlsSrtpDecClass * klass)102 gst_dtls_srtp_dec_class_init (GstDtlsSrtpDecClass * klass)
103 {
104   GObjectClass *gobject_class;
105   GstElementClass *element_class;
106   GstDtlsSrtpBinClass *dtls_srtp_bin_class;
107 
108   gobject_class = (GObjectClass *) klass;
109   element_class = (GstElementClass *) klass;
110   dtls_srtp_bin_class = (GstDtlsSrtpBinClass *) klass;
111 
112   gobject_class->set_property =
113       GST_DEBUG_FUNCPTR (gst_dtls_srtp_dec_set_property);
114   gobject_class->get_property =
115       GST_DEBUG_FUNCPTR (gst_dtls_srtp_dec_get_property);
116 
117   element_class->request_new_pad =
118       GST_DEBUG_FUNCPTR (gst_dtls_srtp_dec_request_new_pad);
119   element_class->release_pad =
120       GST_DEBUG_FUNCPTR (gst_dtls_srtp_dec_release_pad);
121 
122   dtls_srtp_bin_class->remove_dtls_element =
123       GST_DEBUG_FUNCPTR (gst_dtls_srtp_dec_remove_dtls_element);
124 
125   properties[PROP_PEM] =
126       g_param_spec_string ("pem",
127       "PEM string",
128       "A string containing a X509 certificate and RSA private key in PEM format",
129       DEFAULT_PEM,
130       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_DOC_SHOW_DEFAULT);
131 
132   properties[PROP_PEER_PEM] =
133       g_param_spec_string ("peer-pem",
134       "Peer PEM string",
135       "The X509 certificate received in the DTLS handshake, in PEM format",
136       DEFAULT_PEER_PEM, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
137 
138   properties[PROP_CONNECTION_STATE] =
139       g_param_spec_enum ("connection-state",
140       "Connection State",
141       "Current connection state",
142       GST_DTLS_TYPE_CONNECTION_STATE,
143       GST_DTLS_CONNECTION_STATE_NEW, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
144 
145   g_object_class_install_properties (gobject_class, NUM_PROPERTIES, properties);
146 
147   gst_element_class_add_static_pad_template (element_class, &sink_template);
148   gst_element_class_add_static_pad_template (element_class, &rtp_src_template);
149   gst_element_class_add_static_pad_template (element_class, &rtcp_src_template);
150   gst_element_class_add_static_pad_template (element_class, &data_src_template);
151 
152   gst_element_class_set_static_metadata (element_class,
153       "DTLS-SRTP Decoder",
154       "Decoder/Network/DTLS/SRTP",
155       "Decodes SRTP packets with a key received from DTLS",
156       "Patrik Oldsberg patrik.oldsberg@ericsson.com");
157 }
158 
159 static void
on_connection_state_changed(GObject * object,GParamSpec * pspec,gpointer user_data)160 on_connection_state_changed (GObject * object, GParamSpec * pspec,
161     gpointer user_data)
162 {
163   GstDtlsSrtpDec *self = GST_DTLS_SRTP_DEC (user_data);
164 
165   g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_CONNECTION_STATE]);
166 }
167 
168 static void
gst_dtls_srtp_dec_init(GstDtlsSrtpDec * self)169 gst_dtls_srtp_dec_init (GstDtlsSrtpDec * self)
170 {
171   GstElementClass *klass = GST_ELEMENT_GET_CLASS (GST_ELEMENT (self));
172   GstPadTemplate *templ;
173   GstPad *target_pad, *ghost_pad;
174   gboolean ret;
175 
176 /*
177                                  +-----------+
178             +--------------+  .-o|  dtlsdec  |o-R----data
179             |          dtls|o-'  +-----------+
180     sink---o|  dtlsdemux   |
181             |       srt(c)p|o-.  +-----------+
182             +--------------+  '-o|srtp    rtp|o------rtp
183                                  |  srtpdec  |
184                                 o|srtcp  rtcp|o------rtcp
185                                  +-----------+
186 */
187 
188   self->srtp_dec = gst_element_factory_make ("srtpdec", NULL);
189   if (!self->srtp_dec) {
190     GST_ERROR_OBJECT (self,
191         "failed to create srtp_dec, is the srtp plugin registered?");
192     return;
193   }
194   self->dtls_srtp_demux = gst_element_factory_make ("dtlssrtpdemux", NULL);
195   if (!self->dtls_srtp_demux) {
196     GST_ERROR_OBJECT (self, "failed to create dtls_srtp_demux");
197     return;
198   }
199   self->bin.dtls_element = gst_element_factory_make ("dtlsdec", NULL);
200   if (!self->bin.dtls_element) {
201     GST_ERROR_OBJECT (self, "failed to create dtls_dec");
202     return;
203   }
204 
205   gst_bin_add_many (GST_BIN (self),
206       self->dtls_srtp_demux, self->bin.dtls_element, self->srtp_dec, NULL);
207 
208   ret =
209       gst_element_link_pads (self->dtls_srtp_demux, "dtls_src",
210       self->bin.dtls_element, NULL);
211   g_return_if_fail (ret);
212   ret =
213       gst_element_link_pads (self->dtls_srtp_demux, "rtp_src", self->srtp_dec,
214       "rtp_sink");
215   g_return_if_fail (ret);
216 
217   templ = gst_element_class_get_pad_template (klass, "rtp_src");
218   target_pad = gst_element_get_static_pad (self->srtp_dec, "rtp_src");
219   ghost_pad = gst_ghost_pad_new_from_template ("rtp_src", target_pad, templ);
220   gst_object_unref (target_pad);
221   g_return_if_fail (ghost_pad);
222 
223   ret = gst_element_add_pad (GST_ELEMENT (self), ghost_pad);
224   g_return_if_fail (ret);
225 
226   templ = gst_element_class_get_pad_template (klass, "rtcp_src");
227   target_pad = gst_element_get_static_pad (self->srtp_dec, "rtcp_src");
228   ghost_pad = gst_ghost_pad_new_from_template ("rtcp_src", target_pad, templ);
229   gst_object_unref (target_pad);
230   g_return_if_fail (ghost_pad);
231 
232   ret = gst_element_add_pad (GST_ELEMENT (self), ghost_pad);
233   g_return_if_fail (ret);
234 
235   templ = gst_element_class_get_pad_template (klass, "sink");
236   target_pad = gst_element_get_static_pad (self->dtls_srtp_demux, "sink");
237   ghost_pad = gst_ghost_pad_new_from_template ("sink", target_pad, templ);
238   gst_object_unref (target_pad);
239   g_return_if_fail (ghost_pad);
240 
241   ret = gst_element_add_pad (GST_ELEMENT (self), ghost_pad);
242   g_return_if_fail (ret);
243 
244   g_signal_connect (self->srtp_dec, "request-key",
245       G_CALLBACK (on_decoder_request_key), self);
246   g_signal_connect (self->bin.dtls_element, "notify::peer-pem",
247       G_CALLBACK (on_peer_pem), self);
248   g_signal_connect (self->bin.dtls_element, "notify::connection-state",
249       G_CALLBACK (on_connection_state_changed), self);
250 }
251 
252 static void
gst_dtls_srtp_dec_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)253 gst_dtls_srtp_dec_set_property (GObject * object,
254     guint prop_id, const GValue * value, GParamSpec * pspec)
255 {
256   GstDtlsSrtpDec *self = GST_DTLS_SRTP_DEC (object);
257 
258   switch (prop_id) {
259     case PROP_PEM:
260       if (self->bin.dtls_element) {
261         g_object_set_property (G_OBJECT (self->bin.dtls_element), "pem", value);
262       } else {
263         GST_WARNING_OBJECT (self, "tried to set pem after disabling DTLS");
264       }
265       break;
266     default:
267       G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec);
268   }
269 }
270 
271 static void
gst_dtls_srtp_dec_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)272 gst_dtls_srtp_dec_get_property (GObject * object,
273     guint prop_id, GValue * value, GParamSpec * pspec)
274 {
275   GstDtlsSrtpDec *self = GST_DTLS_SRTP_DEC (object);
276 
277   switch (prop_id) {
278     case PROP_PEM:
279       if (self->bin.dtls_element) {
280         g_object_get_property (G_OBJECT (self->bin.dtls_element), "pem", value);
281       } else {
282         GST_WARNING_OBJECT (self, "tried to get pem after disabling DTLS");
283       }
284       break;
285     case PROP_PEER_PEM:
286       if (self->bin.dtls_element) {
287         g_object_get_property (G_OBJECT (self->bin.dtls_element), "peer-pem",
288             value);
289       } else {
290         GST_WARNING_OBJECT (self, "tried to get peer-pem after disabling DTLS");
291       }
292       break;
293     case PROP_CONNECTION_STATE:
294       if (self->bin.dtls_element) {
295         g_object_get_property (G_OBJECT (self->bin.dtls_element),
296             "connection-state", value);
297       } else {
298         GST_WARNING_OBJECT (self,
299             "tried to get connection-state after disabling DTLS");
300       }
301       break;
302     default:
303       G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec);
304   }
305 }
306 
307 static GstPad *
gst_dtls_srtp_dec_request_new_pad(GstElement * element,GstPadTemplate * templ,const gchar * name,const GstCaps * caps)308 gst_dtls_srtp_dec_request_new_pad (GstElement * element,
309     GstPadTemplate * templ, const gchar * name, const GstCaps * caps)
310 {
311   GstDtlsSrtpDec *self = GST_DTLS_SRTP_DEC (element);
312   GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
313   GstPad *ghost_pad = NULL;
314   gboolean ret;
315 
316   GST_DEBUG_OBJECT (element, "pad requested");
317 
318   g_return_val_if_fail (self->bin.dtls_element, NULL);
319   g_return_val_if_fail (!self->bin.key_is_set, NULL);
320 
321   if (templ == gst_element_class_get_pad_template (klass, "data_src")) {
322     GstPad *target_pad;
323 
324     target_pad = gst_element_request_pad_simple (self->bin.dtls_element, "src");
325 
326     ghost_pad = gst_ghost_pad_new_from_template (name, target_pad, templ);
327     gst_object_unref (target_pad);
328     g_return_val_if_fail (ghost_pad, NULL);
329 
330     ret = gst_pad_set_active (ghost_pad, TRUE);
331     g_return_val_if_fail (ret, NULL);
332     ret = gst_element_add_pad (element, ghost_pad);
333     g_return_val_if_fail (ret, NULL);
334 
335     GST_LOG_OBJECT (self, "added data src pad");
336 
337     if (caps) {
338       g_object_set (ghost_pad, "caps", caps, NULL);
339     }
340 
341     return ghost_pad;
342   }
343 
344   g_return_val_if_reached (NULL);
345 }
346 
347 static void
gst_dtls_srtp_dec_release_pad(GstElement * element,GstPad * pad)348 gst_dtls_srtp_dec_release_pad (GstElement * element, GstPad * pad)
349 {
350   GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
351   GstDtlsSrtpDec *self = GST_DTLS_SRTP_DEC (element);
352 
353   if (GST_PAD_PAD_TEMPLATE (pad) ==
354       gst_element_class_get_pad_template (klass, "data_src")) {
355     GstGhostPad *ghost_pad;
356     GstPad *target_pad;
357 
358     ghost_pad = GST_GHOST_PAD (pad);
359     target_pad = gst_ghost_pad_get_target (ghost_pad);
360 
361     if (target_pad != NULL) {
362       gst_element_release_request_pad (self->bin.dtls_element, target_pad);
363 
364       gst_object_unref (target_pad);
365       gst_ghost_pad_set_target (GST_GHOST_PAD (pad), NULL);
366     }
367   }
368 
369   gst_element_remove_pad (element, pad);
370 }
371 
372 static GstCaps *
on_decoder_request_key(GstElement * srtp_decoder,guint ssrc,GstDtlsSrtpBin * bin)373 on_decoder_request_key (GstElement * srtp_decoder,
374     guint ssrc, GstDtlsSrtpBin * bin)
375 {
376   GstCaps *key_caps;
377   GstBuffer *key_buffer = NULL;
378   guint cipher;
379   guint auth;
380 
381   if (bin->key_is_set) {
382     if (bin->key) {
383       if (bin->srtp_cipher && bin->srtp_auth && bin->srtcp_cipher
384           && bin->srtcp_auth) {
385         GST_DEBUG_OBJECT (bin, "setting srtp key");
386         return gst_caps_new_simple ("application/x-srtp",
387             "srtp-key", GST_TYPE_BUFFER, gst_buffer_copy (bin->key),
388             "srtp-auth", G_TYPE_STRING, bin->srtp_auth,
389             "srtcp-auth", G_TYPE_STRING, bin->srtcp_auth,
390             "srtp-cipher", G_TYPE_STRING, bin->srtp_cipher,
391             "srtcp-cipher", G_TYPE_STRING, bin->srtcp_cipher, NULL);
392       } else {
393         GST_WARNING_OBJECT (bin,
394             "srtp key is set but not all ciphers and auths");
395         return NULL;
396       }
397     }
398 
399     GST_DEBUG_OBJECT (bin, "setting srtp key to null");
400     return gst_caps_new_simple ("application/x-srtp",
401         "srtp-key", GST_TYPE_BUFFER, NULL,
402         "srtp-auth", G_TYPE_STRING, "null",
403         "srtcp-auth", G_TYPE_STRING, "null",
404         "srtp-cipher", G_TYPE_STRING, "null",
405         "srtcp-cipher", G_TYPE_STRING, "null", NULL);
406   }
407 
408   if (bin->dtls_element) {
409     g_object_get (bin->dtls_element, "decoder-key", &key_buffer, NULL);
410   }
411 
412   if (key_buffer) {
413     g_object_get (bin->dtls_element,
414         "srtp-cipher", &cipher, "srtp-auth", &auth, NULL);
415 
416     g_return_val_if_fail (cipher == GST_DTLS_SRTP_CIPHER_AES_128_ICM, NULL);
417 
418     key_caps = gst_caps_new_simple ("application/x-srtp",
419         "srtp-key", GST_TYPE_BUFFER, key_buffer,
420         "srtp-cipher", G_TYPE_STRING, "aes-128-icm",
421         "srtcp-cipher", G_TYPE_STRING, "aes-128-icm", NULL);
422 
423     switch (auth) {
424       case GST_DTLS_SRTP_AUTH_HMAC_SHA1_32:
425         gst_caps_set_simple (key_caps,
426             "srtp-auth", G_TYPE_STRING, "hmac-sha1-32",
427             "srtcp-auth", G_TYPE_STRING, "hmac-sha1-32", NULL);
428         break;
429       case GST_DTLS_SRTP_AUTH_HMAC_SHA1_80:
430         gst_caps_set_simple (key_caps,
431             "srtp-auth", G_TYPE_STRING, "hmac-sha1-80",
432             "srtcp-auth", G_TYPE_STRING, "hmac-sha1-80", NULL);
433         break;
434       default:
435         g_return_val_if_reached (NULL);
436         break;
437     }
438 
439     gst_buffer_unref (key_buffer);
440 
441     return key_caps;
442   } else {
443     GST_WARNING_OBJECT (bin, "no srtp key available yet");
444   }
445 
446   return NULL;
447 }
448 
449 static void
on_peer_pem(GstElement * srtp_decoder,GParamSpec * pspec,GstDtlsSrtpDec * self)450 on_peer_pem (GstElement * srtp_decoder, GParamSpec * pspec,
451     GstDtlsSrtpDec * self)
452 {
453   g_return_if_fail (self);
454   g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_PEER_PEM]);
455 }
456 
457 static void
gst_dtls_srtp_dec_remove_dtls_element(GstDtlsSrtpBin * bin)458 gst_dtls_srtp_dec_remove_dtls_element (GstDtlsSrtpBin * bin)
459 {
460   GstDtlsSrtpDec *self = GST_DTLS_SRTP_DEC (bin);
461   GstPad *demux_pad;
462   gulong id;
463 
464   if (!bin->dtls_element) {
465     return;
466   }
467 
468   demux_pad = gst_element_get_static_pad (self->dtls_srtp_demux, "dtls_src");
469 
470   id = gst_pad_add_probe (demux_pad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
471       (GstPadProbeCallback) remove_dtls_decoder_probe_callback,
472       bin->dtls_element, NULL);
473   g_return_if_fail (id);
474   bin->dtls_element = NULL;
475 
476   gst_pad_push_event (demux_pad,
477       gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM,
478           gst_structure_new_empty ("dummy")));
479 
480   gst_object_unref (demux_pad);
481 }
482 
483 static GstPadProbeReturn
remove_dtls_decoder_probe_callback(GstPad * pad,GstPadProbeInfo * info,GstElement * element)484 remove_dtls_decoder_probe_callback (GstPad * pad,
485     GstPadProbeInfo * info, GstElement * element)
486 {
487   gst_pad_remove_probe (pad, GST_PAD_PROBE_INFO_ID (info));
488 
489   gst_element_set_state (GST_ELEMENT (element), GST_STATE_NULL);
490   gst_bin_remove (GST_BIN (GST_ELEMENT_PARENT (element)), element);
491 
492   return GST_PAD_PROBE_OK;
493 }
494