gstdtlsdec.c 22 KB
Newer Older
Sebastian Dröge's avatar
Sebastian Dröge committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
/*
 * Copyright (c) 2014, Ericsson AB. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice, this
 * list of conditions and the following disclaimer in the documentation and/or other
 * materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
 * OF SUCH DAMAGE.
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "gstdtlsdec.h"

#include "gstdtlscertificate.h"

Sebastian Dröge's avatar
Sebastian Dröge committed
34 35 36 37
static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
    GST_PAD_SINK,
    GST_PAD_ALWAYS,
    GST_STATIC_CAPS ("application/x-dtls")
Sebastian Dröge's avatar
Sebastian Dröge committed
38 39
    );

Sebastian Dröge's avatar
Sebastian Dröge committed
40 41 42 43
static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
    GST_PAD_SRC,
    GST_PAD_REQUEST,
    GST_STATIC_CAPS_ANY);
Sebastian Dröge's avatar
Sebastian Dröge committed
44

45 46
GST_DEBUG_CATEGORY_STATIC (gst_dtls_dec_debug);
#define GST_CAT_DEFAULT gst_dtls_dec_debug
Sebastian Dröge's avatar
Sebastian Dröge committed
47

48 49 50
#define gst_dtls_dec_parent_class parent_class
G_DEFINE_TYPE_WITH_CODE (GstDtlsDec, gst_dtls_dec, GST_TYPE_ELEMENT,
    GST_DEBUG_CATEGORY_INIT (gst_dtls_dec_debug, "dtlsdec", 0, "DTLS Decoder"));
Sebastian Dröge's avatar
Sebastian Dröge committed
51

Sebastian Dröge's avatar
Sebastian Dröge committed
52 53 54 55
enum
{
  SIGNAL_ON_KEY_RECEIVED,
  NUM_SIGNALS
Sebastian Dröge's avatar
Sebastian Dröge committed
56 57 58 59
};

static guint signals[NUM_SIGNALS];

Sebastian Dröge's avatar
Sebastian Dröge committed
60 61 62 63 64 65 66 67 68 69 70
enum
{
  PROP_0,
  PROP_CONNECTION_ID,
  PROP_PEM,
  PROP_PEER_PEM,

  PROP_DECODER_KEY,
  PROP_SRTP_CIPHER,
  PROP_SRTP_AUTH,
  NUM_PROPERTIES
Sebastian Dröge's avatar
Sebastian Dröge committed
71 72 73 74 75 76 77 78 79 80 81 82 83
};

static GParamSpec *properties[NUM_PROPERTIES];

#define DEFAULT_CONNECTION_ID NULL
#define DEFAULT_PEM NULL
#define DEFAULT_PEER_PEM NULL

#define DEFAULT_DECODER_KEY NULL
#define DEFAULT_SRTP_CIPHER 0
#define DEFAULT_SRTP_AUTH 0


84 85 86
static void gst_dtls_dec_finalize (GObject *);
static void gst_dtls_dec_dispose (GObject *);
static void gst_dtls_dec_set_property (GObject *, guint prop_id,
Sebastian Dröge's avatar
Sebastian Dröge committed
87
    const GValue *, GParamSpec *);
88
static void gst_dtls_dec_get_property (GObject *, guint prop_id, GValue *,
Sebastian Dröge's avatar
Sebastian Dröge committed
89 90
    GParamSpec *);

91
static GstStateChangeReturn gst_dtls_dec_change_state (GstElement *,
Sebastian Dröge's avatar
Sebastian Dröge committed
92
    GstStateChange);
93
static GstPad *gst_dtls_dec_request_new_pad (GstElement *, GstPadTemplate *,
Sebastian Dröge's avatar
Sebastian Dröge committed
94
    const gchar * name, const GstCaps *);
95
static void gst_dtls_dec_release_pad (GstElement *, GstPad *);
Sebastian Dröge's avatar
Sebastian Dröge committed
96

97 98
static void on_key_received (GstDtlsConnection *, gpointer key, guint cipher,
    guint auth, GstDtlsDec *);
99
static gboolean on_peer_certificate_received (GstDtlsConnection *, gchar * pem,
100
    GstDtlsDec *);
Sebastian Dröge's avatar
Sebastian Dröge committed
101
static GstFlowReturn sink_chain (GstPad *, GstObject * parent, GstBuffer *);
102 103
static GstFlowReturn sink_chain_list (GstPad *, GstObject * parent,
    GstBufferList *);
Sebastian Dröge's avatar
Sebastian Dröge committed
104

105 106 107 108
static GstDtlsAgent *get_agent_by_pem (const gchar * pem);
static void agent_weak_ref_notify (gchar * pem, GstDtlsAgent *);
static void create_connection (GstDtlsDec *, gchar * id);
static void connection_weak_ref_notify (gchar * id, GstDtlsConnection *);
Sebastian Dröge's avatar
Sebastian Dröge committed
109 110

static void
111
gst_dtls_dec_class_init (GstDtlsDecClass * klass)
Sebastian Dröge's avatar
Sebastian Dröge committed
112
{
Sebastian Dröge's avatar
Sebastian Dröge committed
113 114 115 116 117 118
  GObjectClass *gobject_class;
  GstElementClass *element_class;

  gobject_class = (GObjectClass *) klass;
  element_class = (GstElementClass *) klass;

119 120 121 122
  gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_dtls_dec_finalize);
  gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_dtls_dec_dispose);
  gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_dtls_dec_set_property);
  gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_dtls_dec_get_property);
Sebastian Dröge's avatar
Sebastian Dröge committed
123

124
  element_class->change_state = GST_DEBUG_FUNCPTR (gst_dtls_dec_change_state);
Sebastian Dröge's avatar
Sebastian Dröge committed
125
  element_class->request_new_pad =
126 127
      GST_DEBUG_FUNCPTR (gst_dtls_dec_request_new_pad);
  element_class->release_pad = GST_DEBUG_FUNCPTR (gst_dtls_dec_release_pad);
Sebastian Dröge's avatar
Sebastian Dröge committed
128 129 130

  signals[SIGNAL_ON_KEY_RECEIVED] =
      g_signal_new ("on-key-received", G_TYPE_FROM_CLASS (klass),
131
      G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0);
Sebastian Dröge's avatar
Sebastian Dröge committed
132 133 134 135 136 137 138 139 140 141 142

  properties[PROP_CONNECTION_ID] =
      g_param_spec_string ("connection-id",
      "Connection id",
      "Every encoder/decoder pair should have the same, unique, connection-id",
      DEFAULT_CONNECTION_ID, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);

  properties[PROP_PEM] =
      g_param_spec_string ("pem",
      "PEM string",
      "A string containing a X509 certificate and RSA private key in PEM format",
143 144
      DEFAULT_PEM,
      G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_DOC_SHOW_DEFAULT);
Sebastian Dröge's avatar
Sebastian Dröge committed
145 146 147 148 149 150 151 152 153 154

  properties[PROP_PEER_PEM] =
      g_param_spec_string ("peer-pem",
      "Peer PEM string",
      "The X509 certificate received in the DTLS handshake, in PEM format",
      DEFAULT_PEER_PEM, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);

  properties[PROP_DECODER_KEY] =
      g_param_spec_boxed ("decoder-key",
      "Decoder key",
Sebastian Dröge's avatar
Sebastian Dröge committed
155
      "SRTP key that should be used by the decoder",
Sebastian Dröge's avatar
Sebastian Dröge committed
156 157 158 159 160 161
      GST_TYPE_CAPS, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);

  properties[PROP_SRTP_CIPHER] =
      g_param_spec_uint ("srtp-cipher",
      "SRTP cipher",
      "The SRTP cipher selected in the DTLS handshake. "
162 163
      "The value will be set to an GstDtlsSrtpCipher.",
      0, GST_DTLS_SRTP_CIPHER_AES_128_ICM, DEFAULT_SRTP_CIPHER,
Sebastian Dröge's avatar
Sebastian Dröge committed
164 165 166 167 168 169
      G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);

  properties[PROP_SRTP_AUTH] =
      g_param_spec_uint ("srtp-auth",
      "SRTP authentication",
      "The SRTP authentication selected in the DTLS handshake. "
170 171
      "The value will be set to an GstDtlsSrtpAuth.",
      0, GST_DTLS_SRTP_AUTH_HMAC_SHA1_80, DEFAULT_SRTP_AUTH,
Sebastian Dröge's avatar
Sebastian Dröge committed
172 173 174 175
      G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);

  g_object_class_install_properties (gobject_class, NUM_PROPERTIES, properties);

176 177
  gst_element_class_add_static_pad_template (element_class, &src_template);
  gst_element_class_add_static_pad_template (element_class, &sink_template);
Sebastian Dröge's avatar
Sebastian Dröge committed
178 179 180 181 182

  gst_element_class_set_static_metadata (element_class,
      "DTLS Decoder",
      "Decoder/Network/DTLS",
      "Decodes DTLS packets", "Patrik Oldsberg patrik.oldsberg@ericsson.com");
Sebastian Dröge's avatar
Sebastian Dröge committed
183 184
}

Sebastian Dröge's avatar
Sebastian Dröge committed
185
static void
186
gst_dtls_dec_init (GstDtlsDec * self)
Sebastian Dröge's avatar
Sebastian Dröge committed
187
{
Sebastian Dröge's avatar
Sebastian Dröge committed
188 189 190
  self->agent = get_agent_by_pem (NULL);
  self->connection_id = NULL;
  self->connection = NULL;
191
  self->peer_pem = NULL;
Sebastian Dröge's avatar
Sebastian Dröge committed
192

193
  self->decoder_key = NULL;
Sebastian Dröge's avatar
Sebastian Dröge committed
194 195
  self->srtp_cipher = DEFAULT_SRTP_CIPHER;
  self->srtp_auth = DEFAULT_SRTP_AUTH;
Sebastian Dröge's avatar
Sebastian Dröge committed
196

Sebastian Dröge's avatar
Sebastian Dröge committed
197
  g_mutex_init (&self->src_mutex);
Sebastian Dröge's avatar
Sebastian Dröge committed
198

Sebastian Dröge's avatar
Sebastian Dröge committed
199
  self->src = NULL;
200 201
  self->sink = gst_pad_new_from_static_template (&sink_template, "sink");
  g_return_if_fail (self->sink);
Sebastian Dröge's avatar
Sebastian Dröge committed
202

203 204 205
  gst_pad_set_chain_function (self->sink, GST_DEBUG_FUNCPTR (sink_chain));
  gst_pad_set_chain_list_function (self->sink,
      GST_DEBUG_FUNCPTR (sink_chain_list));
Sebastian Dröge's avatar
Sebastian Dröge committed
206

207
  gst_element_add_pad (GST_ELEMENT (self), self->sink);
Sebastian Dröge's avatar
Sebastian Dröge committed
208 209
}

Sebastian Dröge's avatar
Sebastian Dröge committed
210
static void
211
gst_dtls_dec_finalize (GObject * object)
Sebastian Dröge's avatar
Sebastian Dröge committed
212
{
213
  GstDtlsDec *self = GST_DTLS_DEC (object);
Sebastian Dröge's avatar
Sebastian Dröge committed
214

215 216 217
  if (self->decoder_key) {
    gst_buffer_unref (self->decoder_key);
    self->decoder_key = NULL;
Sebastian Dröge's avatar
Sebastian Dröge committed
218
  }
Sebastian Dröge's avatar
Sebastian Dröge committed
219

Sebastian Dröge's avatar
Sebastian Dröge committed
220 221
  g_free (self->connection_id);
  self->connection_id = NULL;
Sebastian Dröge's avatar
Sebastian Dröge committed
222

223 224
  g_free (self->peer_pem);
  self->peer_pem = NULL;
Sebastian Dröge's avatar
Sebastian Dröge committed
225

Sebastian Dröge's avatar
Sebastian Dröge committed
226
  g_mutex_clear (&self->src_mutex);
Sebastian Dröge's avatar
Sebastian Dröge committed
227

Sebastian Dröge's avatar
Sebastian Dröge committed
228
  GST_LOG_OBJECT (self, "finalized");
Sebastian Dröge's avatar
Sebastian Dröge committed
229

Sebastian Dröge's avatar
Sebastian Dröge committed
230
  G_OBJECT_CLASS (parent_class)->finalize (object);
Sebastian Dröge's avatar
Sebastian Dröge committed
231 232
}

Sebastian Dröge's avatar
Sebastian Dröge committed
233
static void
234
gst_dtls_dec_dispose (GObject * object)
Sebastian Dröge's avatar
Sebastian Dröge committed
235
{
236
  GstDtlsDec *self = GST_DTLS_DEC (object);
Sebastian Dröge's avatar
Sebastian Dröge committed
237

Sebastian Dröge's avatar
Sebastian Dröge committed
238 239 240 241
  if (self->agent) {
    g_object_unref (self->agent);
    self->agent = NULL;
  }
Sebastian Dröge's avatar
Sebastian Dröge committed
242

Sebastian Dröge's avatar
Sebastian Dröge committed
243 244 245 246
  if (self->connection) {
    g_object_unref (self->connection);
    self->connection = NULL;
  }
247 248

  G_OBJECT_CLASS (parent_class)->dispose (object);
Sebastian Dröge's avatar
Sebastian Dröge committed
249 250
}

Sebastian Dröge's avatar
Sebastian Dröge committed
251
static void
252
gst_dtls_dec_set_property (GObject * object, guint prop_id,
Sebastian Dröge's avatar
Sebastian Dröge committed
253
    const GValue * value, GParamSpec * pspec)
Sebastian Dröge's avatar
Sebastian Dröge committed
254
{
255
  GstDtlsDec *self = GST_DTLS_DEC (object);
Sebastian Dröge's avatar
Sebastian Dröge committed
256

Sebastian Dröge's avatar
Sebastian Dröge committed
257
  switch (prop_id) {
Sebastian Dröge's avatar
Sebastian Dröge committed
258
    case PROP_CONNECTION_ID:
Sebastian Dröge's avatar
Sebastian Dröge committed
259 260 261 262 263
      g_free (self->connection_id);
      self->connection_id = g_value_dup_string (value);
      g_return_if_fail (self->agent);
      create_connection (self, self->connection_id);
      break;
Sebastian Dröge's avatar
Sebastian Dröge committed
264
    case PROP_PEM:
Sebastian Dröge's avatar
Sebastian Dröge committed
265 266 267 268 269 270 271 272
      if (self->agent) {
        g_object_unref (self->agent);
      }
      self->agent = get_agent_by_pem (g_value_get_string (value));
      if (self->connection_id) {
        create_connection (self, self->connection_id);
      }
      break;
Sebastian Dröge's avatar
Sebastian Dröge committed
273
    default:
Sebastian Dröge's avatar
Sebastian Dröge committed
274 275
      G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec);
  }
Sebastian Dröge's avatar
Sebastian Dröge committed
276 277
}

Sebastian Dröge's avatar
Sebastian Dröge committed
278
static void
279
gst_dtls_dec_get_property (GObject * object, guint prop_id, GValue * value,
Sebastian Dröge's avatar
Sebastian Dröge committed
280
    GParamSpec * pspec)
Sebastian Dröge's avatar
Sebastian Dröge committed
281
{
282
  GstDtlsDec *self = GST_DTLS_DEC (object);
Sebastian Dröge's avatar
Sebastian Dröge committed
283

Sebastian Dröge's avatar
Sebastian Dröge committed
284
  switch (prop_id) {
Sebastian Dröge's avatar
Sebastian Dröge committed
285
    case PROP_CONNECTION_ID:
Sebastian Dröge's avatar
Sebastian Dröge committed
286 287
      g_value_set_string (value, self->connection_id);
      break;
Sebastian Dröge's avatar
Sebastian Dröge committed
288
    case PROP_PEM:
Sebastian Dröge's avatar
Sebastian Dröge committed
289
      g_value_take_string (value,
290
          gst_dtls_agent_get_certificate_pem (self->agent));
Sebastian Dröge's avatar
Sebastian Dröge committed
291
      break;
Sebastian Dröge's avatar
Sebastian Dröge committed
292
    case PROP_PEER_PEM:
293
      g_value_set_string (value, self->peer_pem);
Sebastian Dröge's avatar
Sebastian Dröge committed
294
      break;
Sebastian Dröge's avatar
Sebastian Dröge committed
295
    case PROP_DECODER_KEY:
296
      g_value_set_boxed (value, self->decoder_key);
Sebastian Dröge's avatar
Sebastian Dröge committed
297
      break;
Sebastian Dröge's avatar
Sebastian Dröge committed
298
    case PROP_SRTP_CIPHER:
Sebastian Dröge's avatar
Sebastian Dröge committed
299 300
      g_value_set_uint (value, self->srtp_cipher);
      break;
Sebastian Dröge's avatar
Sebastian Dröge committed
301
    case PROP_SRTP_AUTH:
Sebastian Dröge's avatar
Sebastian Dröge committed
302 303
      g_value_set_uint (value, self->srtp_auth);
      break;
Sebastian Dröge's avatar
Sebastian Dröge committed
304
    default:
Sebastian Dröge's avatar
Sebastian Dröge committed
305 306
      G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec);
  }
Sebastian Dröge's avatar
Sebastian Dröge committed
307 308
}

Sebastian Dröge's avatar
Sebastian Dröge committed
309
static GstStateChangeReturn
310
gst_dtls_dec_change_state (GstElement * element, GstStateChange transition)
Sebastian Dröge's avatar
Sebastian Dröge committed
311
{
312
  GstDtlsDec *self = GST_DTLS_DEC (element);
Sebastian Dröge's avatar
Sebastian Dröge committed
313
  GstStateChangeReturn ret;
Sebastian Dröge's avatar
Sebastian Dröge committed
314

Sebastian Dröge's avatar
Sebastian Dröge committed
315
  switch (transition) {
Sebastian Dröge's avatar
Sebastian Dröge committed
316
    case GST_STATE_CHANGE_NULL_TO_READY:
Sebastian Dröge's avatar
Sebastian Dröge committed
317 318 319 320
      if (self->connection) {
        g_signal_connect_object (self->connection,
            "on-decoder-key", G_CALLBACK (on_key_received), self, 0);
        g_signal_connect_object (self->connection,
321
            "on-peer-certificate", G_CALLBACK (on_peer_certificate_received),
Sebastian Dröge's avatar
Sebastian Dröge committed
322 323 324 325 326 327 328
            self, 0);
      } else {
        GST_WARNING_OBJECT (self,
            "trying to change state to ready without connection id and pem");
        return GST_STATE_CHANGE_FAILURE;
      }
      break;
Sebastian Dröge's avatar
Sebastian Dröge committed
329
    default:
Sebastian Dröge's avatar
Sebastian Dröge committed
330 331
      break;
  }
Sebastian Dröge's avatar
Sebastian Dröge committed
332

Sebastian Dröge's avatar
Sebastian Dröge committed
333
  ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
Sebastian Dröge's avatar
Sebastian Dröge committed
334

Sebastian Dröge's avatar
Sebastian Dröge committed
335
  return ret;
Sebastian Dröge's avatar
Sebastian Dröge committed
336 337
}

338 339 340 341 342 343 344 345 346 347 348 349 350 351 352
static gboolean
forward_sticky_events (GstPad * pad, GstEvent ** event, gpointer user_data)
{
  GstPad *srcpad = GST_PAD_CAST (user_data);
  GstFlowReturn ret;

  ret = gst_pad_store_sticky_event (srcpad, *event);
  if (ret != GST_FLOW_OK) {
    GST_DEBUG_OBJECT (srcpad, "storing sticky event %p (%s) failed: %s", *event,
        GST_EVENT_TYPE_NAME (*event), gst_flow_get_name (ret));
  }

  return TRUE;
}

Sebastian Dröge's avatar
Sebastian Dröge committed
353
static GstPad *
354
gst_dtls_dec_request_new_pad (GstElement * element,
Sebastian Dröge's avatar
Sebastian Dröge committed
355
    GstPadTemplate * tmpl, const gchar * name, const GstCaps * caps)
Sebastian Dröge's avatar
Sebastian Dröge committed
356
{
357
  GstDtlsDec *self = GST_DTLS_DEC (element);
Sebastian Dröge's avatar
Sebastian Dröge committed
358
  GstPad *pad;
Sebastian Dröge's avatar
Sebastian Dröge committed
359

Sebastian Dröge's avatar
Sebastian Dröge committed
360
  GST_DEBUG_OBJECT (element, "requesting pad");
Sebastian Dröge's avatar
Sebastian Dröge committed
361

Sebastian Dröge's avatar
Sebastian Dröge committed
362 363
  g_return_val_if_fail (!self->src, NULL);
  g_return_val_if_fail (tmpl->direction == GST_PAD_SRC, NULL);
Sebastian Dröge's avatar
Sebastian Dröge committed
364

Sebastian Dröge's avatar
Sebastian Dröge committed
365
  g_mutex_lock (&self->src_mutex);
Sebastian Dröge's avatar
Sebastian Dröge committed
366 367 368 369 370 371
  if (self->src) {
    GST_ERROR_OBJECT (self, "Pad %s:%s exists already",
        GST_DEBUG_PAD_NAME (self->src));
    g_mutex_unlock (&self->src_mutex);
    return NULL;
  }
Sebastian Dröge's avatar
Sebastian Dröge committed
372

Sebastian Dröge's avatar
Sebastian Dröge committed
373
  self->src = pad = gst_pad_new_from_template (tmpl, name);
374

Sebastian Dröge's avatar
Sebastian Dröge committed
375
  g_mutex_unlock (&self->src_mutex);
Sebastian Dröge's avatar
Sebastian Dröge committed
376

Sebastian Dröge's avatar
Sebastian Dröge committed
377
  gst_pad_set_active (pad, TRUE);
Sebastian Dröge's avatar
Sebastian Dröge committed
378

Sebastian Dröge's avatar
Sebastian Dröge committed
379 380
  if (caps)
    gst_pad_set_caps (pad, (GstCaps *) caps);
Sebastian Dröge's avatar
Sebastian Dröge committed
381

382 383 384
  /* Forward sticky events to the new srcpad */
  gst_pad_sticky_events_foreach (self->sink, forward_sticky_events, self->src);

Sebastian Dröge's avatar
Sebastian Dröge committed
385
  gst_element_add_pad (element, pad);
Sebastian Dröge's avatar
Sebastian Dröge committed
386

Sebastian Dröge's avatar
Sebastian Dröge committed
387
  return pad;
Sebastian Dröge's avatar
Sebastian Dröge committed
388 389
}

Sebastian Dröge's avatar
Sebastian Dröge committed
390
static void
391
gst_dtls_dec_release_pad (GstElement * element, GstPad * pad)
Sebastian Dröge's avatar
Sebastian Dröge committed
392
{
393
  GstDtlsDec *self = GST_DTLS_DEC (element);
Sebastian Dröge's avatar
Sebastian Dröge committed
394

Sebastian Dröge's avatar
Sebastian Dröge committed
395
  g_return_if_fail (self->src == pad);
Sebastian Dröge's avatar
Sebastian Dröge committed
396 397

  g_mutex_lock (&self->src_mutex);
398

Sebastian Dröge's avatar
Sebastian Dröge committed
399
  self->src = NULL;
Sebastian Dröge's avatar
Sebastian Dröge committed
400
  g_mutex_unlock (&self->src_mutex);
Sebastian Dröge's avatar
Sebastian Dröge committed
401

Sebastian Dröge's avatar
Sebastian Dröge committed
402
  GST_DEBUG_OBJECT (self, "releasing src pad");
Sebastian Dröge's avatar
Sebastian Dröge committed
403

404
  gst_element_remove_pad (element, pad);
Sebastian Dröge's avatar
Sebastian Dröge committed
405 406
}

Sebastian Dröge's avatar
Sebastian Dröge committed
407
static void
408 409
on_key_received (GstDtlsConnection * connection, gpointer key, guint cipher,
    guint auth, GstDtlsDec * self)
Sebastian Dröge's avatar
Sebastian Dröge committed
410
{
Sebastian Dröge's avatar
Sebastian Dröge committed
411 412
  gpointer key_dup;
  gchar *key_str;
Sebastian Dröge's avatar
Sebastian Dröge committed
413

414
  g_return_if_fail (GST_IS_DTLS_DEC (self));
Sebastian Dröge's avatar
Sebastian Dröge committed
415

Sebastian Dröge's avatar
Sebastian Dröge committed
416 417
  self->srtp_cipher = cipher;
  self->srtp_auth = auth;
Sebastian Dröge's avatar
Sebastian Dröge committed
418

419
  key_dup = g_memdup (key, GST_DTLS_SRTP_MASTER_KEY_LENGTH);
420 421 422 423 424 425

  if (self->decoder_key) {
    gst_buffer_unref (self->decoder_key);
    self->decoder_key = NULL;
  }

426
  self->decoder_key =
427
      gst_buffer_new_wrapped (key_dup, GST_DTLS_SRTP_MASTER_KEY_LENGTH);
Sebastian Dröge's avatar
Sebastian Dröge committed
428

429
  key_str = g_base64_encode (key, GST_DTLS_SRTP_MASTER_KEY_LENGTH);
Sebastian Dröge's avatar
Sebastian Dröge committed
430 431
  GST_INFO_OBJECT (self, "received key: %s", key_str);
  g_free (key_str);
Sebastian Dröge's avatar
Sebastian Dröge committed
432

Sebastian Dröge's avatar
Sebastian Dröge committed
433
  g_signal_emit (self, signals[SIGNAL_ON_KEY_RECEIVED], 0);
Sebastian Dröge's avatar
Sebastian Dröge committed
434 435
}

Sebastian Dröge's avatar
Sebastian Dröge committed
436
static gboolean
437
on_peer_certificate_received (GstDtlsConnection * connection, gchar * pem,
438
    GstDtlsDec * self)
Sebastian Dröge's avatar
Sebastian Dröge committed
439
{
440
  g_return_val_if_fail (GST_IS_DTLS_DEC (self), TRUE);
Sebastian Dröge's avatar
Sebastian Dröge committed
441

Sebastian Dröge's avatar
Sebastian Dröge committed
442
  GST_DEBUG_OBJECT (self, "Received peer certificate PEM: \n%s", pem);
Sebastian Dröge's avatar
Sebastian Dröge committed
443

444 445 446 447
  if (self->peer_pem != NULL) {
    g_free (self->peer_pem);
    self->peer_pem = NULL;
  }
448
  self->peer_pem = g_strdup (pem);
Sebastian Dröge's avatar
Sebastian Dröge committed
449

450
  g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_PEER_PEM]);
Sebastian Dröge's avatar
Sebastian Dröge committed
451

Sebastian Dröge's avatar
Sebastian Dröge committed
452
  return TRUE;
Sebastian Dröge's avatar
Sebastian Dröge committed
453 454
}

455
static GstFlowReturn
456 457
process_buffer (GstDtlsDec * self, GstBuffer * buffer)
{
458
  GstFlowReturn flow_ret;
459
  GstMapInfo map_info;
460 461
  GError *err = NULL;
  gsize written = 0;
462 463

  if (!gst_buffer_map (buffer, &map_info, GST_MAP_READWRITE))
464
    return GST_FLOW_ERROR;
465 466 467

  if (!map_info.size) {
    gst_buffer_unmap (buffer, &map_info);
468
    return GST_FLOW_ERROR;
469 470
  }

471
  flow_ret =
472
      gst_dtls_connection_process (self->connection, map_info.data,
473
      map_info.size, &written, &err);
474 475
  gst_buffer_unmap (buffer, &map_info);

476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495
  switch (flow_ret) {
    case GST_FLOW_OK:
      GST_LOG_OBJECT (self,
          "Decoded buffer of size %" G_GSIZE_FORMAT " B to %" G_GSIZE_FORMAT,
          map_info.size, written);
      gst_buffer_set_size (buffer, written);
      break;
    case GST_FLOW_EOS:
      gst_buffer_set_size (buffer, written);
      GST_DEBUG_OBJECT (self, "Peer closed the connection");
      break;
    case GST_FLOW_ERROR:
      GST_ERROR_OBJECT (self, "Error processing buffer: %s", err->message);
      GST_ELEMENT_ERROR (self, RESOURCE, READ, (NULL), ("%s", err->message));
      g_clear_error (&err);
      break;
    default:
      g_assert_not_reached ();
  }
  g_assert (err == NULL);
496

497
  return flow_ret;
498 499
}

500 501 502 503 504 505 506
typedef struct
{
  GstDtlsDec *self;
  GstFlowReturn flow_ret;
  guint processed;
} ProcessListData;

507 508 509
static gboolean
process_buffer_from_list (GstBuffer ** buffer, guint idx, gpointer user_data)
{
510 511 512
  ProcessListData *process_list_data = user_data;
  GstDtlsDec *self = GST_DTLS_DEC (process_list_data->self);
  GstFlowReturn flow_ret;
513 514

  *buffer = gst_buffer_make_writable (*buffer);
515 516 517 518
  flow_ret = process_buffer (self, *buffer);

  process_list_data->flow_ret = flow_ret;
  if (gst_buffer_get_size (*buffer) == 0)
519
    gst_buffer_replace (buffer, NULL);
520 521
  else if (flow_ret != GST_FLOW_ERROR)
    process_list_data->processed++;
522

523
  return flow_ret == GST_FLOW_OK;
524 525 526 527 528 529
}

static GstFlowReturn
sink_chain_list (GstPad * pad, GstObject * parent, GstBufferList * list)
{
  GstDtlsDec *self = GST_DTLS_DEC (parent);
Sebastian Dröge's avatar
Sebastian Dröge committed
530
  GstPad *other_pad;
531
  ProcessListData process_list_data = { self, GST_FLOW_OK, 0 };
532 533

  list = gst_buffer_list_make_writable (list);
534 535 536 537 538 539 540 541 542 543 544 545 546 547
  gst_buffer_list_foreach (list, process_buffer_from_list, &process_list_data);

  /* If we successfully processed at least some buffers then forward those */
  if (process_list_data.flow_ret != GST_FLOW_OK
      && process_list_data.processed == 0) {
    GST_ERROR_OBJECT (self, "Failed to process buffer list: %s",
        gst_flow_get_name (process_list_data.flow_ret));
    gst_buffer_list_unref (list);
    return process_list_data.flow_ret;
  }

  /* Remove all buffers after the first one that failed to be processed */
  gst_buffer_list_remove (list, process_list_data.processed,
      gst_buffer_list_length (list) - process_list_data.processed);
548 549 550 551 552

  if (gst_buffer_list_length (list) == 0) {
    GST_DEBUG_OBJECT (self, "Not produced any buffers");
    gst_buffer_list_unref (list);

553
    return process_list_data.flow_ret;
554 555 556
  }

  g_mutex_lock (&self->src_mutex);
Sebastian Dröge's avatar
Sebastian Dröge committed
557 558 559 560
  other_pad = self->src;
  if (other_pad)
    gst_object_ref (other_pad);
  g_mutex_unlock (&self->src_mutex);
561

Sebastian Dröge's avatar
Sebastian Dröge committed
562
  if (other_pad) {
563 564 565
    gboolean was_eos = process_list_data.flow_ret == GST_FLOW_EOS;

    GST_LOG_OBJECT (self, "pushing buffer list with length %u",
566
        gst_buffer_list_length (list));
567 568 569 570 571 572
    process_list_data.flow_ret = gst_pad_push_list (other_pad, list);

    /* If the peer closed the connection, signal that we're done here now */
    if (was_eos)
      gst_pad_push_event (other_pad, gst_event_new_eos ());

Sebastian Dröge's avatar
Sebastian Dröge committed
573
    gst_object_unref (other_pad);
574
  } else {
575 576
    GST_LOG_OBJECT (self,
        "dropping buffer list with length %d, have no source pad",
577 578 579 580
        gst_buffer_list_length (list));
    gst_buffer_list_unref (list);
  }

581
  return process_list_data.flow_ret;
582 583
}

Sebastian Dröge's avatar
Sebastian Dröge committed
584 585
static GstFlowReturn
sink_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
Sebastian Dröge's avatar
Sebastian Dröge committed
586
{
587
  GstDtlsDec *self = GST_DTLS_DEC (parent);
Sebastian Dröge's avatar
Sebastian Dröge committed
588
  GstFlowReturn ret = GST_FLOW_OK;
Sebastian Dröge's avatar
Sebastian Dröge committed
589
  GstPad *other_pad;
Sebastian Dröge's avatar
Sebastian Dröge committed
590

Sebastian Dröge's avatar
Sebastian Dröge committed
591 592 593 594
  if (!self->agent) {
    gst_buffer_unref (buffer);
    return GST_FLOW_OK;
  }
Sebastian Dröge's avatar
Sebastian Dröge committed
595

Tim-Philipp Müller's avatar
Tim-Philipp Müller committed
596 597
  GST_DEBUG_OBJECT (self,
      "received buffer from %s with length %" G_GSIZE_FORMAT,
Sebastian Dröge's avatar
Sebastian Dröge committed
598
      self->connection_id, gst_buffer_get_size (buffer));
Sebastian Dröge's avatar
Sebastian Dröge committed
599

600
  buffer = gst_buffer_make_writable (buffer);
601 602 603 604
  ret = process_buffer (self, buffer);
  if (ret == GST_FLOW_ERROR) {
    GST_ERROR_OBJECT (self, "Failed to process buffer: %s",
        gst_flow_get_name (ret));
Sebastian Dröge's avatar
Sebastian Dröge committed
605
    gst_buffer_unref (buffer);
606
    return ret;
Sebastian Dröge's avatar
Sebastian Dröge committed
607
  }
Sebastian Dröge's avatar
Sebastian Dröge committed
608

Sebastian Dröge's avatar
Sebastian Dröge committed
609
  g_mutex_lock (&self->src_mutex);
Sebastian Dröge's avatar
Sebastian Dröge committed
610 611 612 613
  other_pad = self->src;
  if (other_pad)
    gst_object_ref (other_pad);
  g_mutex_unlock (&self->src_mutex);
Sebastian Dröge's avatar
Sebastian Dröge committed
614

Sebastian Dröge's avatar
Sebastian Dröge committed
615
  if (other_pad) {
616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631
    gboolean was_eos = (ret == GST_FLOW_EOS);

    if (gst_buffer_get_size (buffer) > 0) {
      GST_LOG_OBJECT (self, "pushing buffer");
      ret = gst_pad_push (other_pad, buffer);
    } else {
      gst_buffer_unref (buffer);
    }

    /* If the peer closed the connection, signal that we're done here now */
    if (was_eos) {
      gst_pad_push_event (other_pad, gst_event_new_eos ());
      if (ret == GST_FLOW_OK)
        ret = GST_FLOW_EOS;
    }

Sebastian Dröge's avatar
Sebastian Dröge committed
632
    gst_object_unref (other_pad);
Sebastian Dröge's avatar
Sebastian Dröge committed
633
  } else {
634
    GST_LOG_OBJECT (self, "dropping buffer, have no source pad");
Sebastian Dröge's avatar
Sebastian Dröge committed
635 636
    gst_buffer_unref (buffer);
  }
Sebastian Dröge's avatar
Sebastian Dröge committed
637

Sebastian Dröge's avatar
Sebastian Dröge committed
638
  return ret;
Sebastian Dröge's avatar
Sebastian Dröge committed
639 640 641
}

static GHashTable *agent_table = NULL;
Sebastian Dröge's avatar
Sebastian Dröge committed
642
G_LOCK_DEFINE_STATIC (agent_table);
Sebastian Dröge's avatar
Sebastian Dröge committed
643

644
static GstDtlsAgent *generated_cert_agent = NULL;
Sebastian Dröge's avatar
Sebastian Dröge committed
645

646
static GstDtlsAgent *
Sebastian Dröge's avatar
Sebastian Dröge committed
647
get_agent_by_pem (const gchar * pem)
Sebastian Dröge's avatar
Sebastian Dröge committed
648
{
649
  GstDtlsAgent *agent;
Sebastian Dröge's avatar
Sebastian Dröge committed
650

Sebastian Dröge's avatar
Sebastian Dröge committed
651 652
  if (!pem) {
    if (g_once_init_enter (&generated_cert_agent)) {
653
      GstDtlsAgent *new_agent;
654
      GObject *certificate;
Sebastian Dröge's avatar
Sebastian Dröge committed
655

656
      certificate = g_object_new (GST_TYPE_DTLS_CERTIFICATE, NULL);
657
      new_agent = g_object_new (GST_TYPE_DTLS_AGENT, "certificate",
658 659
          certificate, NULL);
      g_object_unref (certificate);
Sebastian Dröge's avatar
Sebastian Dröge committed
660

Sebastian Dröge's avatar
Sebastian Dröge committed
661 662 663
      GST_DEBUG_OBJECT (generated_cert_agent,
          "no agent with generated cert found, creating new");
      g_once_init_leave (&generated_cert_agent, new_agent);
Sebastian Dröge's avatar
Sebastian Dröge committed
664
    } else {
Sebastian Dröge's avatar
Sebastian Dröge committed
665 666 667
      GST_DEBUG_OBJECT (generated_cert_agent,
          "using agent with generated cert");
    }
Sebastian Dröge's avatar
Sebastian Dröge committed
668

Sebastian Dröge's avatar
Sebastian Dröge committed
669
    agent = generated_cert_agent;
670
    g_object_ref (agent);
Sebastian Dröge's avatar
Sebastian Dröge committed
671 672
  } else {
    G_LOCK (agent_table);
Sebastian Dröge's avatar
Sebastian Dröge committed
673

Sebastian Dröge's avatar
Sebastian Dröge committed
674 675 676 677
    if (!agent_table) {
      agent_table =
          g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
    }
Sebastian Dröge's avatar
Sebastian Dröge committed
678

679
    agent = GST_DTLS_AGENT (g_hash_table_lookup (agent_table, pem));
Sebastian Dröge's avatar
Sebastian Dröge committed
680

Sebastian Dröge's avatar
Sebastian Dröge committed
681
    if (!agent) {
682 683 684 685 686 687
      GObject *certificate;

      certificate = g_object_new (GST_TYPE_DTLS_CERTIFICATE, "pem", pem, NULL);
      agent = g_object_new (GST_TYPE_DTLS_AGENT, "certificate", certificate,
          NULL);
      g_object_unref (certificate);
Sebastian Dröge's avatar
Sebastian Dröge committed
688

Sebastian Dröge's avatar
Sebastian Dröge committed
689 690
      g_object_weak_ref (G_OBJECT (agent), (GWeakNotify) agent_weak_ref_notify,
          (gpointer) g_strdup (pem));
Sebastian Dröge's avatar
Sebastian Dröge committed
691

Sebastian Dröge's avatar
Sebastian Dröge committed
692
      g_hash_table_insert (agent_table, g_strdup (pem), agent);
Sebastian Dröge's avatar
Sebastian Dröge committed
693

Sebastian Dröge's avatar
Sebastian Dröge committed
694 695 696 697
      GST_DEBUG_OBJECT (agent, "no agent found, created new");
    } else {
      g_object_ref (agent);
      GST_DEBUG_OBJECT (agent, "agent found");
Sebastian Dröge's avatar
Sebastian Dröge committed
698 699
    }

Sebastian Dröge's avatar
Sebastian Dröge committed
700 701 702
    G_UNLOCK (agent_table);
  }

Sebastian Dröge's avatar
Sebastian Dröge committed
703

Sebastian Dröge's avatar
Sebastian Dröge committed
704
  return agent;
Sebastian Dröge's avatar
Sebastian Dröge committed
705 706
}

Sebastian Dröge's avatar
Sebastian Dröge committed
707
static void
708
agent_weak_ref_notify (gchar * pem, GstDtlsAgent * agent)
Sebastian Dröge's avatar
Sebastian Dröge committed
709
{
Sebastian Dröge's avatar
Sebastian Dröge committed
710 711 712
  G_LOCK (agent_table);
  g_hash_table_remove (agent_table, pem);
  G_UNLOCK (agent_table);
Sebastian Dröge's avatar
Sebastian Dröge committed
713

Sebastian Dröge's avatar
Sebastian Dröge committed
714 715
  g_free (pem);
  pem = NULL;
Sebastian Dröge's avatar
Sebastian Dröge committed
716 717 718
}

static GHashTable *connection_table = NULL;
Sebastian Dröge's avatar
Sebastian Dröge committed
719
G_LOCK_DEFINE_STATIC (connection_table);
Sebastian Dröge's avatar
Sebastian Dröge committed
720

721 722
GstDtlsConnection *
gst_dtls_dec_fetch_connection (gchar * id)
Sebastian Dröge's avatar
Sebastian Dröge committed
723
{
724
  GstDtlsConnection *connection;
Sebastian Dröge's avatar
Sebastian Dröge committed
725
  g_return_val_if_fail (id, NULL);
Sebastian Dröge's avatar
Sebastian Dröge committed
726

Sebastian Dröge's avatar
Sebastian Dröge committed
727 728
  GST_DEBUG ("fetching '%s' from connection table, size is %d",
      id, g_hash_table_size (connection_table));
Sebastian Dröge's avatar
Sebastian Dröge committed
729

Sebastian Dröge's avatar
Sebastian Dröge committed
730
  G_LOCK (connection_table);
Sebastian Dröge's avatar
Sebastian Dröge committed
731

Sebastian Dröge's avatar
Sebastian Dröge committed
732
  connection = g_hash_table_lookup (connection_table, id);
Sebastian Dröge's avatar
Sebastian Dröge committed
733

Sebastian Dröge's avatar
Sebastian Dröge committed
734 735 736 737 738 739
  if (connection) {
    g_object_ref (connection);
    g_hash_table_remove (connection_table, id);
  } else {
    GST_WARNING ("no connection with id '%s' found", id);
  }
Sebastian Dröge's avatar
Sebastian Dröge committed
740

Sebastian Dröge's avatar
Sebastian Dröge committed
741
  G_UNLOCK (connection_table);
Sebastian Dröge's avatar
Sebastian Dröge committed
742

Sebastian Dröge's avatar
Sebastian Dröge committed
743
  return connection;
Sebastian Dröge's avatar
Sebastian Dröge committed
744 745
}

Sebastian Dröge's avatar
Sebastian Dröge committed
746
static void
747
create_connection (GstDtlsDec * self, gchar * id)
Sebastian Dröge's avatar
Sebastian Dröge committed
748
{
749 750
  g_return_if_fail (GST_IS_DTLS_DEC (self));
  g_return_if_fail (GST_IS_DTLS_AGENT (self->agent));
Sebastian Dröge's avatar
Sebastian Dröge committed
751

Sebastian Dröge's avatar
Sebastian Dröge committed
752 753 754 755
  if (self->connection) {
    g_object_unref (self->connection);
    self->connection = NULL;
  }
Sebastian Dröge's avatar
Sebastian Dröge committed
756

Sebastian Dröge's avatar
Sebastian Dröge committed
757
  G_LOCK (connection_table);
Sebastian Dröge's avatar
Sebastian Dröge committed
758

Sebastian Dröge's avatar
Sebastian Dröge committed
759 760 761 762
  if (!connection_table) {
    connection_table =
        g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
  }
Sebastian Dröge's avatar
Sebastian Dröge committed
763

Sebastian Dröge's avatar
Sebastian Dröge committed
764 765
  if (g_hash_table_contains (connection_table, id)) {
    G_UNLOCK (connection_table);
Sebastian Dröge's avatar
Sebastian Dröge committed
766

Sebastian Dröge's avatar
Sebastian Dröge committed
767 768
    g_return_if_reached ();
  }
Sebastian Dröge's avatar
Sebastian Dröge committed
769

Sebastian Dröge's avatar
Sebastian Dröge committed
770
  self->connection =
771
      g_object_new (GST_TYPE_DTLS_CONNECTION, "agent", self->agent, NULL);
Sebastian Dröge's avatar
Sebastian Dröge committed
772

Sebastian Dröge's avatar
Sebastian Dröge committed
773 774
  g_object_weak_ref (G_OBJECT (self->connection),
      (GWeakNotify) connection_weak_ref_notify, g_strdup (id));
Sebastian Dröge's avatar
Sebastian Dröge committed
775

Sebastian Dröge's avatar
Sebastian Dröge committed
776
  g_hash_table_insert (connection_table, g_strdup (id), self->connection);
Sebastian Dröge's avatar
Sebastian Dröge committed
777

Sebastian Dröge's avatar
Sebastian Dröge committed
778
  G_UNLOCK (connection_table);
Sebastian Dröge's avatar
Sebastian Dröge committed
779 780
}

Sebastian Dröge's avatar
Sebastian Dröge committed
781
static void
782
connection_weak_ref_notify (gchar * id, GstDtlsConnection * connection)
Sebastian Dröge's avatar
Sebastian Dröge committed
783
{
Sebastian Dröge's avatar
Sebastian Dröge committed
784 785 786
  G_LOCK (connection_table);
  g_hash_table_remove (connection_table, id);
  G_UNLOCK (connection_table);
Sebastian Dröge's avatar
Sebastian Dröge committed
787

Sebastian Dröge's avatar
Sebastian Dröge committed
788 789
  g_free (id);
  id = NULL;
Sebastian Dröge's avatar
Sebastian Dröge committed
790
}